Fixup EAP type definitions
[freeradius.git] / src / modules / rlm_eap / radeapclient.c
1 /*
2  * radeapclient.c       EAP specific radius packet debug tool.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/libradius.h>
29
30 #include <ctype.h>
31
32 #if HAVE_GETOPT_H
33 #       include <getopt.h>
34 #endif
35
36 #include <freeradius-devel/conf.h>
37 #include <freeradius-devel/radpaths.h>
38 #include <freeradius-devel/md5.h>
39
40 #include "eap_types.h"
41 #include "eap_sim.h"
42
43 extern int sha1_data_problems;
44
45 static int retries = 10;
46 static float timeout = 3;
47 static const char *secret = NULL;
48 static int do_output = 1;
49 static int do_summary = 0;
50 static int filedone = 0;
51 static int totalapp = 0;
52 static int totaldeny = 0;
53 static char filesecret[256];
54 char *radius_dir = NULL;
55 const char *progname = "radeapclient";
56 /* fr_randctx randctx; */
57
58 #ifdef WITH_TLS
59 #include <freeradius-devel/tls.h>
60 #endif
61
62 radlog_dest_t radlog_dest = RADLOG_STDERR;
63 const char *radlog_dir = NULL;
64 int debug_flag = 0;
65 char password[256];
66
67 struct eapsim_keys eapsim_mk;
68
69 static void map_eap_types(RADIUS_PACKET *req);
70 static void unmap_eap_types(RADIUS_PACKET *rep);
71 static int map_eapsim_types(RADIUS_PACKET *r);
72 static int unmap_eapsim_types(RADIUS_PACKET *r);
73
74 void debug_pair_list(UNUSED VALUE_PAIR *vp)
75 {
76         return;
77 }
78
79 static void NEVER_RETURNS usage(void)
80 {
81         fprintf(stderr, "Usage: radeapclient [options] server[:port] <command> [<secret>]\n");
82
83         fprintf(stderr, "  <command>    One of auth, acct, status, or disconnect.\n");
84         fprintf(stderr, "  -c count    Send each packet 'count' times.\n");
85         fprintf(stderr, "  -d raddb    Set dictionary directory.\n");
86         fprintf(stderr, "  -f file     Read packets from file, not stdin.\n");
87         fprintf(stderr, "  -r retries  If timeout, retry sending the packet 'retries' times.\n");
88         fprintf(stderr, "  -t timeout  Wait 'timeout' seconds before retrying (may be a floating point number).\n");
89         fprintf(stderr, "  -h          Print usage help information.\n");
90         fprintf(stderr, "  -i id       Set request id to 'id'.  Values may be 0..255\n");
91         fprintf(stderr, "  -S file     read secret from file, not command line.\n");
92         fprintf(stderr, "  -q          Do not print anything out.\n");
93         fprintf(stderr, "  -s          Print out summary information of auth results.\n");
94         fprintf(stderr, "  -v          Show program version information.\n");
95         fprintf(stderr, "  -x          Debugging mode.\n");
96         fprintf(stderr, "  -4          Use IPv4 address of server\n");
97         fprintf(stderr, "  -6          Use IPv6 address of server.\n");
98
99         exit(1);
100 }
101
102 int radlog(int lvl, const char *msg, ...)
103 {
104         va_list ap;
105         int r;
106
107         r = lvl; /* shut up compiler */
108
109         va_start(ap, msg);
110         r = vfprintf(stderr, msg, ap);
111         va_end(ap);
112         fputc('\n', stderr);
113
114         return r;
115 }
116
117 int log_debug(const char *msg, ...)
118 {
119         va_list ap;
120         int r;
121
122         va_start(ap, msg);
123         r = vfprintf(stderr, msg, ap);
124         va_end(ap);
125         fputc('\n', stderr);
126
127         return r;
128 }
129
130 void radlog_request(UNUSED int lvl, UNUSED int priority,
131                     UNUSED REQUEST *request, const char *msg, ...)
132 {
133         va_list ap;
134
135         va_start(ap, msg);
136         vfprintf(stderr, msg, ap);
137         va_end(ap);
138         fputc('\n', stderr);
139 }
140
141 static int getport(const char *name)
142 {
143         struct  servent         *svp;
144
145         svp = getservbyname (name, "udp");
146         if (!svp) {
147                 return 0;
148         }
149
150         return ntohs(svp->s_port);
151 }
152
153 #define R_RECV (0)
154 #define R_SENT (1)
155 static void debug_packet(RADIUS_PACKET *packet, int direction)
156 {
157         VALUE_PAIR *vp;
158         char buffer[1024];
159         const char *received, *from;
160         const fr_ipaddr_t *ip;
161         int port;
162
163         if (!packet) return;
164
165         if (direction == 0) {
166                 received = "Received";
167                 from = "from";  /* what else? */
168                 ip = &packet->src_ipaddr;
169                 port = packet->src_port;
170
171         } else {
172                 received = "Sending";
173                 from = "to";    /* hah! */
174                 ip = &packet->dst_ipaddr;
175                 port = packet->dst_port;
176         }
177
178         /*
179          *      Client-specific debugging re-prints the input
180          *      packet into the client log.
181          *
182          *      This really belongs in a utility library
183          */
184         if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
185                 printf("%s %s packet %s host %s port %d, id=%d, length=%d\n",
186                        received, fr_packet_codes[packet->code], from,
187                        inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
188                        port, packet->id, (int) packet->data_len);
189         } else {
190                 printf("%s packet %s host %s port %d code=%d, id=%d, length=%d\n",
191                        received, from,
192                        inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
193                        port,
194                        packet->code, packet->id, (int) packet->data_len);
195         }
196
197         for (vp = packet->vps; vp != NULL; vp = vp->next) {
198                 vp_prints(buffer, sizeof(buffer), vp);
199                 printf("\t%s\n", buffer);
200         }
201         fflush(stdout);
202 }
203
204
205 static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
206 {
207         int i;
208         struct timeval  tv;
209
210         for (i = 0; i < retries; i++) {
211                 fd_set          rdfdesc;
212
213                 debug_packet(req, R_SENT);
214
215                 rad_send(req, NULL, secret);
216
217                 /* And wait for reply, timing out as necessary */
218                 FD_ZERO(&rdfdesc);
219                 FD_SET(req->sockfd, &rdfdesc);
220
221                 tv.tv_sec = (int)timeout;
222                 tv.tv_usec = 1000000 * (timeout - (int) timeout);
223
224                 /* Something's wrong if we don't get exactly one fd. */
225                 if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) {
226                         continue;
227                 }
228
229                 *rep = rad_recv(req->sockfd, 0);
230                 if (*rep != NULL) {
231                         /*
232                          *      If we get a response from a machine
233                          *      which we did NOT send a request to,
234                          *      then complain.
235                          */
236                         if (((*rep)->src_ipaddr.af != req->dst_ipaddr.af) ||
237                             (memcmp(&(*rep)->src_ipaddr.ipaddr,
238                                     &req->dst_ipaddr.ipaddr,
239                                     ((req->dst_ipaddr.af == AF_INET ? /* AF_INET6? */
240                                       sizeof(req->dst_ipaddr.ipaddr.ip4addr) : /* FIXME: AF_INET6 */
241                                       sizeof(req->dst_ipaddr.ipaddr.ip6addr)))) != 0) ||
242                             ((*rep)->src_port != req->dst_port)) {
243                                 char src[128], dst[128];
244
245                                 ip_ntoh(&(*rep)->src_ipaddr, src, sizeof(src));
246                                 ip_ntoh(&req->dst_ipaddr, dst, sizeof(dst));
247                                 fprintf(stderr, "radclient: ERROR: Sent request to host %s port %d, got response from host %s port %d\n!",
248                                         dst, req->dst_port,
249                                         src, (*rep)->src_port);
250                                 exit(1);
251                         }
252                         break;
253                 } else {        /* NULL: couldn't receive the packet */
254                         fr_perror("radclient:");
255                         exit(1);
256                 }
257         }
258
259         /* No response or no data read (?) */
260         if (i == retries) {
261                 fprintf(stderr, "radclient: no response from server\n");
262                 exit(1);
263         }
264
265         /*
266          *      FIXME: Discard the packet & listen for another.
267          *
268          *      Hmm... we should really be using eapol_test, which does
269          *      a lot more than radeapclient.
270          */
271         if (rad_verify(*rep, req, secret) != 0) {
272                 fr_perror("rad_verify");
273                 exit(1);
274         }
275
276         if (rad_decode(*rep, req, secret) != 0) {
277                 fr_perror("rad_decode");
278                 exit(1);
279         }
280
281         /* libradius debug already prints out the value pairs for us */
282         if (!fr_debug_flag && do_output) {
283                 debug_packet(*rep, R_RECV);
284         }
285         if((*rep)->code == PW_AUTHENTICATION_ACK) {
286                 totalapp++;
287         } else if ((*rep)->code == PW_AUTHENTICATION_REJECT) {
288                 totaldeny++;
289         }
290
291         return 0;
292 }
293
294 static void cleanresp(RADIUS_PACKET *resp)
295 {
296         VALUE_PAIR *vpnext, *vp, **last;
297
298
299         /*
300          * maybe should just copy things we care about, or keep
301          * a copy of the original input and start from there again?
302          */
303         pairdelete(&resp->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
304         pairdelete(&resp->vps, ATTRIBUTE_EAP_BASE+PW_EAP_IDENTITY, 0, TAG_ANY);
305
306         last = &resp->vps;
307         for(vp = *last; vp != NULL; vp = vpnext)
308         {
309                 vpnext = vp->next;
310
311                 if((vp->da->attribute > ATTRIBUTE_EAP_BASE &&
312                     vp->da->attribute <= ATTRIBUTE_EAP_BASE+256) ||
313                    (vp->da->attribute > ATTRIBUTE_EAP_SIM_BASE &&
314                     vp->da->attribute <= ATTRIBUTE_EAP_SIM_BASE+256))
315                 {
316                         *last = vpnext;
317                         pairbasicfree(vp);
318                 } else {
319                         last = &vp->next;
320                 }
321         }
322 }
323
324 /*
325  * we got an EAP-Request/Sim/Start message in a legal state.
326  *
327  * pick a supported version, put it into the reply, and insert a nonce.
328  */
329 static int process_eap_start(RADIUS_PACKET *req,
330                              RADIUS_PACKET *rep)
331 {
332         VALUE_PAIR *vp, *newvp;
333         VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
334         uint16_t *versions, selectedversion;
335         unsigned int i,versioncount;
336
337         /* form new response clear of any EAP stuff */
338         cleanresp(rep);
339
340         if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
341                 fprintf(stderr, "illegal start message has no VERSION_LIST\n");
342                 return 0;
343         }
344
345         versions = (uint16_t *)vp->vp_strvalue;
346
347         /* verify that the attribute length is big enough for a length field */
348         if(vp->length < 4)
349         {
350                 fprintf(stderr, "start message has illegal VERSION_LIST. Too short: %u\n", (unsigned int) vp->length);
351                 return 0;
352         }
353
354         versioncount = ntohs(versions[0])/2;
355         /* verify that the attribute length is big enough for the given number
356          * of versions present.
357          */
358         if((unsigned)vp->length <= (versioncount*2 + 2))
359         {
360                 fprintf(stderr, "start message is too short. Claimed %d versions does not fit in %u bytes\n", versioncount, (unsigned int) vp->length);
361                 return 0;
362         }
363
364         /*
365          * record the versionlist for the MK calculation.
366          */
367         eapsim_mk.versionlistlen = versioncount*2;
368         memcpy(eapsim_mk.versionlist, (unsigned char *)(versions+1),
369                eapsim_mk.versionlistlen);
370
371         /* walk the version list, and pick the one we support, which
372          * at present, is 1, EAP_SIM_VERSION.
373          */
374         selectedversion=0;
375         for(i=0; i < versioncount; i++)
376         {
377                 if(ntohs(versions[i+1]) == EAP_SIM_VERSION)
378                 {
379                         selectedversion=EAP_SIM_VERSION;
380                         break;
381                 }
382         }
383         if(selectedversion == 0)
384         {
385                 fprintf(stderr, "eap-sim start message. No compatible version found. We need %d\n", EAP_SIM_VERSION);
386                 for(i=0; i < versioncount; i++)
387                 {
388                         fprintf(stderr, "\tfound version %d\n",
389                                 ntohs(versions[i+1]));
390                 }
391         }
392
393         /*
394          * now make sure that we have only FULLAUTH_ID_REQ.
395          * I think that it actually might not matter - we can answer in
396          * anyway we like, but it is illegal to have more than one
397          * present.
398          */
399         anyidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
400         fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
401         permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);
402
403         if(fullauthidreq_vp == NULL ||
404            anyidreq_vp != NULL ||
405            permanentidreq_vp != NULL) {
406                 fprintf(stderr, "start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.\n",
407                         (anyidreq_vp != NULL ? "a " : "no "),
408                         (fullauthidreq_vp != NULL ? "a " : "no "),
409                         (permanentidreq_vp != NULL ? "a " : "no "));
410                 return 0;
411         }
412
413         /* okay, we have just any_id_req there, so fill in response */
414
415         /* mark the subtype as being EAP-SIM/Response/Start */
416         newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER);
417         newvp->vp_integer = eapsim_start;
418         pairreplace(&(rep->vps), newvp);
419
420         /* insert selected version into response. */
421         newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION,
422                            0, PW_TYPE_OCTETS);
423         versions = (uint16_t *)newvp->vp_strvalue;
424         versions[0] = htons(selectedversion);
425         newvp->length = 2;
426         pairreplace(&(rep->vps), newvp);
427
428         /* record the selected version */
429         memcpy(eapsim_mk.versionselect, (unsigned char *)versions, 2);
430
431         vp = newvp = NULL;
432
433         {
434                 uint32_t nonce[4];
435                 /*
436                  * insert a nonce_mt that we make up.
437                  */
438                 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT,
439                                    0, PW_TYPE_OCTETS);
440                 newvp->vp_octets[0]=0;
441                 newvp->vp_octets[1]=0;
442                 newvp->length = 18;  /* 16 bytes of nonce + padding */
443
444                 nonce[0]=fr_rand();
445                 nonce[1]=fr_rand();
446                 nonce[2]=fr_rand();
447                 nonce[3]=fr_rand();
448                 memcpy(&newvp->vp_octets[2], nonce, 16);
449                 pairreplace(&(rep->vps), newvp);
450
451                 /* also keep a copy of the nonce! */
452                 memcpy(eapsim_mk.nonce_mt, nonce, 16);
453         }
454
455         {
456                 uint16_t *pidlen, idlen;
457
458                 /*
459                  * insert the identity here.
460                  */
461                 vp = pairfind(rep->vps, PW_USER_NAME, 0, TAG_ANY);
462                 if(vp == NULL)
463                 {
464                         fprintf(stderr, "eap-sim: We need to have a User-Name attribute!\n");
465                         return 0;
466                 }
467                 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY,
468                                    0, PW_TYPE_OCTETS);
469                 idlen = strlen(vp->vp_strvalue);
470                 pidlen = (uint16_t *)newvp->vp_strvalue;
471                 *pidlen = htons(idlen);
472                 newvp->length = idlen + 2;
473
474                 memcpy(&newvp->vp_strvalue[2], vp->vp_strvalue, idlen);
475                 pairreplace(&(rep->vps), newvp);
476
477                 /* record it */
478                 memcpy(eapsim_mk.identity, vp->vp_strvalue, idlen);
479                 eapsim_mk.identitylen = idlen;
480         }
481
482         return 1;
483 }
484
485 /*
486  * we got an EAP-Request/Sim/Challenge message in a legal state.
487  *
488  * use the RAND challenge to produce the SRES result, and then
489  * use that to generate a new MAC.
490  *
491  * for the moment, we ignore the RANDs, then just plug in the SRES
492  * values.
493  *
494  */
495 static int process_eap_challenge(RADIUS_PACKET *req,
496                                  RADIUS_PACKET *rep)
497 {
498         VALUE_PAIR *newvp;
499         VALUE_PAIR *mac, *randvp;
500         VALUE_PAIR *sres1,*sres2,*sres3;
501         VALUE_PAIR *Kc1, *Kc2, *Kc3;
502         uint8_t calcmac[20];
503
504         /* look for the AT_MAC and the challenge data */
505         mac   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY);
506         randvp= pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND, 0, TAG_ANY);
507         if(mac == NULL || randvp == NULL) {
508                 fprintf(stderr, "radeapclient: challenge message needs to contain RAND and MAC\n");
509                 return 0;
510         }
511
512         /*
513          * compare RAND with randX, to verify this is the right response
514          * to this challenge.
515          */
516         {
517           VALUE_PAIR *randcfgvp[3];
518           uint8_t *randcfg[3];
519
520           randcfg[0] = &randvp->vp_octets[2];
521           randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
522           randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2];
523
524           randcfgvp[0] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND1, 0, TAG_ANY);
525           randcfgvp[1] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND2, 0, TAG_ANY);
526           randcfgvp[2] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND3, 0, TAG_ANY);
527
528           if(randcfgvp[0] == NULL ||
529              randcfgvp[1] == NULL ||
530              randcfgvp[2] == NULL) {
531             fprintf(stderr, "radeapclient: needs to have rand1, 2 and 3 set.\n");
532             return 0;
533           }
534
535           if(memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
536              memcmp(randcfg[1], randcfgvp[1]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
537              memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) {
538             int rnum,i,j;
539
540             fprintf(stderr, "radeapclient: one of rand 1,2,3 didn't match\n");
541             for(rnum = 0; rnum < 3; rnum++) {
542               fprintf(stderr, "received   rand %d: ", rnum);
543               j=0;
544               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
545                 if(j==4) {
546                   printf("_");
547                   j=0;
548                 }
549                 j++;
550
551                 fprintf(stderr, "%02x", randcfg[rnum][i]);
552               }
553               fprintf(stderr, "\nconfigured rand %d: ", rnum);
554               j=0;
555               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
556                 if(j==4) {
557                   printf("_");
558                   j=0;
559                 }
560                 j++;
561
562                 fprintf(stderr, "%02x", randcfgvp[rnum]->vp_octets[i]);
563               }
564               fprintf(stderr, "\n");
565             }
566             return 0;
567           }
568         }
569
570         /*
571          * now dig up the sres values from the response packet,
572          * which were put there when we read things in.
573          *
574          * Really, they should be calculated from the RAND!
575          *
576          */
577         sres1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES1, 0, TAG_ANY);
578         sres2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES2, 0, TAG_ANY);
579         sres3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES3, 0, TAG_ANY);
580
581         if(sres1 == NULL ||
582            sres2 == NULL ||
583            sres3 == NULL) {
584                 fprintf(stderr, "radeapclient: needs to have sres1, 2 and 3 set.\n");
585                 return 0;
586         }
587         memcpy(eapsim_mk.sres[0], sres1->vp_strvalue, sizeof(eapsim_mk.sres[0]));
588         memcpy(eapsim_mk.sres[1], sres2->vp_strvalue, sizeof(eapsim_mk.sres[1]));
589         memcpy(eapsim_mk.sres[2], sres3->vp_strvalue, sizeof(eapsim_mk.sres[2]));
590
591         Kc1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC1, 0, TAG_ANY);
592         Kc2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC2, 0, TAG_ANY);
593         Kc3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC3, 0, TAG_ANY);
594
595         if(Kc1 == NULL ||
596            Kc2 == NULL ||
597            Kc3 == NULL) {
598                 fprintf(stderr, "radeapclient: needs to have Kc1, 2 and 3 set.\n");
599                 return 0;
600         }
601         memcpy(eapsim_mk.Kc[0], Kc1->vp_strvalue, sizeof(eapsim_mk.Kc[0]));
602         memcpy(eapsim_mk.Kc[1], Kc2->vp_strvalue, sizeof(eapsim_mk.Kc[1]));
603         memcpy(eapsim_mk.Kc[2], Kc3->vp_strvalue, sizeof(eapsim_mk.Kc[2]));
604
605         /* all set, calculate keys */
606         eapsim_calculate_keys(&eapsim_mk);
607
608         if(debug_flag) {
609           eapsim_dump_mk(&eapsim_mk);
610         }
611
612         /* verify the MAC, now that we have all the keys. */
613         if(eapsim_checkmac(req->vps, eapsim_mk.K_aut,
614                            eapsim_mk.nonce_mt, sizeof(eapsim_mk.nonce_mt),
615                            calcmac)) {
616                 printf("MAC check succeed\n");
617         } else {
618                 int i, j;
619                 j=0;
620                 printf("calculated MAC (");
621                 for (i = 0; i < 20; i++) {
622                         if(j==4) {
623                                 printf("_");
624                                 j=0;
625                         }
626                         j++;
627
628                         printf("%02x", calcmac[i]);
629                 }
630                 printf(" did not match\n");
631                 return 0;
632         }
633
634         /* form new response clear of any EAP stuff */
635         cleanresp(rep);
636
637         /* mark the subtype as being EAP-SIM/Response/Start */
638         newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER);
639         newvp->vp_integer = eapsim_challenge;
640         pairreplace(&(rep->vps), newvp);
641
642         /*
643          * fill the SIM_MAC with a field that will in fact get appended
644          * to the packet before the MAC is calculated
645          */
646         newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0,
647                            PW_TYPE_OCTETS);
648         memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
649         memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
650         memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
651         newvp->length = EAPSIM_SRES_SIZE*3;
652         pairreplace(&(rep->vps), newvp);
653
654         newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, 0, PW_TYPE_OCTETS);
655         memcpy(newvp->vp_strvalue,    eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);
656         newvp->length = EAPSIM_AUTH_SIZE;
657         pairreplace(&(rep->vps), newvp);
658
659         return 1;
660 }
661
662 /*
663  * this code runs the EAP-SIM client state machine.
664  * the *request* is from the server.
665  * the *reponse* is to the server.
666  *
667  */
668 static int respond_eap_sim(RADIUS_PACKET *req,
669                            RADIUS_PACKET *resp)
670 {
671         enum eapsim_clientstates state, newstate;
672         enum eapsim_subtype subtype;
673         VALUE_PAIR *vp, *statevp, *radstate, *eapid;
674         char statenamebuf[32], subtypenamebuf[32];
675
676         if ((radstate = paircopy2(req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
677         {
678                 return 0;
679         }
680
681         if ((eapid = paircopy2(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL)
682         {
683                 return 0;
684         }
685
686         /* first, dig up the state from the request packet, setting
687          * outselves to be in EAP-SIM-Start state if there is none.
688          */
689
690         if((statevp = pairfind(resp->vps, ATTRIBUTE_EAP_SIM_STATE, 0, TAG_ANY)) == NULL)
691         {
692                 /* must be initial request */
693                 statevp = paircreate(ATTRIBUTE_EAP_SIM_STATE, 0, PW_TYPE_INTEGER);
694                 statevp->vp_integer = eapsim_client_init;
695                 pairreplace(&(resp->vps), statevp);
696         }
697         state = statevp->vp_integer;
698
699         /*
700          * map the attributes, and authenticate them.
701          */
702         unmap_eapsim_types(req);
703
704         if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL)
705         {
706                 return 0;
707         }
708         subtype = vp->vp_integer;
709
710         /*
711          * look for the appropriate state, and process incoming message
712          */
713         switch(state) {
714         case eapsim_client_init:
715                 switch(subtype) {
716                 case eapsim_start:
717                         newstate = process_eap_start(req, resp);
718                         break;
719
720                 case eapsim_challenge:
721                 case eapsim_notification:
722                 case eapsim_reauth:
723                 default:
724                         fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
725                                 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
726                                 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
727                         /* invalid state, drop message */
728                         return 0;
729                 }
730                 break;
731
732         case eapsim_client_start:
733                 switch(subtype) {
734                 case eapsim_start:
735                         /* NOT SURE ABOUT THIS ONE, retransmit, I guess */
736                         newstate = process_eap_start(req, resp);
737                         break;
738
739                 case eapsim_challenge:
740                         newstate = process_eap_challenge(req, resp);
741                         break;
742
743                 default:
744                         fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
745                                 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
746                                 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
747                         /* invalid state, drop message */
748                         return 0;
749                 }
750                 break;
751
752
753         default:
754                 fprintf(stderr, "radeapclient: sim in illegal state %s\n",
755                         sim_state2name(state, statenamebuf, sizeof(statenamebuf)));
756                 return 0;
757         }
758
759         /* copy the eap state object in */
760         pairreplace(&(resp->vps), eapid);
761
762         /* update stete info, and send new packet */
763         map_eapsim_types(resp);
764
765         /* copy the radius state object in */
766         pairreplace(&(resp->vps), radstate);
767
768         statevp->vp_integer = newstate;
769         return 1;
770 }
771
772 static int respond_eap_md5(RADIUS_PACKET *req,
773                            RADIUS_PACKET *rep)
774 {
775         VALUE_PAIR *vp, *id, *state;
776         size_t valuesize, namesize;
777         uint8_t identifier;
778         uint8_t *value;
779         uint8_t *name;
780         FR_MD5_CTX      context;
781         uint8_t    response[16];
782
783         cleanresp(rep);
784
785         if ((state = paircopy2(req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
786         {
787                 fprintf(stderr, "radeapclient: no state attribute found\n");
788                 return 0;
789         }
790
791         if ((id = paircopy2(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL)
792         {
793                 fprintf(stderr, "radeapclient: no EAP-ID attribute found\n");
794                 return 0;
795         }
796         identifier = id->vp_integer;
797
798         if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL)
799         {
800                 fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n");
801                 return 0;
802         }
803
804         /* got the details of the MD5 challenge */
805         valuesize = vp->vp_octets[0];
806         value = &vp->vp_octets[1];
807         name  = &vp->vp_octets[valuesize+1];
808         namesize = vp->length - (valuesize + 1);
809
810         /* sanitize items */
811         if(valuesize > vp->length)
812         {
813                 fprintf(stderr, "radeapclient: md5 valuesize if too big (%u > %u)\n",
814                         (unsigned int) valuesize, (unsigned int) vp->length);
815                 return 0;
816         }
817
818         /* now do the CHAP operation ourself, rather than build the
819          * buffer. We could also call rad_chap_encode, but it wants
820          * a CHAP-Challenge, which we don't want to bother with.
821          */
822         fr_MD5Init(&context);
823         fr_MD5Update(&context, &identifier, 1);
824         fr_MD5Update(&context, (uint8_t *) password, strlen(password));
825         fr_MD5Update(&context, value, valuesize);
826         fr_MD5Final(response, &context);
827
828         vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0, PW_TYPE_OCTETS);
829         vp->vp_octets[0]=16;
830         memcpy(&vp->vp_strvalue[1], response, 16);
831         vp->length = 17;
832
833         pairreplace(&(rep->vps), vp);
834
835         pairreplace(&(rep->vps), id);
836
837         /* copy the state object in */
838         pairreplace(&(rep->vps), state);
839
840         return 1;
841 }
842
843
844
845 static int sendrecv_eap(RADIUS_PACKET *rep)
846 {
847         RADIUS_PACKET *req = NULL;
848         VALUE_PAIR *vp, *vpnext;
849         int tried_eap_md5 = 0;
850
851         /*
852          *      Keep a copy of the the User-Password attribute.
853          */
854         if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
855                 strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue));
856
857         } else  if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
858                 strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue));
859                 /*
860                  *      Otherwise keep a copy of the CHAP-Password attribute.
861                  */
862         } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
863                 strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue));
864         } else {
865                 *password = '\0';
866         }
867
868  again:
869         rep->id++;
870
871         /*
872          * if there are EAP types, encode them into an EAP-Message
873          *
874          */
875         map_eap_types(rep);
876
877         /*
878          *  Fix up Digest-Attributes issues
879          */
880         for (vp = rep->vps; vp != NULL; vp = vp->next) {
881                 switch (vp->da->attribute) {
882                 default:
883                         break;
884
885                 case PW_DIGEST_REALM:
886                 case PW_DIGEST_NONCE:
887                 case PW_DIGEST_METHOD:
888                 case PW_DIGEST_URI:
889                 case PW_DIGEST_QOP:
890                 case PW_DIGEST_ALGORITHM:
891                 case PW_DIGEST_BODY_DIGEST:
892                 case PW_DIGEST_CNONCE:
893                 case PW_DIGEST_NONCE_COUNT:
894                 case PW_DIGEST_USER_NAME:
895                         /* overlapping! */
896                         memmove(&vp->vp_strvalue[2], &vp->vp_octets[0], vp->length);
897                         vp->vp_octets[0] = vp->da->attribute - PW_DIGEST_REALM + 1;
898                         vp->length += 2;
899                         vp->vp_octets[1] = vp->length;
900                         vp->da->attribute = PW_DIGEST_ATTRIBUTES;
901                         break;
902                 }
903         }
904
905         /*
906          *      If we've already sent a packet, free up the old
907          *      one, and ensure that the next packet has a unique
908          *      ID and authentication vector.
909          */
910         if (rep->data) {
911                 free(rep->data);
912                 rep->data = NULL;
913         }
914
915         fr_md5_calc(rep->vector, rep->vector,
916                         sizeof(rep->vector));
917
918         if (*password != '\0') {
919                 if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
920                         strlcpy((char *)vp->vp_strvalue, password, sizeof(vp->vp_strvalue));
921                         vp->length = strlen(password);
922
923                 } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
924                         strlcpy((char *)vp->vp_strvalue, password, sizeof(vp->vp_strvalue));
925                         vp->length = strlen(password);
926
927                 } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
928                         strlcpy((char *)vp->vp_strvalue, password, sizeof(vp->vp_strvalue));
929                         vp->length = strlen(password);
930
931                         rad_chap_encode(rep, vp->vp_octets, rep->id, vp);
932                         vp->length = 17;
933                 }
934         } /* there WAS a password */
935
936         /* send the response, wait for the next request */
937         send_packet(rep, &req);
938
939         /* okay got back the packet, go and decode the EAP-Message. */
940         unmap_eap_types(req);
941
942         debug_packet(req, R_RECV);
943
944         /* now look for the code type. */
945         for (vp = req->vps; vp != NULL; vp = vpnext) {
946                 vpnext = vp->next;
947
948                 switch (vp->da->attribute) {
949                 default:
950                         break;
951
952                 case ATTRIBUTE_EAP_BASE+PW_EAP_MD5:
953                         if(respond_eap_md5(req, rep) && tried_eap_md5 < 3)
954                         {
955                                 tried_eap_md5++;
956                                 goto again;
957                         }
958                         break;
959
960                 case ATTRIBUTE_EAP_BASE+PW_EAP_SIM:
961                         if(respond_eap_sim(req, rep))
962                         {
963                                 goto again;
964                         }
965                         break;
966                 }
967         }
968
969         return 1;
970 }
971
972
973 int main(int argc, char **argv)
974 {
975         RADIUS_PACKET *req;
976         char *p;
977         int c;
978         int port = 0;
979         char *filename = NULL;
980         FILE *fp;
981         int count = 1;
982         int id;
983         int force_af = AF_UNSPEC;
984
985         id = ((int)getpid() & 0xff);
986         fr_debug_flag = 0;
987
988         radlog_dest = RADLOG_STDERR;
989
990         while ((c = getopt(argc, argv, "46c:d:f:hi:qst:r:S:xXv")) != EOF)
991         {
992                 switch(c) {
993                 case '4':
994                         force_af = AF_INET;
995                         break;
996                 case '6':
997                         force_af = AF_INET6;
998                         break;
999                 case 'c':
1000                         if (!isdigit((int) *optarg))
1001                                 usage();
1002                         count = atoi(optarg);
1003                         break;
1004                 case 'd':
1005                         radius_dir = strdup(optarg);
1006                         break;
1007                 case 'f':
1008                         filename = optarg;
1009                         break;
1010                 case 'q':
1011                         do_output = 0;
1012                         break;
1013                 case 'x':
1014                         debug_flag++;
1015                         fr_debug_flag++;
1016                         break;
1017
1018                 case 'X':
1019 #if 0
1020                   sha1_data_problems = 1; /* for debugging only */
1021 #endif
1022                   break;
1023
1024
1025
1026                 case 'r':
1027                         if (!isdigit((int) *optarg))
1028                                 usage();
1029                         retries = atoi(optarg);
1030                         break;
1031                 case 'i':
1032                         if (!isdigit((int) *optarg))
1033                                 usage();
1034                         id = atoi(optarg);
1035                         if ((id < 0) || (id > 255)) {
1036                                 usage();
1037                         }
1038                         break;
1039                 case 's':
1040                         do_summary = 1;
1041                         break;
1042                 case 't':
1043                         if (!isdigit((int) *optarg))
1044                                 usage();
1045                         timeout = atof(optarg);
1046                         break;
1047                 case 'v':
1048                         printf("radclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
1049                         exit(0);
1050                         break;
1051                case 'S':
1052                        fp = fopen(optarg, "r");
1053                        if (!fp) {
1054                                fprintf(stderr, "radclient: Error opening %s: %s\n",
1055                                        optarg, strerror(errno));
1056                                exit(1);
1057                        }
1058                        if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
1059                                fprintf(stderr, "radclient: Error reading %s: %s\n",
1060                                        optarg, strerror(errno));
1061                                exit(1);
1062                        }
1063                        fclose(fp);
1064
1065                        /* truncate newline */
1066                        p = filesecret + strlen(filesecret) - 1;
1067                        while ((p >= filesecret) &&
1068                               (*p < ' ')) {
1069                                *p = '\0';
1070                                --p;
1071                        }
1072
1073                        if (strlen(filesecret) < 2) {
1074                                fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
1075                                exit(1);
1076                        }
1077                        secret = filesecret;
1078                        break;
1079                 case 'h':
1080                 default:
1081                         usage();
1082                         break;
1083                 }
1084         }
1085         argc -= (optind - 1);
1086         argv += (optind - 1);
1087
1088         if ((argc < 3)  ||
1089             ((secret == NULL) && (argc < 4))) {
1090                 usage();
1091         }
1092
1093         if (!radius_dir) radius_dir = strdup(RADDBDIR);
1094
1095         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
1096                 fr_perror("radclient");
1097                 return 1;
1098         }
1099
1100         req = rad_alloc(NULL, 1);
1101         if (!req) {
1102                 fr_perror("radclient");
1103                 exit(1);
1104         }
1105
1106 #if 0
1107         {
1108                 FILE *randinit;
1109
1110                 if((randinit = fopen("/dev/urandom", "r")) == NULL)
1111                 {
1112                         perror("/dev/urandom");
1113                 } else {
1114                         fread(randctx.randrsl, 256, 1, randinit);
1115                         fclose(randinit);
1116                 }
1117         }
1118         fr_randinit(&randctx, 1);
1119 #endif
1120
1121         req->id = id;
1122
1123         /*
1124          *      Resolve hostname.
1125          */
1126         if (force_af == AF_UNSPEC) force_af = AF_INET;
1127         req->dst_ipaddr.af = force_af;
1128         if (strcmp(argv[1], "-") != 0) {
1129                 const char *hostname = argv[1];
1130                 const char *portname = argv[1];
1131                 char buffer[256];
1132
1133                 if (*argv[1] == '[') { /* IPv6 URL encoded */
1134                         p = strchr(argv[1], ']');
1135                         if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
1136                                 usage();
1137                         }
1138
1139                         memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
1140                         buffer[p - argv[1] - 1] = '\0';
1141
1142                         hostname = buffer;
1143                         portname = p + 1;
1144
1145                 }
1146                 p = strchr(portname, ':');
1147                 if (p && (strchr(p + 1, ':') == NULL)) {
1148                         *p = '\0';
1149                         portname = p + 1;
1150                 } else {
1151                         portname = NULL;
1152                 }
1153
1154                 if (ip_hton(hostname, force_af, &req->dst_ipaddr) < 0) {
1155                         fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
1156                         exit(1);
1157                 }
1158
1159                 /*
1160                  *      Strip port from hostname if needed.
1161                  */
1162                 if (portname) port = atoi(portname);
1163         }
1164
1165         /*
1166          *      See what kind of request we want to send.
1167          */
1168         if (strcmp(argv[2], "auth") == 0) {
1169                 if (port == 0) port = getport("radius");
1170                 if (port == 0) port = PW_AUTH_UDP_PORT;
1171                 req->code = PW_AUTHENTICATION_REQUEST;
1172
1173         } else if (strcmp(argv[2], "acct") == 0) {
1174                 if (port == 0) port = getport("radacct");
1175                 if (port == 0) port = PW_ACCT_UDP_PORT;
1176                 req->code = PW_ACCOUNTING_REQUEST;
1177                 do_summary = 0;
1178
1179         } else if (strcmp(argv[2], "status") == 0) {
1180                 if (port == 0) port = getport("radius");
1181                 if (port == 0) port = PW_AUTH_UDP_PORT;
1182                 req->code = PW_STATUS_SERVER;
1183
1184         } else if (strcmp(argv[2], "disconnect") == 0) {
1185                 if (port == 0) port = PW_POD_UDP_PORT;
1186                 req->code = PW_DISCONNECT_REQUEST;
1187
1188         } else if (isdigit((int) argv[2][0])) {
1189                 if (port == 0) port = getport("radius");
1190                 if (port == 0) port = PW_AUTH_UDP_PORT;
1191                 req->code = atoi(argv[2]);
1192         } else {
1193                 usage();
1194         }
1195         req->dst_port = port;
1196
1197         /*
1198          *      Add the secret.
1199          */
1200         if (argv[3]) secret = argv[3];
1201
1202         /*
1203          *      Read valuepairs.
1204          *      Maybe read them, from stdin, if there's no
1205          *      filename, or if the filename is '-'.
1206          */
1207         if (filename && (strcmp(filename, "-") != 0)) {
1208                 fp = fopen(filename, "r");
1209                 if (!fp) {
1210                         fprintf(stderr, "radclient: Error opening %s: %s\n",
1211                                 filename, strerror(errno));
1212                         exit(1);
1213                 }
1214         } else {
1215                 fp = stdin;
1216         }
1217
1218         /*
1219          *      Send request.
1220          */
1221         if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
1222                 perror("radclient: socket: ");
1223                 exit(1);
1224         }
1225
1226         while(!filedone) {
1227                 if(req->vps) pairfree(&req->vps);
1228
1229                 if ((req->vps = readvp2(fp, &filedone, "radeapclient:"))
1230                     == NULL) {
1231                         break;
1232                 }
1233
1234                 sendrecv_eap(req);
1235         }
1236
1237         free(radius_dir);
1238         if(do_summary) {
1239                 printf("\n\t   Total approved auths:  %d\n", totalapp);
1240                 printf("\t     Total denied auths:  %d\n", totaldeny);
1241         }
1242         return 0;
1243 }
1244
1245 /*
1246  * given a radius request with some attributes in the EAP range, build
1247  * them all into a single EAP-Message body.
1248  *
1249  * Note that this function will build multiple EAP-Message bodies
1250  * if there are multiple eligible EAP-types. This is incorrect, as the
1251  * recipient will in fact concatenate them.
1252  *
1253  * XXX - we could break the loop once we process one type. Maybe this
1254  *       just deserves an assert?
1255  *
1256  */
1257 static void map_eap_types(RADIUS_PACKET *req)
1258 {
1259         VALUE_PAIR *vp, *vpnext;
1260         int id, eapcode;
1261         eap_packet_t ep;
1262         int eap_type;
1263
1264         vp = pairfind(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY);
1265         if(vp == NULL) {
1266                 id = ((int)getpid() & 0xff);
1267         } else {
1268                 id = vp->vp_integer;
1269         }
1270
1271         vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE, 0, TAG_ANY);
1272         if(vp == NULL) {
1273                 eapcode = PW_EAP_REQUEST;
1274         } else {
1275                 eapcode = vp->vp_integer;
1276         }
1277
1278
1279         for(vp = req->vps; vp != NULL; vp = vpnext) {
1280                 /* save it in case it changes! */
1281                 vpnext = vp->next;
1282
1283                 if(vp->da->attribute >= ATTRIBUTE_EAP_BASE &&
1284                    vp->da->attribute < ATTRIBUTE_EAP_BASE+256) {
1285                         break;
1286                 }
1287         }
1288
1289         if(vp == NULL) {
1290                 return;
1291         }
1292
1293         eap_type = vp->da->attribute - ATTRIBUTE_EAP_BASE;
1294
1295         switch(eap_type) {
1296         case PW_EAP_IDENTITY:
1297         case PW_EAP_NOTIFICATION:
1298         case PW_EAP_NAK:
1299         case PW_EAP_MD5:
1300         case PW_EAP_OTP:
1301         case PW_EAP_GTC:
1302         case PW_EAP_TLS:
1303         case PW_EAP_LEAP:
1304         case PW_EAP_TTLS:
1305         case PW_EAP_PEAP:
1306         default:
1307                 /*
1308                  * no known special handling, it is just encoded as an
1309                  * EAP-message with the given type.
1310                  */
1311
1312                 /* nuke any existing EAP-Messages */
1313                 pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
1314
1315                 memset(&ep, 0, sizeof(ep));
1316                 ep.code = eapcode;
1317                 ep.id   = id;
1318                 ep.type.type = eap_type;
1319                 ep.type.length = vp->length;
1320                 ep.type.data = malloc(vp->length);
1321                 memcpy(ep.type.data,vp->vp_octets, vp->length);
1322                 eap_basic_compose(req, &ep);
1323         }
1324 }
1325
1326 /*
1327  * given a radius request with an EAP-Message body, decode it specific
1328  * attributes.
1329  */
1330 static void unmap_eap_types(RADIUS_PACKET *rep)
1331 {
1332         VALUE_PAIR *eap1;
1333         eap_packet_raw_t *e;
1334         int len;
1335         int type;
1336
1337         /* find eap message */
1338         e = eap_vp2packet(rep->vps);
1339
1340         /* nothing to do! */
1341         if(e == NULL) return;
1342
1343         /* create EAP-ID and EAP-CODE attributes to start */
1344         eap1 = paircreate(ATTRIBUTE_EAP_ID, 0, PW_TYPE_INTEGER);
1345         eap1->vp_integer = e->id;
1346         pairadd(&(rep->vps), eap1);
1347
1348         eap1 = paircreate(ATTRIBUTE_EAP_CODE, 0, PW_TYPE_INTEGER);
1349         eap1->vp_integer = e->code;
1350         pairadd(&(rep->vps), eap1);
1351
1352         switch(e->code)
1353         {
1354         default:
1355         case PW_EAP_SUCCESS:
1356         case PW_EAP_FAILURE:
1357                 /* no data */
1358                 break;
1359
1360         case PW_EAP_REQUEST:
1361         case PW_EAP_RESPONSE:
1362                 /* there is a type field, which we use to create
1363                  * a new attribute */
1364
1365                 /* the length was decode already into the attribute
1366                  * length, and was checked already. Network byte
1367                  * order, just pull it out using math.
1368                  */
1369                 len = e->length[0]*256 + e->length[1];
1370
1371                 /* verify the length is big enough to hold type */
1372                 if(len < 5)
1373                 {
1374                         free(e);
1375                         return;
1376                 }
1377
1378                 type = e->data[0];
1379
1380                 type += ATTRIBUTE_EAP_BASE;
1381                 len -= 5;
1382
1383                 if(len > MAX_STRING_LEN) {
1384                         len = MAX_STRING_LEN;
1385                 }
1386
1387                 eap1 = paircreate(type, 0, PW_TYPE_OCTETS);
1388                 memcpy(eap1->vp_strvalue, &e->data[1], len);
1389                 eap1->length = len;
1390                 pairadd(&(rep->vps), eap1);
1391                 break;
1392         }
1393
1394         free(e);
1395         return;
1396 }
1397
1398 static int map_eapsim_types(RADIUS_PACKET *r)
1399 {
1400         eap_packet_t ep;
1401         int ret;
1402
1403         memset(&ep, 0, sizeof(ep));
1404         ret = map_eapsim_basictypes(r, &ep);
1405         if(ret != 1) {
1406                 return ret;
1407         }
1408         eap_basic_compose(r, &ep);
1409
1410         return 1;
1411 }
1412
1413 static int unmap_eapsim_types(RADIUS_PACKET *r)
1414 {
1415         VALUE_PAIR             *esvp;
1416
1417         esvp = pairfind(r->vps, ATTRIBUTE_EAP_BASE+PW_EAP_SIM, 0, TAG_ANY);
1418         if (esvp == NULL) {
1419                 radlog(L_ERR, "eap: EAP-Sim attribute not found");
1420                 return 0;
1421         }
1422
1423         return unmap_eapsim_basictypes(r, esvp->vp_octets, esvp->length);
1424 }
1425
1426 #ifdef TEST_CASE
1427
1428 #include <assert.h>
1429
1430 const char *radius_dir = RADDBDIR;
1431
1432 int radlog(int lvl, const char *msg, ...)
1433 {
1434         va_list ap;
1435         int r;
1436
1437         va_start(ap, msg);
1438         r = vfprintf(stderr, msg, ap);
1439         va_end(ap);
1440         fputc('\n', stderr);
1441
1442         return r;
1443 }
1444
1445 main(int argc, char *argv[])
1446 {
1447         int filedone;
1448         RADIUS_PACKET *req,*req2;
1449         VALUE_PAIR *vp, *vpkey, *vpextra;
1450         extern unsigned int sha1_data_problems;
1451
1452         req = NULL;
1453         req2 = NULL;
1454         filedone = 0;
1455
1456         if(argc>1) {
1457           sha1_data_problems = 1;
1458         }
1459
1460         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
1461                 fr_perror("radclient");
1462                 return 1;
1463         }
1464
1465         req = rad_alloc(NULL, 1)
1466         if (!req) {
1467                 fr_perror("radclient");
1468                 exit(1);
1469         }
1470
1471         req2 = rad_alloc(NULL, 1);
1472         if (!req2) {
1473                 fr_perror("radclient");
1474                 exit(1);
1475         }
1476
1477         while(!filedone) {
1478                 if(req->vps) pairfree(&req->vps);
1479                 if(req2->vps) pairfree(&req2->vps);
1480
1481                 if ((req->vps = readvp2(stdin, &filedone, "eapsimlib:")) == NULL) {
1482                         break;
1483                 }
1484
1485                 if (fr_debug_flag > 1) {
1486                         printf("\nRead:\n");
1487                         vp_printlist(stdout, req->vps);
1488                 }
1489
1490                 map_eapsim_types(req);
1491                 map_eap_types(req);
1492
1493                 if (fr_debug_flag > 1) {
1494                         printf("Mapped to:\n");
1495                         vp_printlist(stdout, req->vps);
1496                 }
1497
1498                 /* find the EAP-Message, copy it to req2 */
1499                 vp = paircopy2(req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
1500
1501                 if(vp == NULL) continue;
1502
1503                 pairadd(&req2->vps, vp);
1504
1505                 /* only call unmap for sim types here */
1506                 unmap_eap_types(req2);
1507                 unmap_eapsim_types(req2);
1508
1509                 if (fr_debug_flag > 1) {
1510                         printf("Unmapped to:\n");
1511                         vp_printlist(stdout, req2->vps);
1512                 }
1513
1514                 vp = pairfind(req2->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY);
1515                 vpkey   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_KEY, 0, TAG_ANY);
1516                 vpextra = pairfind(req->vps, ATTRIBUTE_EAP_SIM_EXTRA, 0, TAG_ANY);
1517
1518                 if(vp != NULL && vpkey != NULL && vpextra!=NULL) {
1519                         uint8_t calcmac[16];
1520
1521                         /* find the EAP-Message, copy it to req2 */
1522
1523                         memset(calcmac, 0, sizeof(calcmac));
1524                         printf("Confirming MAC...");
1525                         if(eapsim_checkmac(req2->vps, vpkey->vp_strvalue,
1526                                            vpextra->vp_strvalue, vpextra->length,
1527                                            calcmac)) {
1528                                 printf("succeed\n");
1529                         } else {
1530                                 int i, j;
1531
1532                                 printf("calculated MAC (");
1533                                 for (i = 0; i < 20; i++) {
1534                                         if(j==4) {
1535                                                 printf("_");
1536                                                 j=0;
1537                                         }
1538                                         j++;
1539
1540                                         printf("%02x", calcmac[i]);
1541                                 }
1542                                 printf(" did not match\n");
1543                         }
1544                 }
1545
1546                 fflush(stdout);
1547         }
1548 }
1549 #endif
1550