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_USER_PASSWORD:
472 case PW_CHAP_PASSWORD:
473 case PW_MS_CHAP_PASSWORD:
474 strlcpy(trans->password, vp->vp_strvalue, sizeof(trans->password));
477 case PW_RADCLIENT_TEST_NAME:
478 trans->name = vp->vp_strvalue;
481 } /* loop over the VP's we read in */
483 if (packet->dst_port == 0) packet->dst_port = server_port;
485 if (packet->dst_ipaddr.af == AF_UNSPEC) {
486 if (!server_addr_init) {
487 DEBUG("No server was given, and input entry %u did not contain Packet-Dst-IP-Address, ignored.", trans->input_vps->num);
490 packet->dst_ipaddr = server_ipaddr;
493 /* Use the default set on the command line. */
494 if (packet->code == PW_CODE_UNDEFINED) {
495 if (packet_code == PW_CODE_UNDEFINED) {
496 DEBUG("No packet type was given, and input entry %u did not contain Packet-Type, ignored.", trans->input_vps->num);
499 packet->code = packet_code;
502 /* Automatically set the dst port (if one wasn't already set). */
503 if (packet->dst_port == 0) {
504 rc_get_port(packet->code, &packet->dst_port);
505 if (packet->dst_port == 0) {
506 DEBUG("Can't determine destination port for input entry %u, ignored.", trans->input_vps->num);
517 /** Map EAP methods and build EAP-Message (if EAP is involved).
518 * Also allocate the EAP context.
520 static void rc_build_eap_context(rc_transaction_t *trans)
522 if (!trans || !trans->packet) return;
524 RADIUS_PACKET *packet = trans->packet;
526 /* Build EAP-Message (if EAP is involved. Otherwise, do nothing). */
527 int eap_type = rc_map_eap_methods(packet);
530 if (!trans->eap_context) {
531 MEM(trans->eap_context = talloc_zero(trans, rc_eap_context_t));
533 trans->eap_context->eap_type = eap_type;
536 * Keep a copy of the the User-Password or CHAP-Password.
537 * Note: this is not useful for EAP-SIM, but we cannot know what kind
538 * of challenge the server will issue.
541 vp = fr_pair_find_by_num(packet->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
542 if (!vp) vp = fr_pair_find_by_num(packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
543 if (!vp) vp = fr_pair_find_by_num(packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
545 strlcpy(trans->eap_context->password, vp->vp_strvalue, sizeof(trans->eap_context->password));
550 /** Grab an element from the input list. Initialize a new transaction context, using this element.
552 static rc_transaction_t *rc_init_transaction(TALLOC_CTX *ctx)
554 if (!rc_vps_list_in.head || rc_vps_list_in.size == 0) {
555 /* Empty list, can't create a new transaction. */
559 rc_input_vps_t *vps_entry = rc_vps_list_in.head;
561 rc_yank_vps_entry(vps_entry); /* This cannot fail (we checked the list beforehand.) */
563 /* We grabbed an vps entry, now we can initialize a new transaction. */
564 rc_transaction_t *trans;
565 MEM(trans = talloc_zero(ctx, rc_transaction_t));
567 trans->input_vps = vps_entry;
568 trans->id = num_trans ++;
570 talloc_steal(trans, vps_entry); /* It's ours now. */
572 RADIUS_PACKET *packet;
573 MEM(packet = rad_alloc(trans, 1));
574 trans->packet = packet;
576 /* Fill in the packet value pairs. */
577 packet->vps = fr_pair_list_copy(packet, vps_entry->vps_in);
579 /* Initialize the transaction packet. */
580 if (!rc_init_packet(trans)) {
586 /* Update transactions counters. */
593 /** Terminate a transaction.
595 static void rc_finish_transaction(rc_transaction_t *trans)
599 if (trans->event) fr_event_delete(ev_list, &trans->event);
600 rc_deallocate_id(trans);
603 /* Update transactions counters. */
607 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);
611 static uint16_t getport(char const *name)
615 svp = getservbyname(name, "udp");
618 return ntohs(svp->s_port);
622 static void rc_cleanresp(RADIUS_PACKET *resp)
624 VALUE_PAIR *vpnext, *vp, **last;
627 * maybe should just copy things we care about, or keep
628 * a copy of the original input and start from there again?
630 fr_pair_delete_by_num(&resp->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
631 fr_pair_delete_by_num(&resp->vps, PW_EAP_TYPE_BASE+PW_EAP_IDENTITY, 0, TAG_ANY);
634 for (vp = *last; vp != NULL; vp = vpnext)
638 if ((vp->da->attr > PW_EAP_TYPE_BASE &&
639 vp->da->attr <= PW_EAP_TYPE_BASE+256) ||
640 (vp->da->attr > PW_EAP_SIM_BASE &&
641 vp->da->attr <= PW_EAP_SIM_BASE+256))
652 * we got an EAP-Request/Sim/Start message in a legal state.
654 * pick a supported version, put it into the reply, and insert a nonce.
656 static int rc_process_eap_start(rc_eap_context_t *eap_context,
657 RADIUS_PACKET *req, RADIUS_PACKET *rep)
659 VALUE_PAIR *vp, *newvp;
660 VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
661 uint16_t const *versions;
662 uint16_t selectedversion;
663 unsigned int i,versioncount;
665 /* form new response clear of any EAP stuff */
668 if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
669 ERROR("illegal start message has no VERSION_LIST");
673 versions = (uint16_t const *) vp->vp_strvalue;
675 /* verify that the attribute length is big enough for a length field */
676 if (vp->vp_length < 4)
678 ERROR("start message has illegal VERSION_LIST. Too short: %u", (unsigned int) vp->vp_length);
682 versioncount = ntohs(versions[0])/2;
683 /* verify that the attribute length is big enough for the given number
684 * of versions present.
686 if ((unsigned)vp->vp_length <= (versioncount*2 + 2))
688 ERROR("start message is too short. Claimed %d versions does not fit in %u bytes", versioncount, (unsigned int) vp->vp_length);
693 * record the versionlist for the MK calculation.
695 eap_context->eap.sim.keys.versionlistlen = versioncount*2;
696 memcpy(eap_context->eap.sim.keys.versionlist, (unsigned char const *)(versions+1),
697 eap_context->eap.sim.keys.versionlistlen);
699 /* walk the version list, and pick the one we support, which
700 * at present, is 1, EAP_SIM_VERSION.
703 for (i=0; i < versioncount; i++)
705 if (ntohs(versions[i+1]) == EAP_SIM_VERSION)
707 selectedversion=EAP_SIM_VERSION;
711 if (selectedversion == 0)
713 ERROR("eap-sim start message. No compatible version found. We need %d", EAP_SIM_VERSION);
714 for (i=0; i < versioncount; i++)
716 ERROR("\tfound version %d",
717 ntohs(versions[i+1]));
722 * now make sure that we have only FULLAUTH_ID_REQ.
723 * I think that it actually might not matter - we can answer in
724 * anyway we like, but it is illegal to have more than one
727 anyidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
728 fullauthidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
729 permanentidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);
731 if (!fullauthidreq_vp ||
732 anyidreq_vp != NULL ||
733 permanentidreq_vp != NULL) {
734 ERROR("start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.",
735 (anyidreq_vp != NULL ? "a ": "no "),
736 (fullauthidreq_vp != NULL ? "a ": "no "),
737 (permanentidreq_vp != NULL ? "a ": "no "));
741 /* okay, we have just any_id_req there, so fill in response */
743 /* mark the subtype as being EAP-SIM/Response/Start */
744 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SUBTYPE, 0);
745 newvp->vp_integer = EAPSIM_START;
746 fr_pair_replace(&(rep->vps), newvp);
748 /* insert selected version into response. */
750 uint16_t no_versions;
752 no_versions = htons(selectedversion);
754 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SELECTED_VERSION, 0);
755 fr_pair_value_memcpy(newvp, (uint8_t *) &no_versions, 2);
756 fr_pair_replace(&(rep->vps), newvp);
758 /* record the selected version */
759 memcpy(eap_context->eap.sim.keys.versionselect, &no_versions, 2);
768 * insert a nonce_mt that we make up.
775 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_NONCE_MT, 0);
777 p = talloc_zero_array(newvp, uint8_t, 18); /* 18 = 16 bytes of nonce + padding */
778 memcpy(&p[2], nonce, 16);
779 fr_pair_value_memsteal(newvp, p);
781 fr_pair_replace(&(rep->vps), newvp);
783 /* also keep a copy of the nonce! */
784 memcpy(eap_context->eap.sim.keys.nonce_mt, nonce, 16);
793 * insert the identity here.
795 vp = fr_pair_find_by_num(rep->vps, PW_USER_NAME, 0, TAG_ANY);
798 ERROR("eap-sim: We need to have a User-Name attribute!");
801 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_IDENTITY, 0);
803 idlen = strlen(vp->vp_strvalue);
804 p = talloc_zero_array(newvp, uint8_t, idlen + 2);
805 no_idlen = htons(idlen);
806 memcpy(p, &no_idlen, 2);
807 memcpy(p + 2, vp->vp_strvalue, idlen);
808 fr_pair_value_memsteal(newvp, p);
810 fr_pair_replace(&(rep->vps), newvp);
813 memcpy(eap_context->eap.sim.keys.identity, vp->vp_strvalue, idlen);
814 eap_context->eap.sim.keys.identitylen = idlen;
821 * we got an EAP-Request/Sim/Challenge message in a legal state.
823 * use the RAND challenge to produce the SRES result, and then
824 * use that to generate a new MAC.
826 * for the moment, we ignore the RANDs, then just plug in the SRES
830 static int rc_process_eap_challenge(rc_eap_context_t *eap_context,
831 RADIUS_PACKET *req, RADIUS_PACKET *rep)
834 VALUE_PAIR *mac, *randvp;
835 VALUE_PAIR *sres1,*sres2,*sres3;
836 VALUE_PAIR *Kc1, *Kc2, *Kc3;
839 /* look for the AT_MAC and the challenge data */
840 mac = fr_pair_find_by_num(req->vps, PW_EAP_SIM_MAC, 0, TAG_ANY);
841 randvp= fr_pair_find_by_num(req->vps, PW_EAP_SIM_RAND, 0, TAG_ANY);
842 if (!mac || !randvp) {
843 ERROR("challenge message needs to contain RAND and MAC");
848 * compare RAND with randX, to verify this is the right response
852 VALUE_PAIR *randcfgvp[3];
853 uint8_t const *randcfg[3];
855 randcfg[0] = &randvp->vp_octets[2];
856 randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
857 randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2];
859 randcfgvp[0] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND1, 0, TAG_ANY);
860 randcfgvp[1] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND2, 0, TAG_ANY);
861 randcfgvp[2] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND3, 0, TAG_ANY);
866 ERROR("needs to have rand1, 2 and 3 set.");
870 if (memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
871 memcmp(randcfg[1], randcfgvp[1]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
872 memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) {
875 ERROR("one of rand 1,2,3 didn't match");
876 for (rnum = 0; rnum < 3; rnum++) {
877 ERROR("received rand %d: ", rnum);
879 for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
886 ERROR("%02x", randcfg[rnum][i]);
888 ERROR("configured rand %d: ", rnum);
890 for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
897 ERROR("%02x", randcfgvp[rnum]->vp_octets[i]);
905 * now dig up the sres values from the response packet,
906 * which were put there when we read things in.
908 * Really, they should be calculated from the RAND!
911 sres1 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES1, 0, TAG_ANY);
912 sres2 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES2, 0, TAG_ANY);
913 sres3 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES3, 0, TAG_ANY);
918 ERROR("needs to have sres1, 2 and 3 set.");
921 memcpy(eap_context->eap.sim.keys.sres[0], sres1->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[0]));
922 memcpy(eap_context->eap.sim.keys.sres[1], sres2->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[1]));
923 memcpy(eap_context->eap.sim.keys.sres[2], sres3->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[2]));
925 Kc1 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC1, 0, TAG_ANY);
926 Kc2 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC2, 0, TAG_ANY);
927 Kc3 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC3, 0, TAG_ANY);
932 ERROR("needs to have Kc1, 2 and 3 set.");
935 memcpy(eap_context->eap.sim.keys.Kc[0], Kc1->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[0]));
936 memcpy(eap_context->eap.sim.keys.Kc[1], Kc2->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[1]));
937 memcpy(eap_context->eap.sim.keys.Kc[2], Kc3->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[2]));
939 /* all set, calculate keys */
940 eapsim_calculate_keys(&eap_context->eap.sim.keys);
943 eapsim_dump_mk(&eap_context->eap.sim.keys);
946 /* verify the MAC, now that we have all the keys. */
947 if (eapsim_checkmac(NULL, req->vps, eap_context->eap.sim.keys.K_aut,
948 eap_context->eap.sim.keys.nonce_mt, sizeof(eap_context->eap.sim.keys.nonce_mt),
950 DEBUG("MAC check succeed");
954 DEBUG("calculated MAC (");
955 for (i = 0; i < 20; i++) {
962 DEBUG("%02x", calcmac[i]);
964 DEBUG("did not match");
968 /* form new response clear of any EAP stuff */
971 /* mark the subtype as being EAP-SIM/Response/Start */
972 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SUBTYPE, 0);
973 newvp->vp_integer = EAPSIM_CHALLENGE;
974 fr_pair_replace(&(rep->vps), newvp);
979 * fill the SIM_MAC with a field that will in fact get appended
980 * to the packet before the MAC is calculated
982 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_MAC, 0);
984 p = talloc_zero_array(newvp, uint8_t, EAPSIM_SRES_SIZE*3);
985 memcpy(p+EAPSIM_SRES_SIZE * 0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
986 memcpy(p+EAPSIM_SRES_SIZE * 1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
987 memcpy(p+EAPSIM_SRES_SIZE * 2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
988 fr_pair_value_memsteal(newvp, p);
990 fr_pair_replace(&(rep->vps), newvp);
993 newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_KEY, 0);
994 fr_pair_value_memcpy(newvp, eap_context->eap.sim.keys.K_aut, EAPSIM_AUTH_SIZE);
996 fr_pair_replace(&(rep->vps), newvp);
1002 * this code runs the EAP-SIM client state machine.
1003 * the *request* is from the server.
1004 * the *reponse* is to the server.
1007 static int rc_respond_eap_sim(rc_eap_context_t *eap_context,
1008 RADIUS_PACKET *req, RADIUS_PACKET *resp)
1010 enum eapsim_clientstates state, newstate;
1011 enum eapsim_subtype subtype;
1012 VALUE_PAIR *vp, *statevp, *radstate, *eapid;
1013 char statenamebuf[32], subtypenamebuf[32];
1015 if ((radstate = fr_pair_list_copy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
1020 if ((eapid = fr_pair_list_copy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
1025 /* first, dig up the state from the request packet, setting
1026 * ourselves to be in EAP-SIM-Start state if there is none.
1029 if ((statevp = fr_pair_find_by_num(resp->vps, PW_EAP_SIM_STATE, 0, TAG_ANY)) == NULL)
1031 /* must be initial request */
1032 statevp = fr_pair_afrom_num(resp, PW_EAP_SIM_STATE, 0);
1033 statevp->vp_integer = EAPSIM_CLIENT_INIT;
1034 fr_pair_replace(&(resp->vps), statevp);
1036 state = statevp->vp_integer;
1039 * map the attributes, and authenticate them.
1041 rc_unmap_eapsim_types(req);
1043 if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL)
1047 subtype = vp->vp_integer;
1050 * look for the appropriate state, and process incoming message
1053 case EAPSIM_CLIENT_INIT:
1056 newstate = rc_process_eap_start(eap_context, req, resp);
1059 case EAPSIM_CHALLENGE:
1060 case EAPSIM_NOTIFICATION:
1063 ERROR("sim in state %s message %s is illegal. Reply dropped.",
1064 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
1065 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
1066 /* invalid state, drop message */
1071 case EAPSIM_CLIENT_START:
1074 /* NOT SURE ABOUT THIS ONE, retransmit, I guess */
1075 newstate = rc_process_eap_start(eap_context, req, resp);
1078 case EAPSIM_CHALLENGE:
1079 newstate = rc_process_eap_challenge(eap_context, req, resp);
1083 ERROR("sim in state %s message %s is illegal. Reply dropped.",
1084 sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
1085 sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
1086 /* invalid state, drop message */
1093 ERROR("sim in illegal state %s",
1094 sim_state2name(state, statenamebuf, sizeof(statenamebuf)));
1098 /* copy the eap state object in */
1099 fr_pair_replace(&(resp->vps), eapid);
1101 /* update stete info, and send new packet */
1102 rc_map_eapsim_types(resp);
1104 /* copy the radius state object in */
1105 fr_pair_replace(&(resp->vps), radstate);
1107 statevp->vp_integer = newstate;
1111 static int rc_respond_eap_md5(rc_eap_context_t *eap_context,
1112 RADIUS_PACKET *req, RADIUS_PACKET *rep)
1114 VALUE_PAIR *vp, *id, *state;
1117 uint8_t const *value;
1119 uint8_t response[16];
1123 if ((state = fr_pair_list_copy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
1125 ERROR("no state attribute found");
1129 if ((id = fr_pair_list_copy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
1131 ERROR("no EAP-ID attribute found");
1134 identifier = id->vp_integer;
1136 if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL)
1138 ERROR("no EAP-MD5 attribute found");
1142 /* got the details of the MD5 challenge */
1143 valuesize = vp->vp_octets[0];
1144 value = &vp->vp_octets[1];
1146 /* sanitize items */
1147 if (valuesize > vp->vp_length)
1149 ERROR("md5 valuesize if too big (%u > %u)",
1150 (unsigned int) valuesize, (unsigned int) vp->vp_length);
1154 /* now do the CHAP operation ourself, rather than build the
1155 * buffer. We could also call rad_chap_encode, but it wants
1156 * a CHAP-Challenge, which we don't want to bother with.
1158 fr_md5_init(&context);
1159 fr_md5_update(&context, &identifier, 1);
1160 fr_md5_update(&context, (uint8_t *) eap_context->password, strlen(eap_context->password));
1161 fr_md5_update(&context, value, valuesize);
1162 fr_md5_final(response, &context);
1166 uint8_t lg_response;
1168 vp = fr_pair_afrom_num(rep, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0);
1171 p = talloc_zero_array(vp, uint8_t, 17);
1173 memcpy(p, &lg_response, 1);
1174 memcpy(p + 1, response, 16);
1175 fr_pair_value_memsteal(vp, p);
1177 fr_pair_replace(&(rep->vps), vp);
1179 fr_pair_replace(&(rep->vps), id);
1181 /* copy the state object in */
1182 fr_pair_replace(&(rep->vps), state);
1188 /** Allocate a new socket, and add it to the packet list.
1190 static void rc_add_socket(fr_ipaddr_t *src_ipaddr, uint16_t src_port, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
1194 /* Trace what we're doing. */
1195 char src_addr[15+1] = "";
1196 char dst_addr[15+1] = "";
1197 inet_ntop(AF_INET, &(src_ipaddr->ipaddr.ip4addr.s_addr), src_addr, sizeof(src_addr));
1198 inet_ntop(AF_INET, &(dst_ipaddr->ipaddr.ip4addr.s_addr), dst_addr, sizeof(dst_addr));
1200 INFO("Adding new socket: src: %s:%d, dst: %s:%d", src_addr, src_port, dst_addr, dst_port);
1202 mysockfd = fr_socket(src_ipaddr, src_port);
1204 ERROR("Failed to create new socket: %s", fr_strerror());
1208 if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, dst_ipaddr, dst_port, NULL)) {
1209 ERROR("Failed to add new socket: %s", fr_strerror());
1214 DEBUG("Added new socket: %d (num sockets: %d)", mysockfd, num_sockets);
1217 /** Send one packet for a transaction.
1219 static int rc_send_one_packet(rc_transaction_t *trans, RADIUS_PACKET **packet_p)
1221 if (!trans || !packet_p || !*packet_p) return -1;
1225 RADIUS_PACKET *packet = *packet_p;
1227 if (packet->id == -1) {
1228 /* Haven't sent the packet yet. Initialize it. */
1232 rc_build_eap_context(trans); /* In case of EAP, build EAP-Message and initialize EAP context. */
1234 assert(trans->reply == NULL);
1237 packet->src_ipaddr.af = server_ipaddr.af;
1238 int nb_sock_add = 0;
1240 /* Allocate a RADIUS packet ID from a suitable socket of the packet list. */
1241 rcode = fr_packet_list_id_alloc(pl, ipproto, packet_p, NULL);
1243 if (rcode) { /* Got an ID. */
1246 if (nb_sock_add >= 1) {
1247 ERROR("Added %d new socket(s), but still could not get an ID (currently: %d outgoing requests).",
1248 nb_sock_add, fr_packet_list_num_outgoing(pl));
1252 /* Could not find a free packet ID. Allocate a new socket, then try again. */
1253 rc_add_socket(&packet->src_ipaddr, packet->src_port, &packet->dst_ipaddr, packet->dst_port);
1258 assert(packet->id != -1);
1259 assert(packet->data == NULL);
1261 for (i = 0; i < 4; i++) {
1262 ((uint32_t *) packet->vector)[i] = fr_rand();
1269 DEBUG("Transaction: %u, sending packet: %u (id: %u)...", trans->id, trans->num_packet, packet->id);
1271 gettimeofday(&packet->timestamp, NULL); /* set outgoing packet timestamp. */
1273 if (rad_send(packet, NULL, secret) < 0) {
1274 ERROR("Failed to send packet (sockfd: %d, id: %d): %s",
1275 packet->sockfd, packet->id, fr_strerror());
1278 trans->num_packet ++;
1281 if (fr_debug_lvl > 0) fr_packet_header_print(fr_log_fp, packet, false);
1282 if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, packet->vps);
1287 /** Send current packet of a transaction. Arm timeout event.
1289 static int rc_send_transaction_packet(rc_transaction_t *trans, RADIUS_PACKET **packet_p)
1290 // note: we need a 'RADIUS_PACKET **' for fr_packet_list_id_alloc.
1292 if (!trans || !packet_p || !*packet_p) return -1;
1294 int ret = rc_send_one_packet(trans, packet_p);
1296 /* Send successful: arm the timeout callback. */
1297 rc_evprep_packet_timeout(trans);
1302 /** Deallocate RADIUS packet ID.
1304 static void rc_deallocate_id(rc_transaction_t *trans)
1306 if (!trans || !trans->packet ||
1307 (trans->packet->id < 0)) {
1311 RADIUS_PACKET *packet = trans->packet;
1313 DEBUG2("Deallocating (sockfd: %d, id: %d)", packet->sockfd, packet->id);
1316 * One more unused RADIUS ID.
1318 fr_packet_list_id_free(pl, packet, true);
1319 /* note: "true" means automatically yank, so we must *not* yank ourselves before calling (otherwise, it does nothing)
1320 * so, *don't*: fr_packet_list_yank(pl, request->packet); */
1322 /* free more stuff to ensure next allocate won't be stuck on a "full" socket. */
1324 packet->sockfd = -1;
1325 packet->src_ipaddr.af = AF_UNSPEC;
1326 packet->src_port = 0;
1329 * If we've already sent a packet, free up the old one,
1330 * and ensure that the next packet has a unique
1331 * authentication vector.
1334 talloc_free(packet->data);
1335 packet->data = NULL;
1338 if (trans->reply) rad_free(&trans->reply);
1341 /** Receive one packet, maybe.
1343 static int rc_recv_one_packet(struct timeval *tv_wait_time)
1347 rc_transaction_t *trans;
1348 RADIUS_PACKET *reply, **packet_p;
1349 volatile int max_fd;
1350 bool ongoing_trans = false;
1353 /* Wait for reply, timing out as necessary */
1356 max_fd = fr_packet_list_fd_set(pl, &set);
1358 /* no sockets to listen on! */
1362 if (NULL == tv_wait_time) {
1365 tv.tv_sec = tv_wait_time->tv_sec;
1366 tv.tv_usec = tv_wait_time->tv_usec;
1369 if (select(max_fd, &set, NULL, NULL, &tv) <= 0) {
1370 /* No packet was received. */
1375 * Receive the reply.
1377 reply = fr_packet_list_recv(pl, &set);
1379 ERROR("Received bad packet: %s", fr_strerror());
1380 return -1; /* bad packet */
1384 * Look for the packet which matches the reply.
1386 reply->src_ipaddr = server_ipaddr;
1387 reply->src_port = server_port;
1390 * Note: this only works if all packets have the same destination (IP, port).
1391 * We should handle a list of destinations. But we don't. radclient doesn't do it either).
1394 packet_p = fr_packet_list_find_byreply(pl, reply);
1397 /* got reply to packet we didn't send.
1398 * (or maybe we sent it, got no response, freed the ID. Then server responds to first request.)
1400 DEBUG("No outstanding request was found for reply from %s, port %d (sockfd: %d, id: %d)",
1401 inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.ipaddr, buffer, sizeof(buffer)),
1402 reply->src_port, reply->sockfd, reply->id);
1407 trans = fr_packet2myptr(rc_transaction_t, packet, packet_p);
1409 if (trans->event) fr_event_delete(ev_list, &trans->event);
1412 * Fails the signature validation: not a valid reply.
1414 if (rad_verify(reply, trans->packet, secret) < 0) {
1415 /* shared secret is incorrect.
1416 * (or maybe this is a response to another packet we sent, for which we got no response,
1417 * freed the ID, then reused it. Then server responds to first packet.)
1419 DEBUG("Conflicting response authenticator for reply from %s (sockfd: %d, id: %d)",
1420 inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.ipaddr, buffer, sizeof(buffer)),
1421 reply->sockfd, reply->id);
1426 /* Set reply destination = packet source. */
1427 reply->dst_ipaddr = trans->packet->src_ipaddr;
1428 reply->dst_port = trans->packet->src_port;
1430 trans->reply = reply;
1433 if (rad_decode(trans->reply, trans->packet, secret) != 0) {
1434 /* This can fail if packet contains too many attributes. */
1435 DEBUG("Failed decoding reply");
1439 gettimeofday(&trans->reply->timestamp, NULL); /* set received packet timestamp. */
1441 if (trans->eap_context) {
1442 /* Call unmap before packet print (so we can see the decoded EAP stuff). */
1443 rc_unmap_eap_methods(trans->reply);
1446 DEBUG("Transaction: %u, received packet (id: %u).", trans->id, trans->reply->id);
1448 if (fr_debug_lvl > 0) fr_packet_header_print(fr_log_fp, trans->reply, true);
1449 if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, trans->reply->vps);
1451 if (!trans->eap_context) {
1455 /* now look for the code type. */
1456 VALUE_PAIR *vp, *vpnext;
1457 for (vp = trans->reply->vps; vp != NULL; vp = vpnext) {
1460 switch (vp->da->attr) {
1464 case PW_EAP_TYPE_BASE + PW_EAP_MD5:
1465 if (rc_respond_eap_md5(trans->eap_context, trans->reply, trans->packet) && trans->eap_context->eap.md5.tried < 3)
1467 /* answer the challenge from server. */
1468 trans->eap_context->eap.md5.tried ++;
1469 rc_deallocate_id(trans);
1470 rc_send_transaction_packet(trans, &trans->packet);
1471 ongoing_trans = true; // don't free the transaction yet.
1475 case PW_EAP_TYPE_BASE + PW_EAP_SIM:
1476 if (rc_respond_eap_sim(trans->eap_context, trans->reply, trans->packet)) {
1477 /* answer the challenge from server. */
1478 rc_deallocate_id(trans);
1479 rc_send_transaction_packet(trans, &trans->packet);
1480 ongoing_trans = true; // don't free the transaction yet.
1489 /* EAP transaction ends here (no more requests from EAP server). */
1492 * success: if we have EAP-Code = Success, and reply is an Access-Accept.
1494 if (trans->reply->code != PW_CODE_ACCESS_ACCEPT) {
1495 DEBUG("EAP transaction finished, but reply is not an Access-Accept");
1498 vp = fr_pair_find_by_num(trans->reply->vps, PW_EAP_CODE, 0, TAG_ANY);
1499 if ( (!vp) || (vp->vp_integer != 3) ) {
1500 DEBUG("EAP transaction finished, but reply does not contain EAP-Code = Success");
1507 /* Basic statistics (salvaged from old code). TODO: something better. */
1509 if (trans->reply->code == PW_CODE_ACCESS_ACCEPT) {
1511 } else if (trans->reply->code == PW_CODE_ACCESS_REJECT) {
1516 rad_free(&trans->reply);
1517 rad_free(&reply); /* may be NULL */
1519 if (!ongoing_trans) {
1520 rc_deallocate_id(trans);
1521 rc_finish_transaction(trans);
1527 /** Event callback: packet timeout.
1529 static void rc_evcb_packet_timeout(void *ctx)
1531 rc_transaction_t *trans = ctx;
1532 if (!trans || !trans->packet) return;
1534 DEBUG("Timeout for transaction: %d, tries (so far): %d (max: %d)", trans->id, trans->tries, retries);
1536 if (trans->event) fr_event_delete(ev_list, &trans->event);
1538 if (trans->tries < retries) {
1540 rc_send_transaction_packet(trans, &trans->packet);
1542 DEBUG("No response for transaction: %d, giving up", trans->id);
1543 rc_finish_transaction(trans);
1547 /** Prepare event: packet timeout.
1549 static void rc_evprep_packet_timeout(rc_transaction_t *trans)
1551 struct timeval tv_event;
1552 gettimeofday(&tv_event, NULL);
1553 timeradd(&tv_event, &tv_timeout, &tv_event);
1555 if (!fr_event_insert(ev_list, rc_evcb_packet_timeout, (void *)trans, &tv_event, &trans->event)) {
1556 ERROR("Failed to insert event");
1561 /** Trigger all armed events for which time is reached.
1563 static int rc_loop_events(void)
1565 struct timeval when;
1566 uint32_t nb_processed = 0;
1568 if (!fr_event_list_num_elements(ev_list)) return 0;
1571 gettimeofday(&when, NULL);
1572 if (!fr_event_run(ev_list, &when)) {
1578 return nb_processed;
1582 * Handle incoming packets, until nothing more is received.
1584 static int dhb_loop_recv(void)
1586 uint32_t nb_received = 0;
1587 while (rc_recv_one_packet(NULL) > 0) {
1593 /** Loop starting new transactions, until a limit is reached
1594 * (max parallelism, or no more input available.)
1596 static int rc_loop_start_transactions(void)
1601 if (num_ongoing >= parallel) break;
1603 /* Try to initialize a new transaction. */
1604 rc_transaction_t *trans = rc_init_transaction(autofree);
1608 rc_send_transaction_packet(trans, &trans->packet);
1613 /** Main loop: Handle events. Receive and process responses. Start new transactions.
1616 static void rc_main_loop(void)
1619 /* Handle events. */
1622 /* Receive and process response until no more are received (don't wait). */
1625 /* Start new transactions and send the associated packet. */
1626 rc_loop_start_transactions();
1628 /* Check if we're done. */
1629 if ( (rc_vps_list_in.size == 0)
1630 && (fr_packet_list_num_outgoing(pl) == 0) ) {
1634 INFO("Main loop: done.");
1638 void set_radius_dir(TALLOC_CTX *ctx, char const *path)
1643 memcpy(&p, &radius_dir, sizeof(p));
1647 if (path) radius_dir = talloc_strdup(ctx, path);
1651 /** Set a port from the request type if we don't already have one.
1653 static void rc_get_port(PW_CODE type, uint16_t *port)
1657 case PW_CODE_ACCESS_REQUEST:
1658 case PW_CODE_ACCESS_CHALLENGE:
1659 case PW_CODE_STATUS_SERVER:
1660 if (*port == 0) *port = getport("radius");
1661 if (*port == 0) *port = PW_AUTH_UDP_PORT;
1664 case PW_CODE_ACCOUNTING_REQUEST:
1665 if (*port == 0) *port = getport("radacct");
1666 if (*port == 0) *port = PW_ACCT_UDP_PORT;
1669 case PW_CODE_DISCONNECT_REQUEST:
1670 if (*port == 0) *port = PW_POD_UDP_PORT;
1673 case PW_CODE_COA_REQUEST:
1674 if (*port == 0) *port = PW_COA_UDP_PORT;
1677 case PW_CODE_UNDEFINED:
1678 if (*port == 0) *port = 0;
1683 /** Resolve a port to a request type.
1685 static PW_CODE rc_get_code(uint16_t port)
1688 * getport returns 0 if the service doesn't exist
1689 * so we need to return early, to avoid incorrect
1692 if (port == 0) return PW_CODE_UNDEFINED;
1694 if ((port == getport("radius")) || (port == PW_AUTH_UDP_PORT) || (port == PW_AUTH_UDP_PORT_ALT)) {
1695 return PW_CODE_ACCESS_REQUEST;
1697 if ((port == getport("radacct")) || (port == PW_ACCT_UDP_PORT) || (port == PW_ACCT_UDP_PORT_ALT)) {
1698 return PW_CODE_ACCOUNTING_REQUEST;
1700 if (port == PW_COA_UDP_PORT) return PW_CODE_COA_REQUEST;
1701 if (port == PW_POD_UDP_PORT) return PW_CODE_DISCONNECT_REQUEST;
1703 return PW_CODE_UNDEFINED;
1706 /** Resolve server hostname.
1708 static void rc_resolve_hostname(char *server_arg)
1710 if (force_af == AF_UNSPEC) force_af = AF_INET;
1711 server_ipaddr.af = force_af;
1712 if (strcmp(server_arg, "-") != 0) {
1714 char const *hostname = server_arg;
1715 char const *portname = server_arg;
1718 if (*server_arg == '[') { /* IPv6 URL encoded */
1719 p = strchr(server_arg, ']');
1720 if ((size_t) (p - server_arg) >= sizeof(buffer)) {
1724 memcpy(buffer, server_arg + 1, p - server_arg - 1);
1725 buffer[p - server_arg - 1] = '\0';
1731 p = strchr(portname, ':');
1732 if (p && (strchr(p + 1, ':') == NULL)) {
1739 if (ip_hton(&server_ipaddr, force_af, hostname, false) < 0) {
1740 ERROR("%s: Failed to find IP address for host %s: %s", progname, hostname, strerror(errno));
1743 server_addr_init = true;
1745 /* Strip port from hostname if needed. */
1746 if (portname) server_port = atoi(portname);
1749 * Work backwards from the port to determine the packet type
1751 if (packet_code == PW_CODE_UNDEFINED) packet_code = rc_get_code(server_port);
1753 rc_get_port(packet_code, &server_port);
1756 int main(int argc, char **argv)
1760 char *filename = NULL;
1763 static fr_log_t radclient_log = {
1765 .fd = STDOUT_FILENO,
1766 .dst = L_DST_STDOUT,
1771 radlog_init(&radclient_log, false);
1774 * We probably don't want to free the talloc autofree context
1775 * directly, so we'll allocate a new context beneath it, and
1776 * free that before any leak reports.
1778 autofree = talloc_init("main");
1783 set_radius_dir(autofree, RADIUS_DIR);
1785 while ((c = getopt(argc, argv, "46c:d:D:f:hp:qst:r:S:xXv")) != EOF)
1792 force_af = AF_INET6;
1795 set_radius_dir(autofree, optarg);
1798 main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
1804 parallel = atoi(optarg);
1805 if (parallel == 0) parallel = 1;
1806 if (parallel > 65536) parallel = 65536;
1818 sha1_data_problems = 1; /* for debugging only */
1823 if (!isdigit((int) *optarg))
1825 retries = atoi(optarg);
1831 if (!isdigit((int) *optarg))
1833 timeout = atof(optarg);
1836 printf("$Id$ built on "__DATE__ "at "__TIME__ "");
1840 fp = fopen(optarg, "r");
1842 ERROR("Error opening %s: %s",
1843 optarg, fr_syserror(errno));
1846 if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
1847 ERROR("Error reading %s: %s",
1848 optarg, fr_syserror(errno));
1853 /* truncate newline */
1854 p = filesecret + strlen(filesecret) - 1;
1855 while ((p >= filesecret) &&
1861 if (strlen(filesecret) < 2) {
1862 ERROR("Secret in %s is too short", optarg);
1865 secret = filesecret;
1872 argc -= (optind - 1);
1873 argv += (optind - 1);
1876 ((!secret) && (argc < 4))) {
1880 /* Prepare the timeout. */
1881 rc_float_to_timeval(&tv_timeout, timeout);
1883 if (!main_config.dictionary_dir) {
1884 main_config.dictionary_dir = DICTDIR;
1888 * Read the distribution dictionaries first, then
1889 * the ones in raddb.
1891 DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
1892 if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
1893 ERROR("Errors reading dictionary: %s", fr_strerror());
1898 * It's OK if this one doesn't exist.
1900 int rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
1902 ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY, fr_strerror());
1907 * We print this after reading it. That way if
1908 * it doesn't exist, it's OK, and we don't print
1912 DEBUG2("Including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
1916 * Get the request type
1918 if (!isdigit((int) argv[2][0])) {
1919 packet_code = fr_str2int(rc_request_types, argv[2], -2);
1920 if (packet_code == -2) {
1921 ERROR("Unrecognised request type \"%s\"", argv[2]);
1925 packet_code = atoi(argv[2]);
1931 rc_resolve_hostname(argv[1]);
1936 if (argv[3]) secret = argv[3];
1939 * Read input data vp(s) from the file (or stdin).
1941 INFO("Loading input data...");
1942 if (!rc_load_input(autofree, filename, &rc_vps_list_in, 0)
1943 || rc_vps_list_in.size == 0) {
1944 ERROR("No valid input. Nothing to send.");
1947 INFO("Loaded: %d input element(s).", rc_vps_list_in.size);
1949 /* Initialize the packets list. */
1950 MEM(pl = fr_packet_list_create(1));
1952 /* Initialize the events list. */
1953 ev_list = fr_event_list_create(autofree, NULL);
1955 ERROR("Failed to create event list");
1965 INFO("\n\t Total approved auths: %d", totalapp);
1966 INFO("\t Total denied auths: %d", totaldeny);
1969 talloc_free(autofree);
1974 /** Given a radius request with some attributes in the EAP range, build
1975 * them all into a single EAP-Message body.
1977 * If there are multiple eligibles EAP-Type, the first one is picked.
1978 * Function returns 0 if no EAP is involved, or the EAP-Type otherwise.
1980 static int rc_map_eap_methods(RADIUS_PACKET *req)
1982 VALUE_PAIR *vp, *vpnext;
1986 eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t);
1988 vp = fr_pair_find_by_num(req->vps, PW_EAP_ID, 0, TAG_ANY);
1990 id = ((int)getpid() & 0xff);
1992 id = vp->vp_integer;
1995 vp = fr_pair_find_by_num(req->vps, PW_EAP_CODE, 0, TAG_ANY);
1997 eapcode = PW_EAP_REQUEST;
1999 eapcode = vp->vp_integer;
2002 for (vp = req->vps; vp != NULL; vp = vpnext) {
2003 /* save it in case it changes! */
2006 if (vp->da->attr >= PW_EAP_TYPE_BASE &&
2007 vp->da->attr < PW_EAP_TYPE_BASE+256) {
2016 eap_method = vp->da->attr - PW_EAP_TYPE_BASE;
2018 switch (eap_method) {
2019 case PW_EAP_IDENTITY:
2020 case PW_EAP_NOTIFICATION:
2031 * no known special handling, it is just encoded as an
2032 * EAP-message with the given type.
2035 /* nuke any existing EAP-Messages */
2036 fr_pair_delete_by_num(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
2038 pt_ep->code = eapcode;
2040 pt_ep->type.num = eap_method;
2041 pt_ep->type.length = vp->vp_length;
2043 pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->vp_length);
2044 talloc_set_type(pt_ep->type.data, uint8_t);
2046 eap_basic_compose(req, pt_ep);
2053 * given a radius request with an EAP-Message body, decode it specific
2056 static void rc_unmap_eap_methods(RADIUS_PACKET *rep)
2059 eap_packet_raw_t *e;
2065 /* find eap message */
2066 e = eap_vp2packet(NULL, rep->vps);
2068 ERROR("%s", fr_strerror());
2071 /* create EAP-ID and EAP-CODE attributes to start */
2072 eap1 = fr_pair_afrom_num(rep, PW_EAP_ID, 0);
2073 eap1->vp_integer = e->id;
2074 fr_pair_add(&(rep->vps), eap1);
2076 eap1 = fr_pair_afrom_num(rep, PW_EAP_CODE, 0);
2077 eap1->vp_integer = e->code;
2078 fr_pair_add(&(rep->vps), eap1);
2082 case PW_EAP_SUCCESS:
2083 case PW_EAP_FAILURE:
2087 case PW_EAP_REQUEST:
2088 case PW_EAP_RESPONSE:
2089 /* there is a type field, which we use to create
2090 * a new attribute */
2092 /* the length was decode already into the attribute
2093 * length, and was checked already. Network byte
2094 * order, just pull it out using math.
2096 len = e->length[0]*256 + e->length[1];
2098 /* verify the length is big enough to hold type */
2107 type += PW_EAP_TYPE_BASE;
2110 if (len > MAX_STRING_LEN) {
2111 len = MAX_STRING_LEN;
2114 eap1 = fr_pair_afrom_num(rep, type, 0);
2115 fr_pair_value_memcpy(eap1, e->data + 1, len);
2117 fr_pair_add(&(rep->vps), eap1);
2125 static int rc_map_eapsim_types(RADIUS_PACKET *r)
2129 eap_packet_t *pt_ep = talloc_zero(r, eap_packet_t);
2131 ret = map_eapsim_basictypes(r, pt_ep);
2137 eap_basic_compose(r, pt_ep);
2142 static int rc_unmap_eapsim_types(RADIUS_PACKET *r)
2148 esvp = fr_pair_find_by_num(r->vps, PW_EAP_TYPE_BASE+PW_EAP_SIM, 0, TAG_ANY);
2150 ERROR("eap: EAP-Sim attribute not found");
2154 eap_data = talloc_memdup(esvp, esvp->vp_octets, esvp->vp_length);
2155 talloc_set_type(eap_data, uint8_t);
2157 rcode_unmap = unmap_eapsim_basictypes(r, eap_data, esvp->vp_length);
2159 talloc_free(eap_data);