059dcda4b9285791de53fce9fef23c546c5c2b8a
[strongswan.git] / src / libradius / radius_message.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * 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 "radius_message.h"
17
18 #include <utils/debug.h>
19 #include <crypto/hashers/hasher.h>
20
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;
24
25 /**
26 * RADIUS message header
27 */
28 struct rmsg_t {
29 /** message code, radius_message_code_t */
30 u_int8_t code;
31 /** message identifier */
32 u_int8_t identifier;
33 /** length of Code, Identifier, Length, Authenticator and Attributes */
34 u_int16_t length;
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));
40
41 /**
42 * RADIUS message attribute.
43 */
44 struct rattr_t {
45 /** attribute type, radius_attribute_type_t */
46 u_int8_t type;
47 /** length of the attriubte, including the Type, Length and Value fields */
48 u_int8_t length;
49 /** variable length attribute value */
50 u_int8_t value[];
51 } __attribute__((packed));
52
53 /**
54 * Private data of an radius_message_t object.
55 */
56 struct private_radius_message_t {
57
58 /**
59 * Public radius_message_t interface.
60 */
61 radius_message_t public;
62
63 /**
64 * message data, allocated
65 */
66 rmsg_t *msg;
67 };
68
69 /**
70 * Described in header.
71 */
72 void libradius_init(void)
73 {
74 /* empty */
75 }
76
77 ENUM_BEGIN(radius_message_code_names, RMC_ACCESS_REQUEST, RMC_ACCOUNTING_RESPONSE,
78 "Access-Request",
79 "Access-Accept",
80 "Access-Reject",
81 "Accounting-Request",
82 "Accounting-Response");
83 ENUM_NEXT(radius_message_code_names, RMC_ACCESS_CHALLENGE, RMC_ACCESS_CHALLENGE, RMC_ACCOUNTING_RESPONSE,
84 "Access-Challenge");
85 ENUM_NEXT(radius_message_code_names, RMC_DISCONNECT_REQUEST, RMC_COA_NAK, RMC_ACCESS_CHALLENGE,
86 "Disconnect-Request",
87 "Disconnect-ACK",
88 "Disconnect-NAK",
89 "CoA-Request",
90 "CoA-ACK",
91 "CoA-NAK");
92 ENUM_END(radius_message_code_names, RMC_COA_NAK);
93
94 ENUM(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
95 "User-Name",
96 "User-Password",
97 "CHAP-Password",
98 "NAS-IP-Address",
99 "NAS-Port",
100 "Service-Type",
101 "Framed-Protocol",
102 "Framed-IP-Address",
103 "Framed-IP-Netmask",
104 "Framed-Routing",
105 "Filter-Id",
106 "Framed-MTU",
107 "Framed-Compression",
108 "Login-IP-Host",
109 "Login-Service",
110 "Login-TCP-Port",
111 "Unassigned",
112 "Reply-Message",
113 "Callback-Number",
114 "Callback-Id",
115 "Unassigned",
116 "Framed-Route",
117 "Framed-IPX-Network",
118 "State",
119 "Class",
120 "Vendor-Specific",
121 "Session-Timeout",
122 "Idle-Timeout",
123 "Termination-Action",
124 "Called-Station-Id",
125 "Calling-Station-Id",
126 "NAS-Identifier",
127 "Proxy-State",
128 "Login-LAT-Service",
129 "Login-LAT-Node",
130 "Login-LAT-Group",
131 "Framed-AppleTalk-Link",
132 "Framed-AppleTalk-Network",
133 "Framed-AppleTalk-Zone",
134 "Acct-Status-Type",
135 "Acct-Delay-Time",
136 "Acct-Input-Octets",
137 "Acct-Output-Octets",
138 "Acct-Session-Id",
139 "Acct-Authentic",
140 "Acct-Session-Time",
141 "Acct-Input-Packets",
142 "Acct-Output-Packets",
143 "Acct-Terminate-Cause",
144 "Acct-Multi-Session-Id",
145 "Acct-Link-Count",
146 "Acct-Input-Gigawords",
147 "Acct-Output-Gigawords",
148 "Unassigned",
149 "Event-Timestamp",
150 "Egress-VLANID",
151 "Ingress-Filters",
152 "Egress-VLAN-Name",
153 "User-Priority-Table",
154 "CHAP-Challenge",
155 "NAS-Port-Type",
156 "Port-Limit",
157 "Login-LAT-Port",
158 "Tunnel-Type",
159 "Tunnel-Medium-Type",
160 "Tunnel-Client-Endpoint",
161 "Tunnel-Server-Endpoint",
162 "Acct-Tunnel-Connection",
163 "Tunnel-Password",
164 "ARAP-Password",
165 "ARAP-Features",
166 "ARAP-Zone-Access",
167 "ARAP-Security",
168 "ARAP-Security-Data",
169 "Password-Retry",
170 "Prompt",
171 "Connect-Info",
172 "Configuration-Token",
173 "EAP-Message",
174 "Message-Authenticator",
175 "Tunnel-Private-Group-ID",
176 "Tunnel-Assignment-ID",
177 "Tunnel-Preference",
178 "ARAP-Challenge-Response",
179 "Acct-Interim-Interval",
180 "Acct-Tunnel-Packets-Lost",
181 "NAS-Port-Id",
182 "Framed-Pool",
183 "CUI",
184 "Tunnel-Client-Auth-ID",
185 "Tunnel-Server-Auth-ID",
186 "NAS-Filter-Rule",
187 "Unassigned",
188 "Originating-Line-Info",
189 "NAS-IPv6-Address",
190 "Framed-Interface-Id",
191 "Framed-IPv6-Prefix",
192 "Login-IPv6-Host",
193 "Framed-IPv6-Route",
194 "Framed-IPv6-Pool",
195 "Error-Cause",
196 "EAP-Key-Name",
197 "Digest-Response",
198 "Digest-Realm",
199 "Digest-Nonce",
200 "Digest-Response-Auth",
201 "Digest-Nextnonce",
202 "Digest-Method",
203 "Digest-URI",
204 "Digest-Qop",
205 "Digest-Algorithm",
206 "Digest-Entity-Body-Hash",
207 "Digest-CNonce",
208 "Digest-Nonce-Count",
209 "Digest-Username",
210 "Digest-Opaque",
211 "Digest-Auth-Param",
212 "Digest-AKA-Auts",
213 "Digest-Domain",
214 "Digest-Stale",
215 "Digest-HA1",
216 "SIP-AOR",
217 "Delegated-IPv6-Prefix",
218 "MIP6-Feature-Vector",
219 "MIP6-Home-Link-Prefix");
220
221 /**
222 * Attribute enumerator implementation
223 */
224 typedef struct {
225 /** implements enumerator interface */
226 enumerator_t public;
227 /** currently pointing attribute */
228 rattr_t *next;
229 /** bytes left */
230 int left;
231 } attribute_enumerator_t;
232
233 METHOD(enumerator_t, attribute_enumerate, bool,
234 attribute_enumerator_t *this, int *type, chunk_t *data)
235 {
236 if (this->left == 0)
237 {
238 return FALSE;
239 }
240 if (this->left < sizeof(rattr_t) ||
241 this->left < this->next->length)
242 {
243 DBG1(DBG_IKE, "RADIUS message truncated");
244 return FALSE;
245 }
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;
251 return TRUE;
252 }
253
254 METHOD(radius_message_t, create_enumerator, enumerator_t*,
255 private_radius_message_t *this)
256 {
257 attribute_enumerator_t *e;
258
259 if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
260 {
261 return enumerator_create_empty();
262 }
263 INIT(e,
264 .public = {
265 .enumerate = (void*)_attribute_enumerate,
266 .destroy = (void*)free,
267 },
268 .next = (rattr_t*)this->msg->attributes,
269 .left = ntohs(this->msg->length) - sizeof(rmsg_t),
270 );
271 return &e->public;
272 }
273
274 METHOD(radius_message_t, add, void,
275 private_radius_message_t *this, radius_attribute_type_t type, chunk_t data)
276 {
277 rattr_t *attribute;
278
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);
287 }
288
289 METHOD(radius_message_t, sign, bool,
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)
292 {
293 if (rng)
294 {
295 /* build Request-Authenticator */
296 if (!rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator))
297 {
298 return FALSE;
299 }
300 }
301 else
302 {
303 /* prepare build of Response-Authenticator */
304 if (req_auth)
305 {
306 memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
307 }
308 else
309 {
310 memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator));
311 }
312 }
313
314 if (msg_auth)
315 {
316 char buf[HASH_SIZE_MD5];
317
318 /* build Message-Authenticator attribute, using 16 null bytes */
319 memset(buf, 0, sizeof(buf));
320 add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
321 if (!signer->get_signature(signer,
322 chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
323 ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5))
324 {
325 return FALSE;
326 }
327 }
328
329 if (!rng)
330 {
331 chunk_t msg;
332
333 /* build Response-Authenticator */
334 msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
335 if (!hasher->get_hash(hasher, msg, NULL) ||
336 !hasher->get_hash(hasher, secret, this->msg->authenticator))
337 {
338 return FALSE;
339 }
340 }
341 return TRUE;
342 }
343
344 METHOD(radius_message_t, verify, bool,
345 private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
346 hasher_t *hasher, signer_t *signer)
347 {
348 char buf[HASH_SIZE_MD5], res_auth[HASH_SIZE_MD5];
349 enumerator_t *enumerator;
350 int type;
351 chunk_t data, msg;
352 bool has_eap = FALSE, has_auth = FALSE;
353
354 msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
355
356 if (this->msg->code != RMC_ACCESS_REQUEST)
357 {
358 /* replace Response by Request Authenticator for verification */
359 memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5);
360 if (req_auth)
361 {
362 memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
363 }
364 else
365 {
366 memset(this->msg->authenticator, 0, HASH_SIZE_MD5);
367 }
368
369 /* verify Response-Authenticator */
370 if (!hasher->get_hash(hasher, msg, NULL) ||
371 !hasher->get_hash(hasher, secret, buf) ||
372 !memeq(buf, res_auth, HASH_SIZE_MD5))
373 {
374 DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed");
375 return FALSE;
376 }
377 }
378
379 /* verify Message-Authenticator attribute */
380 enumerator = create_enumerator(this);
381 while (enumerator->enumerate(enumerator, &type, &data))
382 {
383 if (type == RAT_MESSAGE_AUTHENTICATOR)
384 {
385 if (data.len != HASH_SIZE_MD5)
386 {
387 DBG1(DBG_CFG, "RADIUS Message-Authenticator invalid length");
388 enumerator->destroy(enumerator);
389 return FALSE;
390 }
391 memcpy(buf, data.ptr, data.len);
392 memset(data.ptr, 0, data.len);
393 if (signer->verify_signature(signer, msg,
394 chunk_create(buf, sizeof(buf))))
395 {
396 /* restore Message-Authenticator */
397 memcpy(data.ptr, buf, data.len);
398 has_auth = TRUE;
399 break;
400 }
401 else
402 {
403 DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
404 enumerator->destroy(enumerator);
405 return FALSE;
406 }
407 }
408 else if (type == RAT_EAP_MESSAGE)
409 {
410 has_eap = TRUE;
411 }
412 }
413 enumerator->destroy(enumerator);
414
415 if (this->msg->code != RMC_ACCESS_REQUEST)
416 {
417 /* restore Response-Authenticator */
418 memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
419 }
420
421 if (has_eap && !has_auth)
422 { /* Message-Authenticator is required if we have an EAP-Message */
423 DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
424 return FALSE;
425 }
426 return TRUE;
427 }
428
429 METHOD(radius_message_t, get_code, radius_message_code_t,
430 private_radius_message_t *this)
431 {
432 return this->msg->code;
433 }
434
435 METHOD(radius_message_t, get_identifier, u_int8_t,
436 private_radius_message_t *this)
437 {
438 return this->msg->identifier;
439 }
440
441 METHOD(radius_message_t, set_identifier, void,
442 private_radius_message_t *this, u_int8_t identifier)
443 {
444 this->msg->identifier = identifier;
445 }
446
447 METHOD(radius_message_t, get_authenticator, u_int8_t*,
448 private_radius_message_t *this)
449 {
450 return this->msg->authenticator;
451 }
452
453
454 METHOD(radius_message_t, get_encoding, chunk_t,
455 private_radius_message_t *this)
456 {
457 return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
458 }
459
460 METHOD(radius_message_t, destroy, void,
461 private_radius_message_t *this)
462 {
463 free(this->msg);
464 free(this);
465 }
466
467 /**
468 * Generic constructor
469 */
470 static private_radius_message_t *radius_message_create_empty()
471 {
472 private_radius_message_t *this;
473
474 INIT(this,
475 .public = {
476 .create_enumerator = _create_enumerator,
477 .add = _add,
478 .get_code = _get_code,
479 .get_identifier = _get_identifier,
480 .set_identifier = _set_identifier,
481 .get_authenticator = _get_authenticator,
482 .get_encoding = _get_encoding,
483 .sign = _sign,
484 .verify = _verify,
485 .destroy = _destroy,
486 },
487 );
488
489 return this;
490 }
491
492 /**
493 * See header
494 */
495 radius_message_t *radius_message_create(radius_message_code_t code)
496 {
497 private_radius_message_t *this = radius_message_create_empty();
498
499 INIT(this->msg,
500 .code = code,
501 .identifier = 0,
502 .length = htons(sizeof(rmsg_t)),
503 );
504
505 return &this->public;
506 }
507
508 /**
509 * See header
510 */
511 radius_message_t *radius_message_parse(chunk_t data)
512 {
513 private_radius_message_t *this = radius_message_create_empty();
514
515 this->msg = malloc(data.len);
516 memcpy(this->msg, data.ptr, data.len);
517 if (data.len < sizeof(rmsg_t) ||
518 ntohs(this->msg->length) != data.len)
519 {
520 DBG1(DBG_IKE, "RADIUS message has invalid length");
521 destroy(this);
522 return NULL;
523 }
524 return &this->public;
525 }