debian experimental exim-daemon-heavy config
[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 */