2 * radeapclient.c EAP specific radius packet debug tool.
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.
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.
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
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>
27 #include <freeradius-devel/libradius.h>
36 #include <freeradius-devel/conf.h>
37 #include <freeradius-devel/radpaths.h>
38 #include <freeradius-devel/md5.h>
40 #include "eap_types.h"
43 extern int sha1_data_problems;
47 static uint32_t parallel = 1;
48 static unsigned int retries = 3;
49 static float timeout = 5;
50 static struct timeval tv_timeout;
51 static char const *secret = NULL;
52 static int do_output = 1;
53 static int do_summary = 0;
54 static int totalapp = 0;
55 static int totaldeny = 0;
56 static char filesecret[256];
57 static char const *radius_dir = NULL;
58 static char const *progname = "radeapclient";
59 /* fr_randctx randctx; */
61 main_config_t main_config;
62 char const *radiusd_version = "";
65 #include <freeradius-devel/tls.h>
68 log_lvl_t rad_debug_lvl = 0;
70 //TODO: move structures to a header file.
72 typedef struct rc_input_vps_list rc_input_vps_list_t;
73 typedef struct rc_input_vps rc_input_vps_t;
74 typedef struct rc_transaction rc_transaction_t;
76 /** Structure which contains EAP context, necessary to perform the full EAP transaction.
78 typedef struct rc_eap_sim_context {
79 struct eapsim_keys keys;
80 } rc_eap_sim_context_t;
82 typedef struct rc_eap_md5_context {
84 } rc_eap_md5_context_t;
86 typedef struct rc_eap_context {
87 int eap_type; //!< contains the EAP-Type
88 char password[256]; //!< copy of User-Password (or CHAP-Password).
90 rc_eap_sim_context_t sim;
91 rc_eap_md5_context_t md5;
96 /** Structure which holds a list of available input vps.
98 struct rc_input_vps_list {
100 rc_input_vps_t *tail;
104 /** Structure which holds an input vps entry (read from file or stdin),
105 * and linkage to previous / next entries.
107 struct rc_input_vps {
108 uint32_t num; //!< The number (within the file) of the input we're reading.
110 VALUE_PAIR *vps_in; //!< the list of attribute/value pairs.
112 rc_input_vps_list_t *list; //!< the list to which this entry belongs (NULL for an unchained entry).
114 rc_input_vps_t *prev;
115 rc_input_vps_t *next;
119 /** Structure which holds a transaction: sent packet, reply received...
121 struct rc_transaction {
122 uint32_t id; //!< id of transaction (0 for the first one).
124 uint32_t num_packet; //!< number of packets sent for this transaction.
126 RADIUS_PACKET *packet;
127 RADIUS_PACKET *reply;
129 rc_input_vps_t *input_vps;
131 rc_eap_context_t *eap_context;
135 fr_event_t *event; //!< armed event (if any).
138 char const *name; //!< Test name (as specified in the request).
142 static TALLOC_CTX *autofree;
143 static uint32_t num_trans = 0; //!< number of transactions initialized.
144 static uint32_t num_started = 0; //!< number of transactions started.
145 static uint32_t num_ongoing = 0; //!< number of ongoing transactions.
146 static uint32_t num_finished = 0; //!< number of finished transactions.
148 static rc_input_vps_list_t rc_vps_list_in; //!< list of available input vps entries.
149 static fr_packet_list_t *pl = NULL; //!< list of outgoing packets.
150 static unsigned int num_sockets = 0; //!< number of allocated sockets.
151 static fr_event_list_t *ev_list = NULL; //!< list of armed events.
153 static int force_af = AF_UNSPEC;
154 static int ipproto = IPPROTO_UDP;
155 static fr_ipaddr_t server_ipaddr;
156 static bool server_addr_init = false;
157 static uint16_t server_port = 0;
158 static int packet_code = PW_CODE_UNDEFINED;
160 static int rc_map_eap_methods(RADIUS_PACKET *req);
161 static void rc_unmap_eap_methods(RADIUS_PACKET *rep);
162 static int rc_map_eapsim_types(RADIUS_PACKET *r);
163 static int rc_unmap_eapsim_types(RADIUS_PACKET *r);
165 static void rc_get_port(PW_CODE type, uint16_t *port);
166 static void rc_evprep_packet_timeout(rc_transaction_t *trans);
167 static void rc_deallocate_id(rc_transaction_t *trans);
170 static void NEVER_RETURNS usage(void)
172 fprintf(stdout, "Usage: radeapclient [options] server[:port] <command> [<secret>]\n");
174 fprintf(stdout, " <command> One of auth, acct, status, coa, disconnect or auto.\n");
175 fprintf(stdout, " -4 Use IPv4 address of server\n");
176 fprintf(stdout, " -6 Use IPv6 address of server.\n");
177 fprintf(stdout, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n");
178 fprintf(stdout, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
179 fprintf(stdout, " -f <file> Read packets from file, not stdin.\n");
180 fprintf(stdout, " -h Print usage help information.\n");
181 fprintf(stdout, " -p <num> Send 'num' packets in parallel.\n");
182 fprintf(stdout, " -q Do not print anything out.\n");
183 fprintf(stdout, " -r <retries> If timeout, retry sending the packet 'retries' times.\n");
184 fprintf(stdout, " -s Print out summary information of auth results.\n");
185 fprintf(stdout, " -S <file> read secret from file, not command line.\n");
186 fprintf(stdout, " -t <timeout> Wait 'timeout' seconds before retrying (may be a floating point number).\n");
187 fprintf(stdout, " -v Show program version information.\n");
188 fprintf(stdout, " -x Debugging mode.\n");
193 static const FR_NAME_NUMBER rc_request_types[] = {
194 { "auth", PW_CODE_ACCESS_REQUEST },
195 { "challenge", PW_CODE_ACCESS_CHALLENGE },
196 { "acct", PW_CODE_ACCOUNTING_REQUEST },
197 { "status", PW_CODE_STATUS_SERVER },
198 { "disconnect", PW_CODE_DISCONNECT_REQUEST },
199 { "coa", PW_CODE_COA_REQUEST },
200 { "auto", PW_CODE_UNDEFINED },
205 int rad_virtual_server(REQUEST UNUSED *request)
207 /*We're not the server so we cannot do this*/
211 /** Convert a float to struct timeval.
213 static void rc_float_to_timeval(struct timeval *tv, float f_val)
215 tv->tv_sec = (time_t)f_val;
216 uint64_t usec = (uint64_t)(f_val * USEC) - (tv->tv_sec * USEC);
220 /** Add an allocated rc_input_vps_t entry to the tail of the list.
222 static void rc_add_vps_entry(rc_input_vps_list_t *list, rc_input_vps_t *entry)
224 if (!list || !entry) return;
227 assert(list->tail == NULL);
231 assert(list->tail != NULL);
232 assert(list->tail->next == NULL);
233 list->tail->next = entry;
234 entry->prev = list->tail;
242 /** Remove a selected rc_input_vps_t entry from its current list.
244 static rc_input_vps_t *rc_yank_vps_entry(rc_input_vps_t *entry)
246 if (!entry) return NULL;
248 if (!entry->list) return entry; /* not in a list, nothing to do. Just return the entry. */
250 rc_input_vps_t *prev, *next;
255 rc_input_vps_list_t *list = entry->list;
257 assert(list->head != NULL); /* entry belongs to a list, so the list can't be empty. */
258 assert(list->tail != NULL); /* same. */
261 assert(list->head != entry); /* if entry has a prev, then entry can't be head. */
265 assert(list->head == entry); /* if entry has no prev, then entry must be head. */
270 assert(list->tail != entry); /* if entry has a next, then entry can't be tail. */
274 assert(list->tail == entry); /* if entry has no next, then entry must be tail. */
285 /** Load input entries (list of vps) from a file or stdin, and add them to the list.
286 * They will be used to initiate transactions.
288 static int rc_load_input(TALLOC_CTX *ctx, char const *filename, rc_input_vps_list_t *list, uint32_t max_entries)
290 FILE *file_in = NULL;
291 bool file_done = false;
292 rc_input_vps_t *request;
294 uint32_t input_num = 0;
296 /* Determine where to read the VP's from. */
297 if (filename && strcmp(filename, "-") != 0) {
298 DEBUG2("Opening input file: %s", filename);
299 file_in = fopen(filename, "r");
301 ERROR("Error opening %s: %s", filename, strerror(errno));
306 DEBUG2("Reading input vps from stdin");
311 /* Loop over the file (or stdin). */
314 MEM(request = talloc_zero(ctx, rc_input_vps_t));
316 if (fr_pair_list_afrom_file(request, &request->vps_in, file_in, &file_done) < 0) {
317 ERROR("Error parsing entry %u from input: %s", input_num, input);
318 talloc_free(request);
321 if (NULL == request->vps_in) {
322 /* Last line might be empty, in this case fr_pair_list_afrom_file will return a NULL vps pointer. Silently ignore this. */
323 talloc_free(request);
327 /* Add that to the list */
328 rc_add_vps_entry(list, request);
330 request->num = list->size;
332 if (max_entries && list->size >= max_entries) {
333 /* Only load what we need. */
336 } while (!file_done);
338 if (file_in != stdin) fclose(file_in);
340 /* And we're done. */
341 DEBUG("Read %d element(s) from input: %s", list->size, input);
345 /** Perform packet initialization for a transaction.
347 static int rc_init_packet(rc_transaction_t *trans)
349 if (!trans || !trans->packet) return 0;
351 RADIUS_PACKET *packet = trans->packet;
356 * Process special attributes
358 for (vp = fr_cursor_init(&cursor, &packet->vps);
360 vp = fr_cursor_next(&cursor)) {
362 * Double quoted strings get marked up as xlat expansions,
363 * but we don't support that in request.
365 if (vp->type == VT_XLAT) {
367 vp->vp_strvalue = vp->value.xlat;
368 vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
371 if (!vp->da->vendor) switch (vp->da->attr) {
376 * Allow it to set the packet type in
377 * the attributes read from the file.
380 packet->code = vp->vp_integer;
383 case PW_PACKET_DST_PORT:
384 packet->dst_port = (vp->vp_integer & 0xffff);
387 case PW_PACKET_DST_IP_ADDRESS:
388 packet->dst_ipaddr.af = AF_INET;
389 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
390 packet->dst_ipaddr.prefix = 32;
393 case PW_PACKET_DST_IPV6_ADDRESS:
394 packet->dst_ipaddr.af = AF_INET6;
395 packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
396 packet->dst_ipaddr.prefix = 128;
399 case PW_PACKET_SRC_PORT:
400 if ((vp->vp_integer < 1024) ||
401 (vp->vp_integer > 65535)) {
402 DEBUG("Invalid value '%u' for Packet-Src-Port", vp->vp_integer);
404 packet->src_port = (vp->vp_integer & 0xffff);
408 case PW_PACKET_SRC_IP_ADDRESS:
409 packet->src_ipaddr.af = AF_INET;
410 packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
411 packet->src_ipaddr.prefix = 32;
414 case PW_PACKET_SRC_IPV6_ADDRESS:
415 packet->src_ipaddr.af = AF_INET6;
416 packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
417 packet->src_ipaddr.prefix = 128;
420 case PW_DIGEST_REALM:
421 case PW_DIGEST_NONCE:
422 case PW_DIGEST_METHOD:
425 case PW_DIGEST_ALGORITHM:
426 case PW_DIGEST_BODY_DIGEST:
427 case PW_DIGEST_CNONCE:
428 case PW_DIGEST_NONCE_COUNT:
429 case PW_DIGEST_USER_NAME:
435 p = talloc_array(vp, uint8_t, vp->vp_length + 2);
437 memcpy(p + 2, vp->vp_octets, vp->vp_length);
438 p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
440 p[1] = vp->vp_length;
442 da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
444 ERROR("Attribute 'Digest-Attributes' not found by value");
450 * Re-do fr_pair_value_memsteal ourselves,
451 * because we play games with
452 * vp->da, and fr_pair_value_memsteal goes
453 * to GREAT lengths to sanitize
454 * and fix and change and
455 * double-check the various
458 memcpy(&q, &vp->vp_octets, sizeof(q));
461 vp->vp_octets = talloc_steal(vp, p);
469 * Keep a copy of the the password attribute.
471 case PW_CLEARTEXT_PASSWORD:
472 case PW_USER_PASSWORD:
473 case PW_CHAP_PASSWORD:
474 case PW_MS_CHAP_PASSWORD:
475 strlcpy(trans->password, vp->vp_strvalue, sizeof(trans->password));
478 case PW_RADCLIENT_TEST_NAME:
479 trans->name = vp->vp_strvalue;
482 } /* loop over the VP's we read in */
484 if (packet->dst_port == 0) packet->dst_port = server_port;
486 if (packet->dst_ipaddr.af == AF_UNSPEC) {
487 if (!server_addr_init) {
488 DEBUG("No server was given, and input entry %u did not contain Packet-Dst-IP-Address, ignored.", trans->input_vps->num);
491 packet->dst_ipaddr = server_ipaddr;
494 /* Use the default set on the command line. */
495 if (packet->code == PW_CODE_UNDEFINED) {
496 if (packet_code == PW_CODE_UNDEFINED) {
497 DEBUG("No packet type was given, and input entry %u did not contain Packet-Type, ignored.", trans->input_vps->num);
500 packet->code = packet_code;
503 /* Automatically set the dst port (if one wasn't already set). */
504 if (packet->dst_port == 0) {
505 rc_get_port(packet->code, &packet->dst_port);
506 if (packet->dst_port == 0) {
507 DEBUG("Can't determine destination port for input entry %u, ignored.", trans->input_vps->num);
518 /** Map EAP methods and build EAP-Message (if EAP is involved).
519 * Also allocate the EAP context.
521 static void rc_build_eap_context(rc_transaction_t *trans)
523 if (!trans || !trans->packet) return;
525 RADIUS_PACKET *packet = trans->packet;
527 /* Build EAP-Message (if EAP is involved. Otherwise, do nothing). */
528 int eap_type = rc_map_eap_methods(packet);
531 if (!trans->eap_context) {
532 MEM(trans->eap_context = talloc_zero(trans, rc_eap_context_t));
534 trans->eap_context->eap_type = eap_type;
537 * Keep a copy of the the User-Password or CHAP-Password.
538 * Note: this is not useful for EAP-SIM, but we cannot know what kind
539 * of challenge the server will issue.
542 vp = fr_pair_find_by_num(packet->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
543 if (!vp) vp = fr_pair_find_by_num(packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
544 if (!vp) vp = fr_pair_find_by_num(packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
546 strlcpy(trans->eap_context->password, vp->vp_strvalue, sizeof(trans->eap_context->password));
551 /** Grab an element from the input list. Initialize a new transaction context, using this element.
553 static rc_transaction_t *rc_init_transaction(TALLOC_CTX *ctx)
555 if (!rc_vps_list_in.head || rc_vps_list_in.size == 0) {
556 /* Empty list, can't create a new transaction. */
560 rc_input_vps_t *vps_entry = rc_vps_list_in.head;
562 rc_yank_vps_entry(vps_entry); /* This cannot fail (we checked the list beforehand.) */
564 /* We grabbed an vps entry, now we can initialize a new transaction. */
565 rc_transaction_t *trans;
566 MEM(trans = talloc_zero(ctx, rc_transaction_t));
568 trans->input_vps = vps_entry;
569 trans->id = num_trans ++;
571 talloc_steal(trans, vps_entry); /* It's ours now. */
573 RADIUS_PACKET *packet;
574 MEM(packet = rad_alloc(trans, 1));
575 trans->packet = packet;
577 /* Fill in the packet value pairs. */
578 packet->vps = fr_pair_list_copy(packet, vps_entry->vps_in);
580 /* Initialize the transaction packet. */
581 if (!rc_init_packet(trans)) {
587 /* Update transactions counters. */
594 /** Terminate a transaction.
596 static void rc_finish_transaction(rc_transaction_t *trans)
600 if (trans->event) fr_event_delete(ev_list, &trans->event);
601 rc_deallocate_id(trans);
604 /* Update transactions counters. */
608 DEBUG4("pl: %d, ev: %d, in: %d", fr_packet_list_num_outgoing(pl), fr_event_list_num_elements(ev_list), rc_vps_list_in.size);
612 static uint16_t getport(char const *name)
616 svp = getservbyname(name, "udp");
619 return ntohs(svp->s_port);
623 static void rc_cleanresp(RADIUS_PACKET *resp)
625 VALUE_PAIR *vpnext, *vp, **last;
628 * maybe should just copy things we care about, or keep
629 * a copy of the original input and start from there again?
631 fr_pair_delete_by_num(&resp->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
632 fr_pair_delete_by_num(&resp->vps, PW_EAP_TYPE_BASE+PW_EAP_IDENTITY, 0, TAG_ANY);
635 for (vp = *last; vp != NULL; vp = vpnext)
639 if ((vp->da->attr > PW_EAP_TYPE_BASE &&
640 vp->da->attr <= PW_EAP_TYPE_BASE+256) ||
641 (vp->da->attr > PW_EAP_SIM_BASE &&
642 vp->da->attr <= PW_EAP_SIM_BASE+256))
653 * we got an EAP-Request/Sim/Start message in a legal state.
655 * pick a supported version, put it into the reply, and insert a nonce.
657 static int rc_process_eap_start(rc_eap_context_t *eap_context,
658 RADIUS_PACKET *req, RADIUS_PACKET *rep)
660 VALUE_PAIR *vp, *newvp;
661 VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
662 uint16_t const *versions;
663 uint16_t selectedversion;
664 unsigned int i,versioncount;
666 /* form new response clear of any EAP stuff */
669 if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
670 ERROR("illegal start message has no VERSION_LIST");
674 versions = (uint16_t const *) vp->vp_strvalue;
676 /* verify that the attribute length is big enough for a length field */
677 if (vp->vp_length < 4)
679 ERROR("start message has illegal VERSION_LIST. Too short: %u", (unsigned int) vp->vp_length);
683 versioncount = ntohs(versions[0])/2;
684 /* verify that the attribute length is big enough for the given number
685 * of versions present.
687 if ((unsigned)vp->vp_length <= (versioncount*2 + 2))
689 ERROR("start message is too short. Claimed %d versions does not fit in %u bytes", versioncount, (unsigned int) vp->vp_length);
694 * record the versionlist for the MK calculation.
696 eap_context->eap.sim.keys.versionlistlen = versioncount*2;
697 memcpy(eap_context->eap.sim.keys.versionlist, (unsigned char const *)(versions+1),
698 eap_context->eap.sim.keys.versionlistlen);
700 /* walk the version list, and pick the one we support, which
701 * at present, is 1, EAP_SIM_VERSION.
704 for (i=0; i < versioncount; i++)
706 if (ntohs(versions[i+1]) == EAP_SIM_VERSION)
708 selectedversion=EAP_SIM_VERSION;
712 if (selectedversion == 0)
714 ERROR("eap-sim start message. No compatible version found. We need %d", EAP_SIM_VERSION);
715 for (i=0; i < versioncount; i++)
717 ERROR("\tfound version %d",
718 ntohs(versions[i+1]));
723 * now make sure that we have only FULLAUTH_ID_REQ.
724 * I think that it actually might not matter - we can answer in
725 * anyway we like, but it is illegal to have more than one
728 anyidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
729 fullauthidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
730 permanentidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);
732 if (!fullauthidreq_vp ||
733 anyidreq_vp != NULL ||
734 permanentidreq_vp != NULL) {
735 ERROR("start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.",
736 (anyidreq_vp != NULL ? "a ": "no "),
737 (fullauthidreq_vp != NULL ? "a ": "no "),
738 (permanentidreq_vp != NULL ? "a ": "no "));
742 /* okay, we have just any_id_req there, so fill in response */
744 /* mark the subtype as being EAP-SIM/Response/Start */
745 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SUBTYPE, 0);
746 newvp->vp_integer = EAPSIM_START;
747 fr_pair_replace(&(rep->vps), newvp);
749 /* insert selected version into response. */
751 uint16_t no_versions;
753 no_versions = htons(selectedversion);
755 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SELECTED_VERSION, 0);
756 fr_pair_value_memcpy(newvp, (uint8_t *) &no_versions, 2);
757 fr_pair_replace(&(rep->vps), newvp);
759 /* record the selected version */
760 memcpy(eap_context->eap.sim.keys.versionselect, &no_versions, 2);
769 * insert a nonce_mt that we make up.
776 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_NONCE_MT, 0);
778 p = talloc_zero_array(newvp, uint8_t, 18); /* 18 = 16 bytes of nonce + padding */
779 memcpy(&p[2], nonce, 16);
780 fr_pair_value_memsteal(newvp, p);
782 fr_pair_replace(&(rep->vps), newvp);
784 /* also keep a copy of the nonce! */
785 memcpy(eap_context->eap.sim.keys.nonce_mt, nonce, 16);
794 * insert the identity here.
796 vp = fr_pair_find_by_num(rep->vps, PW_USER_NAME, 0, TAG_ANY);
799 ERROR("eap-sim: We need to have a User-Name attribute!");
802 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_IDENTITY, 0);
804 idlen = strlen(vp->vp_strvalue);
805 p = talloc_zero_array(newvp, uint8_t, idlen + 2);
806 no_idlen = htons(idlen);
807 memcpy(p, &no_idlen, 2);
808 memcpy(p + 2, vp->vp_strvalue, idlen);
809 fr_pair_value_memsteal(newvp, p);
811 fr_pair_replace(&(rep->vps), newvp);
814 memcpy(eap_context->eap.sim.keys.identity, vp->vp_strvalue, idlen);
815 eap_context->eap.sim.keys.identitylen = idlen;
822 * we got an EAP-Request/Sim/Challenge message in a legal state.
824 * use the RAND challenge to produce the SRES result, and then
825 * use that to generate a new MAC.
827 * for the moment, we ignore the RANDs, then just plug in the SRES
831 static int rc_process_eap_challenge(rc_eap_context_t *eap_context,
832 RADIUS_PACKET *req, RADIUS_PACKET *rep)
835 VALUE_PAIR *mac, *randvp;
836 VALUE_PAIR *sres1,*sres2,*sres3;
837 VALUE_PAIR *Kc1, *Kc2, *Kc3;
840 /* look for the AT_MAC and the challenge data */
841 mac = fr_pair_find_by_num(req->vps, PW_EAP_SIM_MAC, 0, TAG_ANY);
842 randvp= fr_pair_find_by_num(req->vps, PW_EAP_SIM_RAND, 0, TAG_ANY);
843 if (!mac || !randvp) {
844 ERROR("challenge message needs to contain RAND and MAC");
849 * compare RAND with randX, to verify this is the right response
853 VALUE_PAIR *randcfgvp[3];
854 uint8_t const *randcfg[3];
856 randcfg[0] = &randvp->vp_octets[2];
857 randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
858 randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2];
860 randcfgvp[0] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND1, 0, TAG_ANY);
861 randcfgvp[1] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND2, 0, TAG_ANY);
862 randcfgvp[2] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND3, 0, TAG_ANY);
867 ERROR("needs to have rand1, 2 and 3 set.");
871 if (memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
872 memcmp(randcfg[1], randcfgvp[1]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
873 memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) {
876 ERROR("one of rand 1,2,3 didn't match");
877 for (rnum = 0; rnum < 3; rnum++) {
878 ERROR("received rand %d: ", rnum);
880 for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
887 ERROR("%02x", randcfg[rnum][i]);
889 ERROR("configured rand %d: ", rnum);
891 for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
898 ERROR("%02x", randcfgvp[rnum]->vp_octets[i]);
906 * now dig up the sres values from the response packet,
907 * which were put there when we read things in.
909 * Really, they should be calculated from the RAND!
912 sres1 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES1, 0, TAG_ANY);
913 sres2 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES2, 0, TAG_ANY);
914 sres3 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES3, 0, TAG_ANY);
919 ERROR("needs to have sres1, 2 and 3 set.");
922 memcpy(eap_context->eap.sim.keys.sres[0], sres1->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[0]));
923 memcpy(eap_context->eap.sim.keys.sres[1], sres2->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[1]));
924 memcpy(eap_context->eap.sim.keys.sres[2], sres3->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[2]));
926 Kc1 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC1, 0, TAG_ANY);
927 Kc2 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC2, 0, TAG_ANY);
928 Kc3 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC3, 0, TAG_ANY);
933 ERROR("needs to have Kc1, 2 and 3 set.");
936 memcpy(eap_context->eap.sim.keys.Kc[0], Kc1->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[0]));
937 memcpy(eap_context->eap.sim.keys.Kc[1], Kc2->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[1]));
938 memcpy(eap_context->eap.sim.keys.Kc[2], Kc3->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[2]));
940 /* all set, calculate keys */
941 eapsim_calculate_keys(&eap_context->eap.sim.keys);
944 eapsim_dump_mk(&eap_context->eap.sim.keys);
947 /* verify the MAC, now that we have all the keys. */
948 if (eapsim_checkmac(NULL, req->vps, eap_context->eap.sim.keys.K_aut,
949 eap_context->eap.sim.keys.nonce_mt, sizeof(eap_context->eap.sim.keys.nonce_mt),
951 DEBUG("MAC check succeed");
955 DEBUG("calculated MAC (");
956 for (i = 0; i < 20; i++) {
963 DEBUG("%02x", calcmac[i]);
965 DEBUG("did not match");
969 /* form new response clear of any EAP stuff */
972 /* mark the subtype as being EAP-SIM/Response/Start */
973 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SUBTYPE, 0);
974 newvp->vp_integer = EAPSIM_CHALLENGE;
975 fr_pair_replace(&(rep->vps), newvp);
980 * fill the SIM_MAC with a field that will in fact get appended
981 * to the packet before the MAC is calculated
983 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_MAC, 0);
985 p = talloc_zero_array(newvp, uint8_t, EAPSIM_SRES_SIZE*3);
986 memcpy(p+EAPSIM_SRES_SIZE * 0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
987 memcpy(p+EAPSIM_SRES_SIZE * 1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
988 memcpy(p+EAPSIM_SRES_SIZE * 2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
989 fr_pair_value_memsteal(newvp, p);
991 fr_pair_replace(&(rep->vps), newvp);
994 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_KEY, 0);
995 fr_pair_value_memcpy(newvp, eap_context->eap.sim.keys.K_aut, EAPSIM_AUTH_SIZE);
997 fr_pair_replace(&(rep->vps), newvp);
1003 * this code runs the EAP-SIM client state machine.
1004 * the *request* is from the server.
1005 * the *reponse* is to the server.
1008 static int rc_respond_eap_sim(rc_eap_context_t *eap_context,
1009 RADIUS_PACKET *req, RADIUS_PACKET *resp)
1011 enum eapsim_clientstates state, newstate;
1012 enum eapsim_subtype subtype;
1013 VALUE_PAIR *vp, *statevp, *radstate, *eapid;
1014 char statenamebuf[32], subtypenamebuf[32];
1016 if ((radstate = fr_pair_list_copy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
1021 if ((eapid = fr_pair_list_copy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
1026 /* first, dig up the state from the request packet, setting
1027 * ourselves to be in EAP-SIM-Start state if there is none.
1030 if ((statevp = fr_pair_find_by_num(resp->vps, PW_EAP_SIM_STATE, 0, TAG_ANY)) == NULL)
1032 /* must be initial request */
1033 statevp = fr_pair_afrom_num(resp, PW_EAP_SIM_STATE, 0);
1034 statevp->vp_integer = EAPSIM_CLIENT_INIT;
1035 fr_pair_replace(&(resp->vps), statevp);
1037 state = statevp->vp_integer;
1040 * map the attributes, and authenticate them.
1042 rc_unmap_eapsim_types(req);
1044 if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL)
1048 subtype = vp->vp_integer;
1051 * look for the appropriate state, and process incoming message
1054 case EAPSIM_CLIENT_INIT:
1057 newstate = rc_process_eap_start(eap_context, req, resp);
1060 case EAPSIM_CHALLENGE:
1061 case EAPSIM_NOTIFICATION:
1064 ERROR("sim in state %s message %s is illegal. Reply dropped.",
1065 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
1066 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
1067 /* invalid state, drop message */
1072 case EAPSIM_CLIENT_START:
1075 /* NOT SURE ABOUT THIS ONE, retransmit, I guess */
1076 newstate = rc_process_eap_start(eap_context, req, resp);
1079 case EAPSIM_CHALLENGE:
1080 newstate = rc_process_eap_challenge(eap_context, req, resp);
1084 ERROR("sim in state %s message %s is illegal. Reply dropped.",
1085 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
1086 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
1087 /* invalid state, drop message */
1094 ERROR("sim in illegal state %s",
1095 sim_state2name(state, statenamebuf, sizeof(statenamebuf)));
1099 /* copy the eap state object in */
1100 fr_pair_replace(&(resp->vps), eapid);
1102 /* update stete info, and send new packet */
1103 rc_map_eapsim_types(resp);
1105 /* copy the radius state object in */
1106 fr_pair_replace(&(resp->vps), radstate);
1108 statevp->vp_integer = newstate;
1112 static int rc_respond_eap_md5(rc_eap_context_t *eap_context,
1113 RADIUS_PACKET *req, RADIUS_PACKET *rep)
1115 VALUE_PAIR *vp, *id, *state;
1118 uint8_t const *value;
1120 uint8_t response[16];
1124 if ((state = fr_pair_list_copy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
1126 ERROR("no state attribute found");
1130 if ((id = fr_pair_list_copy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
1132 ERROR("no EAP-ID attribute found");
1135 identifier = id->vp_integer;
1137 if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL)
1139 ERROR("no EAP-MD5 attribute found");
1143 /* got the details of the MD5 challenge */
1144 valuesize = vp->vp_octets[0];
1145 value = &vp->vp_octets[1];
1147 /* sanitize items */
1148 if (valuesize > vp->vp_length)
1150 ERROR("md5 valuesize if too big (%u > %u)",
1151 (unsigned int) valuesize, (unsigned int) vp->vp_length);
1155 /* now do the CHAP operation ourself, rather than build the
1156 * buffer. We could also call rad_chap_encode, but it wants
1157 * a CHAP-Challenge, which we don't want to bother with.
1159 fr_md5_init(&context);
1160 fr_md5_update(&context, &identifier, 1);
1161 fr_md5_update(&context, (uint8_t *) eap_context->password, strlen(eap_context->password));
1162 fr_md5_update(&context, value, valuesize);
1163 fr_md5_final(response, &context);
1167 uint8_t lg_response;
1169 vp = fr_pair_afrom_num(rep, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0);
1172 p = talloc_zero_array(vp, uint8_t, 17);
1174 memcpy(p, &lg_response, 1);
1175 memcpy(p + 1, response, 16);
1176 fr_pair_value_memsteal(vp, p);
1178 fr_pair_replace(&(rep->vps), vp);
1180 fr_pair_replace(&(rep->vps), id);
1182 /* copy the state object in */
1183 fr_pair_replace(&(rep->vps), state);
1189 /** Allocate a new socket, and add it to the packet list.
1191 static void rc_add_socket(fr_ipaddr_t *src_ipaddr, uint16_t src_port, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
1195 /* Trace what we're doing. */
1196 char src_addr[15+1] = "";
1197 char dst_addr[15+1] = "";
1198 inet_ntop(AF_INET, &(src_ipaddr->ipaddr.ip4addr.s_addr), src_addr, sizeof(src_addr));
1199 inet_ntop(AF_INET, &(dst_ipaddr->ipaddr.ip4addr.s_addr), dst_addr, sizeof(dst_addr));
1201 INFO("Adding new socket: src: %s:%d, dst: %s:%d", src_addr, src_port, dst_addr, dst_port);
1203 mysockfd = fr_socket(src_ipaddr, src_port);
1205 ERROR("Failed to create new socket: %s", fr_strerror());
1209 if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, dst_ipaddr, dst_port, NULL)) {
1210 ERROR("Failed to add new socket: %s", fr_strerror());
1215 DEBUG("Added new socket: %d (num sockets: %d)", mysockfd, num_sockets);
1218 /** Send one packet for a transaction.
1220 static int rc_send_one_packet(rc_transaction_t *trans, RADIUS_PACKET **packet_p)
1222 if (!trans || !packet_p || !*packet_p) return -1;
1226 RADIUS_PACKET *packet = *packet_p;
1228 if (packet->id == -1) {
1229 /* Haven't sent the packet yet. Initialize it. */
1233 rc_build_eap_context(trans); /* In case of EAP, build EAP-Message and initialize EAP context. */
1235 assert(trans->reply == NULL);
1238 packet->src_ipaddr.af = server_ipaddr.af;
1239 int nb_sock_add = 0;
1241 /* Allocate a RADIUS packet ID from a suitable socket of the packet list. */
1242 rcode = fr_packet_list_id_alloc(pl, ipproto, packet_p, NULL);
1244 if (rcode) { /* Got an ID. */
1247 if (nb_sock_add >= 1) {
1248 ERROR("Added %d new socket(s), but still could not get an ID (currently: %d outgoing requests).",
1249 nb_sock_add, fr_packet_list_num_outgoing(pl));
1253 /* Could not find a free packet ID. Allocate a new socket, then try again. */
1254 rc_add_socket(&packet->src_ipaddr, packet->src_port, &packet->dst_ipaddr, packet->dst_port);
1259 assert(packet->id != -1);
1260 assert(packet->data == NULL);
1262 for (i = 0; i < 4; i++) {
1263 ((uint32_t *) packet->vector)[i] = fr_rand();
1270 DEBUG("Transaction: %u, sending packet: %u (id: %u)...", trans->id, trans->num_packet, packet->id);
1272 gettimeofday(&packet->timestamp, NULL); /* set outgoing packet timestamp. */
1274 if (rad_send(packet, NULL, secret) < 0) {
1275 ERROR("Failed to send packet (sockfd: %d, id: %d): %s",
1276 packet->sockfd, packet->id, fr_strerror());
1279 trans->num_packet ++;
1282 if (fr_debug_lvl > 0) fr_packet_header_print(fr_log_fp, packet, false);
1283 if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, packet->vps);
1288 /** Send current packet of a transaction. Arm timeout event.
1290 static int rc_send_transaction_packet(rc_transaction_t *trans, RADIUS_PACKET **packet_p)
1291 // note: we need a 'RADIUS_PACKET **' for fr_packet_list_id_alloc.
1293 if (!trans || !packet_p || !*packet_p) return -1;
1295 int ret = rc_send_one_packet(trans, packet_p);
1297 /* Send successful: arm the timeout callback. */
1298 rc_evprep_packet_timeout(trans);
1303 /** Deallocate RADIUS packet ID.
1305 static void rc_deallocate_id(rc_transaction_t *trans)
1307 if (!trans || !trans->packet ||
1308 (trans->packet->id < 0)) {
1312 RADIUS_PACKET *packet = trans->packet;
1314 DEBUG2("Deallocating (sockfd: %d, id: %d)", packet->sockfd, packet->id);
1317 * One more unused RADIUS ID.
1319 fr_packet_list_id_free(pl, packet, true);
1320 /* note: "true" means automatically yank, so we must *not* yank ourselves before calling (otherwise, it does nothing)
1321 * so, *don't*: fr_packet_list_yank(pl, request->packet); */
1323 /* free more stuff to ensure next allocate won't be stuck on a "full" socket. */
1325 packet->sockfd = -1;
1326 packet->src_ipaddr.af = AF_UNSPEC;
1327 packet->src_port = 0;
1330 * If we've already sent a packet, free up the old one,
1331 * and ensure that the next packet has a unique
1332 * authentication vector.
1335 talloc_free(packet->data);
1336 packet->data = NULL;
1339 if (trans->reply) rad_free(&trans->reply);
1342 /** Receive one packet, maybe.
1344 static int rc_recv_one_packet(struct timeval *tv_wait_time)
1348 rc_transaction_t *trans;
1349 RADIUS_PACKET *reply, **packet_p;
1350 volatile int max_fd;
1351 bool ongoing_trans = false;
1354 /* Wait for reply, timing out as necessary */
1357 max_fd = fr_packet_list_fd_set(pl, &set);
1359 /* no sockets to listen on! */
1363 if (NULL == tv_wait_time) {
1366 tv.tv_sec = tv_wait_time->tv_sec;
1367 tv.tv_usec = tv_wait_time->tv_usec;
1370 if (select(max_fd, &set, NULL, NULL, &tv) <= 0) {
1371 /* No packet was received. */
1376 * Receive the reply.
1378 reply = fr_packet_list_recv(pl, &set);
1380 ERROR("Received bad packet: %s", fr_strerror());
1381 return -1; /* bad packet */
1385 * Look for the packet which matches the reply.
1387 reply->src_ipaddr = server_ipaddr;
1388 reply->src_port = server_port;
1391 * Note: this only works if all packets have the same destination (IP, port).
1392 * We should handle a list of destinations. But we don't. radclient doesn't do it either).
1395 packet_p = fr_packet_list_find_byreply(pl, reply);
1398 /* got reply to packet we didn't send.
1399 * (or maybe we sent it, got no response, freed the ID. Then server responds to first request.)
1401 DEBUG("No outstanding request was found for reply from %s, port %d (sockfd: %d, id: %d)",
1402 inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.ipaddr, buffer, sizeof(buffer)),
1403 reply->src_port, reply->sockfd, reply->id);
1408 trans = fr_packet2myptr(rc_transaction_t, packet, packet_p);
1410 if (trans->event) fr_event_delete(ev_list, &trans->event);
1413 * Fails the signature validation: not a valid reply.
1415 if (rad_verify(reply, trans->packet, secret) < 0) {
1416 /* shared secret is incorrect.
1417 * (or maybe this is a response to another packet we sent, for which we got no response,
1418 * freed the ID, then reused it. Then server responds to first packet.)
1420 DEBUG("Conflicting response authenticator for reply from %s (sockfd: %d, id: %d)",
1421 inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.ipaddr, buffer, sizeof(buffer)),
1422 reply->sockfd, reply->id);
1427 /* Set reply destination = packet source. */
1428 reply->dst_ipaddr = trans->packet->src_ipaddr;
1429 reply->dst_port = trans->packet->src_port;
1431 trans->reply = reply;
1434 if (rad_decode(trans->reply, trans->packet, secret) != 0) {
1435 /* This can fail if packet contains too many attributes. */
1436 DEBUG("Failed decoding reply");
1440 gettimeofday(&trans->reply->timestamp, NULL); /* set received packet timestamp. */
1442 if (trans->eap_context) {
1443 /* Call unmap before packet print (so we can see the decoded EAP stuff). */
1444 rc_unmap_eap_methods(trans->reply);
1447 DEBUG("Transaction: %u, received packet (id: %u).", trans->id, trans->reply->id);
1449 if (fr_debug_lvl > 0) fr_packet_header_print(fr_log_fp, trans->reply, true);
1450 if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, trans->reply->vps);
1452 if (!trans->eap_context) {
1456 /* now look for the code type. */
1457 VALUE_PAIR *vp, *vpnext;
1458 for (vp = trans->reply->vps; vp != NULL; vp = vpnext) {
1461 switch (vp->da->attr) {
1465 case PW_EAP_TYPE_BASE + PW_EAP_MD5:
1466 if (rc_respond_eap_md5(trans->eap_context, trans->reply, trans->packet) && trans->eap_context->eap.md5.tried < 3)
1468 /* answer the challenge from server. */
1469 trans->eap_context->eap.md5.tried ++;
1470 rc_deallocate_id(trans);
1471 rc_send_transaction_packet(trans, &trans->packet);
1472 ongoing_trans = true; // don't free the transaction yet.
1476 case PW_EAP_TYPE_BASE + PW_EAP_SIM:
1477 if (rc_respond_eap_sim(trans->eap_context, trans->reply, trans->packet)) {
1478 /* answer the challenge from server. */
1479 rc_deallocate_id(trans);
1480 rc_send_transaction_packet(trans, &trans->packet);
1481 ongoing_trans = true; // don't free the transaction yet.
1490 /* EAP transaction ends here (no more requests from EAP server). */
1493 * success: if we have EAP-Code = Success, and reply is an Access-Accept.
1495 if (trans->reply->code != PW_CODE_ACCESS_ACCEPT) {
1496 DEBUG("EAP transaction finished, but reply is not an Access-Accept");
1499 vp = fr_pair_find_by_num(trans->reply->vps, PW_EAP_CODE, 0, TAG_ANY);
1500 if ( (!vp) || (vp->vp_integer != 3) ) {
1501 DEBUG("EAP transaction finished, but reply does not contain EAP-Code = Success");
1508 /* Basic statistics (salvaged from old code). TODO: something better. */
1510 if (trans->reply->code == PW_CODE_ACCESS_ACCEPT) {
1512 } else if (trans->reply->code == PW_CODE_ACCESS_REJECT) {
1517 rad_free(&trans->reply);
1518 rad_free(&reply); /* may be NULL */
1520 if (!ongoing_trans) {
1521 rc_deallocate_id(trans);
1522 rc_finish_transaction(trans);
1528 /** Event callback: packet timeout.
1530 static void rc_evcb_packet_timeout(void *ctx)
1532 rc_transaction_t *trans = ctx;
1533 if (!trans || !trans->packet) return;
1535 DEBUG("Timeout for transaction: %d, tries (so far): %d (max: %d)", trans->id, trans->tries, retries);
1537 if (trans->event) fr_event_delete(ev_list, &trans->event);
1539 if (trans->tries < retries) {
1541 rc_send_transaction_packet(trans, &trans->packet);
1543 DEBUG("No response for transaction: %d, giving up", trans->id);
1544 rc_finish_transaction(trans);
1548 /** Prepare event: packet timeout.
1550 static void rc_evprep_packet_timeout(rc_transaction_t *trans)
1552 struct timeval tv_event;
1553 gettimeofday(&tv_event, NULL);
1554 timeradd(&tv_event, &tv_timeout, &tv_event);
1556 if (!fr_event_insert(ev_list, rc_evcb_packet_timeout, (void *)trans, &tv_event, &trans->event)) {
1557 ERROR("Failed to insert event");
1562 /** Trigger all armed events for which time is reached.
1564 static int rc_loop_events(void)
1566 struct timeval when;
1567 uint32_t nb_processed = 0;
1569 if (!fr_event_list_num_elements(ev_list)) return 0;
1572 gettimeofday(&when, NULL);
1573 if (!fr_event_run(ev_list, &when)) {
1579 return nb_processed;
1583 * Handle incoming packets, until nothing more is received.
1585 static int dhb_loop_recv(void)
1587 uint32_t nb_received = 0;
1588 while (rc_recv_one_packet(NULL) > 0) {
1594 /** Loop starting new transactions, until a limit is reached
1595 * (max parallelism, or no more input available.)
1597 static int rc_loop_start_transactions(void)
1602 if (num_ongoing >= parallel) break;
1604 /* Try to initialize a new transaction. */
1605 rc_transaction_t *trans = rc_init_transaction(autofree);
1609 rc_send_transaction_packet(trans, &trans->packet);
1614 /** Main loop: Handle events. Receive and process responses. Start new transactions.
1617 static void rc_main_loop(void)
1620 /* Handle events. */
1623 /* Receive and process response until no more are received (don't wait). */
1626 /* Start new transactions and send the associated packet. */
1627 rc_loop_start_transactions();
1629 /* Check if we're done. */
1630 if ( (rc_vps_list_in.size == 0)
1631 && (fr_packet_list_num_outgoing(pl) == 0) ) {
1635 INFO("Main loop: done.");
1639 void set_radius_dir(TALLOC_CTX *ctx, char const *path)
1644 memcpy(&p, &radius_dir, sizeof(p));
1648 if (path) radius_dir = talloc_strdup(ctx, path);
1652 /** Set a port from the request type if we don't already have one.
1654 static void rc_get_port(PW_CODE type, uint16_t *port)
1658 case PW_CODE_ACCESS_REQUEST:
1659 case PW_CODE_ACCESS_CHALLENGE:
1660 case PW_CODE_STATUS_SERVER:
1661 if (*port == 0) *port = getport("radius");
1662 if (*port == 0) *port = PW_AUTH_UDP_PORT;
1665 case PW_CODE_ACCOUNTING_REQUEST:
1666 if (*port == 0) *port = getport("radacct");
1667 if (*port == 0) *port = PW_ACCT_UDP_PORT;
1670 case PW_CODE_DISCONNECT_REQUEST:
1671 if (*port == 0) *port = PW_POD_UDP_PORT;
1674 case PW_CODE_COA_REQUEST:
1675 if (*port == 0) *port = PW_COA_UDP_PORT;
1678 case PW_CODE_UNDEFINED:
1679 if (*port == 0) *port = 0;
1684 /** Resolve a port to a request type.
1686 static PW_CODE rc_get_code(uint16_t port)
1689 * getport returns 0 if the service doesn't exist
1690 * so we need to return early, to avoid incorrect
1693 if (port == 0) return PW_CODE_UNDEFINED;
1695 if ((port == getport("radius")) || (port == PW_AUTH_UDP_PORT) || (port == PW_AUTH_UDP_PORT_ALT)) {
1696 return PW_CODE_ACCESS_REQUEST;
1698 if ((port == getport("radacct")) || (port == PW_ACCT_UDP_PORT) || (port == PW_ACCT_UDP_PORT_ALT)) {
1699 return PW_CODE_ACCOUNTING_REQUEST;
1701 if (port == PW_COA_UDP_PORT) return PW_CODE_COA_REQUEST;
1702 if (port == PW_POD_UDP_PORT) return PW_CODE_DISCONNECT_REQUEST;
1704 return PW_CODE_UNDEFINED;
1707 /** Resolve server hostname.
1709 static void rc_resolve_hostname(char *server_arg)
1711 if (force_af == AF_UNSPEC) force_af = AF_INET;
1712 server_ipaddr.af = force_af;
1713 if (strcmp(server_arg, "-") != 0) {
1715 char const *hostname = server_arg;
1716 char const *portname = server_arg;
1719 if (*server_arg == '[') { /* IPv6 URL encoded */
1720 p = strchr(server_arg, ']');
1721 if ((size_t) (p - server_arg) >= sizeof(buffer)) {
1725 memcpy(buffer, server_arg + 1, p - server_arg - 1);
1726 buffer[p - server_arg - 1] = '\0';
1732 p = strchr(portname, ':');
1733 if (p && (strchr(p + 1, ':') == NULL)) {
1740 if (ip_hton(&server_ipaddr, force_af, hostname, false) < 0) {
1741 ERROR("%s: Failed to find IP address for host %s: %s", progname, hostname, strerror(errno));
1744 server_addr_init = true;
1746 /* Strip port from hostname if needed. */
1747 if (portname) server_port = atoi(portname);
1750 * Work backwards from the port to determine the packet type
1752 if (packet_code == PW_CODE_UNDEFINED) packet_code = rc_get_code(server_port);
1754 rc_get_port(packet_code, &server_port);
1757 int main(int argc, char **argv)
1761 char *filename = NULL;
1764 static fr_log_t radclient_log = {
1766 .fd = STDOUT_FILENO,
1767 .dst = L_DST_STDOUT,
1772 radlog_init(&radclient_log, false);
1775 * We probably don't want to free the talloc autofree context
1776 * directly, so we'll allocate a new context beneath it, and
1777 * free that before any leak reports.
1779 autofree = talloc_init("main");
1784 set_radius_dir(autofree, RADIUS_DIR);
1786 while ((c = getopt(argc, argv, "46c:d:D:f:hp:qst:r:S:xXv")) != EOF)
1793 force_af = AF_INET6;
1796 set_radius_dir(autofree, optarg);
1799 main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
1805 parallel = atoi(optarg);
1806 if (parallel == 0) parallel = 1;
1807 if (parallel > 65536) parallel = 65536;
1819 sha1_data_problems = 1; /* for debugging only */
1824 if (!isdigit((int) *optarg))
1826 retries = atoi(optarg);
1832 if (!isdigit((int) *optarg))
1834 timeout = atof(optarg);
1837 printf("$Id$ built on "__DATE__ "at "__TIME__ "");
1841 fp = fopen(optarg, "r");
1843 ERROR("Error opening %s: %s",
1844 optarg, fr_syserror(errno));
1847 if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
1848 ERROR("Error reading %s: %s",
1849 optarg, fr_syserror(errno));
1854 /* truncate newline */
1855 p = filesecret + strlen(filesecret) - 1;
1856 while ((p >= filesecret) &&
1862 if (strlen(filesecret) < 2) {
1863 ERROR("Secret in %s is too short", optarg);
1866 secret = filesecret;
1873 argc -= (optind - 1);
1874 argv += (optind - 1);
1877 ((!secret) && (argc < 4))) {
1881 /* Prepare the timeout. */
1882 rc_float_to_timeval(&tv_timeout, timeout);
1884 if (!main_config.dictionary_dir) {
1885 main_config.dictionary_dir = DICTDIR;
1889 * Read the distribution dictionaries first, then
1890 * the ones in raddb.
1892 DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
1893 if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
1894 ERROR("Errors reading dictionary: %s", fr_strerror());
1899 * It's OK if this one doesn't exist.
1901 int rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
1903 ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY, fr_strerror());
1908 * We print this after reading it. That way if
1909 * it doesn't exist, it's OK, and we don't print
1913 DEBUG2("Including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
1917 * Get the request type
1919 if (!isdigit((int) argv[2][0])) {
1920 packet_code = fr_str2int(rc_request_types, argv[2], -2);
1921 if (packet_code == -2) {
1922 ERROR("Unrecognised request type \"%s\"", argv[2]);
1926 packet_code = atoi(argv[2]);
1932 rc_resolve_hostname(argv[1]);
1937 if (argv[3]) secret = argv[3];
1940 * Read input data vp(s) from the file (or stdin).
1942 INFO("Loading input data...");
1943 if (!rc_load_input(autofree, filename, &rc_vps_list_in, 0)
1944 || rc_vps_list_in.size == 0) {
1945 ERROR("No valid input. Nothing to send.");
1948 INFO("Loaded: %d input element(s).", rc_vps_list_in.size);
1950 /* Initialize the packets list. */
1951 MEM(pl = fr_packet_list_create(1));
1953 /* Initialize the events list. */
1954 ev_list = fr_event_list_create(autofree, NULL);
1956 ERROR("Failed to create event list");
1966 INFO("\n\t Total approved auths: %d", totalapp);
1967 INFO("\t Total denied auths: %d", totaldeny);
1970 talloc_free(autofree);
1975 /** Given a radius request with some attributes in the EAP range, build
1976 * them all into a single EAP-Message body.
1978 * If there are multiple eligibles EAP-Type, the first one is picked.
1979 * Function returns 0 if no EAP is involved, or the EAP-Type otherwise.
1981 static int rc_map_eap_methods(RADIUS_PACKET *req)
1983 VALUE_PAIR *vp, *vpnext;
1987 eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t);
1989 vp = fr_pair_find_by_num(req->vps, PW_EAP_ID, 0, TAG_ANY);
1991 id = ((int)getpid() & 0xff);
1993 id = vp->vp_integer;
1996 vp = fr_pair_find_by_num(req->vps, PW_EAP_CODE, 0, TAG_ANY);
1998 eapcode = PW_EAP_REQUEST;
2000 eapcode = vp->vp_integer;
2003 for (vp = req->vps; vp != NULL; vp = vpnext) {
2004 /* save it in case it changes! */
2007 if (vp->da->attr >= PW_EAP_TYPE_BASE &&
2008 vp->da->attr < PW_EAP_TYPE_BASE+256) {
2017 eap_method = vp->da->attr - PW_EAP_TYPE_BASE;
2019 switch (eap_method) {
2020 case PW_EAP_IDENTITY:
2021 case PW_EAP_NOTIFICATION:
2032 * no known special handling, it is just encoded as an
2033 * EAP-message with the given type.
2036 /* nuke any existing EAP-Messages */
2037 fr_pair_delete_by_num(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
2039 pt_ep->code = eapcode;
2041 pt_ep->type.num = eap_method;
2042 pt_ep->type.length = vp->vp_length;
2044 pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->vp_length);
2045 talloc_set_type(pt_ep->type.data, uint8_t);
2047 eap_basic_compose(req, pt_ep);
2054 * given a radius request with an EAP-Message body, decode it specific
2057 static void rc_unmap_eap_methods(RADIUS_PACKET *rep)
2060 eap_packet_raw_t *e;
2066 /* find eap message */
2067 e = eap_vp2packet(NULL, rep->vps);
2069 ERROR("%s", fr_strerror());
2072 /* create EAP-ID and EAP-CODE attributes to start */
2073 eap1 = fr_pair_afrom_num(rep, PW_EAP_ID, 0);
2074 eap1->vp_integer = e->id;
2075 fr_pair_add(&(rep->vps), eap1);
2077 eap1 = fr_pair_afrom_num(rep, PW_EAP_CODE, 0);
2078 eap1->vp_integer = e->code;
2079 fr_pair_add(&(rep->vps), eap1);
2083 case PW_EAP_SUCCESS:
2084 case PW_EAP_FAILURE:
2088 case PW_EAP_REQUEST:
2089 case PW_EAP_RESPONSE:
2090 /* there is a type field, which we use to create
2091 * a new attribute */
2093 /* the length was decode already into the attribute
2094 * length, and was checked already. Network byte
2095 * order, just pull it out using math.
2097 len = e->length[0]*256 + e->length[1];
2099 /* verify the length is big enough to hold type */
2108 type += PW_EAP_TYPE_BASE;
2111 if (len > MAX_STRING_LEN) {
2112 len = MAX_STRING_LEN;
2115 eap1 = fr_pair_afrom_num(rep, type, 0);
2116 fr_pair_value_memcpy(eap1, e->data + 1, len);
2118 fr_pair_add(&(rep->vps), eap1);
2126 static int rc_map_eapsim_types(RADIUS_PACKET *r)
2130 eap_packet_t *pt_ep = talloc_zero(r, eap_packet_t);
2132 ret = map_eapsim_basictypes(r, pt_ep);
2138 eap_basic_compose(r, pt_ep);
2143 static int rc_unmap_eapsim_types(RADIUS_PACKET *r)
2149 esvp = fr_pair_find_by_num(r->vps, PW_EAP_TYPE_BASE+PW_EAP_SIM, 0, TAG_ANY);
2151 ERROR("eap: EAP-Sim attribute not found");
2155 eap_data = talloc_memdup(esvp, esvp->vp_octets, esvp->vp_length);
2156 talloc_set_type(eap_data, uint8_t);
2158 rcode_unmap = unmap_eapsim_basictypes(r, eap_data, esvp->vp_length);
2160 talloc_free(eap_data);