/* * Compare the request with the "reply" part in the * huntgroup, which normally only contains username or group. * At least one of the "reply" items has to match. */ static int hunt_paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check) { vp_cursor_t cursor; VALUE_PAIR *check_item; VALUE_PAIR *tmp; int result = -1; if (!check) return 0; for (check_item = fr_cursor_init(&cursor, &check); check_item && (result != 0); check_item = fr_cursor_next(&cursor)) { /* FIXME: fr_pair_list_copy should be removed once VALUE_PAIRs are no longer in linked lists */ tmp = fr_pair_copy(request, check_item); tmp->op = check_item->op; result = paircompare(req, request, check_item, NULL); fr_pair_list_free(&tmp); } return result; }
/* * Process an EAP request */ fr_tls_status_t eaptls_process(eap_handler_t *handler) { tls_session_t *tls_session = (tls_session_t *) handler->opaque; EAPTLS_PACKET *tlspacket; fr_tls_status_t status; REQUEST *request = handler->request; if (!request) return FR_TLS_FAIL; RDEBUG2("Continuing EAP-TLS"); SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request); if (handler->certs) fr_pair_add(&request->packet->vps, fr_pair_list_copy(request->packet, handler->certs)); /* * This case is when SSL generates Alert then we * send that alert to the client and then send the EAP-Failure */ status = eaptls_verify(handler); if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { REDEBUG("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); } else { RDEBUG2("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); } switch (status) { default: case FR_TLS_INVALID: case FR_TLS_FAIL: /* * Success means that we're done the initial * handshake. For TTLS, this means send stuff * back to the client, and the client sends us * more tunneled data. */ case FR_TLS_SUCCESS: goto done; /* * Normal TLS request, continue with the "get rest * of fragments" phase. */ case FR_TLS_REQUEST: eaptls_request(handler->eap_ds, tls_session); status = FR_TLS_HANDLED; goto done; /* * The handshake is done, and we're in the "tunnel * data" phase. */ case FR_TLS_OK: RDEBUG2("Done initial handshake"); /* * Get the rest of the fragments. */ case FR_TLS_FIRST_FRAGMENT: case FR_TLS_MORE_FRAGMENTS: case FR_TLS_LENGTH_INCLUDED: break; } /* * Extract the TLS packet from the buffer. */ if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) { status = FR_TLS_FAIL; goto done; } /* * Get the session struct from the handler * * update the dirty_in buffer * * NOTE: This buffer will contain partial data when M bit is set. * * CAUTION while reinitializing this buffer, it should be * reinitialized only when this M bit is NOT set. */ if (tlspacket->dlen != (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) { talloc_free(tlspacket); REDEBUG("Exceeded maximum record size"); status = FR_TLS_FAIL; goto done; } /* * No longer needed. */ talloc_free(tlspacket); /* * SSL initalization is done. Return. * * The TLS data will be in the tls_session structure. */ if (SSL_is_init_finished(tls_session->ssl)) { /* * The initialization may be finished, but if * there more fragments coming, then send ACK, * and get the caller to continue the * conversation. */ if ((status == FR_TLS_MORE_FRAGMENTS) || (status == FR_TLS_FIRST_FRAGMENT)) { /* * Send the ACK. */ eaptls_send_ack(handler, tls_session->peap_flag); RDEBUG2("Init is done, but tunneled data is fragmented"); status = FR_TLS_HANDLED; goto done; } status = tls_application_data(tls_session, request); goto done; } /* * Continue the handshake. */ status = eaptls_operation(status, handler); if (status == FR_TLS_SUCCESS) { #define MAX_SESSION_SIZE (256) size_t size; VALUE_PAIR *vps; char buffer[2 * MAX_SESSION_SIZE + 1]; /* * Restore the cached VPs before processing the * application data. */ size = tls_session->ssl->session->session_id_length; if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE; fr_bin2hex(buffer, tls_session->ssl->session->session_id, size); vps = SSL_SESSION_get_ex_data(tls_session->ssl->session, fr_tls_ex_index_vps); if (!vps) { RWDEBUG("No information in cached session %s", buffer); } else { vp_cursor_t cursor; VALUE_PAIR *vp; RDEBUG("Adding cached attributes from session %s", buffer); /* * The cbtls_get_session() function doesn't have * access to sock->certs or handler->certs, which * is where the certificates normally live. So * the certs are all in the VPS list here, and * have to be manually extracted. */ RINDENT(); for (vp = fr_cursor_init(&cursor, &vps); vp; vp = fr_cursor_next(&cursor)) { /* * TLS-* attrs get added back to * the request list. */ if ((vp->da->vendor == 0) && (vp->da->attr >= PW_TLS_CERT_SERIAL) && (vp->da->attr <= PW_TLS_CLIENT_CERT_SUBJECT_ALT_NAME_UPN)) { /* * Certs already exist. Don't re-add them. */ if (!handler->certs) { rdebug_pair(L_DBG_LVL_2, request, vp, "request:"); fr_pair_add(&request->packet->vps, fr_pair_copy(request->packet, vp)); } } else { rdebug_pair(L_DBG_LVL_2, request, vp, "reply:"); fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp)); } } REXDENT(); } } done: SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL); return status; }
/* * Do the statistics */ static rlm_rcode_t CC_HINT(nonnull) mod_stats(void *instance, void *thread, REQUEST *request) { int i; uint32_t stats_type; rlm_stats_thread_t *t = thread; rlm_stats_t *inst = instance; VALUE_PAIR *vp; rlm_stats_data_t mydata, *stats; fr_cursor_t cursor; char buffer[64]; uint64_t local_stats[sizeof(inst->stats) / sizeof(inst->stats[0])]; /* * Increment counters only in "send foo" sections. * * i.e. only when we have a reply to send. */ if (request->request_state == REQUEST_SEND) { int src_code, dst_code; src_code = request->packet->code; if (src_code >= FR_MAX_PACKET_CODE) src_code = 0; dst_code = request->reply->code; if (dst_code >= FR_MAX_PACKET_CODE) dst_code = 0; t->stats[src_code]++; t->stats[dst_code]++; /* * Update source statistics */ mydata.ipaddr = request->packet->src_ipaddr; stats = rbtree_finddata(t->src, &mydata); if (!stats) { MEM(stats = talloc_zero(t, rlm_stats_data_t)); stats->ipaddr = request->packet->src_ipaddr; stats->created = request->async->recv_time; (void) rbtree_insert(t->src, stats); } stats->last_packet = request->async->recv_time; stats->stats[src_code]++; stats->stats[dst_code]++; /* * Update destination statistics */ mydata.ipaddr = request->packet->dst_ipaddr; stats = rbtree_finddata(t->dst, &mydata); if (!stats) { MEM(stats = talloc_zero(t, rlm_stats_data_t)); stats->ipaddr = request->packet->dst_ipaddr; stats->created = request->async->recv_time; (void) rbtree_insert(t->dst, stats); } stats->last_packet = request->async->recv_time; stats->stats[src_code]++; stats->stats[dst_code]++; /* * @todo - periodically clean up old entries. */ if ((t->last_global_update + NANOSEC) > request->async->recv_time) { return RLM_MODULE_UPDATED; } t->last_global_update = request->async->recv_time; pthread_mutex_lock(&inst->mutex); for (i = 0; i < FR_MAX_PACKET_CODE; i++) { inst->stats[i] += t->stats[i]; t->stats[i] = 0; } pthread_mutex_unlock(&inst->mutex); return RLM_MODULE_UPDATED; } /* * Ignore "authenticate" and anything other than Status-Server */ if ((request->request_state != REQUEST_RECV) || (request->packet->code != FR_CODE_STATUS_SERVER)) { return RLM_MODULE_NOOP; } vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_type, TAG_ANY); if (!vp) { stats_type = FR_FREERADIUS_STATS4_TYPE_VALUE_GLOBAL; } else { stats_type = vp->vp_uint32; } /* * Create attributes based on the statistics. */ fr_cursor_init(&cursor, &request->reply->vps); MEM(pair_update_reply(&vp, attr_freeradius_stats4_type) >= 0); vp->vp_uint32 = stats_type; switch (stats_type) { case FR_FREERADIUS_STATS4_TYPE_VALUE_GLOBAL: /* global */ /* * Merge our stats with the global stats, and then copy * the global stats to a thread-local variable. * * The copy helps minimize mutex contention. */ pthread_mutex_lock(&inst->mutex); for (i = 0; i < FR_MAX_PACKET_CODE; i++) { inst->stats[i] += t->stats[i]; t->stats[i] = 0; } memcpy(&local_stats, inst->stats, sizeof(inst->stats)); pthread_mutex_unlock(&inst->mutex); vp = NULL; break; case FR_FREERADIUS_STATS4_TYPE_VALUE_CLIENT: /* src */ vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY); if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY); if (!vp) return RLM_MODULE_NOOP; mydata.ipaddr = vp->vp_ip; coalesce(local_stats, t, offsetof(rlm_stats_thread_t, src), &mydata); break; case FR_FREERADIUS_STATS4_TYPE_VALUE_LISTENER: /* dst */ vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY); if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY); if (!vp) return RLM_MODULE_NOOP; mydata.ipaddr = vp->vp_ip; coalesce(local_stats, t, offsetof(rlm_stats_thread_t, dst), &mydata); break; default: REDEBUG("Invalid value '%d' for FreeRADIUS-Stats4-type", stats_type); return RLM_MODULE_FAIL; } if (vp ) { vp = fr_pair_copy(request->reply, vp); if (vp) { fr_cursor_append(&cursor, vp); (void) fr_cursor_tail(&cursor); } } strcpy(buffer, "FreeRADIUS-Stats4-"); for (i = 0; i < FR_MAX_PACKET_CODE; i++) { fr_dict_attr_t const *da; if (!local_stats[i]) continue; strlcpy(buffer + 18, fr_packet_codes[i], sizeof(buffer) - 18); da = fr_dict_attr_by_name(dict_radius, buffer); if (!da) continue; vp = fr_pair_afrom_da(request->reply, da); if (!vp) return RLM_MODULE_FAIL; vp->vp_uint64 = local_stats[i]; fr_cursor_append(&cursor, vp); (void) fr_cursor_tail(&cursor); } return RLM_MODULE_OK; }
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; }
static rlm_rcode_t dhcp_process(REQUEST *request) { rlm_rcode_t rcode; unsigned int i; VALUE_PAIR *vp; dhcp_socket_t *sock; /* * If there's a giaddr, save it as the Relay-IP-Address * in the response. That way the later code knows where * to send the reply. */ vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { VALUE_PAIR *relay; /* DHCP-Relay-IP-Address */ MEM(relay = fr_pair_afrom_num(request->reply, DHCP_MAGIC_VENDOR, 222)); relay->vp_ipv4addr = vp->vp_ipv4addr; fr_pair_add(&request->reply->vps, relay); } vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */ if (vp) { fr_dict_enum_t *dv = fr_dict_enum_by_value(vp->da, &vp->data); if (dv) { CONF_SECTION *server, *unlang; RDEBUG("Trying sub-section dhcp %s {...}", dv->alias); server = cf_item_to_section(cf_parent(request->listener->cs)); unlang = cf_section_find(server, "dhcp", dv->alias); rcode = unlang_interpret(request, unlang, RLM_MODULE_NOOP); } else { REDEBUG("Unknown DHCP-Message-Type %d", vp->vp_uint8); rcode = RLM_MODULE_FAIL; } } else { REDEBUG("Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */ if (vp) { request->reply->code = vp->vp_uint8; } else switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: if (request->packet->code == FR_DHCP_DISCOVER) { request->reply->code = FR_DHCP_OFFER; break; } else if (request->packet->code == FR_DHCP_REQUEST) { request->reply->code = FR_DHCP_ACK; break; } request->reply->code = FR_DHCP_NAK; break; default: case RLM_MODULE_REJECT: case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: if (request->packet->code == FR_DHCP_DISCOVER) { request->reply->code = 0; /* ignore the packet */ } else { request->reply->code = FR_DHCP_NAK; } break; case RLM_MODULE_HANDLED: request->reply->code = 0; /* ignore the packet */ break; } /* * TODO: Handle 'output' of RLM_MODULE when acting as a * DHCP relay We may want to not forward packets in * certain circumstances. */ /* * Handle requests when acting as a DHCP relay */ vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */ if (!vp) { RPEDEBUG("Someone deleted the DHCP-Opcode!"); return RLM_MODULE_FAIL; } /* BOOTREPLY received on port 67 (i.e. from a server) */ if (vp->vp_uint8 == 2) { return dhcprelay_process_server_reply(request); } /* Packet from client, and we have DHCP-Relay-To-IP-Address */ if (fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY)) { return dhcprelay_process_client_request(request); } /* else it's a packet from a client, without relaying */ rad_assert(vp->vp_uint8 == 1); /* BOOTREQUEST */ sock = request->listener->data; /* * Handle requests when acting as a DHCP server */ /* * Releases don't get replies. */ if (request->packet->code == FR_DHCP_RELEASE) { request->reply->code = 0; } if (request->reply->code == 0) { return RLM_MODULE_OK; } request->reply->sockfd = request->packet->sockfd; /* * Copy specific fields from packet to reply, if they * don't already exist */ for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) { uint32_t attr = attrnums[i]; if (fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY)) continue; vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY); if (vp) { fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp)); } } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */ rad_assert(vp != NULL); vp->vp_uint8 = 2; /* BOOTREPLY */ /* * Allow NAKs to be delayed for a short period of time. */ if (request->reply->code == FR_DHCP_NAK) { vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY, TAG_ANY); if (vp) { if (vp->vp_uint32 <= 10) { request->response_delay.tv_sec = vp->vp_uint32; request->response_delay.tv_usec = 0; } else { request->response_delay.tv_sec = 10; request->response_delay.tv_usec = 0; } } else { #ifndef USEC #define USEC 1000000 #endif vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY_USEC, TAG_ANY); if (vp) { if (vp->vp_uint32 <= 10 * USEC) { request->response_delay.tv_sec = vp->vp_uint32 / USEC; request->response_delay.tv_usec = vp->vp_uint32 % USEC; } else { request->response_delay.tv_sec = 10; request->response_delay.tv_usec = 0; } } } } /* * Prepare the reply packet for sending through dhcp_socket_send() */ request->reply->dst_ipaddr.af = AF_INET; request->reply->src_ipaddr.af = AF_INET; request->reply->src_ipaddr.prefix = 32; /* * Packet-Src-IP-Address has highest precedence */ vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_SRC_IP_ADDRESS, TAG_ANY); if (vp) { request->reply->if_index = 0; /* Must be 0, we don't know the outbound if_index */ request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * The request was unicast (via a relay) */ } else if (request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST) && request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) { request->reply->src_ipaddr.addr.v4.s_addr = request->packet->dst_ipaddr.addr.v4.s_addr; request->reply->if_index = request->packet->if_index; /* * The listener was bound to an IP address, or we determined * the address automatically, as it was the only address bound * to the interface, and we bound to the interface. */ } else if (sock->src_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) { request->reply->src_ipaddr.addr.v4.s_addr = sock->src_ipaddr.addr.v4.s_addr; #ifdef WITH_IFINDEX_IPADDR_RESOLUTION /* * We built with udpfromto and have the if_index of the receiving * interface, which we can now resolve to an IP address. */ } else if (request->packet->if_index > 0) { fr_ipaddr_t primary; if (fr_ipaddr_from_ifindex(&primary, request->packet->sockfd, request->packet->dst_ipaddr.af, request->packet->if_index) < 0) { RPEDEBUG("Failed determining src_ipaddr from if_index"); return RLM_MODULE_FAIL; } request->reply->src_ipaddr.addr.v4.s_addr = primary.addr.v4.s_addr; #endif /* * There's a Server-Identification attribute */ } else if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 54, TAG_ANY))) { request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; } else { REDEBUG("Unable to determine correct src_ipaddr for response"); return RLM_MODULE_FAIL; } request->reply->dst_port = request->packet->src_port; request->reply->src_port = request->packet->dst_port; /* * Answer to client's nearest DHCP relay. * * Which may be different than the giaddr given in the * packet to the client. i.e. the relay may have a * public IP, but the gateway a private one. */ vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 272, TAG_ANY); /* DHCP-Relay-IP-Address */ if (vp && (vp->vp_ipv4addr != ntohl(INADDR_ANY))) { RDEBUG2("Reply will be unicast to giaddr from original packet"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; request->reply->dst_port = request->packet->dst_port; vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_DST_PORT, TAG_ANY); if (vp) request->reply->dst_port = vp->vp_uint16; return RLM_MODULE_OK; } /* * Answer to client's nearest DHCP gateway. In this * case, the client can reach the gateway, as can the * server. * * We also use *our* source port as the destination port. * Gateways are servers, and listen on the server port, * not the client port. */ vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { RDEBUG2("Reply will be unicast to giaddr"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; request->reply->dst_port = request->packet->dst_port; return RLM_MODULE_OK; } /* * If it's a NAK, or the broadcast flag was set, ond * there's no client-ip-address, send a broadcast. */ if ((request->reply->code == FR_DHCP_NAK) || ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) && /* DHCP-Flags */ (vp->vp_uint32 & 0x8000) && ((vp = fr_pair_find_by_num(request->reply->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("Reply will be broadcast"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); return RLM_MODULE_OK; } /* * RFC 2131, page 23 * * Unicast to ciaddr if present, otherwise to yiaddr. */ if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipv4addr != htonl(INADDR_ANY))) { RDEBUG2("Reply will be sent unicast to &DHCP-Client-IP-Address"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; return RLM_MODULE_OK; } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { REDEBUG("Can't assign address to client: Neither &reply:DHCP-Client-IP-Address nor " "&reply:DHCP-Your-IP-Address set"); /* * There is nowhere to send the response to, so don't bother. */ request->reply->code = 0; return RLM_MODULE_FAIL; } #ifdef SIOCSARP /* * The system is configured to listen for broadcast * packets, which means we'll need to send unicast * replies, to IPs which haven't yet been assigned. * Therefore, we need to update the ARP table. * * However, they haven't specified a interface. So we * can't update the ARP table. And we must send a * broadcast response. */ if (sock->lsock.broadcast && !sock->src_interface) { WARN("You MUST set \"interface\" if you have \"broadcast = yes\""); RDEBUG2("Reply will be broadcast as no interface was defined"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); return RLM_MODULE_OK; } RDEBUG2("Reply will be unicast to &DHCP-Your-IP-Address"); request->reply->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. * Otherwise the packet may not be sent to the client, as * the OS has no ARP entry for it. * * This is a cute hack to avoid us having to create a raw * socket to send DHCP packets. */ if (request->reply->code == FR_DHCP_OFFER) { VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (!hwvp) return RLM_MODULE_FAIL; if (fr_dhcpv4_udp_add_arp_entry(request->reply->sockfd, sock->src_interface, &vp->vp_ip, hwvp->vp_ether) < 0) { RPEDEBUG("Failed adding arp entry"); return RLM_MODULE_FAIL; } } #else if (request->packet->src_ipaddr.addr.v4.s_addr != ntohl(INADDR_NONE)) { RDEBUG2("Reply will be unicast to the unicast source IP address"); request->reply->dst_ipaddr.addr.v4.s_addr = request->packet->src_ipaddr.addr.v4.s_addr; } else { RDEBUG2("Reply will be broadcast as this system does not support ARP updates"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); } #endif return RLM_MODULE_OK; }