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