perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_sim / rlm_eap_sim.c
1 /*
2  * rlm_eap_sim.c    Handles that are called from eap for SIM
3  *
4  * The development of the EAP/SIM support was funded by Internet Foundation
5  * Austria (http://www.nic.at/ipa).
6  *
7  * Version:     $Id$
8  *
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.
13  *
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.
18  *
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
22  *
23  * Copyright 2003  Michael Richardson <mcr@sandelman.ottawa.on.ca>
24  * Copyright 2003  The FreeRADIUS server project
25  *
26  */
27
28 #include "autoconf.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include "../../eap.h"
34 #include "eap_types.h"
35 #include "eap_sim.h"
36
37 #include <rad_assert.h>
38
39 struct eap_sim_server_state {
40         enum eapsim_serverstates state;
41         struct eapsim_keys keys;
42
43 };
44
45 /*
46  * Add value pair to reply
47  */
48 static void add_reply(VALUE_PAIR** vp,
49                       const char* name, const char* value, int len)
50 {
51         VALUE_PAIR *reply_attr;
52         reply_attr = pairmake(name, "", T_OP_EQ);
53         if (!reply_attr) {
54                 DEBUG("rlm_eap_sim: "
55                       "add_reply failed to create attribute %s: %s\n",
56                       name, librad_errstr);
57                 return;
58         }
59
60         memcpy(reply_attr->strvalue, value, len);
61         reply_attr->length = len;
62         pairadd(vp, reply_attr);
63 }
64
65 static void eap_sim_state_free(void *opaque)
66 {
67         struct eap_sim_server_state *ess = (struct eap_sim_server_state *)opaque;
68
69         if (!ess) return;
70
71         free(ess);
72 }
73
74 /*
75  *      build a reply to be sent.
76  */
77 static int eap_sim_compose(EAP_HANDLER *handler)
78 {
79         /* we will set the ID on requests, since we have to HMAC it */
80         handler->eap_ds->set_request_id = 1;
81
82         return map_eapsim_basictypes(handler->request->reply,
83                                      handler->eap_ds->request);
84 }
85
86 static int eap_sim_sendstart(EAP_HANDLER *handler)
87 {
88         VALUE_PAIR **vps, *newvp;
89         uint16_t *words;
90         struct eap_sim_server_state *ess;
91
92         rad_assert(handler->request != NULL);
93         rad_assert(handler->request->reply);
94
95         ess = (struct eap_sim_server_state *)handler->opaque;
96
97         /* these are the outgoing attributes */
98         vps = &handler->request->reply->vps;
99
100         rad_assert(vps != NULL);
101
102         /*
103          * add appropriate TLVs for the EAP things we wish to send.
104          */
105
106         /* the version list. We support only version 1. */
107         newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST,
108                         PW_TYPE_OCTETS);
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);
113         words[2] = 0;
114         pairadd(vps, newvp);
115
116         /* record it in the ess */
117         ess->keys.versionlistlen = 2;
118         memcpy(ess->keys.versionlist, words+1, ess->keys.versionlistlen);
119
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,
122                            PW_TYPE_OCTETS);
123         newvp->length = 2;
124         newvp->strvalue[0]=0;
125         newvp->strvalue[0]=1;
126         pairadd(vps, newvp);
127
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);
132
133         return 1;
134 }
135
136 static int eap_sim_getchalans(VALUE_PAIR *vps, int chalno,
137                               struct eap_sim_server_state *ess)
138 {
139         VALUE_PAIR *vp;
140
141         rad_assert(chalno >= 0 && chalno < 3);
142
143         vp = pairfind(vps, ATTRIBUTE_EAP_SIM_RAND1+chalno);
144         if(vp == NULL) {
145                 /* bad, we can't find stuff! */
146                 DEBUG2("   eap-sim can not find sim-challenge%d",chalno+1);
147                 return 0;
148         }
149         if(vp->length != EAPSIM_RAND_SIZE) {
150                 DEBUG2("   eap-sim chal%d is not 8-bytes: %d", chalno+1,
151                        vp->length);
152                 return 0;
153         }
154         memcpy(ess->keys.rand[chalno], vp->strvalue, EAPSIM_RAND_SIZE);
155
156         vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SRES1+chalno);
157         if(vp == NULL) {
158                 /* bad, we can't find stuff! */
159                 DEBUG2("   eap-sim can not find sim-sres%d",chalno+1);
160                 return 0;
161         }
162         if(vp->length != EAPSIM_SRES_SIZE) {
163                 DEBUG2("   eap-sim sres%d is not 16-bytes: %d", chalno+1,
164                        vp->length);
165                 return 0;
166         }
167         memcpy(ess->keys.sres[chalno], vp->strvalue, EAPSIM_SRES_SIZE);
168
169         vp = pairfind(vps, ATTRIBUTE_EAP_SIM_KC1+chalno);
170         if(vp == NULL) {
171                 /* bad, we can't find stuff! */
172                 DEBUG2("   eap-sim can not find sim-kc%d",chalno+1);
173                 return 0;
174         }
175         if(vp->length != EAPSIM_Kc_SIZE) {
176                 DEBUG2("   eap-sim kc%d is not 8-bytes: %d", chalno+1,
177                        vp->length);
178                 return 0;
179         }
180         memcpy(ess->keys.Kc[chalno], vp->strvalue, EAPSIM_Kc_SIZE);
181
182         return 1;
183 }
184
185 /*
186  * this code sends the challenge itself.
187  *
188  * Challenges will come from one of three places eventually:
189  *
190  * 1  from attributes like ATTRIBUTE_EAP_SIM_RANDx
191  *            (these might be retrived from a database)
192  *
193  * 2  from internally implemented SIM authenticators
194  *            (a simple one based upon XOR will be provided)
195  *
196  * 3  from some kind of SS7 interface.
197  *
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.
201  *
202  */
203 static int eap_sim_sendchallenge(EAP_HANDLER *handler)
204 {
205         struct eap_sim_server_state *ess;
206         VALUE_PAIR **invps, **outvps, *newvp;
207
208         ess = (struct eap_sim_server_state *)handler->opaque;
209         rad_assert(handler->request != NULL);
210         rad_assert(handler->request->reply);
211
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.
215          */
216         invps = &handler->request->packet->vps;
217
218         /* outvps is the data to the client. */
219         outvps= &handler->request->reply->vps;
220
221         printf("+++> EAP-sim decoded packet:\n");
222         vp_printlist(stdout, *invps);
223
224         /* okay, we got the challenges! Put them into an attribute */
225         newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND,
226                            PW_TYPE_OCTETS);
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);
233
234         /* make a copy of the identity */
235         ess->keys.identitylen = strlen(handler->identity);
236         memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen);
237
238         /* all set, calculate keys! */
239         eapsim_calculate_keys(&ess->keys);
240
241 #ifdef EAP_SIM_DEBUG_PRF
242         eapsim_dump_mk(&ess->keys);
243 #endif
244
245         /*
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.
250          */
251
252         newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC,
253                            PW_TYPE_OCTETS);
254         memcpy(newvp->strvalue, ess->keys.nonce_mt, 16);
255         newvp->length = 16;
256         pairreplace(outvps, newvp);
257
258         newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, PW_TYPE_OCTETS);
259         memcpy(newvp->strvalue, ess->keys.K_aut, 16);
260         newvp->length = 16;
261         pairreplace(outvps, newvp);
262
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);
267
268         return 1;
269 }
270
271 #ifndef EAPTLS_MPPE_KEY_LEN
272 #define EAPTLS_MPPE_KEY_LEN     32
273 #endif
274
275 /*
276  * this code sends the success message.
277  *
278  * the only work to be done is the add the appropriate SEND/RECV
279  * radius attributes derived from the MSK.
280  *
281  */
282 static int eap_sim_sendsuccess(EAP_HANDLER *handler)
283 {
284         unsigned char *p;
285         struct eap_sim_server_state *ess;
286         VALUE_PAIR **outvps;
287
288         /* outvps is the data to the client. */
289         outvps= &handler->request->reply->vps;
290         ess = (struct eap_sim_server_state *)handler->opaque;
291
292         p = ess->keys.msk;
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);
296         return 1;
297 }
298
299
300 /*
301  * run the server state machine.
302  */
303 static void eap_sim_stateenter(EAP_HANDLER *handler,
304                                struct eap_sim_server_state *ess,
305                                enum eapsim_serverstates newstate)
306 {
307         switch(newstate) {
308         case eapsim_server_start:
309                 /*
310                  * send the EAP-SIM Start message, listing the
311                  * versions that we support.
312                  */
313                 eap_sim_sendstart(handler);
314                 break;
315
316         case eapsim_server_challenge:
317                 /*
318                  * send the EAP-SIM Challenge message.
319                  */
320                 eap_sim_sendchallenge(handler);
321                 break;
322
323         case eapsim_server_success:
324                 /*
325                  * send the EAP Success message
326                  */
327                 eap_sim_sendsuccess(handler);
328                 handler->eap_ds->request->code = PW_EAP_SUCCESS;
329                 break;
330
331         default:
332                 /*
333                  * nothing to do for this transition.
334                  */
335                 break;
336         }
337
338         ess->state = newstate;
339
340         /* build the target packet */
341         eap_sim_compose(handler);
342 }
343
344 /*
345  *      Initiate the EAP-SIM session by starting the state machine
346  *      and initiating the state.
347  */
348 static int eap_sim_initiate(void *type_data, EAP_HANDLER *handler)
349 {
350         struct eap_sim_server_state *ess;
351         VALUE_PAIR *vp;
352         VALUE_PAIR *outvps;
353
354         outvps = handler->request->reply->vps;
355
356         type_data = type_data;  /* shut up compiler */
357
358         vp = pairfind(outvps, ATTRIBUTE_EAP_SIM_RAND1);
359         if(vp == NULL) {
360                 DEBUG2("   can not initiate sim, no RAND1 attribute");
361                 return 0;
362         }
363
364         ess = malloc(sizeof(struct eap_sim_server_state));
365         if(ess == NULL) {
366                 DEBUG2("   no space for eap sim state");
367                 return 0;
368         }
369
370         handler->opaque = ((void *)ess);
371         handler->free_opaque = eap_sim_state_free;
372
373         handler->stage = AUTHENTICATE;
374
375         /*
376          * save the keying material, because it could change on a subsequent
377          * retrival.
378          *
379          */
380         if((eap_sim_getchalans(outvps, 0, ess) +
381             eap_sim_getchalans(outvps, 1, ess) +
382             eap_sim_getchalans(outvps, 2, ess)) != 3)
383         {
384                 DEBUG2("   can not initiate sim, missing attributes");
385                 return 0;
386         }
387
388
389         eap_sim_stateenter(handler, ess, eapsim_server_start);
390
391         return 1;
392 }
393
394
395 /*
396  * process an EAP-Sim/Response/Start.
397  *
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.
401  *
402  */
403 static int process_eap_sim_start(EAP_HANDLER *handler, VALUE_PAIR *vps)
404 {
405         VALUE_PAIR *nonce_vp, *selectedversion_vp;
406         struct eap_sim_server_state *ess;
407         uint16_t simversion;
408
409         ess = (struct eap_sim_server_state *)handler->opaque;
410
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);
413
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);
418                 return 1;
419         }
420
421         /*
422          * okay, good got stuff that we need. Check the version we found.
423          */
424         if(selectedversion_vp->length < 2) {
425                 DEBUG2("   EAP-Sim version field is too short.");
426                 return 0;
427         }
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);
432                 return 0;
433         }
434
435         /* record it for later keying */
436         memcpy(ess->keys.versionselect, selectedversion_vp->strvalue,
437                sizeof(ess->keys.versionselect));
438
439         /*
440          * double check the nonce size.
441          */
442         if(nonce_vp->length != 18) {
443                 DEBUG2("   EAP-Sim nonce_mt must be 16 bytes (+2 bytes padding), not %d", nonce_vp->length);
444                 return 0;
445         }
446         memcpy(ess->keys.nonce_mt, nonce_vp->strvalue+2, 16);
447
448         /* everything looks good, change states */
449         eap_sim_stateenter(handler, ess, eapsim_server_challenge);
450         return 1;
451 }
452
453
454 /*
455  * process an EAP-Sim/Response/Challenge
456  *
457  * verify that MAC that we received matches what we would have
458  * calculated from the packet with the SRESx appended.
459  *
460  */
461 static int process_eap_sim_challenge(EAP_HANDLER *handler, VALUE_PAIR *vps)
462 {
463         struct eap_sim_server_state *ess;
464         unsigned char srescat[EAPSIM_SRES_SIZE*3];
465         unsigned char calcmac[EAPSIM_CALCMAC_SIZE];
466
467         ess = (struct eap_sim_server_state *)handler->opaque;
468
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);
472
473         /* verify the MAC, now that we have all the keys. */
474         if(eapsim_checkmac(vps, ess->keys.K_aut,
475                            srescat, sizeof(srescat),
476                            calcmac)) {
477                 DEBUG2("MAC check succeed\n");
478         } else {
479                 int i, j;
480                 unsigned char macline[20*3];
481                 char *m = macline;
482
483                 j=0;
484                 for (i = 0; i < EAPSIM_CALCMAC_SIZE; i++) {
485                         if(j==4) {
486                           *m++ = '_';
487                           j=0;
488                         }
489                         j++;
490
491                         sprintf(m, "%02x", calcmac[i]);
492                         m = m + strlen(m);
493                 }
494                 DEBUG2("calculated MAC (%s) did not match", macline);
495                 return 0;
496         }
497
498         /* everything looks good, change states */
499         eap_sim_stateenter(handler, ess, eapsim_server_success);
500         return 1;
501 }
502
503
504 /*
505  *      Authenticate a previously sent challenge.
506  */
507 static int eap_sim_authenticate(void *arg, EAP_HANDLER *handler)
508 {
509         struct eap_sim_server_state *ess;
510         VALUE_PAIR *vp, *vps;
511         enum eapsim_subtype subtype;
512         int success;
513
514         arg = arg; /* shut up compiler */
515
516         ess = (struct eap_sim_server_state *)handler->opaque;
517
518         /* vps is the data from the client */
519         vps = handler->request->packet->vps;
520
521         success= unmap_eapsim_basictypes(handler->request->packet,
522                                          handler->eap_ds->response->type.data,
523                                          handler->eap_ds->response->type.length);
524
525         if(!success) {
526           return 0;
527         }
528
529         /* see what kind of message we have gotten */
530         if((vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SUBTYPE)) == NULL)
531         {
532                 DEBUG2("   no subtype attribute was created, message dropped");
533                 return 0;
534         }
535         subtype = vp->lvalue;
536
537         switch(ess->state) {
538         case eapsim_server_start:
539                 switch(subtype) {
540                 default:
541                         /*
542                          * pretty much anything else here is illegal,
543                          * so we will retransmit the request.
544                          */
545                         eap_sim_stateenter(handler, ess, eapsim_server_start);
546                         return 1;
547
548                 case eapsim_start:
549                         /*
550                          * a response to our EAP-Sim/Request/Start!
551                          *
552                          */
553                         return process_eap_sim_start(handler, vps);
554                 }
555                 break;
556         case eapsim_server_challenge:
557                 switch(subtype) {
558                 default:
559                         /*
560                          * pretty much anything else here is illegal,
561                          * so we will retransmit the request.
562                          */
563                         eap_sim_stateenter(handler, ess, eapsim_server_challenge);
564                         return 1;
565
566                 case eapsim_challenge:
567                         /*
568                          * a response to our EAP-Sim/Request/Challenge!
569                          *
570                          */
571                         return process_eap_sim_challenge(handler, vps);
572                 }
573                 break;
574
575         default:
576                 /* if we get into some other state, die, as this
577                  * is a coding error!
578                  */
579                 DEBUG2("  illegal-unknown state reached in eap_sim_authenticate\n");
580                 abort();
581         }
582
583         return 0;
584 }
585
586 /*
587  *      The module name should be the only globally exported symbol.
588  *      That is, everything else should be 'static'.
589  */
590 EAP_TYPE rlm_eap_sim = {
591         "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 */
597 };
598
599 /*
600  * $Log$
601  * Revision 1.11  2004-02-26 19:04:31  aland
602  *      perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
603  *
604  *      Whitespace changes only, from a fresh checkout.
605  *
606  *      For bug # 13
607  *
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.
611  *
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.
615  *
616  * Revision 1.8  2003/12/29 01:13:43  mcr
617  *      if the un-marshalling fails, then fail the packet.
618  *
619  * Revision 1.7  2003/11/22 00:21:17  mcr
620  *      send the encryption keys to the AccessPoint.
621  *
622  * Revision 1.6  2003/11/22 00:10:18  mcr
623  *      the version list attribute's length of versions is in bytes,
624  *      not entries.
625  *
626  * Revision 1.5  2003/11/21 19:15:51  mcr
627  *      rename "SIM-Chal" to "SIM-Rand" to sync with names in official
628  *      documentation.
629  *
630  * Revision 1.4  2003/11/21 19:02:19  mcr
631  *      pack the RAND attribute properly - should have 2 bytes
632  * reserved.
633  *
634  * Revision 1.3  2003/11/06 15:45:12  aland
635  *      u_int -> uint
636  *
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.
641  *
642  * Revision 1.1  2003/10/29 02:49:19  mcr
643  *      initial commit of eap-sim
644  *
645  * Revision 1.3  2003/09/14 00:44:42  mcr
646  *      finished trivial challenge state.
647  *
648  *
649  */