2 * Copyright (C) 2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
17 #include "tnc_pdp_connections.h"
22 #include <radius_message.h>
23 #include <radius_mppe.h>
28 #include <threading/thread.h>
29 #include <processing/jobs/callback_job.h>
30 #include <sa/eap/eap_method.h>
32 typedef struct private_tnc_pdp_t private_tnc_pdp_t
;
35 * Maximum size of a RADIUS IP packet
37 #define MAX_PACKET 4096
40 * private data of tnc_pdp_t
42 struct private_tnc_pdp_t
{
45 * implements tnc_pdp_t interface
52 identification_t
*server
;
55 * EAP method type to be used
70 * Callback job dispatching commands
75 * RADIUS shared secret
85 * HMAC MD5 signer, with secret set
90 * Random number generator for MS-MPPE salt values
95 * List of registered TNC-PDP connections
97 tnc_pdp_connections_t
*connections
;
102 * Open IPv4 or IPv6 UDP RADIUS socket
104 static int open_socket(int family
, u_int16_t port
)
107 struct sockaddr_storage addr
;
111 memset(&addr
, 0, sizeof(addr
));
112 addr
.ss_family
= family
;
114 /* precalculate constants depending on address family */
119 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&addr
;
121 htoun32(&sin
->sin_addr
.s_addr
, INADDR_ANY
);
122 htoun16(&sin
->sin_port
, port
);
123 addrlen
= sizeof(struct sockaddr_in
);
128 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&addr
;
130 memcpy(&sin6
->sin6_addr
, &in6addr_any
, sizeof(in6addr_any
));
131 htoun16(&sin6
->sin6_port
, port
);
132 addrlen
= sizeof(struct sockaddr_in6
);
139 /* open the socket */
140 skt
= socket(family
, SOCK_DGRAM
, IPPROTO_UDP
);
143 DBG1(DBG_CFG
, "opening RADIUS socket failed: %s", strerror(errno
));
146 if (setsockopt(skt
, SOL_SOCKET
, SO_REUSEADDR
, (void*)&on
, sizeof(on
)) < 0)
148 DBG1(DBG_CFG
, "unable to set SO_REUSEADDR on socket: %s", strerror(errno
));
153 /* bind the socket */
154 if (bind(skt
, (struct sockaddr
*)&addr
, addrlen
) < 0)
156 DBG1(DBG_CFG
, "unable to bind RADIUS socket: %s", strerror(errno
));
165 * Send a RADIUS message to client
167 static void send_message(private_tnc_pdp_t
*this, radius_message_t
*message
,
173 fd
= (client
->get_family(client
) == AF_INET
) ?
this->ipv4
: this->ipv6
;
174 data
= message
->get_encoding(message
);
176 DBG2(DBG_CFG
, "sending RADIUS packet to %#H", client
);
177 DBG3(DBG_CFG
, "%B", &data
);
179 if (sendto(fd
, data
.ptr
, data
.len
, 0, client
->get_sockaddr(client
),
180 *client
->get_sockaddr_len(client
)) != data
.len
)
182 DBG1(DBG_CFG
, "sending RADIUS message failed: %s", strerror(errno
));
187 * Encrypt a MS-MPPE-Send/Recv-Key
189 static chunk_t
encrypt_mppe_key(private_tnc_pdp_t
*this, u_int8_t type
,
190 chunk_t key
, u_int16_t
*salt
,
191 radius_message_t
*request
)
193 chunk_t a
, r
, seed
, data
;
194 u_char b
[HASH_SIZE_MD5
], *c
;
195 mppe_key_t
*mppe_key
;
198 * From RFC2548 (encryption):
199 * b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1)
200 * b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2)
202 * b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i)
205 data
= chunk_alloc(sizeof(mppe_key_t
) +
206 HASH_SIZE_MD5
* (1 + key
.len
/ HASH_SIZE_MD5
));
207 memset(data
.ptr
, 0x00, data
.len
);
209 mppe_key
= (mppe_key_t
*)data
.ptr
;
210 mppe_key
->id
= htonl(PEN_MICROSOFT
);
211 mppe_key
->type
= type
;
212 mppe_key
->length
= data
.len
- sizeof(mppe_key
->id
);
213 mppe_key
->key
[0] = key
.len
;
215 memcpy(&mppe_key
->key
[1], key
.ptr
, key
.len
);
218 * generate a 16 bit unique random salt value for the MPPE stream cipher
219 * the MSB of the salt MUST be set to 1
221 a
= chunk_create((u_char
*)&(mppe_key
->salt
), sizeof(mppe_key
->salt
));
224 this->rng
->get_bytes(this->rng
, a
.len
, a
.ptr
);
227 while (mppe_key
->salt
== *salt
);
229 /* update the salt value */
230 *salt
= mppe_key
->salt
;
232 r
= chunk_create(request
->get_authenticator(request
), HASH_SIZE_MD5
);
233 seed
= chunk_cata("cc", r
, a
);
236 while (c
< data
.ptr
+ data
.len
)
238 /* b(i) = MD5(S + c(i-1)) */
239 this->hasher
->get_hash(this->hasher
, this->secret
, NULL
);
240 this->hasher
->get_hash(this->hasher
, seed
, b
);
242 /* c(i) = b(i) xor p(1) */
243 memxor(c
, b
, HASH_SIZE_MD5
);
245 /* prepare next round */
246 seed
= chunk_create(c
, HASH_SIZE_MD5
);
254 * Send a RADIUS response for a request
256 static void send_response(private_tnc_pdp_t
*this, radius_message_t
*request
,
257 radius_message_code_t code
, eap_payload_t
*eap
,
258 identification_t
*group
, chunk_t msk
, host_t
*client
)
260 radius_message_t
*response
;
261 chunk_t data
, recv
, send
;
262 u_int32_t tunnel_type
;
265 response
= radius_message_create(code
);
268 data
= eap
->get_data(eap
);
269 DBG3(DBG_CFG
, "%N payload %B", eap_type_names
, this->type
, &data
);
271 /* fragment data suitable for RADIUS */
272 while (data
.len
> MAX_RADIUS_ATTRIBUTE_SIZE
)
274 response
->add(response
, RAT_EAP_MESSAGE
,
275 chunk_create(data
.ptr
, MAX_RADIUS_ATTRIBUTE_SIZE
));
276 data
= chunk_skip(data
, MAX_RADIUS_ATTRIBUTE_SIZE
);
278 response
->add(response
, RAT_EAP_MESSAGE
, data
);
282 tunnel_type
= RADIUS_TUNNEL_TYPE_ESP
;
283 htoun32(data
.ptr
, tunnel_type
);
284 data
.len
= sizeof(tunnel_type
);
285 response
->add(response
, RAT_TUNNEL_TYPE
, data
);
286 response
->add(response
, RAT_FILTER_ID
, group
->get_encoding(group
));
290 recv
= chunk_create(msk
.ptr
, msk
.len
/ 2);
291 data
= encrypt_mppe_key(this, MS_MPPE_RECV_KEY
, recv
, &salt
, request
);
292 response
->add(response
, RAT_VENDOR_SPECIFIC
, data
);
295 send
= chunk_create(msk
.ptr
+ recv
.len
, msk
.len
- recv
.len
);
296 data
= encrypt_mppe_key(this, MS_MPPE_SEND_KEY
, send
, &salt
, request
);
297 response
->add(response
, RAT_VENDOR_SPECIFIC
, data
);
300 response
->set_identifier(response
, request
->get_identifier(request
));
301 response
->sign(response
, request
->get_authenticator(request
),
302 this->secret
, this->hasher
, this->signer
, NULL
, TRUE
);
304 DBG1(DBG_CFG
, "sending RADIUS %N to client '%H'", radius_message_code_names
,
306 send_message(this, response
, client
);
307 response
->destroy(response
);
311 * Process EAP message
313 static void process_eap(private_tnc_pdp_t
*this, radius_message_t
*request
,
316 enumerator_t
*enumerator
;
317 eap_payload_t
*in
, *out
= NULL
;
318 eap_method_t
*method
;
320 u_int32_t eap_vendor
;
321 chunk_t data
, message
= chunk_empty
, msk
= chunk_empty
;
322 chunk_t user_name
= chunk_empty
, nas_id
= chunk_empty
;
323 identification_t
*group
= NULL
;
324 radius_message_code_t code
= RMC_ACCESS_CHALLENGE
;
327 enumerator
= request
->create_enumerator(request
);
328 while (enumerator
->enumerate(enumerator
, &type
, &data
))
335 case RAT_NAS_IDENTIFIER
:
338 case RAT_EAP_MESSAGE
:
341 message
= chunk_cat("mc", message
, data
);
348 enumerator
->destroy(enumerator
);
352 in
= eap_payload_create_data(message
);
354 /* apply EAP method selected by RADIUS server */
355 eap_type
= in
->get_type(in
, &eap_vendor
);
357 DBG3(DBG_CFG
, "%N payload %B", eap_type_names
, eap_type
, &message
);
359 if (eap_type
== EAP_IDENTITY
)
361 identification_t
*peer
;
362 chunk_t eap_identity
;
368 eap_identity
= chunk_create(message
.ptr
+ 5, message
.len
- 5);
369 peer
= identification_create_from_data(eap_identity
);
370 method
= charon
->eap
->create_instance(charon
->eap
, this->type
,
371 0, EAP_SERVER
, this->server
, peer
);
377 this->connections
->add(this->connections
, nas_id
, user_name
, peer
,
379 method
->initiate(method
, &out
);
386 identification_t
*data
;
389 method
= this->connections
->get_state(this->connections
, nas_id
,
395 charon
->bus
->set_sa(charon
->bus
, ike_sa
);
397 switch (method
->process(method
, in
, &out
))
400 code
= RMC_ACCESS_CHALLENGE
;
403 code
= RMC_ACCESS_ACCEPT
;
404 method
->get_msk(method
, &msk
);
405 auth
= ike_sa
->get_auth_cfg(ike_sa
, FALSE
);
406 e
= auth
->create_enumerator(auth
);
407 while (e
->enumerate(e
, &type
, &data
))
409 /* look for group memberships */
410 if (type
== AUTH_RULE_GROUP
)
418 out
= eap_payload_create_code(EAP_SUCCESS
,
419 in
->get_identifier(in
));
423 code
= RMC_ACCESS_REJECT
;
425 out
= eap_payload_create_code(EAP_FAILURE
,
426 in
->get_identifier(in
));
428 charon
->bus
->set_sa(charon
->bus
, NULL
);
431 send_response(this, request
, code
, out
, group
, msk
, source
);
434 if (code
== RMC_ACCESS_ACCEPT
|| code
== RMC_ACCESS_REJECT
)
436 this->connections
->remove(this->connections
, nas_id
, user_name
);
446 * Process packets received on the RADIUS socket
448 static job_requeue_t
receive(private_tnc_pdp_t
*this)
452 radius_message_t
*request
;
453 char buffer
[MAX_PACKET
];
454 int max_fd
= 0, selected
= 0, bytes_read
= 0;
461 struct sockaddr_in in4
;
462 struct sockaddr_in6 in6
;
469 FD_SET(this->ipv4
, &rfds
);
473 FD_SET(this->ipv6
, &rfds
);
475 max_fd
= max(this->ipv4
, this->ipv6
);
477 DBG2(DBG_CFG
, "waiting for data on RADIUS sockets");
478 oldstate
= thread_cancelability(TRUE
);
479 if (select(max_fd
+ 1, &rfds
, NULL
, NULL
, NULL
) <= 0)
481 thread_cancelability(oldstate
);
484 thread_cancelability(oldstate
);
486 if (FD_ISSET(this->ipv4
, &rfds
))
488 selected
= this->ipv4
;
490 else if (FD_ISSET(this->ipv6
, &rfds
))
492 selected
= this->ipv6
;
496 /* oops, shouldn't happen */
500 /* read received packet */
502 msg
.msg_namelen
= sizeof(src
);
503 iov
.iov_base
= buffer
;
504 iov
.iov_len
= MAX_PACKET
;
509 bytes_read
= recvmsg(selected
, &msg
, 0);
512 DBG1(DBG_CFG
, "error reading RADIUS socket: %s", strerror(errno
));
515 if (msg
.msg_flags
& MSG_TRUNC
)
517 DBG1(DBG_CFG
, "receive buffer too small, RADIUS packet discarded");
520 source
= host_create_from_sockaddr((sockaddr_t
*)&src
);
521 DBG2(DBG_CFG
, "received RADIUS packet from %#H", source
);
522 DBG3(DBG_CFG
, "%b", buffer
, bytes_read
);
523 request
= radius_message_parse(chunk_create(buffer
, bytes_read
));
526 DBG1(DBG_CFG
, "received RADIUS %N from client '%H'",
527 radius_message_code_names
, request
->get_code(request
), source
);
529 if (request
->verify(request
, NULL
, this->secret
, this->hasher
,
532 process_eap(this, request
, source
);
534 request
->destroy(request
);
539 DBG1(DBG_CFG
, "received invalid RADIUS message, ignored");
541 source
->destroy(source
);
543 return JOB_REQUEUE_FAIR
;
546 METHOD(tnc_pdp_t
, destroy
, void,
547 private_tnc_pdp_t
*this)
551 this->job
->cancel(this->job
);
561 DESTROY_IF(this->server
);
562 DESTROY_IF(this->signer
);
563 DESTROY_IF(this->hasher
);
564 DESTROY_IF(this->rng
);
565 DESTROY_IF(this->connections
);
572 tnc_pdp_t
*tnc_pdp_create(u_int16_t port
)
574 private_tnc_pdp_t
*this;
575 char *secret
, *server
, *eap_type_str
;
581 .ipv4
= open_socket(AF_INET
, port
),
582 .ipv6
= open_socket(AF_INET6
, port
),
583 .hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
),
584 .signer
= lib
->crypto
->create_signer(lib
->crypto
, AUTH_HMAC_MD5_128
),
585 .rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
),
586 .connections
= tnc_pdp_connections_create(),
589 if (!this->hasher
|| !this->signer
|| !this->rng
)
591 DBG1(DBG_CFG
, "RADIUS initialization failed, HMAC/MD5/RNG required");
595 if (!this->ipv4
&& !this->ipv6
)
597 DBG1(DBG_NET
, "could not create any RADIUS sockets");
603 DBG1(DBG_NET
, "could not open IPv4 RADIUS socket, IPv4 disabled");
607 DBG1(DBG_NET
, "could not open IPv6 RADIUS socket, IPv6 disabled");
610 server
= lib
->settings
->get_str(lib
->settings
,
611 "%s.plugins.tnc-pdp.server", NULL
, charon
->name
);
614 DBG1(DBG_CFG
, "missing PDP server name, PDP disabled");
618 this->server
= identification_create_from_string(server
);
620 secret
= lib
->settings
->get_str(lib
->settings
,
621 "%s.plugins.tnc-pdp.secret", NULL
, charon
->name
);
624 DBG1(DBG_CFG
, "missing RADIUS secret, PDP disabled");
628 this->secret
= chunk_create(secret
, strlen(secret
));
629 this->signer
->set_key(this->signer
, this->secret
);
631 eap_type_str
= lib
->settings
->get_str(lib
->settings
,
632 "%s.plugins.tnc-pdp.method", "ttls", charon
->name
);
633 this->type
= eap_type_from_string(eap_type_str
);
636 DBG1(DBG_CFG
, "unrecognized eap method \"%s\"", eap_type_str
);
640 DBG1(DBG_IKE
, "eap method %N selected", eap_type_names
, this->type
);
642 this->job
= callback_job_create_with_prio((callback_job_cb_t
)receive
,
643 this, NULL
, NULL
, JOB_PRIO_CRITICAL
);
644 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->job
);
646 return &this->public;