9d10b34c69da804e330747714ebe98a55d11be1b
[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 if (!(user = string_nextinlist(&radius_args, &sep, NULL, 0))) 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 = US"RADIUS: can't read dictionary";
119
120 else if (!rc_avpair_add(&send, PW_USER_NAME, user, 0))
121   *errptr = US"RADIUS: add user name failed";
122
123 else if (!rc_avpair_add(&send, PW_USER_PASSWORD, CS radius_args, 0))
124   *errptr = US"RADIUS: add password failed");
125
126 else if (!rc_avpair_add(&send, PW_SERVICE_TYPE, &service, 0))
127   *errptr = US"RADIUS: add service type failed";
128
129 #else  /* RADIUS_LIB_RADIUSCLIENT unset => RADIUS_LIB_RADIUSCLIENT2 */
130
131 if (!(h = rc_read_config(RADIUS_CONFIG_FILE)))
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 = US"RADIUS: can't read dictionary";
136
137 else if (!rc_avpair_add(h, &send, PW_USER_NAME, user, Ustrlen(user), 0))
138   *errptr = US"RADIUS: add user name failed";
139
140 else if (!rc_avpair_add(h, &send, PW_USER_PASSWORD, CS radius_args,
141     Ustrlen(radius_args), 0))
142   *errptr = US"RADIUS: add password failed";
143
144 else if (!rc_avpair_add(h, &send, PW_SERVICE_TYPE, &service, 0, 0))
145   *errptr = US"RADIUS: add service type failed";
146
147 #endif  /* RADIUS_LIB_RADIUSCLIENT */
148
149 if (*errptr)
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   case BADRESP_RC:
177   default:
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 if (!(h = rad_auth_open()))
187   {
188   *errptr = string_sprintf("RADIUS: can't initialise libradius");
189   return ERROR;
190   }
191 if (rad_config(h, RADIUS_CONFIG_FILE) != 0 ||
192     rad_create_request(h, RAD_ACCESS_REQUEST) != 0 ||
193     rad_put_string(h, RAD_USER_NAME, CS user) != 0 ||
194     rad_put_string(h, RAD_USER_PASSWORD, CS radius_args) != 0 ||
195     rad_put_int(h, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) != 0 ||
196     rad_put_string(h, RAD_NAS_IDENTIFIER, CS primary_hostname) != 0)
197   {
198   *errptr = string_sprintf("RADIUS: %s", rad_strerror(h));
199   result = ERROR;
200   }
201 else
202   switch (result = rad_send_request(h))
203     {
204     case RAD_ACCESS_ACCEPT:
205       result = OK;
206       break;
207
208     case RAD_ACCESS_REJECT:
209       result = FAIL;
210       break;
211
212     case -1:
213       *errptr = string_sprintf("RADIUS: %s", rad_strerror(h));
214       result = ERROR;
215       break;
216
217     default:
218       *errptr = string_sprintf("RADIUS: unexpected response (%d)", result);
219       result= ERROR;
220       break;
221     }
222
223 if (*errptr) DEBUG(D_auth) debug_printf("%s\n", *errptr);
224 rad_close(h);
225 return result;
226
227 #endif  /* RADIUS_LIB_RADLIB */
228 }
229
230 #endif  /* RADIUS_CONFIG_FILE */
231
232 /* End of call_radius.c */