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