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