2 * Copyright (C) 2009 Martin Willi
3 * 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
16 #include "radius_message.h"
19 #include <crypto/hashers/hasher.h>
21 typedef struct private_radius_message_t private_radius_message_t
;
22 typedef struct rmsg_t rmsg_t
;
23 typedef struct rattr_t rattr_t
;
26 * RADIUS message header
29 /** message code, radius_message_code_t */
31 /** message identifier */
33 /** length of Code, Identifier, Length, Authenticator and Attributes */
35 /** message authenticator, MD5 hash */
36 u_int8_t authenticator
[HASH_SIZE_MD5
];
37 /** variable list of packed attributes */
38 u_int8_t attributes
[];
39 } __attribute__((packed
));
42 * RADIUS message attribute.
45 /** attribute type, radius_attribute_type_t */
47 /** length of the attriubte, including the Type, Length and Value fields */
49 /** variable length attribute value */
51 } __attribute__((packed
));
54 * Private data of an radius_message_t object.
56 struct private_radius_message_t
{
59 * Public radius_message_t interface.
61 radius_message_t
public;
64 * message data, allocated
70 * Described in header.
72 void libradius_init(void)
77 ENUM_BEGIN(radius_message_code_names
, RMC_ACCESS_REQUEST
, RMC_ACCOUNTING_RESPONSE
,
82 "Accounting-Response");
83 ENUM_NEXT(radius_message_code_names
, RMC_ACCESS_CHALLENGE
, RMC_ACCESS_CHALLENGE
, RMC_ACCOUNTING_RESPONSE
,
85 ENUM_NEXT(radius_message_code_names
, RMC_DISCONNECT_REQUEST
, RMC_COA_NAK
, RMC_ACCESS_CHALLENGE
,
92 ENUM_END(radius_message_code_names
, RMC_COA_NAK
);
94 ENUM(radius_attribute_type_names
, RAT_USER_NAME
, RAT_MIP6_HOME_LINK_PREFIX
,
107 "Framed-Compression",
117 "Framed-IPX-Network",
123 "Termination-Action",
125 "Calling-Station-Id",
131 "Framed-AppleTalk-Link",
132 "Framed-AppleTalk-Network",
133 "Framed-AppleTalk-Zone",
137 "Acct-Output-Octets",
141 "Acct-Input-Packets",
142 "Acct-Output-Packets",
143 "Acct-Terminate-Cause",
144 "Acct-Multi-Session-Id",
146 "Acct-Input-Gigawords",
147 "Acct-Output-Gigawords",
153 "User-Priority-Table",
159 "Tunnel-Medium-Type",
160 "Tunnel-Client-Endpoint",
161 "Tunnel-Server-Endpoint",
162 "Acct-Tunnel-Connection",
168 "ARAP-Security-Data",
172 "Configuration-Token",
174 "Message-Authenticator",
175 "Tunnel-Private-Group-ID",
176 "Tunnel-Assignment-ID",
178 "ARAP-Challenge-Response",
179 "Acct-Interim-Interval",
180 "Acct-Tunnel-Packets-Lost",
184 "Tunnel-Client-Auth-ID",
185 "Tunnel-Server-Auth-ID",
188 "Originating-Line-Info",
190 "Framed-Interface-Id",
191 "Framed-IPv6-Prefix",
200 "Digest-Response-Auth",
206 "Digest-Entity-Body-Hash",
208 "Digest-Nonce-Count",
217 "Delegated-IPv6-Prefix",
218 "MIP6-Feature-Vector",
219 "MIP6-Home-Link-Prefix");
222 * Attribute enumerator implementation
225 /** implements enumerator interface */
227 /** currently pointing attribute */
231 } attribute_enumerator_t
;
233 METHOD(enumerator_t
, attribute_enumerate
, bool,
234 attribute_enumerator_t
*this, int *type
, chunk_t
*data
)
240 if (this->left
< sizeof(rattr_t
) ||
241 this->left
< this->next
->length
)
243 DBG1(DBG_IKE
, "RADIUS message truncated");
246 *type
= this->next
->type
;
247 data
->ptr
= this->next
->value
;
248 data
->len
= this->next
->length
- sizeof(rattr_t
);
249 this->left
-= this->next
->length
;
250 this->next
= ((void*)this->next
) + this->next
->length
;
254 METHOD(radius_message_t
, create_enumerator
, enumerator_t
*,
255 private_radius_message_t
*this)
257 attribute_enumerator_t
*e
;
259 if (ntohs(this->msg
->length
) < sizeof(rmsg_t
) + sizeof(rattr_t
))
261 return enumerator_create_empty();
265 .enumerate
= (void*)_attribute_enumerate
,
266 .destroy
= (void*)free
,
268 .next
= (rattr_t
*)this->msg
->attributes
,
269 .left
= ntohs(this->msg
->length
) - sizeof(rmsg_t
),
274 METHOD(radius_message_t
, add
, void,
275 private_radius_message_t
*this, radius_attribute_type_t type
, chunk_t data
)
279 data
.len
= min(data
.len
, MAX_RADIUS_ATTRIBUTE_SIZE
);
280 this->msg
= realloc(this->msg
,
281 ntohs(this->msg
->length
) + sizeof(rattr_t
) + data
.len
);
282 attribute
= ((void*)this->msg
) + ntohs(this->msg
->length
);
283 attribute
->type
= type
;
284 attribute
->length
= data
.len
+ sizeof(rattr_t
);
285 memcpy(attribute
->value
, data
.ptr
, data
.len
);
286 this->msg
->length
= htons(ntohs(this->msg
->length
) + attribute
->length
);
289 METHOD(radius_message_t
, sign
, void,
290 private_radius_message_t
*this, u_int8_t
*req_auth
, chunk_t secret
,
291 hasher_t
*hasher
, signer_t
*signer
, rng_t
*rng
, bool msg_auth
)
295 /* build Request-Authenticator */
296 rng
->get_bytes(rng
, HASH_SIZE_MD5
, this->msg
->authenticator
);
300 /* prepare build of Response-Authenticator */
303 memcpy(this->msg
->authenticator
, req_auth
, HASH_SIZE_MD5
);
307 memset(this->msg
->authenticator
, 0, sizeof(this->msg
->authenticator
));
313 char buf
[HASH_SIZE_MD5
];
315 /* build Message-Authenticator attribute, using 16 null bytes */
316 memset(buf
, 0, sizeof(buf
));
317 add(this, RAT_MESSAGE_AUTHENTICATOR
, chunk_create(buf
, sizeof(buf
)));
318 signer
->get_signature(signer
,
319 chunk_create((u_char
*)this->msg
, ntohs(this->msg
->length
)),
320 ((u_char
*)this->msg
) + ntohs(this->msg
->length
) - HASH_SIZE_MD5
);
327 /* build Response-Authenticator */
328 msg
= chunk_create((u_char
*)this->msg
, ntohs(this->msg
->length
));
329 hasher
->get_hash(hasher
, msg
, NULL
);
330 hasher
->get_hash(hasher
, secret
, this->msg
->authenticator
);
334 METHOD(radius_message_t
, verify
, bool,
335 private_radius_message_t
*this, u_int8_t
*req_auth
, chunk_t secret
,
336 hasher_t
*hasher
, signer_t
*signer
)
338 char buf
[HASH_SIZE_MD5
], res_auth
[HASH_SIZE_MD5
];
339 enumerator_t
*enumerator
;
342 bool has_eap
= FALSE
, has_auth
= FALSE
;
344 msg
= chunk_create((u_char
*)this->msg
, ntohs(this->msg
->length
));
346 if (this->msg
->code
!= RMC_ACCESS_REQUEST
)
348 /* replace Response by Request Authenticator for verification */
349 memcpy(res_auth
, this->msg
->authenticator
, HASH_SIZE_MD5
);
352 memcpy(this->msg
->authenticator
, req_auth
, HASH_SIZE_MD5
);
356 memset(this->msg
->authenticator
, 0, HASH_SIZE_MD5
);
359 /* verify Response-Authenticator */
360 hasher
->get_hash(hasher
, msg
, NULL
);
361 hasher
->get_hash(hasher
, secret
, buf
);
362 if (!memeq(buf
, res_auth
, HASH_SIZE_MD5
))
364 DBG1(DBG_CFG
, "RADIUS Response-Authenticator verification failed");
369 /* verify Message-Authenticator attribute */
370 enumerator
= create_enumerator(this);
371 while (enumerator
->enumerate(enumerator
, &type
, &data
))
373 if (type
== RAT_MESSAGE_AUTHENTICATOR
)
375 if (data
.len
!= HASH_SIZE_MD5
)
377 DBG1(DBG_CFG
, "RADIUS Message-Authenticator invalid length");
378 enumerator
->destroy(enumerator
);
381 memcpy(buf
, data
.ptr
, data
.len
);
382 memset(data
.ptr
, 0, data
.len
);
383 if (signer
->verify_signature(signer
, msg
,
384 chunk_create(buf
, sizeof(buf
))))
386 /* restore Message-Authenticator */
387 memcpy(data
.ptr
, buf
, data
.len
);
393 DBG1(DBG_CFG
, "RADIUS Message-Authenticator verification failed");
394 enumerator
->destroy(enumerator
);
398 else if (type
== RAT_EAP_MESSAGE
)
403 enumerator
->destroy(enumerator
);
405 if (this->msg
->code
!= RMC_ACCESS_REQUEST
)
407 /* restore Response-Authenticator */
408 memcpy(this->msg
->authenticator
, res_auth
, HASH_SIZE_MD5
);
411 if (has_eap
&& !has_auth
)
412 { /* Message-Authenticator is required if we have an EAP-Message */
413 DBG1(DBG_CFG
, "RADIUS Message-Authenticator attribute missing");
419 METHOD(radius_message_t
, get_code
, radius_message_code_t
,
420 private_radius_message_t
*this)
422 return this->msg
->code
;
425 METHOD(radius_message_t
, get_identifier
, u_int8_t
,
426 private_radius_message_t
*this)
428 return this->msg
->identifier
;
431 METHOD(radius_message_t
, set_identifier
, void,
432 private_radius_message_t
*this, u_int8_t identifier
)
434 this->msg
->identifier
= identifier
;
437 METHOD(radius_message_t
, get_authenticator
, u_int8_t
*,
438 private_radius_message_t
*this)
440 return this->msg
->authenticator
;
444 METHOD(radius_message_t
, get_encoding
, chunk_t
,
445 private_radius_message_t
*this)
447 return chunk_create((u_char
*)this->msg
, ntohs(this->msg
->length
));
450 METHOD(radius_message_t
, destroy
, void,
451 private_radius_message_t
*this)
458 * Generic constructor
460 static private_radius_message_t
*radius_message_create_empty()
462 private_radius_message_t
*this;
466 .create_enumerator
= _create_enumerator
,
468 .get_code
= _get_code
,
469 .get_identifier
= _get_identifier
,
470 .set_identifier
= _set_identifier
,
471 .get_authenticator
= _get_authenticator
,
472 .get_encoding
= _get_encoding
,
485 radius_message_t
*radius_message_create(radius_message_code_t code
)
487 private_radius_message_t
*this = radius_message_create_empty();
492 .length
= htons(sizeof(rmsg_t
)),
495 return &this->public;
501 radius_message_t
*radius_message_parse(chunk_t data
)
503 private_radius_message_t
*this = radius_message_create_empty();
505 this->msg
= malloc(data
.len
);
506 memcpy(this->msg
, data
.ptr
, data
.len
);
507 if (data
.len
< sizeof(rmsg_t
) ||
508 ntohs(this->msg
->length
) != data
.len
)
510 DBG1(DBG_IKE
, "RADIUS message has invalid length");
514 return &this->public;