1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2020 - 2022 */
6 /* Copyright (c) University of Cambridge 1995 - 2016 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
10 /* This file was originally supplied by Ian Kirk. The libradius support came
15 /* This module contains functions that call the Radius authentication
18 We can't just compile this code and allow the library mechanism to omit the
19 functions if they are not wanted, because we need to have the Radius headers
20 available for compiling. Therefore, compile these functions only if
21 RADIUS_CONFIG_FILE is defined. However, some compilers don't like compiling
22 empty modules, so keep them happy with a dummy when skipping the rest. Make it
23 reference itself to stop picky compilers complaining that it is unused, and put
24 in a dummy argument to stop even pickier compilers complaining about infinite
25 loops. Then use a mutually-recursive pair as gcc is just getting stupid. */
27 #ifndef RADIUS_CONFIG_FILE
28 static void dummy(int x);
29 static void dummy2(int x) { dummy(x-1); }
30 static void dummy(int x) { dummy2(x-1); }
31 #else /* RADIUS_CONFIG_FILE */
34 /* Two different Radius libraries are supported. The default is radiusclient,
35 using its original API. At release 0.4.0 the API changed. */
37 #ifdef RADIUS_LIB_RADLIB
40 # if !defined(RADIUS_LIB_RADIUSCLIENT) && !defined(RADIUS_LIB_RADIUSCLIENTNEW)
41 # define RADIUS_LIB_RADIUSCLIENT
44 # ifdef RADIUS_LIB_RADIUSCLIENTNEW
45 # define ENV FREERADIUSCLIENT_ENV /* Avoid clash with Berkeley DB */
46 # include <freeradius-client.h>
48 # include <radiusclient.h>
54 /*************************************************
55 * Perform RADIUS authentication *
56 *************************************************/
58 /* This function calls the Radius authentication mechanism, passing over one or
62 s a colon-separated list of strings
63 errptr where to point an error message
65 Returns: OK if authentication succeeded
66 FAIL if authentication failed
67 ERROR some other error condition
71 auth_call_radius(const uschar *s, uschar **errptr)
74 const uschar *radius_args = s;
78 #ifdef RADIUS_LIB_RADLIB
81 #ifdef RADIUS_LIB_RADIUSCLIENTNEW
84 VALUE_PAIR *send = NULL;
86 unsigned int service = PW_AUTHENTICATE_ONLY;
91 if (!(user = string_nextinlist(&radius_args, &sep, NULL, 0))) user = US"";
93 DEBUG(D_auth) debug_printf("Running RADIUS authentication for user \"%s\" "
94 "and \"%s\"\n", user, radius_args);
99 /* Authenticate using the radiusclient library */
101 #ifndef RADIUS_LIB_RADLIB
105 #ifdef RADIUS_LIB_RADIUSCLIENT
106 if (rc_read_config(RADIUS_CONFIG_FILE) != 0)
107 *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE);
109 else if (rc_read_dictionary(rc_conf_str("dictionary")) != 0)
110 *errptr = US"RADIUS: can't read dictionary";
112 else if (!rc_avpair_add(&send, PW_USER_NAME, user, 0))
113 *errptr = US"RADIUS: add user name failed";
115 else if (!rc_avpair_add(&send, PW_USER_PASSWORD, CS radius_args, 0))
116 *errptr = US"RADIUS: add password failed");
118 else if (!rc_avpair_add(&send, PW_SERVICE_TYPE, &service, 0))
119 *errptr = US"RADIUS: add service type failed";
121 #else /* RADIUS_LIB_RADIUSCLIENT unset => RADIUS_LIB_RADIUSCLIENT2 */
123 if (!(h = rc_read_config(RADIUS_CONFIG_FILE)))
124 *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE);
126 else if (rc_read_dictionary(h, rc_conf_str(h, "dictionary")) != 0)
127 *errptr = US"RADIUS: can't read dictionary";
129 else if (!rc_avpair_add(h, &send, PW_USER_NAME, user, Ustrlen(user), 0))
130 *errptr = US"RADIUS: add user name failed";
132 else if (!rc_avpair_add(h, &send, PW_USER_PASSWORD, CS radius_args,
133 Ustrlen(radius_args), 0))
134 *errptr = US"RADIUS: add password failed";
136 else if (!rc_avpair_add(h, &send, PW_SERVICE_TYPE, &service, 0, 0))
137 *errptr = US"RADIUS: add service type failed";
139 #endif /* RADIUS_LIB_RADIUSCLIENT */
143 DEBUG(D_auth) debug_printf("%s\n", *errptr);
147 #ifdef RADIUS_LIB_RADIUSCLIENT
148 result = rc_auth(0, send, &received, msg);
150 result = rc_auth(h, 0, send, &received, msg);
153 DEBUG(D_auth) debug_printf("RADIUS code returned %d\n", result);
165 *errptr = US"RADIUS: timed out";
170 *errptr = string_sprintf("RADIUS: unexpected response (%d)", result);
174 #else /* RADIUS_LIB_RADLIB is set */
176 /* Authenticate using the libradius library */
178 if (!(h = rad_auth_open()))
180 *errptr = string_sprintf("RADIUS: can't initialise libradius");
183 if (rad_config(h, RADIUS_CONFIG_FILE) != 0 ||
184 rad_create_request(h, RAD_ACCESS_REQUEST) != 0 ||
185 rad_put_string(h, RAD_USER_NAME, CS user) != 0 ||
186 rad_put_string(h, RAD_USER_PASSWORD, CS radius_args) != 0 ||
187 rad_put_int(h, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) != 0 ||
188 rad_put_string(h, RAD_NAS_IDENTIFIER, CS primary_hostname) != 0)
190 *errptr = string_sprintf("RADIUS: %s", rad_strerror(h));
194 switch (result = rad_send_request(h))
196 case RAD_ACCESS_ACCEPT:
200 case RAD_ACCESS_REJECT:
205 *errptr = string_sprintf("RADIUS: %s", rad_strerror(h));
210 *errptr = string_sprintf("RADIUS: unexpected response (%d)", result);
215 if (*errptr) DEBUG(D_auth) debug_printf("%s\n", *errptr);
219 #endif /* RADIUS_LIB_RADLIB */
224 /******************************************************************************/
227 static void * rad_functions[] = {
228 [RADIUS_AUTH_CALL] = auth_call_radius,
231 misc_module_info radius_module_info =
235 .dyn_magic = MISC_MODULE_MAGIC,
238 .functions = rad_functions,
239 .functions_count = nelem(rad_functions),
242 #endif /* RADIUS_CONFIG_FILE */
244 /* End of call_radius.c */