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