/** Setup an LDAP sync request * * Allocates a request, with request/reply packets. * * Sets various fields in the request->packet with information from the file descriptor * we received from libldap. * * @param[in] listen The common listener encapsulating the libldap fd. * @param[in] inst of the proto_ldap_sync module. * @param[in] sync_id the unique identifier of the sync. * @return * - A new request on success. * - NULL on error. */ static REQUEST *proto_ldap_request_setup(rad_listen_t *listen, proto_ldap_inst_t *inst, int sync_id) { TALLOC_CTX *ctx; RADIUS_PACKET *packet; REQUEST *request; ctx = talloc_pool(NULL, main_config->talloc_pool_size); if (!ctx) return NULL; talloc_set_name_const(ctx, "ldap_inst_pool"); packet = fr_radius_alloc(ctx, false); packet->sockfd = listen->fd; packet->id = sync_id; packet->src_ipaddr = inst->dst_ipaddr; packet->src_port = inst->dst_port; packet->dst_ipaddr = inst->src_ipaddr; packet->dst_port = inst->src_port; gettimeofday(&packet->timestamp, NULL); request = request_setup(ctx, listen, packet, inst->client, NULL); if (!request) return NULL; request->process = request_queued; return request; }
/** Allocate a new RADIUS_PACKET response * * @param ctx the context in which the packet is allocated. May be NULL if * the packet is not associated with a REQUEST. * @param packet The request packet. * @return * - New RADIUS_PACKET. * - NULL on error. */ RADIUS_PACKET *fr_radius_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet) { RADIUS_PACKET *reply; if (!packet) return NULL; reply = fr_radius_alloc(ctx, false); if (!reply) return NULL; /* * Initialize the fields from the request. */ reply->sockfd = packet->sockfd; reply->dst_ipaddr = packet->src_ipaddr; reply->src_ipaddr = packet->dst_ipaddr; reply->dst_port = packet->src_port; reply->src_port = packet->dst_port; reply->if_index = packet->if_index; reply->id = packet->id; reply->code = 0; /* UNKNOWN code */ memset(reply->vector, 0, sizeof(reply->vector)); reply->vps = NULL; reply->data = NULL; reply->data_len = 0; #ifdef WITH_TCP reply->proto = packet->proto; #endif return reply; }
/** Duplicate a RADIUS_PACKET * * @param ctx the context in which the packet is allocated. May be NULL if * the packet is not associated with a REQUEST. * @param in The packet to copy * @return * - New RADIUS_PACKET. * - NULL on error. */ RADIUS_PACKET *fr_radius_copy(TALLOC_CTX *ctx, RADIUS_PACKET const *in) { RADIUS_PACKET *out; out = fr_radius_alloc(ctx, false); if (!out) return NULL; /* * Bootstrap by copying everything. */ memcpy(out, in, sizeof(*out)); /* * Then reset necessary fields */ out->sockfd = -1; out->data = NULL; out->data_len = 0; if (fr_pair_list_copy(out, &out->vps, in->vps) < 0) { talloc_free(out); return NULL; } return out; }
/** Allocate a new request using values from radsnmp config * * @param conf radsnmp config. * @param fd the request will be sent on. * @return new request. */ static RADIUS_PACKET *radsnmp_alloc(radsnmp_conf_t *conf, int fd) { RADIUS_PACKET *request; request = fr_radius_alloc(conf, true); request->code = conf->code; request->id = conf->last_used_id; conf->last_used_id = (conf->last_used_id + 1) & UINT8_MAX; memcpy(&request->dst_ipaddr, &conf->server_ipaddr, sizeof(request->dst_ipaddr)); request->dst_port = conf->server_port; request->sockfd = fd; return request; }
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; }
int proxy_tls_recv(rad_listen_t *listener) { listen_socket_t *sock = listener->data; char buffer[256]; RADIUS_PACKET *packet; uint8_t *data; ssize_t data_len; if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0; DEBUG3("Proxy SSL socket has data to read"); PTHREAD_MUTEX_LOCK(&sock->mutex); data_len = proxy_tls_read(listener); PTHREAD_MUTEX_UNLOCK(&sock->mutex); if (data_len < 0) { DEBUG("Closing TLS socket to home server"); PTHREAD_MUTEX_LOCK(&sock->mutex); tls_socket_close(listener); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } if (data_len == 0) return 0; /* not done yet */ data = sock->data; packet = fr_radius_alloc(sock, false); packet->sockfd = listener->fd; packet->src_ipaddr = sock->other_ipaddr; packet->src_port = sock->other_port; packet->dst_ipaddr = sock->my_ipaddr; packet->dst_port = sock->my_port; packet->code = data[0]; packet->id = data[1]; packet->data_len = data_len; packet->data = talloc_array(packet, uint8_t, packet->data_len); memcpy(packet->data, data, packet->data_len); memcpy(packet->vector, packet->data + 4, 16); /* * FIXME: Client MIB updates? */ switch (packet->code) { case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCESS_CHALLENGE: case PW_CODE_ACCESS_REJECT: break; #ifdef WITH_ACCOUNTING case PW_CODE_ACCOUNTING_RESPONSE: break; #endif default: /* * FIXME: Update MIB for packet types? */ ERROR("Invalid packet code %d sent to a proxy port " "from home server %s port %d - ID %d : IGNORED", packet->code, fr_inet_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), packet->src_port, packet->id); fr_radius_free(&packet); return 0; } if (!request_proxy_reply(packet)) { fr_radius_free(&packet); return 0; } return 1; }
static int tls_socket_recv(rad_listen_t *listener) { bool doing_init = false; ssize_t rcode; RADIUS_PACKET *packet; REQUEST *request; listen_socket_t *sock = listener->data; fr_tls_status_t status; RADCLIENT *client = sock->client; if (!sock->packet) { sock->packet = fr_radius_alloc(sock, false); if (!sock->packet) return 0; sock->packet->sockfd = listener->fd; sock->packet->src_ipaddr = sock->other_ipaddr; sock->packet->src_port = sock->other_port; sock->packet->dst_ipaddr = sock->my_ipaddr; sock->packet->dst_port = sock->my_port; if (sock->request) sock->request->packet = talloc_steal(sock->request, sock->packet); } /* * Allocate a REQUEST for debugging, and initialize the TLS session. */ if (!sock->request) { sock->request = request = request_alloc(sock); if (!sock->request) { ERROR("Out of memory"); return 0; } rad_assert(request->packet == NULL); rad_assert(sock->packet != NULL); request->packet = talloc_steal(request, sock->packet); request->component = "<tls-connect>"; request->reply = fr_radius_alloc(request, false); if (!request->reply) return 0; rad_assert(sock->tls_session == NULL); sock->tls_session = tls_session_init_server(sock, listener->tls, sock->request, listener->tls->require_client_cert); if (!sock->tls_session) { TALLOC_FREE(sock->request); sock->packet = NULL; return 0; } SSL_set_ex_data(sock->tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request); doing_init = true; } rad_assert(sock->request != NULL); rad_assert(sock->request->packet != NULL); rad_assert(sock->packet != NULL); rad_assert(sock->tls_session != NULL); request = sock->request; RDEBUG3("Reading from socket %d", request->packet->sockfd); PTHREAD_MUTEX_LOCK(&sock->mutex); rcode = read(request->packet->sockfd, sock->tls_session->dirty_in.data, sizeof(sock->tls_session->dirty_in.data)); if ((rcode < 0) && (errno == ECONNRESET)) { do_close: PTHREAD_MUTEX_UNLOCK(&sock->mutex); DEBUG("Closing TLS socket from client port %u", sock->other_port); tls_socket_close(listener); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } if (rcode < 0) { RDEBUG("Error reading TLS socket: %s", fr_syserror(errno)); goto do_close; } /* * Normal socket close. */ if (rcode == 0) goto do_close; sock->tls_session->dirty_in.used = rcode; dump_hex("READ FROM SSL", sock->tls_session->dirty_in.data, sock->tls_session->dirty_in.used); /* * Catch attempts to use non-SSL. */ if (doing_init && (sock->tls_session->dirty_in.data[0] != handshake)) { RDEBUG("Non-TLS data sent to TLS socket: closing"); goto do_close; } /* * If we need to do more initialization, do that here. */ if (!SSL_is_init_finished(sock->tls_session->ssl)) { if (!tls_handshake_recv(request, sock->tls_session)) { RDEBUG("FAILED in TLS handshake receive"); goto do_close; } /* * More ACK data to send. Do so. */ if (sock->tls_session->dirty_out.used > 0) { tls_socket_write(listener, request); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } /* * FIXME: Run the request through a virtual * server in order to see if we like the * certificate presented by the client. */ } /* * Try to get application data. */ status = tls_application_data(sock->tls_session, request); RDEBUG("Application data status %d", status); if (status == FR_TLS_RECORD_FRAGMENT_MORE) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } if (sock->tls_session->clean_out.used == 0) { PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; } /* * We now have a bunch of application data. */ dump_hex("TUNNELED DATA > ", sock->tls_session->clean_out.data, sock->tls_session->clean_out.used); /* * If the packet is a complete RADIUS packet, return it to * the caller. Otherwise... */ if ((sock->tls_session->clean_out.used < 20) || (((sock->tls_session->clean_out.data[2] << 8) | sock->tls_session->clean_out.data[3]) != (int) sock->tls_session->clean_out.used)) { RDEBUG("Received bad packet: Length %zd contents %d", sock->tls_session->clean_out.used, (sock->tls_session->clean_out.data[2] << 8) | sock->tls_session->clean_out.data[3]); goto do_close; } packet = sock->packet; packet->data = talloc_array(packet, uint8_t, sock->tls_session->clean_out.used); packet->data_len = sock->tls_session->clean_out.used; sock->tls_session->record_to_buff(&sock->tls_session->clean_out, packet->data, packet->data_len); packet->vps = NULL; PTHREAD_MUTEX_UNLOCK(&sock->mutex); if (!fr_radius_ok(packet, 0, NULL)) { if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); DEBUG("Closing TLS socket from client"); PTHREAD_MUTEX_LOCK(&sock->mutex); tls_socket_close(listener); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; /* do_close unlocks the mutex */ } /* * Copied from src/lib/radius.c, fr_radius_recv(); */ if (fr_debug_lvl) { char host_ipaddr[INET6_ADDRSTRLEN]; if (is_radius_code(packet->code)) { RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d", fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->id, (int) packet->data_len); } else { RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), packet->src_port, packet->code, packet->id, (int) packet->data_len); } } FR_STATS_INC(auth, total_requests); return 1; }
/* * 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; }
/* * 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; }
/* * 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; }
static int dhcprelay_process_client_request(REQUEST *request) { int rcode; uint8_t maxhops = 16; VALUE_PAIR *vp, *giaddr; dhcp_socket_t *sock; RADIUS_PACKET *packet; rad_assert(request->packet->data[0] == 1); /* * Do the forward by ourselves, do not rely on dhcp_socket_send() */ request->reply->code = 0; /* * It's invalid to have giaddr=0 AND a relay option */ giaddr = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (giaddr && (giaddr->vp_ipv4addr == htonl(INADDR_ANY)) && fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 82, TAG_ANY)) { /* DHCP-Relay-Agent-Information */ RDEBUG2("Received packet with giaddr = 0 and containing relay option: Discarding packet"); return 1; } /* * RFC 1542 (BOOTP), page 15 * * Drop requests if hop-count > 16 or admin specified another value */ if ((vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 271, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */ maxhops = vp->vp_uint32; } vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 259, TAG_ANY); /* DHCP-Hop-Count */ rad_assert(vp != NULL); if (vp->vp_uint8 > maxhops) { RDEBUG2("Number of hops is greater than %d: not relaying", maxhops); return 1; } else { /* Increment hop count */ vp->vp_uint8++; } sock = request->listener->data; /* * Don't muck with the original request packet. That's * bad form. Plus, dhcp_encode() does nothing if * packet->data is already set. */ packet = fr_radius_alloc(request, false); rcode = -1; /* * Forward the request to the next server using the * incoming request as a template. */ packet->code = request->packet->code; packet->sockfd = request->packet->sockfd; /* * Forward the request to the next server using the * incoming request as a template. */ /* set SRC ipaddr/port to the listener ipaddr/port */ packet->src_ipaddr.af = AF_INET; packet->src_ipaddr.addr.v4.s_addr = sock->lsock.my_ipaddr.addr.v4.s_addr; packet->src_port = sock->lsock.my_port; vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY); /* DHCP-Relay-To-IP-Address */ rad_assert(vp != NULL); /* set DEST ipaddr/port to the next server ipaddr/port */ packet->dst_ipaddr.af = AF_INET; packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; packet->dst_port = sock->lsock.my_port; packet->vps = request->packet->vps; /* hackity hack */ /* * Relaying is not proxying, we just forward it on and forget * about it, not sending a response to the DHCP client. */ dhcp_packet_debug(request, packet, false); if (fr_dhcpv4_packet_encode(packet) < 0) { RPERROR("Failed encoding DHCP packet"); goto error; } rcode = fr_dhcpv4_udp_packet_send(packet); error: packet->vps = NULL; talloc_free(packet); return rcode; }
/* * We've seen a reply from a server. * i.e. we're a relay. */ static int dhcprelay_process_server_reply(REQUEST *request) { int rcode; VALUE_PAIR *vp, *giaddr; dhcp_socket_t *sock; RADIUS_PACKET *packet; rad_assert(request->packet->data[0] == 2); /* * Do the forward by ourselves, do not rely on dhcp_socket_send() */ request->reply->code = 0; sock = request->listener->data; /* * Check that packet is for us. */ giaddr = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ /* --with-udpfromto is needed just for the following test */ if (!giaddr || giaddr->vp_ipv4addr != request->packet->dst_ipaddr.addr.v4.s_addr) { RDEBUG2("Packet received from server was not for us (was for 0x%x). Discarding packet", ntohl(request->packet->dst_ipaddr.addr.v4.s_addr)); return 1; } /* * Don't muck with the original request packet. That's * bad form. Plus, dhcp_encode() does nothing if * packet->data is already set. */ packet = fr_radius_alloc(request, false); rcode = -1; /* * Forward the request to the next server using the * incoming request as a template. */ packet->code = request->packet->code; packet->sockfd = request->packet->sockfd; /* set SRC ipaddr/port to the listener ipaddr/port */ packet->src_ipaddr.af = AF_INET; packet->src_port = sock->lsock.my_port; /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */ packet->dst_ipaddr.af = AF_INET; /* * We're a relay, and send the reply to giaddr. */ packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); packet->dst_port = request->packet->dst_port; /* server port */ vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY); /* DHCP-Relay-To-IP-Address */ if (vp) { RDEBUG("DHCP: response will be relayed to previous gateway"); packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; giaddr->vp_ipv4addr = vp->vp_ipv4addr; } else if ((packet->code == FR_DHCP_NAK) || !sock->src_interface || ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) /* DHCP-Flags */ && (vp->vp_uint32 & 0x8000) && ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipv4addr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG2("Response will be broadcast"); packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); } else if ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { /* * RFC 2131, page 23 * * Unicast to * - ciaddr if present * otherwise to yiaddr */ packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; } else { vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { RPEDEBUG("Failed to find IP Address for request"); goto error; } RDEBUG2("Response will be unicast to &DHCP-Your-IP-Address"); packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * When sending a DHCP_OFFER, make sure our ARP table * contains an entry for the client IP address, or else * packet may not be forwarded if it was the first time * the client was requesting an IP address. */ if (request->packet->code == FR_DHCP_OFFER) { VALUE_PAIR *hwvp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (hwvp == NULL) { RDEBUG2("DHCP_OFFER packet received with no Client Hardware Address. " "Discarding packet"); goto error; } if (fr_dhcpv4_udp_add_arp_entry(request->packet->sockfd, sock->src_interface, &vp->vp_ip, hwvp->vp_ether) < 0) { REDEBUG("Failed adding ARP entry"); goto error; } } } packet->vps = request->packet->vps; /* hackity hack */ /* * Our response doesn't go through process.c */ dhcp_packet_debug(request, packet, false); if (fr_dhcpv4_packet_encode(packet) < 0) { RPERROR("Failed encoding DHCP packet"); goto error; } rcode = fr_dhcpv4_udp_packet_send(request->packet); error: packet->vps = NULL; talloc_free(packet); return rcode; }