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