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