import from HEAD:
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24 static const char rcsid[] = "$Id$";
25
26 #include "autoconf.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #if HAVE_UNISTD_H
32 #       include <unistd.h>
33 #endif
34
35 #include <string.h>
36 #include <ctype.h>
37 #include <netdb.h>
38 #include <sys/socket.h>
39
40 #if HAVE_NETINET_IN_H
41 #       include <netinet/in.h>
42 #endif
43
44 #if HAVE_SYS_SELECT_H
45 #       include <sys/select.h>
46 #endif
47
48 #if HAVE_GETOPT_H
49 #       include <getopt.h>
50 #endif
51
52 #include "conf.h"
53 #include "radpaths.h"
54 #include "missing.h"
55 #include "../../include/md5.h"
56
57 #include "eap_types.h"
58 #include "eap_sim.h"
59
60 extern int sha1_data_problems;
61
62 static int retries = 10;
63 static float timeout = 3;
64 static const char *secret = NULL;
65 static int do_output = 1;
66 static int do_summary = 0;
67 static int filedone = 0;
68 static int totalapp = 0;
69 static int totaldeny = 0;
70 static char filesecret[256];
71 const char *radius_dir = RADDBDIR;
72 const char *progname = "radeapclient";
73 /* lrad_randctx randctx; */
74
75
76 radlog_dest_t radlog_dest = RADLOG_STDERR;
77 const char *radlog_dir = NULL;
78 int debug_flag = 0;
79 struct main_config_t mainconfig;
80 char password[256];
81
82 struct eapsim_keys eapsim_mk;
83
84 static void NEVER_RETURNS usage(void)
85 {
86         fprintf(stderr, "Usage: radeapclient [options] server[:port] <command> [<secret>]\n");
87
88         fprintf(stderr, "  <command>    One of auth, acct, status, or disconnect.\n");
89         fprintf(stderr, "  -c count    Send each packet 'count' times.\n");
90         fprintf(stderr, "  -d raddb    Set dictionary directory.\n");
91         fprintf(stderr, "  -f file     Read packets from file, not stdin.\n");
92         fprintf(stderr, "  -r retries  If timeout, retry sending the packet 'retries' times.\n");
93         fprintf(stderr, "  -t timeout  Wait 'timeout' seconds before retrying (may be a floating point number).\n");
94         fprintf(stderr, "  -i id       Set request id to 'id'.  Values may be 0..255\n");
95         fprintf(stderr, "  -S file     read secret from file, not command line.\n");
96         fprintf(stderr, "  -q          Do not print anything out.\n");
97         fprintf(stderr, "  -s          Print out summary information of auth results.\n");
98         fprintf(stderr, "  -v          Show program version information.\n");
99         fprintf(stderr, "  -x          Debugging mode.\n");
100
101         exit(1);
102 }
103
104 int radlog(int lvl, const char *msg, ...)
105 {
106         va_list ap;
107         int r;
108
109         r = lvl; /* shut up compiler */
110
111         va_start(ap, msg);
112         r = vfprintf(stderr, msg, ap);
113         va_end(ap);
114         fputc('\n', stderr);
115
116         return r;
117 }
118
119 int log_debug(const char *msg, ...)
120 {
121         va_list ap;
122         int r;
123
124         va_start(ap, msg);
125         r = vfprintf(stderr, msg, ap);
126         va_end(ap);
127         fputc('\n', stderr);
128
129         return r;
130 }
131
132 static int getport(const char *name)
133 {
134         struct  servent         *svp;
135
136         svp = getservbyname (name, "udp");
137         if (!svp) {
138                 return 0;
139         }
140
141         return ntohs(svp->s_port);
142 }
143
144 static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
145 {
146         int i;
147         struct timeval  tv;
148
149         for (i = 0; i < retries; i++) {
150                 fd_set          rdfdesc;
151
152                 rad_send(req, NULL, secret);
153
154                 /* And wait for reply, timing out as necessary */
155                 FD_ZERO(&rdfdesc);
156                 FD_SET(req->sockfd, &rdfdesc);
157
158                 tv.tv_sec = (int)timeout;
159                 tv.tv_usec = 1000000 * (timeout - (int) timeout);
160
161                 /* Something's wrong if we don't get exactly one fd. */
162                 if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) {
163                         continue;
164                 }
165
166                 *rep = rad_recv(req->sockfd);
167                 if (*rep != NULL) {
168                         /*
169                          *      If we get a response from a machine
170                          *      which we did NOT send a request to,
171                          *      then complain.
172                          */
173                         if (((*rep)->src_ipaddr != req->dst_ipaddr) ||
174                             ((*rep)->src_port != req->dst_port)) {
175                                 char src[64], dst[64];
176
177                                 ip_ntoa(src, (*rep)->src_ipaddr);
178                                 ip_ntoa(dst, req->dst_ipaddr);
179                                 fprintf(stderr, "radclient: ERROR: Sent request to host %s port %d, got response from host %s port %d\n!",
180                                         dst, req->dst_port,
181                                         src, (*rep)->src_port);
182                                 exit(1);
183                         }
184                         break;
185                 } else {        /* NULL: couldn't receive the packet */
186                         librad_perror("radclient:");
187                         exit(1);
188                 }
189         }
190
191         /* No response or no data read (?) */
192         if (i == retries) {
193                 fprintf(stderr, "radclient: no response from server\n");
194                 exit(1);
195         }
196
197         /*
198          *      FIXME: Discard the packet & listen for another.
199          *
200          *      Hmm... we should really be using eapol_test, which does
201          *      a lot more than radeapclient.
202          */
203         if (rad_verify(*rep, req, secret) != 0) {
204                 librad_perror("rad_verify");
205                 exit(1);
206         }
207
208         if (rad_decode(*rep, req, secret) != 0) {
209                 librad_perror("rad_decode");
210                 exit(1);
211         }
212
213         /* libradius debug already prints out the value pairs for us */
214         if (!librad_debug && do_output) {
215                 printf("Received response ID %d, code %d, length = %d\n",
216                                 (*rep)->id, (*rep)->code, (*rep)->data_len);
217                 vp_printlist(stdout, (*rep)->vps);
218         }
219         if((*rep)->code == PW_AUTHENTICATION_ACK) {
220                 totalapp++;
221         } else {
222                 totaldeny++;
223         }
224
225         return 0;
226 }
227
228 static void cleanresp(RADIUS_PACKET *resp)
229 {
230         VALUE_PAIR *vpnext, *vp, **last;
231
232
233         /*
234          * maybe should just copy things we care about, or keep
235          * a copy of the original input and start from there again?
236          */
237         pairdelete(&resp->vps, PW_EAP_MESSAGE);
238         pairdelete(&resp->vps, ATTRIBUTE_EAP_BASE+PW_EAP_IDENTITY);
239
240         last = &resp->vps;
241         for(vp = *last; vp != NULL; vp = vpnext)
242         {
243                 vpnext = vp->next;
244
245                 if((vp->attribute > ATTRIBUTE_EAP_BASE &&
246                     vp->attribute <= ATTRIBUTE_EAP_BASE+256) ||
247                    (vp->attribute > ATTRIBUTE_EAP_SIM_BASE &&
248                     vp->attribute <= ATTRIBUTE_EAP_SIM_BASE+256))
249                 {
250                         *last = vpnext;
251                         pairbasicfree(vp);
252                 } else {
253                         last = &vp->next;
254                 }
255         }
256 }
257
258 /*
259  * we got an EAP-Request/Sim/Start message in a legal state.
260  *
261  * pick a supported version, put it into the reply, and insert a nonce.
262  */
263 static int process_eap_start(RADIUS_PACKET *req,
264                              RADIUS_PACKET *rep)
265 {
266         VALUE_PAIR *vp, *newvp;
267         VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
268         uint16_t *versions, selectedversion;
269         unsigned int i,versioncount;
270
271         /* form new response clear of any EAP stuff */
272         cleanresp(rep);
273
274         if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST)) == NULL) {
275                 fprintf(stderr, "illegal start message has no VERSION_LIST\n");
276                 return 0;
277         }
278
279         versions = (uint16_t *)vp->strvalue;
280
281         /* verify that the attribute length is big enough for a length field */
282         if(vp->length < 4)
283         {
284                 fprintf(stderr, "start message has illegal VERSION_LIST. Too short: %d\n", vp->length);
285                 return 0;
286         }
287
288         versioncount = ntohs(versions[0])/2;
289         /* verify that the attribute length is big enough for the given number
290          * of versions present.
291          */
292         if((unsigned)vp->length <= (versioncount*2 + 2))
293         {
294                 fprintf(stderr, "start message is too short. Claimed %d versions does not fit in %d bytes\n", versioncount, vp->length);
295                 return 0;
296         }
297
298         /*
299          * record the versionlist for the MK calculation.
300          */
301         eapsim_mk.versionlistlen = versioncount*2;
302         memcpy(eapsim_mk.versionlist, (unsigned char *)(versions+1),
303                eapsim_mk.versionlistlen);
304
305         /* walk the version list, and pick the one we support, which
306          * at present, is 1, EAP_SIM_VERSION.
307          */
308         selectedversion=0;
309         for(i=0; i < versioncount; i++)
310         {
311                 if(ntohs(versions[i+1]) == EAP_SIM_VERSION)
312                 {
313                         selectedversion=EAP_SIM_VERSION;
314                         break;
315                 }
316         }
317         if(selectedversion == 0)
318         {
319                 fprintf(stderr, "eap-sim start message. No compatible version found. We need %d\n", EAP_SIM_VERSION);
320                 for(i=0; i < versioncount; i++)
321                 {
322                         fprintf(stderr, "\tfound version %d\n",
323                                 ntohs(versions[i+1]));
324                 }
325         }
326
327         /*
328          * now make sure that we have only FULLAUTH_ID_REQ.
329          * I think that it actually might not matter - we can answer in
330          * anyway we like, but it is illegal to have more than one
331          * present.
332          */
333         anyidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ);
334         fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ);
335         permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ);
336
337         if(fullauthidreq_vp == NULL ||
338            anyidreq_vp != NULL ||
339            permanentidreq_vp != NULL) {
340                 fprintf(stderr, "start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.\n",
341                         (anyidreq_vp != NULL ? "a " : "no "),
342                         (fullauthidreq_vp != NULL ? "a " : "no "),
343                         (permanentidreq_vp != NULL ? "a " : "no "));
344                 return 0;
345         }
346
347         /* okay, we have just any_id_req there, so fill in response */
348
349         /* mark the subtype as being EAP-SIM/Response/Start */
350         newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
351         newvp->lvalue = eapsim_start;
352         pairreplace(&(rep->vps), newvp);
353
354         /* insert selected version into response. */
355         newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION,
356                            PW_TYPE_OCTETS);
357         versions = (uint16_t *)newvp->strvalue;
358         versions[0] = htons(selectedversion);
359         newvp->length = 2;
360         pairreplace(&(rep->vps), newvp);
361
362         /* record the selected version */
363         memcpy(eapsim_mk.versionselect, (unsigned char *)versions, 2);
364
365         vp = newvp = NULL;
366
367         {
368                 uint32_t nonce[4];
369                 /*
370                  * insert a nonce_mt that we make up.
371                  */
372                 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT,
373                                    PW_TYPE_OCTETS);
374                 newvp->strvalue[0]=0;
375                 newvp->strvalue[1]=0;
376                 newvp->length = 18;  /* 16 bytes of nonce + padding */
377
378                 nonce[0]=lrad_rand();
379                 nonce[1]=lrad_rand();
380                 nonce[2]=lrad_rand();
381                 nonce[3]=lrad_rand();
382                 memcpy(&newvp->strvalue[2], nonce, 16);
383                 pairreplace(&(rep->vps), newvp);
384
385                 /* also keep a copy of the nonce! */
386                 memcpy(eapsim_mk.nonce_mt, nonce, 16);
387         }
388
389         {
390                 uint16_t *pidlen, idlen;
391
392                 /*
393                  * insert the identity here.
394                  */
395                 vp = pairfind(rep->vps, PW_USER_NAME);
396                 if(vp == NULL)
397                 {
398                         fprintf(stderr, "eap-sim: We need to have a User-Name attribute!\n");
399                         return 0;
400                 }
401                 newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY,
402                                    PW_TYPE_OCTETS);
403                 idlen = strlen(vp->strvalue);
404                 pidlen = (uint16_t *)newvp->strvalue;
405                 *pidlen = htons(idlen);
406                 newvp->length = idlen + 2;
407
408                 memcpy(&newvp->strvalue[2], vp->strvalue, idlen);
409                 pairreplace(&(rep->vps), newvp);
410
411                 /* record it */
412                 memcpy(eapsim_mk.identity, vp->strvalue, idlen);
413                 eapsim_mk.identitylen = idlen;
414         }
415
416         return 1;
417 }
418
419 /*
420  * we got an EAP-Request/Sim/Challenge message in a legal state.
421  *
422  * use the RAND challenge to produce the SRES result, and then
423  * use that to generate a new MAC.
424  *
425  * for the moment, we ignore the RANDs, then just plug in the SRES
426  * values.
427  *
428  */
429 static int process_eap_challenge(RADIUS_PACKET *req,
430                                  RADIUS_PACKET *rep)
431 {
432         VALUE_PAIR *newvp;
433         VALUE_PAIR *mac, *randvp;
434         VALUE_PAIR *sres1,*sres2,*sres3;
435         VALUE_PAIR *Kc1, *Kc2, *Kc3;
436         uint8_t calcmac[20];
437
438         /* look for the AT_MAC and the challenge data */
439         mac   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC);
440         randvp= pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND);
441         if(mac == NULL || rand == NULL) {
442                 fprintf(stderr, "radeapclient: challenge message needs to contain RAND and MAC\n");
443                 return 0;
444         }
445
446         /*
447          * compare RAND with randX, to verify this is the right response
448          * to this challenge.
449          */
450         {
451           VALUE_PAIR *randcfgvp[3];
452           unsigned char *randcfg[3];
453
454           randcfg[0] = &randvp->strvalue[2];
455           randcfg[1] = &randvp->strvalue[2+EAPSIM_RAND_SIZE];
456           randcfg[2] = &randvp->strvalue[2+EAPSIM_RAND_SIZE*2];
457
458           randcfgvp[0] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND1);
459           randcfgvp[1] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND2);
460           randcfgvp[2] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND3);
461
462           if(randcfgvp[0] == NULL ||
463              randcfgvp[1] == NULL ||
464              randcfgvp[2] == NULL) {
465             fprintf(stderr, "radeapclient: needs to have rand1, 2 and 3 set.\n");
466             return 0;
467           }
468
469           if(memcmp(randcfg[0], randcfgvp[0]->strvalue, EAPSIM_RAND_SIZE)!=0 ||
470              memcmp(randcfg[1], randcfgvp[1]->strvalue, EAPSIM_RAND_SIZE)!=0 ||
471              memcmp(randcfg[2], randcfgvp[2]->strvalue, EAPSIM_RAND_SIZE)!=0) {
472             int rnum,i,j;
473
474             fprintf(stderr, "radeapclient: one of rand 1,2,3 didn't match\n");
475             for(rnum = 0; rnum < 3; rnum++) {
476               fprintf(stderr, "received   rand %d: ", rnum);
477               j=0;
478               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
479                 if(j==4) {
480                   printf("_");
481                   j=0;
482                 }
483                 j++;
484
485                 fprintf(stderr, "%02x", randcfg[rnum][i]);
486               }
487               fprintf(stderr, "\nconfigured rand %d: ", rnum);
488               j=0;
489               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
490                 if(j==4) {
491                   printf("_");
492                   j=0;
493                 }
494                 j++;
495
496                 fprintf(stderr, "%02x", randcfgvp[rnum]->strvalue[i]);
497               }
498               fprintf(stderr, "\n");
499             }
500             return 0;
501           }
502         }
503
504         /*
505          * now dig up the sres values from the response packet,
506          * which were put there when we read things in.
507          *
508          * Really, they should be calculated from the RAND!
509          *
510          */
511         sres1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES1);
512         sres2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES2);
513         sres3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES3);
514
515         if(sres1 == NULL ||
516            sres2 == NULL ||
517            sres3 == NULL) {
518                 fprintf(stderr, "radeapclient: needs to have sres1, 2 and 3 set.\n");
519                 return 0;
520         }
521         memcpy(eapsim_mk.sres[0], sres1->strvalue, sizeof(eapsim_mk.sres[0]));
522         memcpy(eapsim_mk.sres[1], sres2->strvalue, sizeof(eapsim_mk.sres[1]));
523         memcpy(eapsim_mk.sres[2], sres3->strvalue, sizeof(eapsim_mk.sres[2]));
524
525         Kc1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC1);
526         Kc2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC2);
527         Kc3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC3);
528
529         if(Kc1 == NULL ||
530            Kc2 == NULL ||
531            Kc3 == NULL) {
532                 fprintf(stderr, "radeapclient: needs to have Kc1, 2 and 3 set.\n");
533                 return 0;
534         }
535         memcpy(eapsim_mk.Kc[0], Kc1->strvalue, sizeof(eapsim_mk.Kc[0]));
536         memcpy(eapsim_mk.Kc[1], Kc2->strvalue, sizeof(eapsim_mk.Kc[1]));
537         memcpy(eapsim_mk.Kc[2], Kc3->strvalue, sizeof(eapsim_mk.Kc[2]));
538
539         /* all set, calculate keys */
540         eapsim_calculate_keys(&eapsim_mk);
541
542         if(debug_flag) {
543           eapsim_dump_mk(&eapsim_mk);
544         }
545
546         /* verify the MAC, now that we have all the keys. */
547         if(eapsim_checkmac(req->vps, eapsim_mk.K_aut,
548                            eapsim_mk.nonce_mt, sizeof(eapsim_mk.nonce_mt),
549                            calcmac)) {
550                 printf("MAC check succeed\n");
551         } else {
552                 int i, j;
553                 j=0;
554                 printf("calculated MAC (");
555                 for (i = 0; i < 20; i++) {
556                         if(j==4) {
557                                 printf("_");
558                                 j=0;
559                         }
560                         j++;
561
562                         printf("%02x", calcmac[i]);
563                 }
564                 printf(" did not match\n");
565                 return 0;
566         }
567
568         /* form new response clear of any EAP stuff */
569         cleanresp(rep);
570
571         /* mark the subtype as being EAP-SIM/Response/Start */
572         newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
573         newvp->lvalue = eapsim_challenge;
574         pairreplace(&(rep->vps), newvp);
575
576         /*
577          * fill the SIM_MAC with a field that will in fact get appended
578          * to the packet before the MAC is calculated
579          */
580         newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC,
581                            PW_TYPE_OCTETS);
582         memcpy(newvp->strvalue+EAPSIM_SRES_SIZE*0, sres1->strvalue, EAPSIM_SRES_SIZE);
583         memcpy(newvp->strvalue+EAPSIM_SRES_SIZE*1, sres2->strvalue, EAPSIM_SRES_SIZE);
584         memcpy(newvp->strvalue+EAPSIM_SRES_SIZE*2, sres3->strvalue, EAPSIM_SRES_SIZE);
585         newvp->length = EAPSIM_SRES_SIZE*3;
586         pairreplace(&(rep->vps), newvp);
587
588         newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, PW_TYPE_OCTETS);
589         memcpy(newvp->strvalue,    eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);
590         newvp->length = EAPSIM_AUTH_SIZE;
591         pairreplace(&(rep->vps), newvp);
592
593         return 1;
594 }
595
596 /*
597  * this code runs the EAP-SIM client state machine.
598  * the *request* is from the server.
599  * the *reponse* is to the server.
600  *
601  */
602 static int respond_eap_sim(RADIUS_PACKET *req,
603                            RADIUS_PACKET *resp)
604 {
605         enum eapsim_clientstates state, newstate;
606         enum eapsim_subtype subtype;
607         VALUE_PAIR *vp, *statevp, *radstate, *eapid;
608         char statenamebuf[32], subtypenamebuf[32];
609
610         if ((radstate = paircopy2(req->vps, PW_STATE)) == NULL)
611         {
612                 return 0;
613         }
614
615         if ((eapid = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL)
616         {
617                 return 0;
618         }
619
620         /* first, dig up the state from the request packet, setting
621          * outselves to be in EAP-SIM-Start state if there is none.
622          */
623
624         if((statevp = pairfind(resp->vps, ATTRIBUTE_EAP_SIM_STATE)) == NULL)
625         {
626                 /* must be initial request */
627                 statevp = paircreate(ATTRIBUTE_EAP_SIM_STATE, PW_TYPE_INTEGER);
628                 statevp->lvalue = eapsim_client_init;
629                 pairreplace(&(resp->vps), statevp);
630         }
631         state = statevp->lvalue;
632
633         /*
634          * map the attributes, and authenticate them.
635          */
636         unmap_eapsim_types(req);
637
638         printf("<+++ EAP-sim decoded packet:\n");
639         vp_printlist(stdout, req->vps);
640
641         if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_SUBTYPE)) == NULL)
642         {
643                 return 0;
644         }
645         subtype = vp->lvalue;
646
647         /*
648          * look for the appropriate state, and process incoming message
649          */
650         switch(state) {
651         case eapsim_client_init:
652                 switch(subtype) {
653                 case eapsim_start:
654                         newstate = process_eap_start(req, resp);
655                         break;
656
657                 case eapsim_challenge:
658                 case eapsim_notification:
659                 case eapsim_reauth:
660                 default:
661                         fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
662                                 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
663                                 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
664                         /* invalid state, drop message */
665                         return 0;
666                 }
667                 break;
668
669         case eapsim_client_start:
670                 switch(subtype) {
671                 case eapsim_start:
672                         /* NOT SURE ABOUT THIS ONE, retransmit, I guess */
673                         newstate = process_eap_start(req, resp);
674                         break;
675
676                 case eapsim_challenge:
677                         newstate = process_eap_challenge(req, resp);
678                         break;
679
680                 default:
681                         fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
682                                 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
683                                 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
684                         /* invalid state, drop message */
685                         return 0;
686                 }
687                 break;
688
689
690         default:
691                 fprintf(stderr, "radeapclient: sim in illegal state %s\n",
692                         sim_state2name(state, statenamebuf, sizeof(statenamebuf)));
693                 return 0;
694         }
695
696         /* copy the eap state object in */
697         pairreplace(&(resp->vps), eapid);
698
699         /* update stete info, and send new packet */
700         map_eapsim_types(resp);
701
702         /* copy the radius state object in */
703         pairreplace(&(resp->vps), radstate);
704
705         statevp->lvalue = newstate;
706         return 1;
707 }
708
709 static int respond_eap_md5(RADIUS_PACKET *req,
710                            RADIUS_PACKET *rep)
711 {
712         VALUE_PAIR *vp, *id, *state;
713         int valuesize, namesize;
714         unsigned char identifier;
715         unsigned char *value;
716         unsigned char *name;
717         MD5_CTX context;
718         char    response[16];
719
720         cleanresp(rep);
721
722         if ((state = paircopy2(req->vps, PW_STATE)) == NULL)
723         {
724                 fprintf(stderr, "radeapclient: no state attribute found\n");
725                 return 0;
726         }
727
728         if ((id = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL)
729         {
730                 fprintf(stderr, "radeapclient: no EAP-ID attribute found\n");
731                 return 0;
732         }
733         identifier = id->lvalue;
734
735         if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5)) == NULL)
736         {
737                 fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n");
738                 return 0;
739         }
740
741         /* got the details of the MD5 challenge */
742         valuesize = vp->strvalue[0];
743         value = &vp->strvalue[1];
744         name  = &vp->strvalue[valuesize+1];
745         namesize = vp->length - (valuesize + 1);
746
747         /* sanitize items */
748         if(valuesize > vp->length)
749         {
750                 fprintf(stderr, "radeapclient: md5 valuesize if too big (%d > %d)\n",
751                         valuesize, vp->length);
752                 return 0;
753         }
754
755         /* now do the CHAP operation ourself, rather than build the
756          * buffer. We could also call rad_chap_encode, but it wants
757          * a CHAP-Challenge, which we don't want to bother with.
758          */
759         librad_MD5Init(&context);
760         librad_MD5Update(&context, &identifier, 1);
761         librad_MD5Update(&context, password, strlen(password));
762         librad_MD5Update(&context, value, valuesize);
763         librad_MD5Final(response, &context);
764
765         vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, PW_TYPE_OCTETS);
766         vp->strvalue[0]=16;
767         memcpy(&vp->strvalue[1], response, 16);
768         vp->length = 17;
769
770         pairreplace(&(rep->vps), vp);
771
772         pairreplace(&(rep->vps), id);
773
774         /* copy the state object in */
775         pairreplace(&(rep->vps), state);
776
777         return 1;
778 }
779
780
781
782 static int sendrecv_eap(RADIUS_PACKET *rep)
783 {
784         RADIUS_PACKET *req = NULL;
785         VALUE_PAIR *vp, *vpnext;
786         int tried_eap_md5 = 0;
787
788         /*
789          *      Keep a copy of the the User-Password attribute.
790          */
791         if ((vp = pairfind(rep->vps, ATTRIBUTE_EAP_MD5_PASSWORD)) != NULL) {
792                 strNcpy(password, (char *)vp->strvalue, sizeof(vp->strvalue));
793
794         } else  if ((vp = pairfind(rep->vps, PW_PASSWORD)) != NULL) {
795                 strNcpy(password, (char *)vp->strvalue, sizeof(vp->strvalue));
796                 /*
797                  *      Otherwise keep a copy of the CHAP-Password attribute.
798                  */
799         } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD)) != NULL) {
800                 strNcpy(password, (char *)vp->strvalue, sizeof(vp->strvalue));
801         } else {
802                 *password = '\0';
803         }
804
805  again:
806         rep->id++;
807
808         printf("\n+++> About to send encoded packet:\n");
809         vp_printlist(stdout, rep->vps);
810
811         /*
812          * if there are EAP types, encode them into an EAP-Message
813          *
814          */
815         map_eap_types(rep);
816
817         /*
818          *  Fix up Digest-Attributes issues
819          */
820         for (vp = rep->vps; vp != NULL; vp = vp->next) {
821                 switch (vp->attribute) {
822                 default:
823                         break;
824
825                 case PW_DIGEST_REALM:
826                 case PW_DIGEST_NONCE:
827                 case PW_DIGEST_METHOD:
828                 case PW_DIGEST_URI:
829                 case PW_DIGEST_QOP:
830                 case PW_DIGEST_ALGORITHM:
831                 case PW_DIGEST_BODY_DIGEST:
832                 case PW_DIGEST_CNONCE:
833                 case PW_DIGEST_NONCE_COUNT:
834                 case PW_DIGEST_USER_NAME:
835                         /* overlapping! */
836                         memmove(&vp->strvalue[2], &vp->strvalue[0], vp->length);
837                         vp->strvalue[0] = vp->attribute - PW_DIGEST_REALM + 1;
838                         vp->length += 2;
839                         vp->strvalue[1] = vp->length;
840                         vp->attribute = PW_DIGEST_ATTRIBUTES;
841                         break;
842                 }
843         }
844
845         /*
846          *      If we've already sent a packet, free up the old
847          *      one, and ensure that the next packet has a unique
848          *      ID and authentication vector.
849          */
850         if (rep->data) {
851                 free(rep->data);
852                 rep->data = NULL;
853         }
854
855         librad_md5_calc(rep->vector, rep->vector,
856                         sizeof(rep->vector));
857
858         if (*password != '\0') {
859                 if ((vp = pairfind(rep->vps, PW_PASSWORD)) != NULL) {
860                         strNcpy((char *)vp->strvalue, password, strlen(password) + 1);
861                         vp->length = strlen(password);
862
863                 } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD)) != NULL) {
864                         strNcpy((char *)vp->strvalue, password, strlen(password) + 1);
865                         vp->length = strlen(password);
866
867                         rad_chap_encode(rep, (char *) vp->strvalue, rep->id, vp);
868                         vp->length = 17;
869                 }
870         } /* there WAS a password */
871
872         /* send the response, wait for the next request */
873         send_packet(rep, &req);
874
875         /* okay got back the packet, go and decode the EAP-Message. */
876         unmap_eap_types(req);
877
878         printf("<+++ EAP decoded packet:\n");
879         vp_printlist(stdout, req->vps);
880
881         /* now look for the code type. */
882         for (vp = req->vps; vp != NULL; vp = vpnext) {
883                 vpnext = vp->next;
884
885                 switch (vp->attribute) {
886                 default:
887                         break;
888
889                 case ATTRIBUTE_EAP_BASE+PW_EAP_MD5:
890                         if(respond_eap_md5(req, rep) && tried_eap_md5 < 3)
891                         {
892                                 tried_eap_md5++;
893                                 goto again;
894                         }
895                         break;
896
897                 case ATTRIBUTE_EAP_BASE+PW_EAP_SIM:
898                         if(respond_eap_sim(req, rep))
899                         {
900                                 goto again;
901                         }
902                         break;
903                 }
904         }
905
906         return 1;
907 }
908
909
910 int main(int argc, char **argv)
911 {
912         RADIUS_PACKET *req;
913         char *p;
914         int c;
915         int port = 0;
916         char *filename = NULL;
917         FILE *fp;
918         int count = 1;
919         int id;
920
921         id = ((int)getpid() & 0xff);
922         librad_debug = 0;
923
924         radlog_dest = RADLOG_STDERR;
925
926         while ((c = getopt(argc, argv, "c:d:f:hi:qst:r:S:xXv")) != EOF)
927         {
928                 switch(c) {
929                 case 'c':
930                         if (!isdigit((int) *optarg))
931                                 usage();
932                         count = atoi(optarg);
933                         break;
934                 case 'd':
935                         radius_dir = optarg;
936                         break;
937                 case 'f':
938                         filename = optarg;
939                         break;
940                 case 'q':
941                         do_output = 0;
942                         break;
943                 case 'x':
944                         debug_flag++;
945                         librad_debug++;
946                         break;
947
948                 case 'X':
949 #if 0
950                   sha1_data_problems = 1; /* for debugging only */
951 #endif
952                   break;
953
954
955
956                 case 'r':
957                         if (!isdigit((int) *optarg))
958                                 usage();
959                         retries = atoi(optarg);
960                         break;
961                 case 'i':
962                         if (!isdigit((int) *optarg))
963                                 usage();
964                         id = atoi(optarg);
965                         if ((id < 0) || (id > 255)) {
966                                 usage();
967                         }
968                         break;
969                 case 's':
970                         do_summary = 1;
971                         break;
972                 case 't':
973                         if (!isdigit((int) *optarg))
974                                 usage();
975                         timeout = atof(optarg);
976                         break;
977                 case 'v':
978                         printf("radclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
979                         exit(0);
980                         break;
981                case 'S':
982                        fp = fopen(optarg, "r");
983                        if (!fp) {
984                                fprintf(stderr, "radclient: Error opening %s: %s\n",
985                                        optarg, strerror(errno));
986                                exit(1);
987                        }
988                        if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
989                                fprintf(stderr, "radclient: Error reading %s: %s\n",
990                                        optarg, strerror(errno));
991                                exit(1);
992                        }
993                        fclose(fp);
994
995                        /* truncate newline */
996                        p = filesecret + strlen(filesecret) - 1;
997                        while ((p >= filesecret) &&
998                               (*p < ' ')) {
999                                *p = '\0';
1000                                --p;
1001                        }
1002
1003                        if (strlen(filesecret) < 2) {
1004                                fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
1005                                exit(1);
1006                        }
1007                        secret = filesecret;
1008                        break;
1009                 case 'h':
1010                 default:
1011                         usage();
1012                         break;
1013                 }
1014         }
1015         argc -= (optind - 1);
1016         argv += (optind - 1);
1017
1018         if ((argc < 3)  ||
1019             ((secret == NULL) && (argc < 4))) {
1020                 usage();
1021         }
1022
1023         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
1024                 librad_perror("radclient");
1025                 return 1;
1026         }
1027
1028         if ((req = rad_alloc(1)) == NULL) {
1029                 librad_perror("radclient");
1030                 exit(1);
1031         }
1032
1033 #if 0
1034         {
1035                 FILE *randinit;
1036
1037                 if((randinit = fopen("/dev/urandom", "r")) == NULL)
1038                 {
1039                         perror("/dev/urandom");
1040                 } else {
1041                         fread(randctx.randrsl, 256, 1, randinit);
1042                         fclose(randinit);
1043                 }
1044         }
1045         lrad_randinit(&randctx, 1);
1046 #endif
1047
1048         req->id = id;
1049
1050         /*
1051          *      Strip port from hostname if needed.
1052          */
1053         if ((p = strchr(argv[1], ':')) != NULL) {
1054                 *p++ = 0;
1055                 port = atoi(p);
1056         }
1057
1058         /*
1059          *      See what kind of request we want to send.
1060          */
1061         if (strcmp(argv[2], "auth") == 0) {
1062                 if (port == 0) port = getport("radius");
1063                 if (port == 0) port = PW_AUTH_UDP_PORT;
1064                 req->code = PW_AUTHENTICATION_REQUEST;
1065
1066         } else if (strcmp(argv[2], "acct") == 0) {
1067                 if (port == 0) port = getport("radacct");
1068                 if (port == 0) port = PW_ACCT_UDP_PORT;
1069                 req->code = PW_ACCOUNTING_REQUEST;
1070                 do_summary = 0;
1071
1072         } else if (strcmp(argv[2], "status") == 0) {
1073                 if (port == 0) port = getport("radius");
1074                 if (port == 0) port = PW_AUTH_UDP_PORT;
1075                 req->code = PW_STATUS_SERVER;
1076
1077         } else if (strcmp(argv[2], "disconnect") == 0) {
1078                 if (port == 0) port = PW_POD_UDP_PORT;
1079                 req->code = PW_DISCONNECT_REQUEST;
1080
1081         } else if (isdigit((int) argv[2][0])) {
1082                 if (port == 0) port = getport("radius");
1083                 if (port == 0) port = PW_AUTH_UDP_PORT;
1084                 req->code = atoi(argv[2]);
1085         } else {
1086                 usage();
1087         }
1088
1089         /*
1090          *      Ensure that the configuration is initialized.
1091          */
1092         memset(&mainconfig, 0, sizeof(mainconfig));
1093
1094         /*
1095          *      Resolve hostname.
1096          */
1097         req->dst_port = port;
1098         req->dst_ipaddr = ip_getaddr(argv[1]);
1099         if (req->dst_ipaddr == INADDR_NONE) {
1100                 fprintf(stderr, "radclient: Failed to find IP address for host %s\n", argv[1]);
1101                 exit(1);
1102         }
1103
1104         /*
1105          *      Add the secret.
1106          */
1107         if (argv[3]) secret = argv[3];
1108
1109         /*
1110          *      Read valuepairs.
1111          *      Maybe read them, from stdin, if there's no
1112          *      filename, or if the filename is '-'.
1113          */
1114         if (filename && (strcmp(filename, "-") != 0)) {
1115                 fp = fopen(filename, "r");
1116                 if (!fp) {
1117                         fprintf(stderr, "radclient: Error opening %s: %s\n",
1118                                 filename, strerror(errno));
1119                         exit(1);
1120                 }
1121         } else {
1122                 fp = stdin;
1123         }
1124
1125         /*
1126          *      Send request.
1127          */
1128         if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
1129                 perror("radclient: socket: ");
1130                 exit(1);
1131         }
1132
1133         while(!filedone) {
1134                 if(req->vps) pairfree(&req->vps);
1135
1136                 if ((req->vps = readvp2(fp, &filedone, "radeapclient:"))
1137                     == NULL) {
1138                         break;
1139                 }
1140
1141                 sendrecv_eap(req);
1142         }
1143
1144         if(do_summary) {
1145                 printf("\n\t   Total approved auths:  %d\n", totalapp);
1146                 printf("\t     Total denied auths:  %d\n", totaldeny);
1147         }
1148         return 0;
1149 }