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