2 * rlm_eap_sim.c Handles that are called from eap for SIM
4 * The development of the EAP/SIM support was funded by Internet Foundation
5 * Austria (http://www.nic.at/ipa).
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
24 * Copyright 2003 The FreeRADIUS server project
33 #include "../../eap.h"
34 #include "eap_types.h"
37 #include <rad_assert.h>
39 struct eap_sim_server_state {
40 enum eapsim_serverstates state;
41 struct eapsim_keys keys;
46 * Add value pair to reply
48 static void add_reply(VALUE_PAIR** vp,
49 const char* name, const char* value, int len)
51 VALUE_PAIR *reply_attr;
52 reply_attr = pairmake(name, "", T_OP_EQ);
55 "add_reply failed to create attribute %s: %s\n",
60 memcpy(reply_attr->strvalue, value, len);
61 reply_attr->length = len;
62 pairadd(vp, reply_attr);
65 static void eap_sim_state_free(void *opaque)
67 struct eap_sim_server_state *ess = (struct eap_sim_server_state *)opaque;
75 * build a reply to be sent.
77 static int eap_sim_compose(EAP_HANDLER *handler)
79 /* we will set the ID on requests, since we have to HMAC it */
80 handler->eap_ds->set_request_id = 1;
82 return map_eapsim_basictypes(handler->request->reply,
83 handler->eap_ds->request);
86 static int eap_sim_sendstart(EAP_HANDLER *handler)
88 VALUE_PAIR **vps, *newvp;
90 struct eap_sim_server_state *ess;
92 rad_assert(handler->request != NULL);
93 rad_assert(handler->request->reply);
95 ess = (struct eap_sim_server_state *)handler->opaque;
97 /* these are the outgoing attributes */
98 vps = &handler->request->reply->vps;
100 rad_assert(vps != NULL);
103 * add appropriate TLVs for the EAP things we wish to send.
106 /* the version list. We support only version 1. */
107 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST,
109 words = (uint16_t *)newvp->strvalue;
110 newvp->length = 3*sizeof(uint16_t);
111 words[0] = htons(1*sizeof(uint16_t));
112 words[1] = htons(EAP_SIM_VERSION);
116 /* set the EAP_ID - new value */
117 newvp = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
118 newvp->lvalue = ess->sim_id++;
119 pairreplace(vps, newvp);
121 /* record it in the ess */
122 ess->keys.versionlistlen = 2;
123 memcpy(ess->keys.versionlist, words+1, ess->keys.versionlistlen);
125 /* the ANY_ID attribute. We do not support re-auth or pseudonym */
126 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ,
129 newvp->strvalue[0]=0;
130 newvp->strvalue[0]=1;
133 /* the SUBTYPE, set to start. */
134 newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
135 newvp->lvalue = eapsim_start;
136 pairreplace(vps, newvp);
141 static int eap_sim_getchalans(VALUE_PAIR *vps, int chalno,
142 struct eap_sim_server_state *ess)
146 rad_assert(chalno >= 0 && chalno < 3);
148 vp = pairfind(vps, ATTRIBUTE_EAP_SIM_RAND1+chalno);
150 /* bad, we can't find stuff! */
151 DEBUG2(" eap-sim can not find sim-challenge%d",chalno+1);
154 if(vp->length != EAPSIM_RAND_SIZE) {
155 DEBUG2(" eap-sim chal%d is not 8-bytes: %d", chalno+1,
159 memcpy(ess->keys.rand[chalno], vp->strvalue, EAPSIM_RAND_SIZE);
161 vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SRES1+chalno);
163 /* bad, we can't find stuff! */
164 DEBUG2(" eap-sim can not find sim-sres%d",chalno+1);
167 if(vp->length != EAPSIM_SRES_SIZE) {
168 DEBUG2(" eap-sim sres%d is not 16-bytes: %d", chalno+1,
172 memcpy(ess->keys.sres[chalno], vp->strvalue, EAPSIM_SRES_SIZE);
174 vp = pairfind(vps, ATTRIBUTE_EAP_SIM_KC1+chalno);
176 /* bad, we can't find stuff! */
177 DEBUG2(" eap-sim can not find sim-kc%d",chalno+1);
180 if(vp->length != EAPSIM_Kc_SIZE) {
181 DEBUG2(" eap-sim kc%d is not 8-bytes: %d", chalno+1,
185 memcpy(ess->keys.Kc[chalno], vp->strvalue, EAPSIM_Kc_SIZE);
191 * this code sends the challenge itself.
193 * Challenges will come from one of three places eventually:
195 * 1 from attributes like ATTRIBUTE_EAP_SIM_RANDx
196 * (these might be retrived from a database)
198 * 2 from internally implemented SIM authenticators
199 * (a simple one based upon XOR will be provided)
201 * 3 from some kind of SS7 interface.
203 * For now, they only come from attributes.
204 * It might be that the best way to do 2/3 will be with a different
205 * module to generate/calculate things.
208 static int eap_sim_sendchallenge(EAP_HANDLER *handler)
210 struct eap_sim_server_state *ess;
211 VALUE_PAIR **invps, **outvps, *newvp;
213 ess = (struct eap_sim_server_state *)handler->opaque;
214 rad_assert(handler->request != NULL);
215 rad_assert(handler->request->reply);
217 /* invps is the data from the client.
218 * but, this is for non-protocol data here. We should
219 * already have consumed any client originated data.
221 invps = &handler->request->packet->vps;
223 /* outvps is the data to the client. */
224 outvps= &handler->request->reply->vps;
226 printf("+++> EAP-sim decoded packet:\n");
227 vp_printlist(stdout, *invps);
229 /* okay, we got the challenges! Put them into an attribute */
230 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND,
232 memset(newvp->strvalue, 0, 2); /* clear reserved bytes */
233 memcpy(newvp->strvalue+2+EAPSIM_RAND_SIZE*0, ess->keys.rand[0], EAPSIM_RAND_SIZE);
234 memcpy(newvp->strvalue+2+EAPSIM_RAND_SIZE*1, ess->keys.rand[1], EAPSIM_RAND_SIZE);
235 memcpy(newvp->strvalue+2+EAPSIM_RAND_SIZE*2, ess->keys.rand[2], EAPSIM_RAND_SIZE);
236 newvp->length = 2+EAPSIM_RAND_SIZE*3;
237 pairadd(outvps, newvp);
239 /* set the EAP_ID - new value */
240 newvp = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
241 newvp->lvalue = ess->sim_id++;
242 pairreplace(outvps, newvp);
244 /* make a copy of the identity */
245 ess->keys.identitylen = strlen(handler->identity);
246 memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen);
248 /* all set, calculate keys! */
249 eapsim_calculate_keys(&ess->keys);
251 #ifdef EAP_SIM_DEBUG_PRF
252 eapsim_dump_mk(&ess->keys);
256 * need to include an AT_MAC attribute so that it will get
257 * calculated. The NONCE_MT and the MAC are both 16 bytes, so
258 * we store the NONCE_MT in the MAC for the encoder, which
259 * will pull it out before it does the operation.
262 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC,
264 memcpy(newvp->strvalue, ess->keys.nonce_mt, 16);
266 pairreplace(outvps, newvp);
268 newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, PW_TYPE_OCTETS);
269 memcpy(newvp->strvalue, ess->keys.K_aut, 16);
271 pairreplace(outvps, newvp);
273 /* the SUBTYPE, set to challenge. */
274 newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
275 newvp->lvalue = eapsim_challenge;
276 pairreplace(outvps, newvp);
281 #ifndef EAPTLS_MPPE_KEY_LEN
282 #define EAPTLS_MPPE_KEY_LEN 32
286 * this code sends the success message.
288 * the only work to be done is the add the appropriate SEND/RECV
289 * radius attributes derived from the MSK.
292 static int eap_sim_sendsuccess(EAP_HANDLER *handler)
295 struct eap_sim_server_state *ess;
299 /* outvps is the data to the client. */
300 outvps= &handler->request->reply->vps;
301 ess = (struct eap_sim_server_state *)handler->opaque;
303 /* set the EAP_ID - new value */
304 newvp = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
305 newvp->lvalue = ess->sim_id++;
306 pairreplace(outvps, newvp);
309 add_reply(outvps, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
310 p += EAPTLS_MPPE_KEY_LEN;
311 add_reply(outvps, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN);
317 * run the server state machine.
319 static void eap_sim_stateenter(EAP_HANDLER *handler,
320 struct eap_sim_server_state *ess,
321 enum eapsim_serverstates newstate)
324 case eapsim_server_start:
326 * send the EAP-SIM Start message, listing the
327 * versions that we support.
329 eap_sim_sendstart(handler);
332 case eapsim_server_challenge:
334 * send the EAP-SIM Challenge message.
336 eap_sim_sendchallenge(handler);
339 case eapsim_server_success:
341 * send the EAP Success message
343 eap_sim_sendsuccess(handler);
344 handler->eap_ds->request->code = PW_EAP_SUCCESS;
349 * nothing to do for this transition.
354 ess->state = newstate;
356 /* build the target packet */
357 eap_sim_compose(handler);
361 * Initiate the EAP-SIM session by starting the state machine
362 * and initiating the state.
364 static int eap_sim_initiate(void *type_data, EAP_HANDLER *handler)
366 struct eap_sim_server_state *ess;
371 outvps = handler->request->reply->vps;
373 type_data = type_data; /* shut up compiler */
375 vp = pairfind(outvps, ATTRIBUTE_EAP_SIM_RAND1);
377 DEBUG2(" can not initiate sim, no RAND1 attribute");
381 ess = malloc(sizeof(struct eap_sim_server_state));
383 DEBUG2(" no space for eap sim state");
387 handler->opaque = ((void *)ess);
388 handler->free_opaque = eap_sim_state_free;
390 handler->stage = AUTHENTICATE;
393 * save the keying material, because it could change on a subsequent
397 if((eap_sim_getchalans(outvps, 0, ess) +
398 eap_sim_getchalans(outvps, 1, ess) +
399 eap_sim_getchalans(outvps, 2, ess)) != 3)
401 DEBUG2(" can not initiate sim, missing attributes");
406 * this value doesn't have be strong, but it is good if it
407 * is different now and then
410 ess->sim_id = (n & 0xff);
412 eap_sim_stateenter(handler, ess, eapsim_server_start);
419 * process an EAP-Sim/Response/Start.
421 * verify that client chose a version, and provided a NONCE_MT,
422 * and if so, then change states to challenge, and send the new
423 * challenge, else, resend the Request/Start.
426 static int process_eap_sim_start(EAP_HANDLER *handler, VALUE_PAIR *vps)
428 VALUE_PAIR *nonce_vp, *selectedversion_vp;
429 struct eap_sim_server_state *ess;
432 ess = (struct eap_sim_server_state *)handler->opaque;
434 nonce_vp = pairfind(vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT);
435 selectedversion_vp = pairfind(vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION);
437 if(nonce_vp == NULL ||
438 selectedversion_vp == NULL) {
439 DEBUG2(" client did not select a version and send a NONCE");
440 eap_sim_stateenter(handler, ess, eapsim_server_start);
445 * okay, good got stuff that we need. Check the version we found.
447 if(selectedversion_vp->length < 2) {
448 DEBUG2(" EAP-Sim version field is too short.");
451 memcpy(&simversion, selectedversion_vp->strvalue, sizeof(simversion));
452 simversion = ntohs(simversion);
453 if(simversion != EAP_SIM_VERSION) {
454 DEBUG2(" EAP-Sim version %d is unknown.", simversion);
458 /* record it for later keying */
459 memcpy(ess->keys.versionselect, selectedversion_vp->strvalue,
460 sizeof(ess->keys.versionselect));
463 * double check the nonce size.
465 if(nonce_vp->length != 18) {
466 DEBUG2(" EAP-Sim nonce_mt must be 16 bytes (+2 bytes padding), not %d", nonce_vp->length);
469 memcpy(ess->keys.nonce_mt, nonce_vp->strvalue+2, 16);
471 /* everything looks good, change states */
472 eap_sim_stateenter(handler, ess, eapsim_server_challenge);
478 * process an EAP-Sim/Response/Challenge
480 * verify that MAC that we received matches what we would have
481 * calculated from the packet with the SRESx appended.
484 static int process_eap_sim_challenge(EAP_HANDLER *handler, VALUE_PAIR *vps)
486 struct eap_sim_server_state *ess;
487 unsigned char srescat[EAPSIM_SRES_SIZE*3];
488 unsigned char calcmac[EAPSIM_CALCMAC_SIZE];
490 ess = (struct eap_sim_server_state *)handler->opaque;
492 memcpy(srescat +(0*EAPSIM_SRES_SIZE), ess->keys.sres[0], EAPSIM_SRES_SIZE);
493 memcpy(srescat +(1*EAPSIM_SRES_SIZE), ess->keys.sres[1], EAPSIM_SRES_SIZE);
494 memcpy(srescat +(2*EAPSIM_SRES_SIZE), ess->keys.sres[2], EAPSIM_SRES_SIZE);
496 /* verify the MAC, now that we have all the keys. */
497 if(eapsim_checkmac(vps, ess->keys.K_aut,
498 srescat, sizeof(srescat),
500 DEBUG2("MAC check succeed\n");
503 unsigned char macline[20*3];
507 for (i = 0; i < EAPSIM_CALCMAC_SIZE; i++) {
514 sprintf(m, "%02x", calcmac[i]);
517 DEBUG2("calculated MAC (%s) did not match", macline);
521 /* everything looks good, change states */
522 eap_sim_stateenter(handler, ess, eapsim_server_success);
528 * Authenticate a previously sent challenge.
530 static int eap_sim_authenticate(void *arg, EAP_HANDLER *handler)
532 struct eap_sim_server_state *ess;
533 VALUE_PAIR *vp, *vps;
534 enum eapsim_subtype subtype;
537 arg = arg; /* shut up compiler */
539 ess = (struct eap_sim_server_state *)handler->opaque;
541 /* vps is the data from the client */
542 vps = handler->request->packet->vps;
544 success= unmap_eapsim_basictypes(handler->request->packet,
545 handler->eap_ds->response->type.data,
546 handler->eap_ds->response->type.length);
552 /* see what kind of message we have gotten */
553 if((vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SUBTYPE)) == NULL)
555 DEBUG2(" no subtype attribute was created, message dropped");
558 subtype = vp->lvalue;
561 case eapsim_server_start:
565 * pretty much anything else here is illegal,
566 * so we will retransmit the request.
568 eap_sim_stateenter(handler, ess, eapsim_server_start);
573 * a response to our EAP-Sim/Request/Start!
576 return process_eap_sim_start(handler, vps);
579 case eapsim_server_challenge:
583 * pretty much anything else here is illegal,
584 * so we will retransmit the request.
586 eap_sim_stateenter(handler, ess, eapsim_server_challenge);
589 case eapsim_challenge:
591 * a response to our EAP-Sim/Request/Challenge!
594 return process_eap_sim_challenge(handler, vps);
599 /* if we get into some other state, die, as this
602 DEBUG2(" illegal-unknown state reached in eap_sim_authenticate\n");
610 * The module name should be the only globally exported symbol.
611 * That is, everything else should be 'static'.
613 EAP_TYPE rlm_eap_sim = {
615 NULL, /* XXX attach */
616 eap_sim_initiate, /* Start the initial request */
617 NULL, /* XXX authorization */
618 eap_sim_authenticate, /* authentication */
619 NULL /* XXX detach */
624 * Revision 1.12 2004-03-19 02:20:35 mcr
625 * increment the EAP-id on each stage of the transaction.
627 * Revision 1.11 2004/02/26 19:04:31 aland
628 * perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
630 * Whitespace changes only, from a fresh checkout.
634 * Revision 1.10 2004/01/30 20:35:33 mcr
635 * capture the RAND/SRES/Kc when we initialize the SIM
636 * rather than later, when they may have changed.
638 * Revision 1.9 2004/01/30 19:38:29 mcr
639 * added some debugging of why EAP-sim might not want to
640 * handle the request - lacking RAND1 attribute.
642 * Revision 1.8 2003/12/29 01:13:43 mcr
643 * if the un-marshalling fails, then fail the packet.
645 * Revision 1.7 2003/11/22 00:21:17 mcr
646 * send the encryption keys to the AccessPoint.
648 * Revision 1.6 2003/11/22 00:10:18 mcr
649 * the version list attribute's length of versions is in bytes,
652 * Revision 1.5 2003/11/21 19:15:51 mcr
653 * rename "SIM-Chal" to "SIM-Rand" to sync with names in official
656 * Revision 1.4 2003/11/21 19:02:19 mcr
657 * pack the RAND attribute properly - should have 2 bytes
660 * Revision 1.3 2003/11/06 15:45:12 aland
663 * Revision 1.2 2003/10/31 22:33:45 mcr
664 * fixes for version list length types.
665 * do not include length in hash.
666 * use defines rather than constant sizes.
668 * Revision 1.1 2003/10/29 02:49:19 mcr
669 * initial commit of eap-sim
671 * Revision 1.3 2003/09/14 00:44:42 mcr
672 * finished trivial challenge state.
676 * c-file-style: "linux"