6313595be1d5042a105a2e4bd93955378fc0aa19
[strongswan.git] / src / libradius / radius_socket.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "radius_socket.h"
17
18 #include <errno.h>
19 #include <unistd.h>
20
21 #include <pen/pen.h>
22 #include <debug.h>
23
24 /**
25 * Microsoft specific vendor attributes
26 */
27 #define MS_MPPE_SEND_KEY 16
28 #define MS_MPPE_RECV_KEY 17
29
30 typedef struct private_radius_socket_t private_radius_socket_t;
31
32 /**
33 * Private data of an radius_socket_t object.
34 */
35 struct private_radius_socket_t {
36
37 /**
38 * Public radius_socket_t interface.
39 */
40 radius_socket_t public;
41
42 /**
43 * Server port for authentication
44 */
45 u_int16_t auth_port;
46
47 /**
48 * socket file descriptor for authentication
49 */
50 int auth_fd;
51
52 /**
53 * Server port for accounting
54 */
55 u_int16_t acct_port;
56
57 /**
58 * socket file descriptor for accounting
59 */
60 int acct_fd;
61
62 /**
63 * Server address
64 */
65 char *address;
66
67 /**
68 * current RADIUS identifier
69 */
70 u_int8_t identifier;
71
72 /**
73 * hasher to use for response verification
74 */
75 hasher_t *hasher;
76
77 /**
78 * HMAC-MD5 signer to build Message-Authenticator attribute
79 */
80 signer_t *signer;
81
82 /**
83 * random number generator for RADIUS request authenticator
84 */
85 rng_t *rng;
86
87 /**
88 * RADIUS secret
89 */
90 chunk_t secret;
91 };
92
93 /**
94 * Check or establish RADIUS connection
95 */
96 static bool check_connection(private_radius_socket_t *this,
97 int *fd, u_int16_t port)
98 {
99 if (*fd == -1)
100 {
101 host_t *server;
102
103 server = host_create_from_dns(this->address, AF_UNSPEC, port);
104 if (!server)
105 {
106 DBG1(DBG_CFG, "resolving RADIUS server address '%s' failed",
107 this->address);
108 return FALSE;
109 }
110 *fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP);
111 if (*fd == -1)
112 {
113 DBG1(DBG_CFG, "opening RADIUS socket for %#H failed: %s",
114 server, strerror(errno));
115 server->destroy(server);
116 return FALSE;
117 }
118 if (connect(*fd, server->get_sockaddr(server),
119 *server->get_sockaddr_len(server)) < 0)
120 {
121 DBG1(DBG_CFG, "connecting RADIUS socket to %#H failed: %s",
122 server, strerror(errno));
123 server->destroy(server);
124 close(*fd);
125 *fd = -1;
126 return FALSE;
127 }
128 server->destroy(server);
129 }
130 return TRUE;
131 }
132
133 METHOD(radius_socket_t, request, radius_message_t*,
134 private_radius_socket_t *this, radius_message_t *request)
135 {
136 chunk_t data;
137 int i, *fd;
138 u_int16_t port;
139 rng_t *rng = NULL;
140
141 if (request->get_code(request) == RMC_ACCOUNTING_REQUEST)
142 {
143 fd = &this->acct_fd;
144 port = this->acct_port;
145 }
146 else
147 {
148 fd = &this->auth_fd;
149 port = this->auth_port;
150 rng = this->rng;
151 }
152
153 /* set Message Identifier */
154 request->set_identifier(request, this->identifier++);
155 /* sign the request */
156 request->sign(request, NULL, this->secret, this->hasher, this->signer,
157 rng, rng != NULL);
158
159 if (!check_connection(this, fd, port))
160 {
161 return NULL;
162 }
163
164 data = request->get_encoding(request);
165 DBG3(DBG_CFG, "%B", &data);
166
167 /* timeout after 2, 3, 4, 5 seconds */
168 for (i = 2; i <= 5; i++)
169 {
170 radius_message_t *response;
171 bool retransmit = FALSE;
172 struct timeval tv;
173 char buf[4096];
174 fd_set fds;
175 int res;
176
177 if (send(*fd, data.ptr, data.len, 0) != data.len)
178 {
179 DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
180 return NULL;
181 }
182 tv.tv_sec = i;
183 tv.tv_usec = 0;
184
185 while (TRUE)
186 {
187 FD_ZERO(&fds);
188 FD_SET(*fd, &fds);
189 res = select((*fd) + 1, &fds, NULL, NULL, &tv);
190 /* TODO: updated tv to time not waited. Linux does this for us. */
191 if (res < 0)
192 { /* failed */
193 DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
194 strerror(errno));
195 break;
196 }
197 if (res == 0)
198 { /* timeout */
199 DBG1(DBG_CFG, "retransmitting RADIUS message");
200 retransmit = TRUE;
201 break;
202 }
203 res = recv(*fd, buf, sizeof(buf), MSG_DONTWAIT);
204 if (res <= 0)
205 {
206 DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
207 strerror(errno));
208 break;
209 }
210 response = radius_message_parse(chunk_create(buf, res));
211 if (response)
212 {
213 if (response->verify(response,
214 request->get_authenticator(request), this->secret,
215 this->hasher, this->signer))
216 {
217 return response;
218 }
219 response->destroy(response);
220 }
221 DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
222 }
223 if (!retransmit)
224 {
225 break;
226 }
227 }
228 DBG1(DBG_CFG, "RADIUS server is not responding");
229 return NULL;
230 }
231
232 /**
233 * Decrypt a MS-MPPE-Send/Recv-Key
234 */
235 static chunk_t decrypt_mppe_key(private_radius_socket_t *this, u_int16_t salt,
236 chunk_t C, radius_message_t *request)
237 {
238 chunk_t A, R, P, seed;
239 u_char *c, *p;
240
241 /**
242 * From RFC2548 (encryption):
243 * b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1)
244 * b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2)
245 * . . .
246 * b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i)
247 */
248
249 if (C.len % HASH_SIZE_MD5 || C.len < HASH_SIZE_MD5)
250 {
251 return chunk_empty;
252 }
253
254 A = chunk_create((u_char*)&salt, sizeof(salt));
255 R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
256 P = chunk_alloca(C.len);
257 p = P.ptr;
258 c = C.ptr;
259
260 seed = chunk_cata("cc", R, A);
261
262 while (c < C.ptr + C.len)
263 {
264 /* b(i) = MD5(S + c(i-1)) */
265 this->hasher->get_hash(this->hasher, this->secret, NULL);
266 this->hasher->get_hash(this->hasher, seed, p);
267
268 /* p(i) = b(i) xor c(1) */
269 memxor(p, c, HASH_SIZE_MD5);
270
271 /* prepare next round */
272 seed = chunk_create(c, HASH_SIZE_MD5);
273 c += HASH_SIZE_MD5;
274 p += HASH_SIZE_MD5;
275 }
276
277 /* remove truncation, first byte is key length */
278 if (*P.ptr >= P.len)
279 { /* decryption failed? */
280 return chunk_empty;
281 }
282 return chunk_clone(chunk_create(P.ptr + 1, *P.ptr));
283 }
284
285 METHOD(radius_socket_t, decrypt_msk, chunk_t,
286 private_radius_socket_t *this, radius_message_t *request,
287 radius_message_t *response)
288 {
289 struct {
290 u_int32_t id;
291 u_int8_t type;
292 u_int8_t length;
293 u_int16_t salt;
294 u_int8_t key[];
295 } __attribute__((packed)) *mppe_key;
296 enumerator_t *enumerator;
297 chunk_t data, send = chunk_empty, recv = chunk_empty;
298 int type;
299
300 enumerator = response->create_enumerator(response);
301 while (enumerator->enumerate(enumerator, &type, &data))
302 {
303 if (type == RAT_VENDOR_SPECIFIC &&
304 data.len > sizeof(*mppe_key))
305 {
306 mppe_key = (void*)data.ptr;
307 if (ntohl(mppe_key->id) == PEN_MICROSOFT &&
308 mppe_key->length == data.len - sizeof(mppe_key->id))
309 {
310 data = chunk_create(mppe_key->key, data.len - sizeof(*mppe_key));
311 if (mppe_key->type == MS_MPPE_SEND_KEY)
312 {
313 send = decrypt_mppe_key(this, mppe_key->salt, data, request);
314 }
315 if (mppe_key->type == MS_MPPE_RECV_KEY)
316 {
317 recv = decrypt_mppe_key(this, mppe_key->salt, data, request);
318 }
319 }
320 }
321 }
322 enumerator->destroy(enumerator);
323 if (send.ptr && recv.ptr)
324 {
325 return chunk_cat("mm", recv, send);
326 }
327 chunk_clear(&send);
328 chunk_clear(&recv);
329 return chunk_empty;
330 }
331
332 METHOD(radius_socket_t, destroy, void,
333 private_radius_socket_t *this)
334 {
335 DESTROY_IF(this->hasher);
336 DESTROY_IF(this->signer);
337 DESTROY_IF(this->rng);
338 if (this->auth_fd != -1)
339 {
340 close(this->auth_fd);
341 };
342 if (this->acct_fd != -1)
343 {
344 close(this->acct_fd);
345 }
346 free(this);
347 }
348
349 /**
350 * See header
351 */
352 radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
353 u_int16_t acct_port, chunk_t secret)
354 {
355 private_radius_socket_t *this;
356
357 INIT(this,
358 .public = {
359 .request = _request,
360 .decrypt_msk = _decrypt_msk,
361 .destroy = _destroy,
362 },
363 .address = address,
364 .auth_port = auth_port,
365 .auth_fd = -1,
366 .acct_port = acct_port,
367 .acct_fd = -1,
368 );
369
370 this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
371 this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
372 this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
373 if (!this->hasher || !this->signer || !this->rng)
374 {
375 DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
376 destroy(this);
377 return NULL;
378 }
379 this->secret = secret;
380 this->signer->set_key(this->signer, secret);
381 /* we use a random identifier, helps if we restart often */
382 this->identifier = random();
383
384 return &this->public;
385 }