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