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