implemented MS_MPPE encryption
[strongswan.git] / src / libcharon / plugins / tnc_pdp / tnc_pdp.c
1 /*
2 * Copyright (C) 2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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 "tnc_pdp.h"
17 #include "tnc_pdp_connections.h"
18
19 #include <errno.h>
20 #include <unistd.h>
21
22 #include <radius_message.h>
23 #include <radius_mppe.h>
24
25 #include <daemon.h>
26 #include <debug.h>
27 #include <pen/pen.h>
28 #include <threading/thread.h>
29 #include <processing/jobs/callback_job.h>
30 #include <sa/authenticators/eap/eap_method.h>
31
32 typedef struct private_tnc_pdp_t private_tnc_pdp_t;
33
34 /**
35 * Maximum size of a RADIUS IP packet
36 */
37 #define MAX_PACKET 4096
38
39 /**
40 * private data of tnc_pdp_t
41 */
42 struct private_tnc_pdp_t {
43
44 /**
45 * implements tnc_pdp_t interface
46 */
47 tnc_pdp_t public;
48
49 /**
50 * ID of the server
51 */
52 identification_t *server;
53
54 /**
55 * EAP method type to be used
56 */
57 eap_type_t type;
58
59 /**
60 * IPv4 RADIUS socket
61 */
62 int ipv4;
63
64 /**
65 * IPv6 RADIUS socket
66 */
67 int ipv6;
68
69 /**
70 * Callback job dispatching commands
71 */
72 callback_job_t *job;
73
74 /**
75 * RADIUS shared secret
76 */
77 chunk_t secret;
78
79 /**
80 * MD5 hasher
81 */
82 hasher_t *hasher;
83
84 /**
85 * HMAC MD5 signer, with secret set
86 */
87 signer_t *signer;
88
89 /**
90 * Random number generator for MS-MPPE salt values
91 */
92 rng_t *rng;
93
94 /**
95 * List of registered TNC-PDP connections
96 */
97 tnc_pdp_connections_t *connections;
98 };
99
100
101 /**
102 * Open IPv4 or IPv6 UDP RADIUS socket
103 */
104 static int open_socket(private_tnc_pdp_t *this, int family, u_int16_t port)
105 {
106 int on = TRUE;
107 struct sockaddr_storage addr;
108 socklen_t addrlen;
109 int skt;
110
111 memset(&addr, 0, sizeof(addr));
112 addr.ss_family = family;
113
114 /* precalculate constants depending on address family */
115 switch (family)
116 {
117 case AF_INET:
118 {
119 struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
120
121 htoun32(&sin->sin_addr.s_addr, INADDR_ANY);
122 htoun16(&sin->sin_port, port);
123 addrlen = sizeof(struct sockaddr_in);
124 break;
125 }
126 case AF_INET6:
127 {
128 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
129
130 memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any));
131 htoun16(&sin6->sin6_port, port);
132 addrlen = sizeof(struct sockaddr_in6);
133 break;
134 }
135 default:
136 return 0;
137 }
138
139 /* open the socket */
140 skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
141 if (skt < 0)
142 {
143 DBG1(DBG_CFG, "opening RADIUS socket failed: %s", strerror(errno));
144 return 0;
145 }
146 if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
147 {
148 DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
149 close(skt);
150 return 0;
151 }
152
153 /* bind the socket */
154 if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0)
155 {
156 DBG1(DBG_CFG, "unable to bind RADIUS socket: %s", strerror(errno));
157 close(skt);
158 return 0;
159 }
160
161 return skt;
162 }
163
164 /**
165 * Send a RADIUS message to client
166 */
167 static void send_message(private_tnc_pdp_t *this, radius_message_t *message,
168 host_t *client)
169 {
170 int fd;
171 chunk_t data;
172
173 fd = (client->get_family(client) == AF_INET) ? this->ipv4 : this->ipv6;
174 data = message->get_encoding(message);
175
176 DBG2(DBG_CFG, "sending RADIUS packet to %#H", client);
177 DBG3(DBG_CFG, "%B", &data);
178
179 if (sendto(fd, data.ptr, data.len, 0, client->get_sockaddr(client),
180 *client->get_sockaddr_len(client)) != data.len)
181 {
182 DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
183 }
184 }
185
186 /**
187 * Encrypt a MS-MPPE-Send/Recv-Key
188 */
189 static chunk_t encrypt_mppe_key(private_tnc_pdp_t *this, u_int8_t type,
190 chunk_t key, radius_message_t *request)
191 {
192 chunk_t a, r, seed, data;
193 u_char b[HASH_SIZE_MD5], *c;
194 mppe_key_t *mppe_key;
195
196 /**
197 * From RFC2548 (encryption):
198 * b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1)
199 * b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2)
200 * . . .
201 * b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i)
202 */
203
204 data = chunk_alloc(sizeof(mppe_key_t) +
205 HASH_SIZE_MD5 * (1 + key.len / HASH_SIZE_MD5));
206 memset(data.ptr, 0x00, data.len);
207
208 mppe_key = (mppe_key_t*)data.ptr;
209 mppe_key->id = htonl(PEN_MICROSOFT);
210 mppe_key->type = type;
211 mppe_key->length = data.len - sizeof(mppe_key->id);
212 mppe_key->key[0] = key.len;
213
214 memcpy(&mppe_key->key[1], key.ptr, key.len);
215
216 /* generate a 16 bit random salt value */
217 a = chunk_create((u_char*)&(mppe_key->salt), sizeof(mppe_key->salt));
218 this->rng->get_bytes(this->rng, a.len, a.ptr);
219
220 /* the MSB of the salt MUST be set to 1 */
221 *a.ptr |= 0x80;
222
223 r = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
224 seed = chunk_cata("cc", r, a);
225 c = mppe_key->key;
226
227 while (c < data.ptr + data.len)
228 {
229 /* b(i) = MD5(S + c(i-1)) */
230 this->hasher->get_hash(this->hasher, this->secret, NULL);
231 this->hasher->get_hash(this->hasher, seed, b);
232
233 /* c(i) = b(i) xor p(1) */
234 memxor(c, b, HASH_SIZE_MD5);
235
236 /* prepare next round */
237 seed = chunk_create(c, HASH_SIZE_MD5);
238 c += HASH_SIZE_MD5;
239 }
240
241 return data;
242 }
243
244 /**
245 * Send a RADIUS response for a request
246 */
247 static void send_response(private_tnc_pdp_t *this, radius_message_t *request,
248 radius_message_code_t code, eap_payload_t *eap,
249 identification_t *group, chunk_t msk, host_t *client)
250 {
251 radius_message_t *response;
252 chunk_t data, recv, send;
253 u_int32_t tunnel_type;
254
255 response = radius_message_create(code);
256 if (eap)
257 {
258 data = eap->get_data(eap);
259 DBG3(DBG_CFG, "%N payload %B", eap_type_names, this->type, &data);
260
261 /* fragment data suitable for RADIUS */
262 while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE)
263 {
264 response->add(response, RAT_EAP_MESSAGE,
265 chunk_create(data.ptr, MAX_RADIUS_ATTRIBUTE_SIZE));
266 data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE);
267 }
268 response->add(response, RAT_EAP_MESSAGE, data);
269 }
270 if (group)
271 {
272 tunnel_type = RADIUS_TUNNEL_TYPE_ESP;
273 htoun32(data.ptr, tunnel_type);
274 data.len = sizeof(tunnel_type);
275 response->add(response, RAT_TUNNEL_TYPE, data);
276 response->add(response, RAT_FILTER_ID, group->get_encoding(group));
277 }
278 if (msk.len)
279 {
280 recv = chunk_create(msk.ptr, msk.len / 2);
281 data = encrypt_mppe_key(this, MS_MPPE_RECV_KEY, recv, request);
282 response->add(response, RAT_VENDOR_SPECIFIC, data);
283 chunk_free(&data);
284
285 send = chunk_create(msk.ptr + recv.len, msk.len - recv.len);
286 data = encrypt_mppe_key(this, MS_MPPE_SEND_KEY, send, request);
287 response->add(response, RAT_VENDOR_SPECIFIC, data);
288 chunk_free(&data);
289 }
290 response->set_identifier(response, request->get_identifier(request));
291 response->sign(response, request->get_authenticator(request),
292 this->secret, this->hasher, this->signer, NULL, TRUE);
293
294 DBG1(DBG_CFG, "sending RADIUS %N to client '%H'", radius_message_code_names,
295 code, client);
296 send_message(this, response, client);
297 response->destroy(response);
298 }
299
300 /**
301 * Process EAP message
302 */
303 static void process_eap(private_tnc_pdp_t *this, radius_message_t *request,
304 host_t *source)
305 {
306 enumerator_t *enumerator;
307 eap_payload_t *in, *out = NULL;
308 eap_method_t *method;
309 eap_type_t eap_type;
310 u_int32_t eap_vendor;
311 chunk_t data, message = chunk_empty, msk = chunk_empty;
312 chunk_t user_name = chunk_empty, nas_id = chunk_empty;
313 identification_t *group = NULL;
314 radius_message_code_t code = RMC_ACCESS_CHALLENGE;
315 int type;
316
317 enumerator = request->create_enumerator(request);
318 while (enumerator->enumerate(enumerator, &type, &data))
319 {
320 switch (type)
321 {
322 case RAT_USER_NAME:
323 user_name = data;
324 break;
325 case RAT_NAS_IDENTIFIER:
326 nas_id = data;
327 break;
328 case RAT_EAP_MESSAGE:
329 if (data.len)
330 {
331 message = chunk_cat("mc", message, data);
332 }
333 break;
334 default:
335 break;
336 }
337 }
338 enumerator->destroy(enumerator);
339
340 if (message.len)
341 {
342 in = eap_payload_create_data(message);
343
344 /* apply EAP method selected by RADIUS server */
345 eap_type = in->get_type(in, &eap_vendor);
346
347 DBG3(DBG_CFG, "%N payload %B", eap_type_names, eap_type, &message);
348
349 if (eap_type == EAP_IDENTITY)
350 {
351 identification_t *peer;
352 chunk_t eap_identity;
353
354 if (message.len < 5)
355 {
356 goto end;
357 }
358 eap_identity = chunk_create(message.ptr + 5, message.len - 5);
359 peer = identification_create_from_data(eap_identity);
360 method = charon->eap->create_instance(charon->eap, this->type,
361 0, EAP_SERVER, this->server, peer);
362 if (!method)
363 {
364 peer->destroy(peer);
365 goto end;
366 }
367 this->connections->add(this->connections, nas_id, user_name, peer,
368 method);
369 method->initiate(method, &out);
370 }
371 else
372 {
373 ike_sa_t *ike_sa;
374 auth_cfg_t *auth;
375 auth_rule_t type;
376 identification_t *data;
377 enumerator_t *e;
378
379 method = this->connections->get_state(this->connections, nas_id,
380 user_name, &ike_sa);
381 if (!method)
382 {
383 goto end;
384 }
385 charon->bus->set_sa(charon->bus, ike_sa);
386
387 switch (method->process(method, in, &out))
388 {
389 case NEED_MORE:
390 code = RMC_ACCESS_CHALLENGE;
391 break;
392 case SUCCESS:
393 code = RMC_ACCESS_ACCEPT;
394 method->get_msk(method, &msk);
395 auth = ike_sa->get_auth_cfg(ike_sa, FALSE);
396 e = auth->create_enumerator(auth);
397 while (e->enumerate(e, &type, &data))
398 {
399 /* look for group memberships */
400 if (type == AUTH_RULE_GROUP)
401 {
402 group = data;
403 }
404 }
405 e->destroy(e);
406
407 DESTROY_IF(out);
408 out = eap_payload_create_code(EAP_SUCCESS,
409 in->get_identifier(in));
410 break;
411 case FAILED:
412 default:
413 code = RMC_ACCESS_REJECT;
414 DESTROY_IF(out);
415 out = eap_payload_create_code(EAP_FAILURE,
416 in->get_identifier(in));
417 }
418 charon->bus->set_sa(charon->bus, NULL);
419 }
420
421 send_response(this, request, code, out, group, msk, source);
422 out->destroy(out);
423
424 if (code == RMC_ACCESS_ACCEPT || code == RMC_ACCESS_REJECT)
425 {
426 this->connections->remove(this->connections, nas_id, user_name);
427 }
428
429 end:
430 free(message.ptr);
431 in->destroy(in);
432 }
433 }
434
435 /**
436 * Process packets received on the RADIUS socket
437 */
438 static job_requeue_t receive(private_tnc_pdp_t *this)
439 {
440 while (TRUE)
441 {
442 radius_message_t *request;
443 char buffer[MAX_PACKET];
444 int max_fd = 0, selected = 0, bytes_read = 0;
445 fd_set rfds;
446 bool oldstate;
447 host_t *source;
448 struct msghdr msg;
449 struct iovec iov;
450 union {
451 struct sockaddr_in in4;
452 struct sockaddr_in6 in6;
453 } src;
454
455 FD_ZERO(&rfds);
456
457 if (this->ipv4)
458 {
459 FD_SET(this->ipv4, &rfds);
460 }
461 if (this->ipv6)
462 {
463 FD_SET(this->ipv6, &rfds);
464 }
465 max_fd = max(this->ipv4, this->ipv6);
466
467 DBG2(DBG_CFG, "waiting for data on RADIUS sockets");
468 oldstate = thread_cancelability(TRUE);
469 if (select(max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
470 {
471 thread_cancelability(oldstate);
472 continue;
473 }
474 thread_cancelability(oldstate);
475
476 if (FD_ISSET(this->ipv4, &rfds))
477 {
478 selected = this->ipv4;
479 }
480 else if (FD_ISSET(this->ipv6, &rfds))
481 {
482 selected = this->ipv6;
483 }
484 else
485 {
486 /* oops, shouldn't happen */
487 continue;
488 }
489
490 /* read received packet */
491 msg.msg_name = &src;
492 msg.msg_namelen = sizeof(src);
493 iov.iov_base = buffer;
494 iov.iov_len = MAX_PACKET;
495 msg.msg_iov = &iov;
496 msg.msg_iovlen = 1;
497 msg.msg_flags = 0;
498
499 bytes_read = recvmsg(selected, &msg, 0);
500 if (bytes_read < 0)
501 {
502 DBG1(DBG_CFG, "error reading RADIUS socket: %s", strerror(errno));
503 continue;
504 }
505 if (msg.msg_flags & MSG_TRUNC)
506 {
507 DBG1(DBG_CFG, "receive buffer too small, RADIUS packet discarded");
508 continue;
509 }
510 source = host_create_from_sockaddr((sockaddr_t*)&src);
511 DBG2(DBG_CFG, "received RADIUS packet from %#H", source);
512 DBG3(DBG_CFG, "%b", buffer, bytes_read);
513 request = radius_message_parse(chunk_create(buffer, bytes_read));
514 if (request)
515 {
516 DBG1(DBG_CFG, "received RADIUS %N from client '%H'",
517 radius_message_code_names, request->get_code(request), source);
518
519 if (request->verify(request, NULL, this->secret, this->hasher,
520 this->signer))
521 {
522 process_eap(this, request, source);
523 }
524 request->destroy(request);
525
526 }
527 else
528 {
529 DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
530 }
531 source->destroy(source);
532 }
533 return JOB_REQUEUE_FAIR;
534 }
535
536 METHOD(tnc_pdp_t, destroy, void,
537 private_tnc_pdp_t *this)
538 {
539 if (this->job)
540 {
541 this->job->cancel(this->job);
542 }
543 if (this->ipv4)
544 {
545 close(this->ipv4);
546 }
547 if (this->ipv6)
548 {
549 close(this->ipv6);
550 }
551 DESTROY_IF(this->server);
552 DESTROY_IF(this->signer);
553 DESTROY_IF(this->hasher);
554 DESTROY_IF(this->rng);
555 DESTROY_IF(this->connections);
556 free(this);
557 }
558
559 /*
560 * see header file
561 */
562 tnc_pdp_t *tnc_pdp_create(u_int16_t port)
563 {
564 private_tnc_pdp_t *this;
565 char *secret, *server, *eap_type_str;
566
567 INIT(this,
568 .public = {
569 .destroy = _destroy,
570 },
571 .ipv4 = open_socket(this, AF_INET, port),
572 .ipv6 = open_socket(this, AF_INET6, port),
573 .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5),
574 .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128),
575 .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
576 .connections = tnc_pdp_connections_create(),
577 );
578
579 if (!this->hasher || !this->signer || !this->rng)
580 {
581 DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
582 destroy(this);
583 return NULL;
584 }
585 if (!this->ipv4 && !this->ipv6)
586 {
587 DBG1(DBG_NET, "could not create any RADIUS sockets");
588 destroy(this);
589 return NULL;
590 }
591 if (!this->ipv4)
592 {
593 DBG1(DBG_NET, "could not open IPv4 RADIUS socket, IPv4 disabled");
594 }
595 if (!this->ipv6)
596 {
597 DBG1(DBG_NET, "could not open IPv6 RADIUS socket, IPv6 disabled");
598 }
599
600 server = lib->settings->get_str(lib->settings,
601 "charon.plugins.tnc-pdp.server", NULL);
602 if (!server)
603 {
604 DBG1(DBG_CFG, "missing PDP server name, PDP disabled");
605 destroy(this);
606 return NULL;
607 }
608 this->server = identification_create_from_string(server);
609
610 secret = lib->settings->get_str(lib->settings,
611 "charon.plugins.tnc-pdp.secret", NULL);
612 if (!secret)
613 {
614 DBG1(DBG_CFG, "missing RADIUS secret, PDP disabled");
615 destroy(this);
616 return NULL;
617 }
618 this->secret = chunk_create(secret, strlen(secret));
619 this->signer->set_key(this->signer, this->secret);
620
621 eap_type_str = lib->settings->get_str(lib->settings,
622 "charon.plugins.tnc-pdp.method", "ttls");
623 this->type = eap_type_from_string(eap_type_str);
624 if (this->type == 0)
625 {
626 DBG1(DBG_CFG, "unrecognized eap method \"%s\"", eap_type_str);
627 destroy(this);
628 return NULL;
629 }
630 DBG1(DBG_IKE, "eap method %N selected", eap_type_names, this->type);
631
632 this->job = callback_job_create_with_prio((callback_job_cb_t)receive,
633 this, NULL, NULL, JOB_PRIO_CRITICAL);
634 lib->processor->queue_job(lib->processor, (job_t*)this->job);
635
636 return &this->public;
637 }
638