static FR_CODE eap_fast_eap_payload(REQUEST *request, eap_session_t *eap_session, tls_session_t *tls_session, VALUE_PAIR *tlv_eap_payload) { FR_CODE code = FR_CODE_ACCESS_REJECT; rlm_rcode_t rcode; VALUE_PAIR *vp; eap_fast_tunnel_t *t; REQUEST *fake; RDEBUG2("Processing received EAP Payload"); /* * Allocate a fake REQUEST structure. */ fake = request_alloc_fake(request, NULL); rad_assert(!fake->packet->vps); t = talloc_get_type_abort(tls_session->opaque, eap_fast_tunnel_t); /* * Add the tunneled attributes to the fake request. */ fake->packet->vps = fr_pair_afrom_da(fake->packet, attr_eap_message); fr_pair_value_memcpy(fake->packet->vps, tlv_eap_payload->vp_octets, tlv_eap_payload->vp_length, false); RDEBUG2("Got tunneled request"); log_request_pair_list(L_DBG_LVL_1, request, fake->packet->vps, NULL); /* * Tell the request that it's a fake one. */ MEM(fr_pair_add_by_da(fake->packet, &vp, &fake->packet->vps, attr_freeradius_proxied_to) >= 0); fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1"), '\0', false); /* * Update other items in the REQUEST data structure. */ fake->username = fr_pair_find_by_da(fake->packet->vps, attr_user_name, TAG_ANY); fake->password = fr_pair_find_by_da(fake->packet->vps, attr_user_password, TAG_ANY); /* * No User-Name, try to create one from stored data. */ if (!fake->username) { /* * No User-Name in the stored data, look for * an EAP-Identity, and pull it out of there. */ if (!t->username) { vp = fr_pair_find_by_da(fake->packet->vps, attr_eap_message, TAG_ANY); if (vp && (vp->vp_length >= EAP_HEADER_LEN + 2) && (vp->vp_strvalue[0] == FR_EAP_CODE_RESPONSE) && (vp->vp_strvalue[EAP_HEADER_LEN] == FR_EAP_METHOD_IDENTITY) && (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) { /* * Create & remember a User-Name */ MEM(t->username = fr_pair_afrom_da(t, attr_user_name)); t->username->vp_tainted = true; fr_pair_value_bstrncpy(t->username, vp->vp_octets + 5, vp->vp_length - 5); RDEBUG2("Got tunneled identity of %pV", &t->username->data); } else { /* * Don't reject the request outright, * as it's permitted to do EAP without * user-name. */ RWDEBUG2("No EAP-Identity found to start EAP conversation"); } } /* else there WAS a t->username */ if (t->username) { vp = fr_pair_copy(fake->packet, t->username); fr_pair_add(&fake->packet->vps, vp); fake->username = vp; } } /* else the request ALREADY had a User-Name */ if (t->stage == EAP_FAST_AUTHENTICATION) { /* FIXME do this only for MSCHAPv2 */ VALUE_PAIR *tvp; tvp = fr_pair_afrom_da(fake, attr_eap_type); tvp->vp_uint32 = t->default_provisioning_method; fr_pair_add(&fake->control, tvp); /* * RFC 5422 section 3.2.3 - Authenticating Using EAP-FAST-MSCHAPv2 */ if (t->mode == EAP_FAST_PROVISIONING_ANON) { tvp = fr_pair_afrom_da(fake, attr_ms_chap_challenge); fr_pair_value_memcpy(tvp, t->keyblock->server_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, false); fr_pair_add(&fake->control, tvp); RHEXDUMP(L_DBG_LVL_MAX, t->keyblock->server_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, "MSCHAPv2 auth_challenge"); tvp = fr_pair_afrom_da(fake, attr_ms_chap_peer_challenge); fr_pair_value_memcpy(tvp, t->keyblock->client_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, false); fr_pair_add(&fake->control, tvp); RHEXDUMP(L_DBG_LVL_MAX, t->keyblock->client_challenge, RADIUS_CHAP_CHALLENGE_LENGTH, "MSCHAPv2 peer_challenge"); } } /* * Call authentication recursively, which will * do PAP, CHAP, MS-CHAP, etc. */ eap_virtual_server(request, fake, eap_session, t->virtual_server); /* * Decide what to do with the reply. */ switch (fake->reply->code) { case 0: /* No reply code, must be proxied... */ #ifdef WITH_PROXY vp = fr_pair_find_by_da(fake->control, attr_proxy_to_realm, TAG_ANY); if (vp) { int ret; eap_tunnel_data_t *tunnel; RDEBUG2("Tunneled authentication will be proxied to %pV", &vp->data); /* * Tell the original request that it's going to be proxied. */ fr_pair_list_copy_by_da(request, &request->control, fake->control, attr_proxy_to_realm); /* * Seed the proxy packet with the tunneled request. */ rad_assert(!request->proxy); /* * FIXME: Actually proxy stuff */ request->proxy = request_alloc_fake(request, NULL); request->proxy->packet = talloc_steal(request->proxy, fake->packet); memset(&request->proxy->packet->src_ipaddr, 0, sizeof(request->proxy->packet->src_ipaddr)); memset(&request->proxy->packet->src_ipaddr, 0, sizeof(request->proxy->packet->src_ipaddr)); request->proxy->packet->src_port = 0; request->proxy->packet->dst_port = 0; fake->packet = NULL; fr_radius_packet_free(&fake->reply); fake->reply = NULL; /* * Set up the callbacks for the tunnel */ tunnel = talloc_zero(request, eap_tunnel_data_t); tunnel->tls_session = tls_session; /* * Associate the callback with the request. */ ret = request_data_add(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK, tunnel, false, false, false); fr_cond_assert(ret == 0); /* * rlm_eap.c has taken care of associating the eap_session * with the fake request. * * So we associate the fake request with this request. */ ret = request_data_add(request, request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK, fake, true, false, false); fr_cond_assert(ret == 0); fake = NULL; /* * Didn't authenticate the packet, but we're proxying it. */ code = FR_CODE_STATUS_CLIENT; } else #endif /* WITH_PROXY */ { REDEBUG("No tunneled reply was found, and the request was not proxied: rejecting the user"); code = FR_CODE_ACCESS_REJECT; } break; default: /* * Returns RLM_MODULE_FOO, and we want to return FR_FOO */ rcode = process_reply(eap_session, tls_session, request, fake->reply); switch (rcode) { case RLM_MODULE_REJECT: code = FR_CODE_ACCESS_REJECT; break; case RLM_MODULE_HANDLED: code = FR_CODE_ACCESS_CHALLENGE; break; case RLM_MODULE_OK: code = FR_CODE_ACCESS_ACCEPT; break; default: code = FR_CODE_ACCESS_REJECT; break; } break; } talloc_free(fake); return code; }
/* * Initialize the request. */ static RADIUS_PACKET *request_init(char const *filename) { FILE *fp; fr_cursor_t cursor; VALUE_PAIR *vp; bool filedone = false; RADIUS_PACKET *request; /* * Determine where to read the VP's from. */ if (filename) { fp = fopen(filename, "r"); if (!fp) { ERROR("Error opening %s: %s", filename, fr_syserror(errno)); return NULL; } } else { fp = stdin; } request = fr_radius_alloc(NULL, false); /* * Read the VP's. */ if (fr_pair_list_afrom_file(NULL, dict_dhcpv4, &request->vps, fp, &filedone) < 0) { fr_perror("dhcpclient"); fr_radius_packet_free(&request); if (fp != stdin) fclose(fp); return NULL; } /* * Fix / set various options */ for (vp = fr_cursor_init(&cursor, &request->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Xlat expansions are not supported. Convert xlat to value box (if possible). */ if (vp->type == VT_XLAT) { fr_type_t type = vp->da->type; if (fr_value_box_from_str(vp, &vp->data, &type, NULL, vp->xlat, -1, '\0', false) < 0) { fr_perror("dhcpclient"); fr_radius_packet_free(&request); if (fp != stdin) fclose(fp); return NULL; } vp->type = VT_DATA; } /* * Allow to set packet type using DHCP-Message-Type */ if (vp->da == attr_dhcp_message_type) { request->code = vp->vp_uint8; /* * Allow it to set the packet type in * the attributes read from the file. * (this takes precedence over the command argument.) */ } else if (vp->da == attr_packet_type) { request->code = vp->vp_uint32; } else if (vp->da == attr_packet_dst_port) { request->dst_port = vp->vp_uint16; } else if ((vp->da == attr_packet_dst_ip_address) || (vp->da == attr_packet_dst_ipv6_address)) { memcpy(&request->dst_ipaddr, &vp->vp_ip, sizeof(request->src_ipaddr)); } else if (vp->da == attr_packet_src_port) { request->src_port = vp->vp_uint16; } else if ((vp->da == attr_packet_src_ip_address) || (vp->da == attr_packet_src_ipv6_address)) { memcpy(&request->src_ipaddr, &vp->vp_ip, sizeof(request->src_ipaddr)); } /* switch over the attribute */ } /* loop over the VP's we read in */ if (fp != stdin) fclose(fp); /* * And we're done. */ return request; }
/* * For a client, receive a DHCP packet from a raw packet * socket. Make sure it matches the ongoing request. * * FIXME: split this into two, recv_raw_packet, and verify(packet, original) */ RADIUS_PACKET *fr_dhcv4_raw_packet_recv(int sockfd, struct sockaddr_ll *link_layer, RADIUS_PACKET *request) { VALUE_PAIR *vp; RADIUS_PACKET *packet; uint8_t const *code; uint32_t magic, xid; ssize_t data_len; uint8_t *raw_packet; ethernet_header_t *eth_hdr; ip_header_t *ip_hdr; udp_header_t *udp_hdr; dhcp_packet_t *dhcp_hdr; uint16_t udp_src_port; uint16_t udp_dst_port; size_t dhcp_data_len; socklen_t sock_len; packet = fr_radius_alloc(NULL, false); if (!packet) { fr_strerror_printf("Failed allocating packet"); return NULL; } raw_packet = talloc_zero_array(packet, uint8_t, MAX_PACKET_SIZE); if (!raw_packet) { fr_strerror_printf("Out of memory"); fr_radius_packet_free(&packet); return NULL; } packet->sockfd = sockfd; /* a packet was received (but maybe it is not for us) */ sock_len = sizeof(struct sockaddr_ll); data_len = recvfrom(sockfd, raw_packet, MAX_PACKET_SIZE, 0, (struct sockaddr *)link_layer, &sock_len); uint8_t data_offset = ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE; /* DHCP data datas after Ethernet, IP, UDP */ if (data_len <= data_offset) DISCARD_RP("Payload (%d) smaller than required for layers 2+3+4", (int)data_len); /* map raw packet to packet header of the different layers (Ethernet, IP, UDP) */ eth_hdr = (ethernet_header_t *)raw_packet; /* * Check Ethernet layer data (L2) */ if (ntohs(eth_hdr->ether_type) != ETH_TYPE_IP) DISCARD_RP("Ethernet type (%d) != IP", ntohs(eth_hdr->ether_type)); /* * If Ethernet destination is not broadcast (ff:ff:ff:ff:ff:ff) * Check if it matches the source HW address used (DHCP-Client-Hardware-Address = 267) */ if ((memcmp(ð_bcast, ð_hdr->ether_dst, ETH_ADDR_LEN) != 0) && (vp = fr_pair_find_by_da(request->vps, attr_dhcp_client_hardware_address, TAG_ANY)) && ((vp->vp_type == FR_TYPE_ETHERNET) && (memcmp(vp->vp_ether, ð_hdr->ether_dst, ETH_ADDR_LEN) != 0))) { /* No match. */ DISCARD_RP("Ethernet destination (%pV) is not broadcast and doesn't match request source (%pV)", fr_box_ether(eth_hdr->ether_dst), &vp->data); } /* * Ethernet is OK. Now look at IP. */ ip_hdr = (ip_header_t *)(raw_packet + ETH_HDR_SIZE); /* * Check IPv4 layer data (L3) */ if (ip_hdr->ip_p != IPPROTO_UDP) DISCARD_RP("IP protocol (%d) != UDP", ip_hdr->ip_p); /* * note: checking the destination IP address is not * useful (it would be the offered IP address - which we * don't know beforehand, or the broadcast address). */ /* * Now check UDP. */ udp_hdr = (udp_header_t *)(raw_packet + ETH_HDR_SIZE + IP_HDR_SIZE); /* * Check UDP layer data (L4) */ udp_src_port = ntohs(udp_hdr->src); udp_dst_port = ntohs(udp_hdr->dst); /* * Check DHCP layer data */ dhcp_data_len = data_len - data_offset; if (dhcp_data_len < MIN_PACKET_SIZE) DISCARD_RP("DHCP packet is too small (%zu < %i)", dhcp_data_len, MIN_PACKET_SIZE); if (dhcp_data_len > MAX_PACKET_SIZE) DISCARD_RP("DHCP packet is too large (%zu > %i)", dhcp_data_len, MAX_PACKET_SIZE); dhcp_hdr = (dhcp_packet_t *)(raw_packet + ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE); if (dhcp_hdr->htype != 1) DISCARD_RP("DHCP hardware type (%d) != Ethernet (1)", dhcp_hdr->htype); if (dhcp_hdr->hlen != 6) DISCARD_RP("DHCP hardware address length (%d) != 6", dhcp_hdr->hlen); magic = ntohl(dhcp_hdr->option_format); if (magic != DHCP_OPTION_MAGIC_NUMBER) DISCARD_RP("DHCP magic cookie (0x%04x) != DHCP (0x%04x)", magic, DHCP_OPTION_MAGIC_NUMBER); /* * Reply transaction id must match value from request. */ xid = ntohl(dhcp_hdr->xid); if (xid != (uint32_t)request->id) DISCARD_RP("DHCP transaction ID (0x%04x) != xid from request (0x%04x)", xid, request->id) /* all checks ok! this is a DHCP reply we're interested in. */ packet->data_len = dhcp_data_len; packet->data = talloc_memdup(packet, raw_packet + data_offset, dhcp_data_len); TALLOC_FREE(raw_packet); packet->id = xid; code = fr_dhcpv4_packet_get_option((dhcp_packet_t const *) packet->data, packet->data_len, attr_dhcp_message_type); if (!code) { fr_strerror_printf("No message-type option was found in the packet"); fr_radius_packet_free(&packet); return NULL; } if ((code[1] < 1) || (code[2] == 0) || (code[2] > 8)) { fr_strerror_printf("Unknown value for message-type option"); fr_radius_packet_free(&packet); return NULL; } packet->code = code[2]; /* * Create a unique vector from the MAC address and the * DHCP opcode. This is a hack for the RADIUS * infrastructure in the rest of the server. * * Note: packet->data[2] == 6, which is smaller than * sizeof(packet->vector) * * FIXME: Look for client-identifier in packet, * and use that, too? */ memset(packet->vector, 0, sizeof(packet->vector)); memcpy(packet->vector, packet->data + 28, packet->data[2]); packet->vector[packet->data[2]] = packet->code & 0xff; packet->src_port = udp_src_port; packet->dst_port = udp_dst_port; packet->src_ipaddr.af = AF_INET; packet->src_ipaddr.addr.v4.s_addr = ip_hdr->ip_src.s_addr; packet->dst_ipaddr.af = AF_INET; packet->dst_ipaddr.addr.v4.s_addr = ip_hdr->ip_dst.s_addr; return packet; }
/* * Check if an incoming request is "ok" * * It takes packets, not requests. It sees if the packet looks * OK. If so, it does a number of sanity checks on it. */ static int arp_socket_recv(rad_listen_t *listener) { int ret; arp_socket_t *sock = listener->data; pcap_t *handle = sock->lsock.pcap->handle; const uint8_t *data; struct pcap_pkthdr *header; ssize_t link_len; arp_over_ether_t const *arp; RADIUS_PACKET *packet; ret = pcap_next_ex(handle, &header, &data); if (ret == 0) { DEBUG("No packet retrieved from pcap."); return 0; /* no packet */ } if (ret < 0) { ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle)); return 0; } link_len = fr_pcap_link_layer_offset(data, header->caplen, sock->lsock.pcap->link_layer); if (link_len < 0) { PERROR("Failed determining link layer header offset"); return 0; } /* * Silently ignore it if it's too small to be ARP. * * This can happen when pcap gets overloaded and starts truncating packets. */ if (header->caplen < (link_len + sizeof(*arp))) { ERROR("Packet too small, we require at least %zu bytes, got %i bytes", link_len + sizeof(*arp), header->caplen); return 0; } data += link_len; arp = (arp_over_ether_t const *) data; if (ntohs(arp->htype) != ARPHRD_ETHER) return 0; if (ntohs(arp->ptype) != 0x0800) return 0; if (arp->hlen != ETHER_ADDR_LEN) return 0; /* FIXME: malformed error */ if (arp->plen != 4) return 0; /* FIXME: malformed packet error */ packet = talloc_zero(NULL, RADIUS_PACKET); if (!packet) return 0; packet->dst_port = 1; /* so it's not a "fake" request */ packet->data_len = header->caplen - link_len; packet->data = talloc_memdup(packet, arp, packet->data_len); talloc_set_type(packet->data, uint8_t); DEBUG("ARP received on interface %s", sock->lsock.interface); if (!request_receive(NULL, listener, packet, &sock->client, arp_process)) { fr_radius_packet_free(&packet); return 0; } return 1; }