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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
24 * Copyright 2003 The FreeRADIUS server project
28 #include <freeradius-devel/autoconf.h>
33 #include "../../eap.h"
34 #include "eap_types.h"
37 #include <freeradius-devel/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->vp_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->vp_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->vp_strvalue[0]=0;
130 newvp->vp_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->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->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->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->vp_strvalue, 0, 2); /* clear reserved bytes */
233 memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*0, ess->keys.rand[0], EAPSIM_RAND_SIZE);
234 memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*1, ess->keys.rand[1], EAPSIM_RAND_SIZE);
235 memcpy(newvp->vp_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->vp_strvalue, ess->keys.nonce_mt, 16);
266 pairreplace(outvps, newvp);
268 newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, PW_TYPE_OCTETS);
269 memcpy(newvp->vp_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->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->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->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 */