/* * Initialize a radclient data structure and add it to * the global linked list. */ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files) { FILE *packets, *filters = NULL; vp_cursor_t cursor; VALUE_PAIR *vp; rc_request_t *request; bool packets_done = false; uint64_t num = 0; assert(files->packets != NULL); /* * Determine where to read the VP's from. */ if (strcmp(files->packets, "-") != 0) { packets = fopen(files->packets, "r"); if (!packets) { ERROR("Error opening %s: %s", files->packets, strerror(errno)); return 0; } /* * Read in the pairs representing the expected response. */ if (files->filters) { filters = fopen(files->filters, "r"); if (!filters) { ERROR("Error opening %s: %s", files->filters, strerror(errno)); fclose(packets); return 0; } } } else { packets = stdin; } /* * Loop until the file is done. */ do { /* * Allocate it. */ request = talloc_zero(ctx, rc_request_t); if (!request) { ERROR("Out of memory"); goto error; } talloc_set_destructor(request, _rc_request_free); request->packet = rad_alloc(request, 1); if (!request->packet) { ERROR("Out of memory"); goto error; } #ifdef WITH_TCP request->packet->src_ipaddr = client_ipaddr; request->packet->src_port = client_port; request->packet->dst_ipaddr = server_ipaddr; request->packet->dst_port = server_port; request->packet->proto = ipproto; #endif request->files = files; request->packet->id = -1; /* allocate when sending */ request->num = num++; /* * Read the request VP's. */ if (readvp2(&request->packet->vps, request->packet, packets, &packets_done) < 0) { ERROR("Error parsing \"%s\"", files->packets); goto error; } fr_cursor_init(&cursor, &request->filter); vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY); if (vp) { fr_cursor_remove(&cursor); request->packet_code = vp->vp_integer; talloc_free(vp); } else { request->packet_code = packet_code; /* Use the default set on the command line */ } /* * Read in filter VP's. */ if (filters) { bool filters_done; if (readvp2(&request->filter, request, filters, &filters_done) < 0) { ERROR("Error parsing \"%s\"", files->filters); goto error; } if (!request->filter) { goto error; } if (filters_done && !packets_done) { ERROR("Differing number of packets/filters in %s:%s " "(too many requests))", files->packets, files->filters); goto error; } if (!filters_done && packets_done) { ERROR("Differing number of packets/filters in %s:%s " "(too many filters))", files->packets, files->filters); goto error; } fr_cursor_init(&cursor, &request->filter); vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY); if (vp) { fr_cursor_remove(&cursor); request->filter_code = vp->vp_integer; talloc_free(vp); } /* * xlat expansions aren't supported here */ for (vp = fr_cursor_init(&cursor, &request->filter); vp; vp = fr_cursor_next(&cursor)) { if (vp->type == VT_XLAT) { vp->type = VT_DATA; vp->vp_strvalue = vp->value.xlat; } } /* * This allows efficient list comparisons later */ pairsort(&request->filter, attrtagcmp); } /* * Determine the response code from the request (if not already set) */ if (!request->filter_code) { switch (request->packet_code) { case PW_CODE_AUTHENTICATION_REQUEST: request->filter_code = PW_CODE_AUTHENTICATION_ACK; break; case PW_CODE_ACCOUNTING_REQUEST: request->filter_code = PW_CODE_ACCOUNTING_RESPONSE; break; case PW_CODE_COA_REQUEST: request->filter_code = PW_CODE_COA_ACK; break; case PW_CODE_DISCONNECT_REQUEST: request->filter_code = PW_CODE_DISCONNECT_ACK; break; default: break; } } /* * Keep a copy of the the User-Password attribute. */ if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(request->password, vp->vp_strvalue, sizeof(request->password)); /* * Otherwise keep a copy of the CHAP-Password attribute. */ } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(request->password, vp->vp_strvalue, sizeof(request->password)); } else if ((vp = pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(request->password, vp->vp_strvalue, sizeof(request->password)); } else { request->password[0] = '\0'; } /* * Fix up Digest-Attributes issues */ for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Double quoted strings get marked up as xlat expansions, * but we don't support that in request. */ if (vp->type == VT_XLAT) { vp->vp_strvalue = vp->value.xlat; vp->value.xlat = NULL; vp->type = VT_DATA; } if (!vp->da->vendor) switch (vp->da->attr) { default: break; /* * Allow it to set the packet type in * the attributes read from the file. */ case PW_PACKET_TYPE: request->packet->code = vp->vp_integer; break; case PW_PACKET_DST_PORT: request->packet->dst_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_DST_IP_ADDRESS: request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_DST_IPV6_ADDRESS: request->packet->dst_ipaddr.af = AF_INET6; request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_PACKET_SRC_PORT: request->packet->src_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_SRC_IP_ADDRESS: request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_SRC_IPV6_ADDRESS: request->packet->src_ipaddr.af = AF_INET6; request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { DICT_ATTR const *da; uint8_t *p, *q; p = talloc_array(vp, uint8_t, vp->length + 2); memcpy(p + 2, vp->vp_octets, vp->length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->length += 2; p[1] = vp->length; da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0); if (!da) { ERROR("Out of memory"); goto error; } vp->da = da; /* * Re-do pairmemsteal ourselves, * because we play games with * vp->da, and pairmemsteal goes * to GREAT lengths to sanitize * and fix and change and * double-check the various * fields. */ memcpy(&q, &vp->vp_octets, sizeof(q)); talloc_free(q); vp->vp_octets = talloc_steal(vp, p); vp->type = VT_DATA; VERIFY_VP(vp); } break; } } /* loop over the VP's we read in */ /* * Automatically set the port if we don't have a global * or packet specific one. */ if ((server_port == 0) && (request->packet->dst_port == 0)) { radclient_get_port(request->packet->code, &request->packet->dst_port); } /* * Add it to the tail of the list. */ if (!request_head) { assert(rc_request_tail == NULL); request_head = request; request->prev = NULL; } else { assert(rc_request_tail->next == NULL); rc_request_tail->next = request; request->prev = rc_request_tail; } rc_request_tail = request; request->next = NULL; } while (!packets_done); /* loop until the file is done. */ if (packets != stdin) fclose(packets); if (filters) fclose(filters); /* * And we're done. */ return 1; error: talloc_free(request); if (packets != stdin) fclose(packets); if (filters) fclose(filters); return 0; }
static REQUEST *request_setup(FILE *fp) { VALUE_PAIR *vp; REQUEST *request; vp_cursor_t cursor; struct timeval now; /* * Create and initialize the new request. */ request = request_alloc(NULL); gettimeofday(&now, NULL); request->timestamp = now; request->packet = fr_radius_alloc(request, false); if (!request->packet) { ERROR("No memory"); talloc_free(request); return NULL; } request->packet->timestamp = now; request->reply = fr_radius_alloc(request, false); if (!request->reply) { ERROR("No memory"); talloc_free(request); return NULL; } request->listener = listen_alloc(request); request->client = client_alloc(request); request->number = 0; request->master_state = REQUEST_ACTIVE; request->child_state = REQUEST_RUNNING; request->handle = NULL; request->server = talloc_typed_strdup(request, "default"); request->root = &main_config; /* * Read packet from fp */ if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, fp, &filedone) < 0) { fr_perror("unittest"); talloc_free(request); return NULL; } /* * Set the defaults for IPs, etc. */ request->packet->code = PW_CODE_ACCESS_REQUEST; request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->src_port = 18120; request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->dst_port = 1812; /* * Copied from radclient */ #if 1 /* * Fix up Digest-Attributes issues */ for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Double quoted strings get marked up as xlat expansions, * but we don't support that here. */ if (vp->type == VT_XLAT) { vp->vp_strvalue = vp->xlat; vp->xlat = NULL; vp->type = VT_DATA; } if (!vp->da->vendor) switch (vp->da->attr) { default: break; /* * Allow it to set the packet type in * the attributes read from the file. */ case PW_PACKET_TYPE: request->packet->code = vp->vp_integer; break; case PW_PACKET_DST_PORT: request->packet->dst_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_DST_IP_ADDRESS: request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->packet->dst_ipaddr.prefix = 32; break; case PW_PACKET_DST_IPV6_ADDRESS: request->packet->dst_ipaddr.af = AF_INET6; request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; request->packet->dst_ipaddr.prefix = 128; break; case PW_PACKET_SRC_PORT: request->packet->src_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_SRC_IP_ADDRESS: request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->packet->src_ipaddr.prefix = 32; break; case PW_PACKET_SRC_IPV6_ADDRESS: request->packet->src_ipaddr.af = AF_INET6; request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; request->packet->src_ipaddr.prefix = 128; break; case PW_CHAP_PASSWORD: { int i, already_hex = 0; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->vp_length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = 1; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { uint8_t *p; size_t len, len2; len = len2 = vp->vp_length; if (len2 < 17) len2 = 17; p = talloc_zero_array(vp, uint8_t, len2); memcpy(p, vp->vp_strvalue, len); fr_radius_encode_chap_password(p, request->packet, fr_rand() & 0xff, vp); vp->vp_octets = p; vp->vp_length = 17; } } break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { fr_dict_attr_t const *da; uint8_t *p, *q; p = talloc_array(vp, uint8_t, vp->vp_length + 2); memcpy(p + 2, vp->vp_octets, vp->vp_length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->vp_length += 2; p[1] = vp->vp_length; da = fr_dict_attr_by_num(NULL, 0, PW_DIGEST_ATTRIBUTES); rad_assert(da != NULL); vp->da = da; /* * Re-do fr_pair_value_memsteal ourselves, * because we play games with * vp->da, and fr_pair_value_memsteal goes * to GREAT lengths to sanitize * and fix and change and * double-check the various * fields. */ memcpy(&q, &vp->vp_octets, sizeof(q)); talloc_free(q); vp->vp_octets = talloc_steal(vp, p); vp->type = VT_DATA; VERIFY_VP(vp); } break; } } /* loop over the VP's we read in */ #endif if (rad_debug_lvl) { for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Take this opportunity to verify all the VALUE_PAIRs are still valid. */ if (!talloc_get_type(vp, VALUE_PAIR)) { ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp)); fr_log_talloc_report(vp); rad_assert(0); } fr_pair_fprint(fr_log_fp, vp); } fflush(fr_log_fp); } /* * FIXME: set IPs, etc. */ request->packet->code = PW_CODE_ACCESS_REQUEST; request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->src_port = 18120; request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->dst_port = 1812; /* * Build the reply template from the request. */ request->reply->sockfd = request->packet->sockfd; request->reply->dst_ipaddr = request->packet->src_ipaddr; request->reply->src_ipaddr = request->packet->dst_ipaddr; request->reply->dst_port = request->packet->src_port; request->reply->src_port = request->packet->dst_port; request->reply->id = request->packet->id; request->reply->code = 0; /* UNKNOWN code */ memcpy(request->reply->vector, request->packet->vector, sizeof(request->reply->vector)); request->reply->vps = NULL; request->reply->data = NULL; request->reply->data_len = 0; /* * Debugging */ request->log.lvl = rad_debug_lvl; request->log.func = vradlog_request; request->username = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_NAME, TAG_ANY); request->password = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY); return request; }
static int sendrecv_eap(RADIUS_PACKET *rep) { RADIUS_PACKET *req = NULL; VALUE_PAIR *vp, *vpnext; int tried_eap_md5 = 0; if (!rep) return -1; /* * Keep a copy of the the User-Password attribute. */ if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(password, vp->vp_strvalue, sizeof(password)); } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(password, vp->vp_strvalue, sizeof(password)); /* * Otherwise keep a copy of the CHAP-Password attribute. */ } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(password, vp->vp_strvalue, sizeof(password)); } else { *password = '******'; } again: rep->id++; /* * if there are EAP types, encode them into an EAP-Message * */ map_eap_methods(rep); /* * Fix up Digest-Attributes issues */ for (vp = rep->vps; vp != NULL; vp = vp->next) { switch (vp->da->attr) { default: break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { DICT_ATTR const *da; uint8_t *p, *q; p = talloc_array(vp, uint8_t, vp->length + 2); memcpy(p + 2, vp->vp_octets, vp->length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->length += 2; p[1] = vp->length; da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0); vp->da = da; /* * Re-do pairmemsteal ourselves, * because we play games with * vp->da, and pairmemsteal goes * to GREAT lengths to sanitize * and fix and change and * double-check the various * fields. */ memcpy(&q, &vp->vp_octets, sizeof(q)); talloc_free(q); vp->vp_octets = talloc_steal(vp, p); vp->type = VT_DATA; VERIFY_VP(vp); } break; } } /* * If we've already sent a packet, free up the old * one, and ensure that the next packet has a unique * ID and authentication vector. */ if (rep->data) { talloc_free(rep->data); rep->data = NULL; } fr_md5_calc(rep->vector, rep->vector, sizeof(rep->vector)); if (*password != '\0') { if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, password); } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, password); } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, password); uint8_t *p; p = talloc_zero_array(vp, uint8_t, 17); rad_chap_encode(rep, p, rep->id, vp); pairmemsteal(vp, p); } } /* there WAS a password */ /* send the response, wait for the next request */ send_packet(rep, &req); if (!req) { ERROR("Failed getting response (EAP-Request from server)"); return -1; } /* okay got back the packet, go and decode the EAP-Message. */ unmap_eap_methods(req); debug_packet(req, R_RECV); /* now look for the code type. */ for (vp = req->vps; vp != NULL; vp = vpnext) { vpnext = vp->next; switch (vp->da->attr) { default: break; case PW_EAP_TYPE_BASE + PW_EAP_MD5: if (respond_eap_md5(req, rep) && tried_eap_md5 < 3) { tried_eap_md5++; goto again; } break; case PW_EAP_TYPE_BASE + PW_EAP_SIM: if (respond_eap_sim(req, rep)) { goto again; } break; } } return 1; }
/* * Initialize a radclient data structure and add it to * the global linked list. */ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files) { FILE *packets, *filters = NULL; vp_cursor_t cursor; VALUE_PAIR *vp; rc_request_t *request; bool packets_done = false; uint64_t num = 0; assert(files->packets != NULL); /* * Determine where to read the VP's from. */ if (strcmp(files->packets, "-") != 0) { packets = fopen(files->packets, "r"); if (!packets) { ERROR("Error opening %s: %s", files->packets, strerror(errno)); return 0; } /* * Read in the pairs representing the expected response. */ if (files->filters) { filters = fopen(files->filters, "r"); if (!filters) { ERROR("Error opening %s: %s", files->filters, strerror(errno)); fclose(packets); return 0; } } } else { packets = stdin; } /* * Loop until the file is done. */ do { /* * Allocate it. */ request = talloc_zero(ctx, rc_request_t); if (!request) { ERROR("Out of memory"); goto error; } request->packet = fr_radius_alloc(request, true); if (!request->packet) { ERROR("Out of memory"); goto error; } #ifdef WITH_TCP request->packet->src_ipaddr = client_ipaddr; request->packet->src_port = client_port; request->packet->dst_ipaddr = server_ipaddr; request->packet->dst_port = server_port; request->packet->proto = ipproto; #endif request->files = files; request->packet->id = -1; /* allocate when sending */ request->num = num++; /* * Read the request VP's. */ if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, packets, &packets_done) < 0) { char const *input; if ((files->packets[0] == '-') && (files->packets[1] == '\0')) { input = "stdin"; } else { input = files->packets; } REDEBUG("Error parsing \"%s\"", input); goto error; } /* * Skip empty entries */ if (!request->packet->vps) { talloc_free(request); continue; } /* * Read in filter VP's. */ if (filters) { bool filters_done; if (fr_pair_list_afrom_file(request, &request->filter, filters, &filters_done) < 0) { REDEBUG("Error parsing \"%s\"", files->filters); goto error; } if (filters_done && !packets_done) { REDEBUG("Differing number of packets/filters in %s:%s " "(too many requests))", files->packets, files->filters); goto error; } if (!filters_done && packets_done) { REDEBUG("Differing number of packets/filters in %s:%s " "(too many filters))", files->packets, files->filters); goto error; } /* * xlat expansions aren't supported here */ for (vp = fr_cursor_init(&cursor, &request->filter); vp; vp = fr_cursor_next(&cursor)) { if (vp->type == VT_XLAT) { vp->type = VT_DATA; vp->vp_strvalue = vp->xlat; vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1; } if (vp->da->vendor == 0 ) switch (vp->da->attr) { case PW_RESPONSE_PACKET_TYPE: case PW_PACKET_TYPE: fr_cursor_remove(&cursor); /* so we don't break the filter */ request->filter_code = vp->vp_integer; talloc_free(vp); default: break; } } /* * This allows efficient list comparisons later */ fr_pair_list_sort(&request->filter, fr_pair_cmp_by_da_tag); } /* * Process special attributes */ for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Double quoted strings get marked up as xlat expansions, * but we don't support that in request. */ if (vp->type == VT_XLAT) { vp->type = VT_DATA; vp->vp_strvalue = vp->xlat; vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1; } if (!vp->da->vendor) switch (vp->da->attr) { default: break; /* * Allow it to set the packet type in * the attributes read from the file. */ case PW_PACKET_TYPE: request->packet->code = vp->vp_integer; break; case PW_RESPONSE_PACKET_TYPE: request->filter_code = vp->vp_integer; break; case PW_PACKET_DST_PORT: request->packet->dst_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_DST_IP_ADDRESS: request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->packet->dst_ipaddr.prefix = 32; break; case PW_PACKET_DST_IPV6_ADDRESS: request->packet->dst_ipaddr.af = AF_INET6; request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; request->packet->dst_ipaddr.prefix = 128; break; case PW_PACKET_SRC_PORT: if ((vp->vp_integer < 1024) || (vp->vp_integer > 65535)) { ERROR("Invalid value '%u' for Packet-Src-Port", vp->vp_integer); goto error; } request->packet->src_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_SRC_IP_ADDRESS: request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->packet->src_ipaddr.prefix = 32; break; case PW_PACKET_SRC_IPV6_ADDRESS: request->packet->src_ipaddr.af = AF_INET6; request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; request->packet->src_ipaddr.prefix = 128; break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { fr_dict_attr_t const *da; uint8_t *p, *q; p = talloc_array(vp, uint8_t, vp->vp_length + 2); memcpy(p + 2, vp->vp_octets, vp->vp_length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->vp_length += 2; p[1] = vp->vp_length; da = fr_dict_attr_by_num(NULL, 0, PW_DIGEST_ATTRIBUTES); if (!da) { ERROR("Out of memory"); goto error; } vp->da = da; /* * Re-do fr_pair_value_memsteal ourselves, * because we play games with * vp->da, and fr_pair_value_memsteal goes * to GREAT lengths to sanitize * and fix and change and * double-check the various * fields. */ memcpy(&q, &vp->vp_octets, sizeof(q)); talloc_free(q); vp->vp_octets = talloc_steal(vp, p); vp->type = VT_DATA; VERIFY_VP(vp); } break; /* * Cache this for later. */ case PW_CLEARTEXT_PASSWORD: request->password = vp; break; /* * Keep a copy of the the password attribute. */ case PW_CHAP_PASSWORD: /* * If it's already hex, do nothing. */ if ((vp->vp_length == 17) && (already_hex(vp))) break; /* * CHAP-Password is octets, so it may not be zero terminated. */ request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password", "", T_OP_EQ); fr_pair_value_bstrncpy(request->password, vp->vp_strvalue, vp->vp_length); break; case PW_USER_PASSWORD: case PW_MS_CHAP_PASSWORD: request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password", vp->vp_strvalue, T_OP_EQ); break; case PW_RADCLIENT_TEST_NAME: request->name = vp->vp_strvalue; break; } } /* loop over the VP's we read in */ /* * Use the default set on the command line */ if (request->packet->code == PW_CODE_UNDEFINED) request->packet->code = packet_code; /* * Default to the filename */ if (!request->name) request->name = request->files->packets; /* * Automatically set the response code from the request code * (if one wasn't already set). */ if (request->filter_code == PW_CODE_UNDEFINED) { switch (request->packet->code) { case PW_CODE_ACCESS_REQUEST: request->filter_code = PW_CODE_ACCESS_ACCEPT; break; case PW_CODE_ACCOUNTING_REQUEST: request->filter_code = PW_CODE_ACCOUNTING_RESPONSE; break; case PW_CODE_COA_REQUEST: request->filter_code = PW_CODE_COA_ACK; break; case PW_CODE_DISCONNECT_REQUEST: request->filter_code = PW_CODE_DISCONNECT_ACK; break; case PW_CODE_STATUS_SERVER: switch (radclient_get_code(request->packet->dst_port)) { case PW_CODE_ACCESS_REQUEST: request->filter_code = PW_CODE_ACCESS_ACCEPT; break; case PW_CODE_ACCOUNTING_REQUEST: request->filter_code = PW_CODE_ACCOUNTING_RESPONSE; break; default: request->filter_code = PW_CODE_UNDEFINED; break; } break; case PW_CODE_UNDEFINED: REDEBUG("Both Packet-Type and Response-Packet-Type undefined, specify at least one, " "or a well known RADIUS port"); goto error; default: REDEBUG("Can't determine expected Response-Packet-Type for Packet-Type %i", request->packet->code); goto error; } /* * Automatically set the request code from the response code * (if one wasn't already set). */ } else if (request->packet->code == PW_CODE_UNDEFINED) { switch (request->filter_code) { case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCESS_REJECT: request->packet->code = PW_CODE_ACCESS_REQUEST; break; case PW_CODE_ACCOUNTING_RESPONSE: request->packet->code = PW_CODE_ACCOUNTING_REQUEST; break; case PW_CODE_DISCONNECT_ACK: case PW_CODE_DISCONNECT_NAK: request->packet->code = PW_CODE_DISCONNECT_REQUEST; break; case PW_CODE_COA_ACK: case PW_CODE_COA_NAK: request->packet->code = PW_CODE_COA_REQUEST; break; default: REDEBUG("Can't determine expected Packet-Type for Response-Packet-Type %i", request->filter_code); goto error; } } /* * Automatically set the dst port (if one wasn't already set). */ if (request->packet->dst_port == 0) { radclient_get_port(request->packet->code, &request->packet->dst_port); if (request->packet->dst_port == 0) { REDEBUG("Can't determine destination port"); goto error; } } /* * Add it to the tail of the list. */ if (!request_head) { assert(rc_request_tail == NULL); request_head = request; request->prev = NULL; } else { assert(rc_request_tail->next == NULL); rc_request_tail->next = request; request->prev = rc_request_tail; } rc_request_tail = request; request->next = NULL; /* * Set the destructor so it removes itself from the * request list when freed. We don't set this until * the packet is actually in the list, else we trigger * the asserts in the free callback. */ talloc_set_destructor(request, _rc_request_free); } while (!packets_done); /* loop until the file is done. */ if (packets != stdin) fclose(packets); if (filters) fclose(filters); /* * And we're done. */ return 1; error: talloc_free(request); if (packets != stdin) fclose(packets); if (filters) fclose(filters); return 0; }
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST. * * Takes a single value_pair_map_t, resolves request and list identifiers * to pointers in the current request, then attempts to retrieve module * specific value(s) using callback, and adds the resulting values to the * correct request/list. * * @param request The current request. * @param map specifying destination attribute and location and src identifier. * @param func to retrieve module specific values and convert them to * VALUE_PAIRS. * @param ctx to be passed to func. * @param src name to be used in debugging if different from map value. * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success. */ int radius_map2request(REQUEST *request, value_pair_map_t const *map, UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx) { int rcode, num; VALUE_PAIR **list, *vp, *head = NULL; REQUEST *context; TALLOC_CTX *parent; vp_cursor_t cursor; /* * Sanity check inputs. We can have a list or attribute * as a destination. */ if ((map->dst->type != VPT_TYPE_LIST) && (map->dst->type != VPT_TYPE_ATTR)) { REDEBUG("Invalid mapping destination"); return -2; } context = request; if (radius_request(&context, map->dst->request) < 0) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } /* * If there's no CoA packet and we're updating it, * auto-allocate it. */ if (((map->dst->list == PAIR_LIST_COA) || (map->dst->list == PAIR_LIST_DM)) && !request->coa) { request_alloc_coa(context); if (map->dst->list == PAIR_LIST_COA) { context->coa->proxy->code = PW_CODE_COA_REQUEST; } else { context->coa->proxy->code = PW_CODE_DISCONNECT_REQUEST; } } list = radius_list(context, map->dst->list); if (!list) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } parent = radius_list_ctx(context, map->dst->list); /* * The callback should either return -1 to signify operations error, -2 when it can't find the * attribute or list being referenced, or 0 to signify success. * It may return "sucess", but still have no VPs to work with. */ rcode = func(&head, request, map, ctx); if (rcode < 0) { rad_assert(!head); return rcode; } if (!head) return 0; /* * Reparent the VP */ for (vp = fr_cursor_init(&cursor, &head); vp; vp = fr_cursor_next(&cursor)) { VERIFY_VP(vp); if (debug_flag) debug_map(request, map, vp); (void) talloc_steal(parent, vp); } /* * List to list copies. */ if (map->dst->type == VPT_TYPE_LIST) { switch (map->op) { case T_OP_CMP_FALSE: rad_assert(head == NULL); pairfree(list); if (map->dst->list == PAIR_LIST_REQUEST) { context->username = NULL; context->password = NULL; } break; case T_OP_SET: if (map->src->type == VPT_TYPE_LIST) { pairfree(list); *list = head; } else { case T_OP_EQ: rad_assert(map->src->type == VPT_TYPE_EXEC); pairmove(parent, list, &head); pairfree(&head); } if (map->dst->list == PAIR_LIST_REQUEST) { context->username = pairfind(head, PW_USER_NAME, 0, TAG_ANY); context->password = pairfind(head, PW_USER_PASSWORD, 0, TAG_ANY); } break; case T_OP_ADD: pairadd(list, head); break; default: pairfree(&head); return -1; } return 0; } /* * We now should have only one destination attribute, and * only one source attribute. */ rad_assert(head->next == NULL); /* * Find the destination attribute. We leave with either * the cursor and vp pointing to the attribute, or vp is * NULL. */ num = map->dst->num; for (vp = fr_cursor_init(&cursor, list); vp != NULL; vp = fr_cursor_next(&cursor)) { VERIFY_VP(vp); if ((vp->da == map->dst->da) && (!vp->da->flags.has_tag || (map->dst->tag == TAG_ANY) || (vp->tag == map->dst->tag))) { if (num == 0) break; num--; } } /* * Figure out what to do with the source attribute. */ switch (map->op) { case T_OP_CMP_FALSE: /* remove matching attributes */ pairfree(&head); if (!vp) return 0; /* * Wildcard: delete all of the matching ones, * based on tag. */ if (!map->dst->num) { pairdelete(list, map->dst->da->attr, map->dst->da->vendor, map->dst->tag); vp = NULL; } else { /* * We've found the Nth one. Delete it, and only * it. */ vp = fr_cursor_remove(&cursor); } /* * Check that the User-Name and User-Password * caches point to the correct attribute. */ fixup: if (map->dst->list == PAIR_LIST_REQUEST) { context->username = pairfind(*list, PW_USER_NAME, 0, TAG_ANY); context->password = pairfind(*list, PW_USER_PASSWORD, 0, TAG_ANY); } pairfree(&vp); return 0; case T_OP_EQ: /* set only if not already set */ if (vp) { pairfree(&head); return 0; } fr_cursor_insert(&cursor, head); goto fixup; case T_OP_SET: /* over-write if existing, or else add */ if (vp) vp = fr_cursor_remove(&cursor); fr_cursor_insert(&cursor, head); goto fixup; case T_OP_ADD: /* append no matter what */ vp = NULL; pairadd(list, head); goto fixup; case T_OP_SUB: /* delete if it matches */ head->op = T_OP_CMP_EQ; rcode = radius_compare_vps(NULL, head, vp); pairfree(&head); if (rcode == 0) { vp = fr_cursor_remove(&cursor); goto fixup; } return 0; default: /* filtering operators */ /* * If the VP doesn't exist, the filters will add * it with the given value. */ if (!vp) { fr_cursor_insert(&cursor, head); goto fixup; } break; } /* * The LHS exists. We need to limit it's value based on * the operator, and the value of the RHS. */ head->op = map->op; rcode = radius_compare_vps(NULL, head, vp); head->op = T_OP_SET; switch (map->op) { case T_OP_CMP_EQ: if (rcode == 0) { leave: pairfree(&head); break; } replace: vp = fr_cursor_remove(&cursor); fr_cursor_insert(&cursor, head); goto fixup; case T_OP_LE: if (rcode <= 0) goto leave; goto replace; case T_OP_GE: if (rcode >= 0) goto leave; goto replace; default: pairfree(&head); return -1; } return 0; }
/** Return a VP from a value_pair_tmpl_t * * @param out where to write the retrieved vp. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int radius_vpt_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; if (out) *out = NULL; if (radius_request(&request, vpt->request) < 0) { return -3; } vps = radius_list(request, vpt->list); if (!vps) { return -2; } switch (vpt->type) { /* * May not may not be found, but it *is* a known * name. */ case VPT_TYPE_ATTR: if (vpt->num == 0) { vp = pairfind(*vps, vpt->da->attr, vpt->da->vendor, vpt->tag); if (!vp) return -1; } else { int num; vp_cursor_t cursor; /* * It's faster to just repeat the 3-4 lines of pairfind here. */ num = vpt->num; for (vp = fr_cursor_init(&cursor, vps); vp != NULL; vp = fr_cursor_next(&cursor)) { VERIFY_VP(vp); if ((vp->da == vpt->da) && (!vp->da->flags.has_tag || (vpt->tag == TAG_ANY) || (vp->tag == vpt->tag))) { if (num == 0) { *out = vp; return 0; } num--; } } return -1; } break; case VPT_TYPE_LIST: vp = *vps; break; default: /* * literal, xlat, regex, exec, data. * no attribute. */ return -1; } if (out) { *out = vp; } return 0; }
/** Create and insert a cache entry. * * @return RLM_MODULE_OK on success, RLM_MODULE_UPDATED if we merged the cache entry and RLM_MODULE_FAIL on failure. */ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, char const *key, int ttl) { VALUE_PAIR *vp, *to_cache; vp_cursor_t src_list, cached_request, cached_reply, cached_control; value_pair_map_t const *map; bool merge = false; rlm_cache_entry_t *c; if ((inst->max_entries > 0) && inst->module->count && (inst->module->count(inst, request, handle) > inst->max_entries)) { RWDEBUG("Cache is full: %d entries", inst->max_entries); return RLM_MODULE_FAIL; } c = cache_alloc(inst, request); if (!c) return RLM_MODULE_FAIL; c->key = talloc_typed_strdup(c, key); c->created = c->expires = request->timestamp; c->expires += ttl; RDEBUG("Creating new cache entry"); fr_cursor_init(&cached_request, &c->packet); fr_cursor_init(&cached_reply, &c->reply); fr_cursor_init(&cached_control, &c->control); for (map = inst->maps; map != NULL; map = map->next) { rad_assert(map->lhs && map->rhs); if (map_to_vp(&to_cache, request, map, NULL) < 0) { RDEBUG("Skipping %s", map->rhs->name); continue; } /* * Reparent the VPs map_to_vp may return multiple. */ for (vp = fr_cursor_init(&src_list, &to_cache); vp; vp = fr_cursor_next(&src_list)) { VERIFY_VP(vp); /* * Prevent people from accidentally caching * cache control attributes. */ if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) { case PW_CACHE_TTL: case PW_CACHE_STATUS_ONLY: case PW_CACHE_READ_ONLY: case PW_CACHE_MERGE: case PW_CACHE_ENTRY_HITS: RDEBUG2("Skipping %s", vp->da->name); continue; default: break; } RINDENT(); if (RDEBUG_ENABLED2) map_debug_log(request, map, vp); REXDENT(); (void) talloc_steal(c, vp); vp->op = map->op; switch (map->lhs->tmpl_list) { case PAIR_LIST_REQUEST: fr_cursor_insert(&cached_request, vp); break; case PAIR_LIST_REPLY: fr_cursor_insert(&cached_reply, vp); break; case PAIR_LIST_CONTROL: fr_cursor_insert(&cached_control, vp); break; default: rad_assert(0); /* should have been caught by validation */ } } } /* * Check to see if we need to merge the entry into the request */ vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY); if (vp && (vp->vp_integer > 0)) merge = true; if (merge) cache_merge(inst, request, c); for (;;) { cache_status_t ret; ret = inst->module->insert(inst, request, handle, c); switch (ret) { case CACHE_RECONNECT: if (cache_reconnect(inst, request, handle) == 0) continue; return RLM_MODULE_FAIL; case CACHE_OK: RDEBUG("Commited entry, TTL %d seconds", ttl); cache_free(inst, &c); return merge ? RLM_MODULE_UPDATED : RLM_MODULE_OK; default: talloc_free(c); /* Failed insertion - use talloc_free not the driver free */ return RLM_MODULE_FAIL; } } }
/* * Process and reply to an authentication request * * The return value of this function isn't actually used right now, so * it's not entirely clear if it is returning the right things. --Pac. */ int rad_authenticate(REQUEST *request) { #ifdef WITH_SESSION_MGMT VALUE_PAIR *check_item; #endif VALUE_PAIR *module_msg; VALUE_PAIR *tmp = NULL; int result; char autz_retry = 0; int autz_type = 0; #ifdef WITH_PROXY /* * If this request got proxied to another server, we need * to check whether it authenticated the request or not. */ if (request->proxy_reply) { switch (request->proxy_reply->code) { /* * Reply of ACCEPT means accept, thus set Auth-Type * accordingly. */ case PW_CODE_AUTHENTICATION_ACK: tmp = radius_paircreate(request, &request->config_items, PW_AUTH_TYPE, 0); if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT; goto authenticate; /* * Challenges are punted back to the NAS without any * further processing. */ case PW_CODE_ACCESS_CHALLENGE: request->reply->code = PW_CODE_ACCESS_CHALLENGE; return RLM_MODULE_OK; /* * ALL other replies mean reject. (this is fail-safe) * * Do NOT do any authorization or authentication. They * are being rejected, so we minimize the amount of work * done by the server, by rejecting them here. */ case PW_CODE_AUTHENTICATION_REJECT: rad_authlog("Login incorrect (Home Server says so)", request, 0); request->reply->code = PW_CODE_AUTHENTICATION_REJECT; return RLM_MODULE_REJECT; default: rad_authlog("Login incorrect (Home Server failed to respond)", request, 0); return RLM_MODULE_REJECT; } } #endif /* * Look for, and cache, passwords. */ if (!request->password) { request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); } if (!request->password) { request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY); } /* * Get the user's authorization information from the database */ autz_redo: result = process_authorize(autz_type, request); switch (result) { case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: break; case RLM_MODULE_HANDLED: return result; case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) { char msg[MAX_STRING_LEN + 16]; snprintf(msg, sizeof(msg), "Invalid user (%s)", module_msg->vp_strvalue); rad_authlog(msg,request,0); } else { rad_authlog("Invalid user", request, 0); } request->reply->code = PW_CODE_AUTHENTICATION_REJECT; return result; } if (!autz_retry) { tmp = pairfind(request->config_items, PW_AUTZ_TYPE, 0, TAG_ANY); if (tmp) { autz_type = tmp->vp_integer; RDEBUG2("Using Autz-Type %s", dict_valnamebyattr(PW_AUTZ_TYPE, 0, autz_type)); autz_retry = 1; goto autz_redo; } } /* * If we haven't already proxied the packet, then check * to see if we should. Maybe one of the authorize * modules has decided that a proxy should be used. If * so, get out of here and send the packet. */ if ( #ifdef WITH_PROXY (request->proxy == NULL) && #endif ((tmp = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY)) != NULL)) { REALM *realm; realm = realm_find2(tmp->vp_strvalue); /* * Don't authenticate, as the request is going to * be proxied. */ if (realm && realm->auth_pool) { return RLM_MODULE_OK; } /* * Catch users who set Proxy-To-Realm to a LOCAL * realm (sigh). But don't complain if it is * *the* LOCAL realm. */ if (realm &&(strcmp(realm->name, "LOCAL") != 0)) { RWDEBUG2("You set Proxy-To-Realm = %s, but it is a LOCAL realm! Cancelling proxy request.", realm->name); } if (!realm) { RWDEBUG2("You set Proxy-To-Realm = %s, but the realm does not exist! Cancelling invalid proxy request.", tmp->vp_strvalue); } } #ifdef WITH_PROXY authenticate: #endif /* * Validate the user */ do { result = rad_check_password(request); if (result > 0) { /* don't reply! */ return RLM_MODULE_HANDLED; } } while(0); /* * Failed to validate the user. * * We PRESUME that the code which failed will clean up * request->reply->vps, to be ONLY the reply items it * wants to send back. */ if (result < 0) { RDEBUG2("Failed to authenticate the user"); request->reply->code = PW_CODE_AUTHENTICATION_REJECT; if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) { char msg[MAX_STRING_LEN+19]; snprintf(msg, sizeof(msg), "Login incorrect (%s)", module_msg->vp_strvalue); rad_authlog(msg, request, 0); } else { rad_authlog("Login incorrect", request, 0); } if (request->password) { VERIFY_VP(request->password); /* double check: maybe the secret is wrong? */ if ((debug_flag > 1) && (request->password->da->attr == PW_USER_PASSWORD)) { uint8_t const *p; p = (uint8_t const *) request->password->vp_strvalue; while (*p) { int size; size = fr_utf8_char(p); if (!size) { RWDEBUG("Unprintable characters in the password. Double-check the " "shared secret on the server and the NAS!"); break; } p += size; } } } } #ifdef WITH_SESSION_MGMT if (result >= 0 && (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) { int r, session_type = 0; char logstr[1024]; char umsg[MAX_STRING_LEN + 1]; char const *user_msg = NULL; tmp = pairfind(request->config_items, PW_SESSION_TYPE, 0, TAG_ANY); if (tmp) { session_type = tmp->vp_integer; RDEBUG2("Using Session-Type %s", dict_valnamebyattr(PW_SESSION_TYPE, 0, session_type)); } /* * User authenticated O.K. Now we have to check * for the Simultaneous-Use parameter. */ if (request->username && (r = process_checksimul(session_type, request, check_item->vp_integer)) != 0) { char mpp_ok = 0; if (r == 2) { /* Multilink attempt. Check if port-limit > simultaneous-use */ VALUE_PAIR *port_limit; if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT, 0, TAG_ANY)) != NULL && port_limit->vp_integer > check_item->vp_integer) { RDEBUG2("MPP is OK"); mpp_ok = 1; } } if (!mpp_ok) { if (check_item->vp_integer > 1) { snprintf(umsg, sizeof(umsg), "\r\nYou are already logged in %d times - access denied\r\n\n", (int)check_item->vp_integer); user_msg = umsg; } else { user_msg = "\r\nYou are already logged in - access denied\r\n\n"; } request->reply->code = PW_CODE_AUTHENTICATION_REJECT; /* * They're trying to log in too many times. * Remove ALL reply attributes. */ pairfree(&request->reply->vps); pairmake_reply("Reply-Message", user_msg, T_OP_SET); snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s", check_item->vp_integer, r == 2 ? "[MPP attempt]" : ""); rad_authlog(logstr, request, 1); result = -1; } } } #endif /* * Result should be >= 0 here - if not, it means the user * is rejected, so we just process post-auth and return. */ if (result < 0) { return RLM_MODULE_REJECT; } /* * Set the reply to Access-Accept, if it hasn't already * been set to something. (i.e. Access-Challenge) */ if (request->reply->code == 0) request->reply->code = PW_CODE_AUTHENTICATION_ACK; if ((module_msg = pairfind(request->packet->vps, PW_MODULE_SUCCESS_MESSAGE, 0, TAG_ANY)) != NULL) { char msg[MAX_STRING_LEN+12]; snprintf(msg, sizeof(msg), "Login OK (%s)", module_msg->vp_strvalue); rad_authlog(msg, request, 1); } else { rad_authlog("Login OK", request, 1); } return result; }
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST. * * Takes a single value_pair_map_t, resolves request and list identifiers * to pointers in the current request, then attempts to retrieve module * specific value(s) using callback, and adds the resulting values to the * correct request/list. * * @param request The current request. * @param map specifying destination attribute and location and src identifier. * @param func to retrieve module specific values and convert them to * VALUE_PAIRS. * @param ctx to be passed to func. * @param src name to be used in debugging if different from map value. * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success. */ int radius_map2request(REQUEST *request, value_pair_map_t const *map, UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx) { int rcode; vp_cursor_t cursor; VALUE_PAIR **list, *vp, *head = NULL; char buffer[1024]; if (radius_request(&request, map->dst->request) < 0) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } list = radius_list(request, map->dst->list); if (!list) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } /* * The callback should either return -1 to signify operations error, -2 when it can't find the * attribute or list being referenced, or 0 to signify success. * It may return "sucess", but still have no VPs to work with. * Only if it returned an error code should it not write anything to the head pointer. */ rcode = func(&head, request, map, ctx); if (rcode < 0) { rad_assert(!head); return rcode; } if (!head) return 0; VERIFY_VP(head); if (debug_flag) for (vp = paircursor(&cursor, &head); vp; vp = pairnext(&cursor)) { char *value; switch (map->src->type) { /* * Just print the value being assigned */ default: case VPT_TYPE_LITERAL: vp_prints_value(buffer, sizeof(buffer), vp, '\''); value = buffer; break; case VPT_TYPE_XLAT: vp_prints_value(buffer, sizeof(buffer), vp, '"'); value = buffer; break; case VPT_TYPE_DATA: vp_prints_value(buffer, sizeof(buffer), vp, 0); value = buffer; break; /* * Just printing the value doesn't make sense, but we still * want to know what it was... */ case VPT_TYPE_LIST: vp_prints_value(buffer, sizeof(buffer), vp, '\''); value = talloc_asprintf(request, "&%s%s -> %s", map->src->name, vp->da->name, buffer); break; case VPT_TYPE_ATTR: vp_prints_value(buffer, sizeof(buffer), vp, '\''); value = talloc_asprintf(request, "&%s -> %s", map->src->name, buffer); break; } RDEBUG("\t\t%s %s %s", map->dst->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value); if (value != buffer) talloc_free(value); } /* * Use pairmove so the operator is respected */ radius_pairmove(request, list, head); return 0; }
/** Insert a single VP * * @todo don't use with pairdelete */ void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add) { VALUE_PAIR *i; if (!add) { return; } VERIFY_VP(add); /* * Only allow one VP to by inserted at a time */ add->next = NULL; /* * Cursor was initialised with a pointer to a NULL value_pair */ if (!*cursor->first) { *cursor->first = add; cursor->current = add; return; } /* * We don't yet know where the last VALUE_PAIR is * * Assume current is closer to the end of the list and use that if available. */ if (!cursor->last) { cursor->last = cursor->current ? cursor->current : *cursor->first; } VERIFY_VP(cursor->last); /* * Something outside of the cursor added another VALUE_PAIR */ if (cursor->last->next) { for (i = cursor->last; i; i = i->next) { VERIFY_VP(i); cursor->last = i; } } /* * Either current was never set, or something iterated to the end of the * attribute list. */ if (!cursor->current) { cursor->current = add; } /* * If there's no next cursor, and the pair we just inserted has additional * linked pairs, we need to set next to be the next VP in the list. */ if (!cursor->next) { cursor->next = add->next; } cursor->last->next = add; }
/** Copy pairs matching a VPT in the current request * * @param ctx to allocate new VALUE_PAIRs under. * @param out where to write the copied vps. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; REQUEST *current = request; vp_cursor_t from, to; rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST)); if (out) *out = NULL; if (radius_request(¤t, vpt->tmpl_request) < 0) { return -3; } vps = radius_list(request, vpt->tmpl_list); if (!vps) { return -2; } switch (vpt->type) { /* * May not be found, but it *is* a known name. */ case TMPL_TYPE_ATTR: { int num; (void) fr_cursor_init(&to, out); (void) fr_cursor_init(&from, vps); vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag); if (!vp) return -1; switch (vpt->tmpl_num) { /* Copy all pairs of this type (and tag) */ case NUM_ALL: do { VERIFY_VP(vp); vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } while ((vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag))); break; /* Specific attribute number */ default: for (num = vpt->tmpl_num; num && vp; num--, vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)) { VERIFY_VP(vp); } if (!vp) return -1; /* FALL-THROUGH */ /* Just copy the first pair */ case NUM_ANY: vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } } break; case TMPL_TYPE_LIST: vp = paircopy(ctx, *vps); if (!vp) return 0; fr_cursor_merge(&to, vp); break; default: rad_assert(0); } return 0; }