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