cc269dcd5521bbf7f825287cd950e2ada38e1863
[users/heiko/exim.git] / src / src / auths / call_radius.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 */
6 /* Copyright (c) University of Cambridge 1995 - 2016 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9 /* This file was originally supplied by Ian Kirk. The libradius support came
10 from Alex Kiernan. */
11
12 /* ugly hack to work around redefinition of ENV by radiusclient.h and
13  * db.h: define _DB_H_ so the db.h include thinks it's already included,
14  * we can get away with it like this, since this file doesn't use any db
15  * functions. */
16 #ifndef _DB_H_
17 # define _DB_H_ 1
18 # define _DB_EXT_PROT_IN_ 1
19 # define DB void
20 #endif
21
22 #include "../exim.h"
23
24 /* This module contains functions that call the Radius authentication
25 mechanism.
26
27 We can't just compile this code and allow the library mechanism to omit the
28 functions if they are not wanted, because we need to have the Radius headers
29 available for compiling. Therefore, compile these functions only if
30 RADIUS_CONFIG_FILE is defined. However, some compilers don't like compiling
31 empty modules, so keep them happy with a dummy when skipping the rest. Make it
32 reference itself to stop picky compilers complaining that it is unused, and put
33 in a dummy argument to stop even pickier compilers complaining about infinite
34 loops. Then use a mutually-recursive pair as gcc is just getting stupid. */
35
36 #ifndef RADIUS_CONFIG_FILE
37 static void dummy(int x);
38 static void dummy2(int x) { dummy(x-1); }
39 static void dummy(int x) { dummy2(x-1); }
40 #else  /* RADIUS_CONFIG_FILE */
41
42
43 /* Two different Radius libraries are supported. The default is radiusclient,
44 using its original API. At release 0.4.0 the API changed. */
45
46 #ifdef RADIUS_LIB_RADLIB
47   #include <radlib.h>
48 #else
49   #if !defined(RADIUS_LIB_RADIUSCLIENT) && !defined(RADIUS_LIB_RADIUSCLIENTNEW)
50   # define RADIUS_LIB_RADIUSCLIENT
51   #endif
52
53   #ifdef RADIUS_LIB_RADIUSCLIENTNEW
54   # include <freeradius-client.h>
55   #else
56   # include <radiusclient.h>
57   #endif
58 #endif
59
60
61
62 /*************************************************
63 *              Perform RADIUS authentication     *
64 *************************************************/
65
66 /* This function calls the Radius authentication mechanism, passing over one or
67 more data strings.
68
69 Arguments:
70   s        a colon-separated list of strings
71   errptr   where to point an error message
72
73 Returns:   OK if authentication succeeded
74            FAIL if authentication failed
75            ERROR some other error condition
76 */
77
78 int
79 auth_call_radius(const uschar *s, uschar **errptr)
80 {
81 uschar *user;
82 const uschar *radius_args = s;
83 int result;
84 int sep = 0;
85
86 #ifdef RADIUS_LIB_RADLIB
87   struct rad_handle *h;
88 #else
89   #ifdef RADIUS_LIB_RADIUSCLIENTNEW
90     rc_handle *h;
91   #endif
92   VALUE_PAIR *send = NULL;
93   VALUE_PAIR *received;
94   unsigned int service = PW_AUTHENTICATE_ONLY;
95   char msg[4096];
96 #endif
97
98
99 user = string_nextinlist(&radius_args, &sep, big_buffer, big_buffer_size);
100 if (!user) user = US"";
101
102 DEBUG(D_auth) debug_printf("Running RADIUS authentication for user \"%s\" "
103                "and \"%s\"\n", user, radius_args);
104
105 *errptr = NULL;
106
107
108 /* Authenticate using the radiusclient library */
109
110 #ifndef RADIUS_LIB_RADLIB
111
112 rc_openlog("exim");
113
114 #ifdef RADIUS_LIB_RADIUSCLIENT
115 if (rc_read_config(RADIUS_CONFIG_FILE) != 0)
116   *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE);
117
118 else if (rc_read_dictionary(rc_conf_str("dictionary")) != 0)
119   *errptr = US"RADIUS: can't read dictionary";
120
121 else if (!rc_avpair_add(&send, PW_USER_NAME, user, 0))
122   *errptr = US"RADIUS: add user name failed";
123
124 else if (!rc_avpair_add(&send, PW_USER_PASSWORD, CS radius_args, 0))
125   *errptr = US"RADIUS: add password failed");
126
127 else if (!rc_avpair_add(&send, PW_SERVICE_TYPE, &service, 0))
128   *errptr = US"RADIUS: add service type failed";
129
130 #else  /* RADIUS_LIB_RADIUSCLIENT unset => RADIUS_LIB_RADIUSCLIENT2 */
131
132 if (!(h = rc_read_config(RADIUS_CONFIG_FILE)))
133   *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE);
134
135 else if (rc_read_dictionary(h, rc_conf_str(h, "dictionary")) != 0)
136   *errptr = US"RADIUS: can't read dictionary";
137
138 else if (!rc_avpair_add(h, &send, PW_USER_NAME, user, Ustrlen(user), 0))
139   *errptr = US"RADIUS: add user name failed";
140
141 else if (!rc_avpair_add(h, &send, PW_USER_PASSWORD, CS radius_args,
142     Ustrlen(radius_args), 0))
143   *errptr = US"RADIUS: add password failed";
144
145 else if (!rc_avpair_add(h, &send, PW_SERVICE_TYPE, &service, 0, 0))
146   *errptr = US"RADIUS: add service type failed";
147
148 #endif  /* RADIUS_LIB_RADIUSCLIENT */
149
150 if (*errptr)
151   {
152   DEBUG(D_auth) debug_printf("%s\n", *errptr);
153   return ERROR;
154   }
155
156 #ifdef RADIUS_LIB_RADIUSCLIENT
157 result = rc_auth(0, send, &received, msg);
158 #else
159 result = rc_auth(h, 0, send, &received, msg);
160 #endif
161
162 DEBUG(D_auth) debug_printf("RADIUS code returned %d\n", result);
163
164 switch (result)
165   {
166   case OK_RC:
167     return OK;
168
169   case REJECT_RC:
170   case ERROR_RC:
171     return FAIL;
172
173   case TIMEOUT_RC:
174     *errptr = US"RADIUS: timed out";
175     return ERROR;
176
177   case BADRESP_RC:
178   default:
179     *errptr = string_sprintf("RADIUS: unexpected response (%d)", result);
180     return ERROR;
181   }
182
183 #else  /* RADIUS_LIB_RADLIB is set */
184
185 /* Authenticate using the libradius library */
186
187 if (!(h = rad_auth_open()))
188   {
189   *errptr = string_sprintf("RADIUS: can't initialise libradius");
190   return ERROR;
191   }
192 if (rad_config(h, RADIUS_CONFIG_FILE) != 0 ||
193     rad_create_request(h, RAD_ACCESS_REQUEST) != 0 ||
194     rad_put_string(h, RAD_USER_NAME, CS user) != 0 ||
195     rad_put_string(h, RAD_USER_PASSWORD, CS radius_args) != 0 ||
196     rad_put_int(h, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) != 0 ||
197     rad_put_string(h, RAD_NAS_IDENTIFIER, CS primary_hostname) != 0)
198   {
199   *errptr = string_sprintf("RADIUS: %s", rad_strerror(h));
200   result = ERROR;
201   }
202 else
203   switch (result = rad_send_request(h))
204     {
205     case RAD_ACCESS_ACCEPT:
206       result = OK;
207       break;
208
209     case RAD_ACCESS_REJECT:
210       result = FAIL;
211       break;
212
213     case -1:
214       *errptr = string_sprintf("RADIUS: %s", rad_strerror(h));
215       result = ERROR;
216       break;
217
218     default:
219       *errptr = string_sprintf("RADIUS: unexpected response (%d)", result);
220       result= ERROR;
221       break;
222     }
223
224 if (*errptr) DEBUG(D_auth) debug_printf("%s\n", *errptr);
225 rad_close(h);
226 return result;
227
228 #endif  /* RADIUS_LIB_RADLIB */
229 }
230
231 #endif  /* RADIUS_CONFIG_FILE */
232
233 /* End of call_radius.c */