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