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 /* record it in the ess */
117 ess->keys.versionlistlen = 2;
118 memcpy(ess->keys.versionlist, words+1, ess->keys.versionlistlen);
120 /* the ANY_ID attribute. We do not support re-auth or pseudonym */
121 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ,
124 newvp->strvalue[0]=0;
125 newvp->strvalue[0]=1;
128 /* the SUBTYPE, set to start. */
129 newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
130 newvp->lvalue = eapsim_start;
131 pairreplace(vps, newvp);
136 static int eap_sim_getchalans(VALUE_PAIR *vps, int chalno,
137 struct eap_sim_server_state *ess)
141 rad_assert(chalno >= 0 && chalno < 3);
143 vp = pairfind(vps, ATTRIBUTE_EAP_SIM_RAND1+chalno);
145 /* bad, we can't find stuff! */
146 DEBUG2(" eap-sim can not find sim-challenge%d",chalno+1);
149 if(vp->length != EAPSIM_RAND_SIZE) {
150 DEBUG2(" eap-sim chal%d is not 8-bytes: %d", chalno+1,
154 memcpy(ess->keys.rand[chalno], vp->strvalue, EAPSIM_RAND_SIZE);
156 vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SRES1+chalno);
158 /* bad, we can't find stuff! */
159 DEBUG2(" eap-sim can not find sim-sres%d",chalno+1);
162 if(vp->length != EAPSIM_SRES_SIZE) {
163 DEBUG2(" eap-sim sres%d is not 16-bytes: %d", chalno+1,
167 memcpy(ess->keys.sres[chalno], vp->strvalue, EAPSIM_SRES_SIZE);
169 vp = pairfind(vps, ATTRIBUTE_EAP_SIM_KC1+chalno);
171 /* bad, we can't find stuff! */
172 DEBUG2(" eap-sim can not find sim-kc%d",chalno+1);
175 if(vp->length != EAPSIM_Kc_SIZE) {
176 DEBUG2(" eap-sim kc%d is not 8-bytes: %d", chalno+1,
180 memcpy(ess->keys.Kc[chalno], vp->strvalue, EAPSIM_Kc_SIZE);
186 * this code sends the challenge itself.
188 * Challenges will come from one of three places eventually:
190 * 1 from attributes like ATTRIBUTE_EAP_SIM_RANDx
191 * (these might be retrived from a database)
193 * 2 from internally implemented SIM authenticators
194 * (a simple one based upon XOR will be provided)
196 * 3 from some kind of SS7 interface.
198 * For now, they only come from attributes.
199 * It might be that the best way to do 2/3 will be with a different
200 * module to generate/calculate things.
203 static int eap_sim_sendchallenge(EAP_HANDLER *handler)
205 struct eap_sim_server_state *ess;
206 VALUE_PAIR **invps, **outvps, *newvp;
208 ess = (struct eap_sim_server_state *)handler->opaque;
209 rad_assert(handler->request != NULL);
210 rad_assert(handler->request->reply);
212 /* invps is the data from the client.
213 * but, this is for non-protocol data here. We should
214 * already have consumed any client originated data.
216 invps = &handler->request->packet->vps;
218 /* outvps is the data to the client. */
219 outvps= &handler->request->reply->vps;
221 printf("+++> EAP-sim decoded packet:\n");
222 vp_printlist(stdout, *invps);
224 /* okay, we got the challenges! Put them into an attribute */
225 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND,
227 memset(newvp->strvalue, 0, 2); /* clear reserved bytes */
228 memcpy(newvp->strvalue+2+EAPSIM_RAND_SIZE*0, ess->keys.rand[0], EAPSIM_RAND_SIZE);
229 memcpy(newvp->strvalue+2+EAPSIM_RAND_SIZE*1, ess->keys.rand[1], EAPSIM_RAND_SIZE);
230 memcpy(newvp->strvalue+2+EAPSIM_RAND_SIZE*2, ess->keys.rand[2], EAPSIM_RAND_SIZE);
231 newvp->length = 2+EAPSIM_RAND_SIZE*3;
232 pairadd(outvps, newvp);
234 /* make a copy of the identity */
235 ess->keys.identitylen = strlen(handler->identity);
236 memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen);
238 /* all set, calculate keys! */
239 eapsim_calculate_keys(&ess->keys);
241 #ifdef EAP_SIM_DEBUG_PRF
242 eapsim_dump_mk(&ess->keys);
246 * need to include an AT_MAC attribute so that it will get
247 * calculated. The NONCE_MT and the MAC are both 16 bytes, so
248 * we store the NONCE_MT in the MAC for the encoder, which
249 * will pull it out before it does the operation.
252 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC,
254 memcpy(newvp->strvalue, ess->keys.nonce_mt, 16);
256 pairreplace(outvps, newvp);
258 newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, PW_TYPE_OCTETS);
259 memcpy(newvp->strvalue, ess->keys.K_aut, 16);
261 pairreplace(outvps, newvp);
263 /* the SUBTYPE, set to challenge. */
264 newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
265 newvp->lvalue = eapsim_challenge;
266 pairreplace(outvps, newvp);
271 #ifndef EAPTLS_MPPE_KEY_LEN
272 #define EAPTLS_MPPE_KEY_LEN 32
276 * this code sends the success message.
278 * the only work to be done is the add the appropriate SEND/RECV
279 * radius attributes derived from the MSK.
282 static int eap_sim_sendsuccess(EAP_HANDLER *handler)
285 struct eap_sim_server_state *ess;
288 /* outvps is the data to the client. */
289 outvps= &handler->request->reply->vps;
290 ess = (struct eap_sim_server_state *)handler->opaque;
293 add_reply(outvps, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
294 p += EAPTLS_MPPE_KEY_LEN;
295 add_reply(outvps, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN);
301 * run the server state machine.
303 static void eap_sim_stateenter(EAP_HANDLER *handler,
304 struct eap_sim_server_state *ess,
305 enum eapsim_serverstates newstate)
308 case eapsim_server_start:
310 * send the EAP-SIM Start message, listing the
311 * versions that we support.
313 eap_sim_sendstart(handler);
316 case eapsim_server_challenge:
318 * send the EAP-SIM Challenge message.
320 eap_sim_sendchallenge(handler);
323 case eapsim_server_success:
325 * send the EAP Success message
327 eap_sim_sendsuccess(handler);
328 handler->eap_ds->request->code = PW_EAP_SUCCESS;
333 * nothing to do for this transition.
338 ess->state = newstate;
340 /* build the target packet */
341 eap_sim_compose(handler);
345 * Initiate the EAP-SIM session by starting the state machine
346 * and initiating the state.
348 static int eap_sim_initiate(void *type_data, EAP_HANDLER *handler)
350 struct eap_sim_server_state *ess;
354 outvps = handler->request->reply->vps;
356 type_data = type_data; /* shut up compiler */
358 vp = pairfind(outvps, ATTRIBUTE_EAP_SIM_RAND1);
360 DEBUG2(" can not initiate sim, no RAND1 attribute");
364 ess = malloc(sizeof(struct eap_sim_server_state));
366 DEBUG2(" no space for eap sim state");
370 handler->opaque = ((void *)ess);
371 handler->free_opaque = eap_sim_state_free;
373 handler->stage = AUTHENTICATE;
376 * save the keying material, because it could change on a subsequent
380 if((eap_sim_getchalans(outvps, 0, ess) +
381 eap_sim_getchalans(outvps, 1, ess) +
382 eap_sim_getchalans(outvps, 2, ess)) != 3)
384 DEBUG2(" can not initiate sim, missing attributes");
389 eap_sim_stateenter(handler, ess, eapsim_server_start);
396 * process an EAP-Sim/Response/Start.
398 * verify that client chose a version, and provided a NONCE_MT,
399 * and if so, then change states to challenge, and send the new
400 * challenge, else, resend the Request/Start.
403 static int process_eap_sim_start(EAP_HANDLER *handler, VALUE_PAIR *vps)
405 VALUE_PAIR *nonce_vp, *selectedversion_vp;
406 struct eap_sim_server_state *ess;
409 ess = (struct eap_sim_server_state *)handler->opaque;
411 nonce_vp = pairfind(vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT);
412 selectedversion_vp = pairfind(vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION);
414 if(nonce_vp == NULL ||
415 selectedversion_vp == NULL) {
416 DEBUG2(" client did not select a version and send a NONCE");
417 eap_sim_stateenter(handler, ess, eapsim_server_start);
422 * okay, good got stuff that we need. Check the version we found.
424 if(selectedversion_vp->length < 2) {
425 DEBUG2(" EAP-Sim version field is too short.");
428 memcpy(&simversion, selectedversion_vp->strvalue, sizeof(simversion));
429 simversion = ntohs(simversion);
430 if(simversion != EAP_SIM_VERSION) {
431 DEBUG2(" EAP-Sim version %d is unknown.", simversion);
435 /* record it for later keying */
436 memcpy(ess->keys.versionselect, selectedversion_vp->strvalue,
437 sizeof(ess->keys.versionselect));
440 * double check the nonce size.
442 if(nonce_vp->length != 18) {
443 DEBUG2(" EAP-Sim nonce_mt must be 16 bytes (+2 bytes padding), not %d", nonce_vp->length);
446 memcpy(ess->keys.nonce_mt, nonce_vp->strvalue+2, 16);
448 /* everything looks good, change states */
449 eap_sim_stateenter(handler, ess, eapsim_server_challenge);
455 * process an EAP-Sim/Response/Challenge
457 * verify that MAC that we received matches what we would have
458 * calculated from the packet with the SRESx appended.
461 static int process_eap_sim_challenge(EAP_HANDLER *handler, VALUE_PAIR *vps)
463 struct eap_sim_server_state *ess;
464 unsigned char srescat[EAPSIM_SRES_SIZE*3];
465 unsigned char calcmac[EAPSIM_CALCMAC_SIZE];
467 ess = (struct eap_sim_server_state *)handler->opaque;
469 memcpy(srescat +(0*EAPSIM_SRES_SIZE), ess->keys.sres[0], EAPSIM_SRES_SIZE);
470 memcpy(srescat +(1*EAPSIM_SRES_SIZE), ess->keys.sres[1], EAPSIM_SRES_SIZE);
471 memcpy(srescat +(2*EAPSIM_SRES_SIZE), ess->keys.sres[2], EAPSIM_SRES_SIZE);
473 /* verify the MAC, now that we have all the keys. */
474 if(eapsim_checkmac(vps, ess->keys.K_aut,
475 srescat, sizeof(srescat),
477 DEBUG2("MAC check succeed\n");
480 unsigned char macline[20*3];
484 for (i = 0; i < EAPSIM_CALCMAC_SIZE; i++) {
491 sprintf(m, "%02x", calcmac[i]);
494 DEBUG2("calculated MAC (%s) did not match", macline);
498 /* everything looks good, change states */
499 eap_sim_stateenter(handler, ess, eapsim_server_success);
505 * Authenticate a previously sent challenge.
507 static int eap_sim_authenticate(void *arg, EAP_HANDLER *handler)
509 struct eap_sim_server_state *ess;
510 VALUE_PAIR *vp, *vps;
511 enum eapsim_subtype subtype;
514 arg = arg; /* shut up compiler */
516 ess = (struct eap_sim_server_state *)handler->opaque;
518 /* vps is the data from the client */
519 vps = handler->request->packet->vps;
521 success= unmap_eapsim_basictypes(handler->request->packet,
522 handler->eap_ds->response->type.data,
523 handler->eap_ds->response->type.length);
529 /* see what kind of message we have gotten */
530 if((vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SUBTYPE)) == NULL)
532 DEBUG2(" no subtype attribute was created, message dropped");
535 subtype = vp->lvalue;
538 case eapsim_server_start:
542 * pretty much anything else here is illegal,
543 * so we will retransmit the request.
545 eap_sim_stateenter(handler, ess, eapsim_server_start);
550 * a response to our EAP-Sim/Request/Start!
553 return process_eap_sim_start(handler, vps);
556 case eapsim_server_challenge:
560 * pretty much anything else here is illegal,
561 * so we will retransmit the request.
563 eap_sim_stateenter(handler, ess, eapsim_server_challenge);
566 case eapsim_challenge:
568 * a response to our EAP-Sim/Request/Challenge!
571 return process_eap_sim_challenge(handler, vps);
576 /* if we get into some other state, die, as this
579 DEBUG2(" illegal-unknown state reached in eap_sim_authenticate\n");
587 * The module name should be the only globally exported symbol.
588 * That is, everything else should be 'static'.
590 EAP_TYPE rlm_eap_sim = {
592 NULL, /* XXX attach */
593 eap_sim_initiate, /* Start the initial request */
594 NULL, /* XXX authorization */
595 eap_sim_authenticate, /* authentication */
596 NULL /* XXX detach */
601 * Revision 1.11 2004-02-26 19:04:31 aland
602 * perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
604 * Whitespace changes only, from a fresh checkout.
608 * Revision 1.10 2004/01/30 20:35:33 mcr
609 * capture the RAND/SRES/Kc when we initialize the SIM
610 * rather than later, when they may have changed.
612 * Revision 1.9 2004/01/30 19:38:29 mcr
613 * added some debugging of why EAP-sim might not want to
614 * handle the request - lacking RAND1 attribute.
616 * Revision 1.8 2003/12/29 01:13:43 mcr
617 * if the un-marshalling fails, then fail the packet.
619 * Revision 1.7 2003/11/22 00:21:17 mcr
620 * send the encryption keys to the AccessPoint.
622 * Revision 1.6 2003/11/22 00:10:18 mcr
623 * the version list attribute's length of versions is in bytes,
626 * Revision 1.5 2003/11/21 19:15:51 mcr
627 * rename "SIM-Chal" to "SIM-Rand" to sync with names in official
630 * Revision 1.4 2003/11/21 19:02:19 mcr
631 * pack the RAND attribute properly - should have 2 bytes
634 * Revision 1.3 2003/11/06 15:45:12 aland
637 * Revision 1.2 2003/10/31 22:33:45 mcr
638 * fixes for version list length types.
639 * do not include length in hash.
640 * use defines rather than constant sizes.
642 * Revision 1.1 2003/10/29 02:49:19 mcr
643 * initial commit of eap-sim
645 * Revision 1.3 2003/09/14 00:44:42 mcr
646 * finished trivial challenge state.