2 * eapsimlib.c based upon draft-haverinen-pppext-eap-sim-11.txt.
4 * The development of the EAP/SIM support was funded by Internet Foundation
5 * Austria (http://www.nic.at/ipa).
7 * code common to EAP-SIM clients and to servers.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 * Copyright 2000-2003,2006 The FreeRADIUS server project
26 * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
30 * EAP-SIM PACKET FORMAT
31 * ------- ------ ------
33 * EAP Request and Response Packet Format
34 * --- ------- --- -------- ------ ------
36 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
37 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 * | Code | Identifier | Length |
39 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40 * | Type | SIM-Type | SIM-Length | value ... |
41 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 * with SIM-Type/SIM-Length/Value... repeating. SIM-Length is in units
44 * of 32 bits, and includes the Sim-Type/Sim-Length fields.
46 * The SIM-Type's are mapped to ATTRIBUTE_EAP_SIM_BASE+Sim-type and
47 * unmapped by these functions.
51 #include <freeradius-devel/ident.h>
54 #include <freeradius-devel/autoconf.h>
55 #include <freeradius-devel/missing.h>
56 #include <freeradius-devel/libradius.h>
57 #include "eap_types.h"
59 #include <freeradius-devel/sha1.h>
62 * given a radius request with many attribues in the EAP-SIM range, build
63 * them all into a single EAP-SIM body.
66 int map_eapsim_basictypes(RADIUS_PACKET *r, EAP_PACKET *ep)
70 uint8_t *encodedmsg, *attr;
71 unsigned int id, eapcode;
72 unsigned char *macspace, *append;
74 unsigned char subtype;
81 * encodedmsg is now an EAP-SIM message.
82 * it might be too big for putting into an EAP-Type-SIM
85 vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_SUBTYPE);
88 subtype = eapsim_start;
95 vp = pairfind(r->vps, ATTRIBUTE_EAP_ID);
98 id = ((int)getpid() & 0xff);
105 vp = pairfind(r->vps, ATTRIBUTE_EAP_CODE);
108 eapcode = PW_EAP_REQUEST;
112 eapcode = vp->lvalue;
117 * take a walk through the attribute list to see how much space
118 * that we need to encode all of this.
121 for(vp = r->vps; vp != NULL; vp = vp->next)
126 if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE ||
127 vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256)
135 * the AT_MAC attribute is a bit different, when we get to this
136 * attribute, we pull the contents out, save it for later
137 * processing, set the size to 16 bytes (plus 2 bytes padding).
139 * At this point, we only care about the size.
141 if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) {
145 /* round up to next multiple of 4, after taking in
146 * account the type and length bytes
148 roundedlen = (vplen + 2 + 3) & ~3;
149 encoded_size += roundedlen;
152 if (ep->code != PW_EAP_SUCCESS)
154 ep->id = (id & 0xff);
155 ep->type.type = PW_EAP_SIM;
158 * if no attributes were found, do very little.
161 if(encoded_size == 0)
163 encodedmsg = malloc(3);
164 /* FIX: could be NULL */
166 encodedmsg[0]=subtype;
171 ep->type.data = encodedmsg;
178 * figured out the length, so malloc some space for the results.
180 * Note that we do not bother going through an "EAP" stage, which
181 * is a bit strange compared to the unmap, which expects to see
182 * an EAP-SIM virtual attributes.
184 * EAP is 1-code, 1-identifier, 2-length, 1-type = 5 overhead.
186 * SIM code adds a subtype, and 2 bytes of reserved = 3.
190 /* malloc space for it */
193 encodedmsg = malloc(encoded_size);
194 if (encodedmsg == NULL) {
195 radlog(L_ERR, "eapsim: out of memory allocating %d bytes", encoded_size+5);
198 memset(encodedmsg, 0, encoded_size);
201 * now walk the attributes again, sticking them in.
203 * we go three bytes into the encoded message, because there are two
204 * bytes of reserved, and we will fill the "subtype" in later.
209 for(vp = r->vps; vp != NULL; vp = vp->next)
213 if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE ||
214 vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256)
220 * the AT_MAC attribute is a bit different, when we get to this
221 * attribute, we pull the contents out, save it for later
222 * processing, set the size to 16 bytes (plus 2 bytes padding).
224 * At this point, we put in zeros, and remember where the
227 if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) {
229 memset(&attr[2], 0, 18);
231 append = vp->vp_octets;
232 appendlen = vp->length;
235 roundedlen = (vp->length + 2 + 3) & ~3;
236 memset(attr, 0, roundedlen);
237 memcpy(&attr[2], vp->vp_strvalue, vp->length);
239 attr[0] = vp->attribute - ATTRIBUTE_EAP_SIM_BASE;
240 attr[1] = roundedlen >> 2;
245 encodedmsg[0] = subtype;
247 ep->type.length = encoded_size;
248 ep->type.data = encodedmsg;
251 * if macspace was set and we have a key,
252 * then we should calculate the HMAC-SHA1 of the resulting EAP-SIM
253 * packet, appended with the value of append.
255 vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_KEY);
256 if(macspace != NULL && vp != NULL)
258 unsigned char *buffer;
260 uint16_t hmaclen, total_length = 0;
261 unsigned char sha1digest[20];
263 total_length = EAP_HEADER_LEN + 1 + encoded_size;
264 hmaclen = total_length + appendlen;
265 buffer = (unsigned char *)malloc(hmaclen);
266 hdr = (eap_packet_t *)buffer;
268 radlog(L_ERR, "rlm_eap: out of memory");
273 hdr->code = eapcode & 0xFF;
274 hdr->id = (id & 0xFF);
275 total_length = htons(total_length);
276 memcpy(hdr->length, &total_length, sizeof(uint16_t));
278 hdr->data[0] = PW_EAP_SIM;
281 memcpy(&hdr->data[1], encodedmsg, encoded_size);
284 memcpy(&hdr->data[encoded_size+1], append, appendlen);
287 lrad_hmac_sha1(buffer, hmaclen,
288 vp->vp_octets, vp->length,
291 /* done with the buffer, free it */
294 /* now copy the digest to where it belongs in the AT_MAC */
295 /* note that it is truncated to 128-bits */
296 memcpy(macspace, sha1digest, 16);
299 /* if we had an AT_MAC and no key, then fail */
300 if(macspace != NULL && vp == NULL)
302 if(encodedmsg != NULL)
310 int map_eapsim_types(RADIUS_PACKET *r)
315 memset(&ep, 0, sizeof(ep));
316 ret = map_eapsim_basictypes(r, &ep);
320 eap_basic_compose(r, &ep);
326 * given a radius request with an EAP-SIM body, decode it into TLV pairs
328 * return value is TRUE if it succeeded, false if there was something
329 * wrong and the packet should be discarded.
332 int unmap_eapsim_basictypes(RADIUS_PACKET *r,
333 uint8_t *attr, unsigned int attrlen)
336 int eapsim_attribute;
337 unsigned int eapsim_len;
338 int es_attribute_count;
340 es_attribute_count=0;
342 /* big enough to have even a single attribute */
344 radlog(L_ERR, "eap: EAP-Sim attribute too short: %d < 2", attrlen);
348 newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
349 if (!newvp) return 0;
350 newvp->lvalue = attr[0];
352 pairadd(&(r->vps), newvp);
357 /* now, loop processing each attribute that we find */
361 radlog(L_ERR, "eap: EAP-Sim attribute %d too short: %d < 2", es_attribute_count, attrlen);
365 eapsim_attribute = attr[0];
366 eapsim_len = attr[1] * 4;
368 if(eapsim_len > attrlen) {
369 radlog(L_ERR, "eap: EAP-Sim attribute %d (no.%d) has length longer than data (%d > %d)"
371 , es_attribute_count, eapsim_len, attrlen);
375 if(eapsim_len > MAX_STRING_LEN) {
376 eapsim_len = MAX_STRING_LEN;
378 if (eapsim_len < 2) {
379 radlog(L_ERR, "eap: EAP-Sim attribute %d (no.%d) has length too small",
380 eapsim_attribute, es_attribute_count);
384 newvp = paircreate(eapsim_attribute+ATTRIBUTE_EAP_SIM_BASE, PW_TYPE_OCTETS);
385 memcpy(newvp->vp_strvalue, &attr[2], eapsim_len-2);
386 newvp->length = eapsim_len-2;
387 pairadd(&(r->vps), newvp);
390 /* advance pointers, decrement length */
392 attrlen -= eapsim_len;
393 es_attribute_count++;
398 int unmap_eapsim_types(RADIUS_PACKET *r)
402 esvp = pairfind(r->vps, ATTRIBUTE_EAP_BASE+PW_EAP_SIM);
404 radlog(L_ERR, "eap: EAP-Sim attribute not found");
408 return unmap_eapsim_basictypes(r, esvp->vp_octets, esvp->length);
412 * calculate the MAC for the EAP message, given the key.
413 * The "extra" will be appended to the EAP message and included in the
418 eapsim_checkmac(VALUE_PAIR *rvps,
419 uint8_t key[EAPSIM_AUTH_SIZE],
420 uint8_t *extra, int extralen,
429 mac = pairfind(rvps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC);
432 || mac->length != 18) {
433 /* can't check a packet with no AT_MAC attribute */
437 /* get original copy of EAP message, note that it was sanitized
438 * to have a valid length, which we depend upon.
440 e = eap_attribute(rvps);
447 /* make copy big enough for everything */
448 elen = e->length[0]*256 + e->length[1];
449 len = elen + extralen;
451 buffer = malloc(len);
458 memcpy(buffer, e, elen);
459 memcpy(buffer+elen, extra, extralen);
462 * now look for the AT_MAC attribute in the copy of the buffer
463 * and make sure that the checksum is zero.
469 /* first attribute is 8 bytes into the EAP packet.
470 * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved.
473 while(attr < (buffer+elen)) {
474 if(attr[0] == PW_EAP_SIM_MAC) {
475 /* zero the data portion, after making sure
476 * the size is >=5. Maybe future versions.
477 * will use more bytes, so be liberal.
483 memset(&attr[4], 0, (attr[1]-1)*4);
485 /* advance the pointer */
490 /* now, HMAC-SHA1 it with the key. */
491 lrad_hmac_sha1(buffer, len,
495 if(memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0) {
508 * definitions changed to take a buffer for unknowns
509 * as this is more thread safe.
511 const char *simstates[]={ "init", "start", NULL };
513 const char *sim_state2name(enum eapsim_clientstates state,
517 if(state >= eapsim_client_maxstates)
519 snprintf(statenamebuf, statenamebuflen,
520 "eapstate:%d", state);
525 return simstates[state];
529 const char *subtypes[]={ "subtype0", "subtype1", "subtype2", "subtype3",
530 "subtype4", "subtype5", "subtype6", "subtype7",
531 "subtype8", "subtype9",
539 const char *sim_subtype2name(enum eapsim_subtype subtype,
540 char *subtypenamebuf,
541 int subtypenamebuflen)
543 if(subtype >= eapsim_max_subtype)
545 snprintf(subtypenamebuf, subtypenamebuflen,
546 "illegal-subtype:%d", subtype);
547 return subtypenamebuf;
551 return subtypes[subtype];
561 const char *radius_dir = RADDBDIR;
563 int radlog(int lvl, const char *msg, ...)
569 r = vfprintf(stderr, msg, ap);
576 main(int argc, char *argv[])
579 RADIUS_PACKET *req,*req2;
580 VALUE_PAIR *vp, *vpkey, *vpextra;
581 extern unsigned int sha1_data_problems;
588 sha1_data_problems = 1;
591 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
592 librad_perror("radclient");
596 if ((req = rad_alloc(1)) == NULL) {
597 librad_perror("radclient");
601 if ((req2 = rad_alloc(1)) == NULL) {
602 librad_perror("radclient");
607 if(req->vps) pairfree(&req->vps);
608 if(req2->vps) pairfree(&req2->vps);
610 if ((req->vps = readvp2(stdin, &filedone, "eapsimlib:")) == NULL) {
615 vp_printlist(stdout, req->vps);
617 map_eapsim_types(req);
619 printf("Mapped to:\n");
620 vp_printlist(stdout, req->vps);
622 /* find the EAP-Message, copy it to req2 */
623 vp = paircopy2(req->vps, PW_EAP_MESSAGE);
625 if(vp == NULL) continue;
627 pairadd(&req2->vps, vp);
629 /* only call unmap for sim types here */
630 unmap_eap_types(req2);
631 unmap_eapsim_types(req2);
633 printf("Unmapped to:\n");
634 vp_printlist(stdout, req2->vps);
636 vp = pairfind(req2->vps,
637 ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC);
638 vpkey = pairfind(req->vps, ATTRIBUTE_EAP_SIM_KEY);
639 vpextra = pairfind(req->vps, ATTRIBUTE_EAP_SIM_EXTRA);
641 if(vp != NULL && vpkey != NULL && vpextra!=NULL) {
644 /* find the EAP-Message, copy it to req2 */
646 memset(calcmac, 0, sizeof(calcmac));
647 printf("Confirming MAC...");
648 if(eapsim_checkmac(req2->vps, vpkey->vp_strvalue,
649 vpextra->vp_strvalue, vpextra->length,
655 printf("calculated MAC (");
656 for (i = 0; i < 20; i++) {
663 printf("%02x", calcmac[i]);
665 printf(" did not match\n");