b62745ad2d485945a4a0907d1c582b25cfd8773b
[strongswan.git] / src / libcharon / 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
16 #include "radius_message.h"
17
18 #include <daemon.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 ENUM_BEGIN(radius_message_code_names, RMC_ACCESS_REQUEST, RMC_ACCOUNTING_RESPONSE,
70 "Access-Request",
71 "Access-Accept",
72 "Access-Reject",
73 "Accounting-Request",
74 "Accounting-Response");
75 ENUM_NEXT(radius_message_code_names, RMC_ACCESS_CHALLENGE, RMC_ACCESS_CHALLENGE, RMC_ACCOUNTING_RESPONSE,
76 "Access-Challenge");
77 ENUM_END(radius_message_code_names, RMC_ACCESS_CHALLENGE);
78
79 ENUM(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
80 "User-Name",
81 "User-Password",
82 "CHAP-Password",
83 "NAS-IP-Address",
84 "NAS-Port",
85 "Service-Type",
86 "Framed-Protocol",
87 "Framed-IP-Address",
88 "Framed-IP-Netmask",
89 "Framed-Routing",
90 "Filter-Id",
91 "Framed-MTU",
92 "Framed-Compression",
93 "Login-IP-Host",
94 "Login-Service",
95 "Login-TCP-Port",
96 "Unassigned",
97 "Reply-Message",
98 "Callback-Number",
99 "Callback-Id",
100 "Unassigned",
101 "Framed-Route",
102 "Framed-IPX-Network",
103 "State",
104 "Class",
105 "Vendor-Specific",
106 "Session-Timeout",
107 "Idle-Timeout",
108 "Termination-Action",
109 "Called-Station-Id",
110 "Calling-Station-Id",
111 "NAS-Identifier",
112 "Proxy-State",
113 "Login-LAT-Service",
114 "Login-LAT-Node",
115 "Login-LAT-Group",
116 "Framed-AppleTalk-Link",
117 "Framed-AppleTalk-Network",
118 "Framed-AppleTalk-Zone",
119 "Acct-Status-Type",
120 "Acct-Delay-Time",
121 "Acct-Input-Octets",
122 "Acct-Output-Octets",
123 "Acct-Session-Id",
124 "Acct-Authentic",
125 "Acct-Session-Time",
126 "Acct-Input-Packets",
127 "Acct-Output-Packets",
128 "Acct-Terminate-Cause",
129 "Acct-Multi-Session-Id",
130 "Acct-Link-Count",
131 "Acct-Input-Gigawords",
132 "Acct-Output-Gigawords",
133 "Unassigned",
134 "Event-Timestamp",
135 "Egress-VLANID",
136 "Ingress-Filters",
137 "Egress-VLAN-Name",
138 "User-Priority-Table",
139 "CHAP-Challenge",
140 "NAS-Port-Type",
141 "Port-Limit",
142 "Login-LAT-Port",
143 "Tunnel-Type",
144 "Tunnel-Medium-Type",
145 "Tunnel-Client-Endpoint",
146 "Tunnel-Server-Endpoint",
147 "Acct-Tunnel-Connection",
148 "Tunnel-Password",
149 "ARAP-Password",
150 "ARAP-Features",
151 "ARAP-Zone-Access",
152 "ARAP-Security",
153 "ARAP-Security-Data",
154 "Password-Retry",
155 "Prompt",
156 "Connect-Info",
157 "Configuration-Token",
158 "EAP-Message",
159 "Message-Authenticator",
160 "Tunnel-Private-Group-ID",
161 "Tunnel-Assignment-ID",
162 "Tunnel-Preference",
163 "ARAP-Challenge-Response",
164 "Acct-Interim-Interval",
165 "Acct-Tunnel-Packets-Lost",
166 "NAS-Port-Id",
167 "Framed-Pool",
168 "CUI",
169 "Tunnel-Client-Auth-ID",
170 "Tunnel-Server-Auth-ID",
171 "NAS-Filter-Rule",
172 "Unassigned",
173 "Originating-Line-Info",
174 "NAS-IPv6-Address",
175 "Framed-Interface-Id",
176 "Framed-IPv6-Prefix",
177 "Login-IPv6-Host",
178 "Framed-IPv6-Route",
179 "Framed-IPv6-Pool",
180 "Error-Cause",
181 "EAP-Key-Name",
182 "Digest-Response",
183 "Digest-Realm",
184 "Digest-Nonce",
185 "Digest-Response-Auth",
186 "Digest-Nextnonce",
187 "Digest-Method",
188 "Digest-URI",
189 "Digest-Qop",
190 "Digest-Algorithm",
191 "Digest-Entity-Body-Hash",
192 "Digest-CNonce",
193 "Digest-Nonce-Count",
194 "Digest-Username",
195 "Digest-Opaque",
196 "Digest-Auth-Param",
197 "Digest-AKA-Auts",
198 "Digest-Domain",
199 "Digest-Stale",
200 "Digest-HA1",
201 "SIP-AOR",
202 "Delegated-IPv6-Prefix",
203 "MIP6-Feature-Vector",
204 "MIP6-Home-Link-Prefix");
205
206 /**
207 * Attribute enumerator implementation
208 */
209 typedef struct {
210 /** implements enumerator interface */
211 enumerator_t public;
212 /** currently pointing attribute */
213 rattr_t *next;
214 /** bytes left */
215 int left;
216 } attribute_enumerator_t;
217
218 METHOD(enumerator_t, attribute_enumerate, bool,
219 attribute_enumerator_t *this, int *type, chunk_t *data)
220 {
221 if (this->left == 0)
222 {
223 return FALSE;
224 }
225 if (this->left < sizeof(rattr_t) ||
226 this->left < this->next->length)
227 {
228 DBG1(DBG_IKE, "RADIUS message truncated");
229 return FALSE;
230 }
231 *type = this->next->type;
232 data->ptr = this->next->value;
233 data->len = this->next->length - sizeof(rattr_t);
234 this->left -= this->next->length;
235 this->next = ((void*)this->next) + this->next->length;
236 return TRUE;
237 }
238
239 METHOD(radius_message_t, create_enumerator, enumerator_t*,
240 private_radius_message_t *this)
241 {
242 attribute_enumerator_t *e;
243
244 if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
245 {
246 return enumerator_create_empty();
247 }
248 INIT(e,
249 .public = {
250 .enumerate = (void*)_attribute_enumerate,
251 .destroy = (void*)free,
252 },
253 .next = (rattr_t*)this->msg->attributes,
254 .left = ntohs(this->msg->length) - sizeof(rmsg_t),
255 );
256 return &e->public;
257 }
258
259 METHOD(radius_message_t, add, void,
260 private_radius_message_t *this, radius_attribute_type_t type, chunk_t data)
261 {
262 rattr_t *attribute;
263
264 data.len = min(data.len, 253);
265 this->msg = realloc(this->msg,
266 ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
267 attribute = ((void*)this->msg) + ntohs(this->msg->length);
268 attribute->type = type;
269 attribute->length = data.len + sizeof(rattr_t);
270 memcpy(attribute->value, data.ptr, data.len);
271 this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
272 }
273
274 METHOD(radius_message_t, sign, void,
275 private_radius_message_t *this, rng_t *rng, signer_t *signer,
276 hasher_t *hasher, chunk_t secret)
277 {
278 if (this->msg->code == RMC_ACCOUNTING_REQUEST)
279 {
280 chunk_t msg;
281
282 memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator));
283 msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
284 hasher->get_hash(hasher, msg, NULL);
285 hasher->get_hash(hasher, secret, this->msg->authenticator);
286 }
287 else
288 {
289 char buf[HASH_SIZE_MD5];
290
291 /* build Request-Authenticator */
292 rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator);
293
294 /* build Message-Authenticator attribute, using 16 null bytes */
295 memset(buf, 0, sizeof(buf));
296 add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
297 signer->get_signature(signer,
298 chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
299 ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5);
300 }
301 }
302
303 METHOD(radius_message_t, verify, bool,
304 private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
305 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 METHOD(radius_message_t, get_code, radius_message_code_t,
374 private_radius_message_t *this)
375 {
376 return this->msg->code;
377 }
378
379 METHOD(radius_message_t, get_identifier, u_int8_t,
380 private_radius_message_t *this)
381 {
382 return this->msg->identifier;
383 }
384
385 METHOD(radius_message_t, set_identifier, void,
386 private_radius_message_t *this, u_int8_t identifier)
387 {
388 this->msg->identifier = identifier;
389 }
390
391 METHOD(radius_message_t, get_authenticator, u_int8_t*,
392 private_radius_message_t *this)
393 {
394 return this->msg->authenticator;
395 }
396
397
398 METHOD(radius_message_t, get_encoding, chunk_t,
399 private_radius_message_t *this)
400 {
401 return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
402 }
403
404 METHOD(radius_message_t, destroy, void,
405 private_radius_message_t *this)
406 {
407 free(this->msg);
408 free(this);
409 }
410
411 /**
412 * Generic constructor
413 */
414 static private_radius_message_t *radius_message_create()
415 {
416 private_radius_message_t *this;
417
418 INIT(this,
419 .public = {
420 .create_enumerator = _create_enumerator,
421 .add = _add,
422 .get_code = _get_code,
423 .get_identifier = _get_identifier,
424 .set_identifier = _set_identifier,
425 .get_authenticator = _get_authenticator,
426 .get_encoding = _get_encoding,
427 .sign = _sign,
428 .verify = _verify,
429 .destroy = _destroy,
430 },
431 );
432
433 return this;
434 }
435
436 /**
437 * See header
438 */
439 radius_message_t *radius_message_create_request(radius_message_code_t code)
440 {
441 private_radius_message_t *this = radius_message_create();
442
443 INIT(this->msg,
444 .code = code,
445 .identifier = 0,
446 .length = htons(sizeof(rmsg_t)),
447 );
448
449 return &this->public;
450 }
451
452 /**
453 * See header
454 */
455 radius_message_t *radius_message_parse_response(chunk_t data)
456 {
457 private_radius_message_t *this = radius_message_create();
458
459 this->msg = malloc(data.len);
460 memcpy(this->msg, data.ptr, data.len);
461 if (data.len < sizeof(rmsg_t) ||
462 ntohs(this->msg->length) != data.len)
463 {
464 DBG1(DBG_IKE, "RADIUS message has invalid length");
465 destroy(this);
466 return NULL;
467 }
468 return &this->public;
469 }
470