/* * If the NAS wasn't smart enought to add a NAS-IP-Address * to the request, then add it ourselves. */ static int add_nas_attr(REQUEST *request) { VALUE_PAIR *nas; switch (request->packet->src_ipaddr.af) { case AF_INET: nas = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY); if (!nas) { nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IP_ADDRESS, 0); nas->vp_ipaddr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr; } break; case AF_INET6: nas = pairfind(request->packet->vps, PW_NAS_IPV6_ADDRESS, 0, TAG_ANY); if (!nas) { nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IPV6_ADDRESS, 0); memcpy(&nas->vp_ipv6addr, &request->packet->src_ipaddr.ipaddr, sizeof(request->packet->src_ipaddr.ipaddr)); } break; default: ERROR("Unknown address family for packet"); return -1; } return 0; }
/** Allocate a request packet * * This is done once per request with the same packet being sent to multiple realms. */ static rlm_rcode_t rlm_replicate_alloc(RADIUS_PACKET **out, REQUEST *request, pair_lists_t list, PW_CODE code) { rlm_rcode_t rcode = RLM_MODULE_OK; RADIUS_PACKET *packet = NULL; VALUE_PAIR *vp, **vps; *out = NULL; packet = rad_alloc(request, 1); if (!packet) { return RLM_MODULE_FAIL; } packet->code = code; /* * Figure out which list in the request were replicating */ vps = radius_list(request, list); if (!vps) { RWDEBUG("List '%s' doesn't exist for this packet", fr_int2str(pair_lists, list, "<INVALID>")); rcode = RLM_MODULE_INVALID; goto error; } /* * Don't assume the list actually contains any attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto error; } } /* * For CHAP, create the CHAP-Challenge if it doesn't exist. */ if ((code == PW_CODE_ACCESS_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0); pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN); } *out = packet; return rcode; error: talloc_free(packet); return rcode; }
/* * Check if account has expired, and if user may login now. */ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) { rlm_expiration_t *inst = instance; VALUE_PAIR *vp, *check_item = NULL; char msg[MAX_STRING_LEN]; if ((check_item = pairfind(request->config_items, PW_EXPIRATION, 0, TAG_ANY)) != NULL){ /* * Has this user's password expired? * * If so, remove ALL reply attributes, * and add our own Reply-Message, saying * why they're being rejected. */ RDEBUG("Checking Expiration time: '%s'",check_item->vp_strvalue); if (((time_t) check_item->vp_date) <= request->timestamp) { RDEBUG("Account has expired"); if (inst->msg && inst->msg[0]){ if (!radius_xlat(msg, sizeof(msg), inst->msg, request, NULL, NULL)) { radlog(L_ERR, "rlm_expiration: xlat failed."); return RLM_MODULE_FAIL; } pairfree(&request->reply->vps); pairmake_reply("Reply-Message", msg, T_OP_ADD); } RDEBUGE("Account has expired [Expiration %s]",check_item->vp_strvalue); return RLM_MODULE_USERLOCK; } /* * Else the account hasn't expired, but it may do so * in the future. Set Session-Timeout. */ vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, PW_SESSION_TIMEOUT, 0); vp->vp_date = (uint32_t) (((time_t) check_item->vp_date) - request->timestamp); } else if (vp->vp_date > ((uint32_t) (((time_t) check_item->vp_date) - request->timestamp))) { vp->vp_date = (uint32_t) (((time_t) check_item->vp_date) - request->timestamp); } } else return RLM_MODULE_NOOP; return RLM_MODULE_OK; }
static void request_stats_addvp(REQUEST *request, fr_stats2vp *table, fr_stats_t *stats) { int i; VALUE_PAIR *vp; for (i = 0; table[i].attribute != 0; i++) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(table[i].attribute), PW_TYPE_INTEGER); if (!vp) continue; vp->vp_integer = *(int *)(((char *) stats) + table[i].offset); } }
static void request_stats_addvp(REQUEST *request, fr_stats2vp *table, fr_stats_t *stats) { int i; fr_uint_t counter; VALUE_PAIR *vp; for (i = 0; table[i].attribute != 0; i++) { vp = radius_paircreate(request->reply, &request->reply->vps, table[i].attribute, VENDORPEC_FREERADIUS); if (!vp) continue; counter = *(fr_uint_t *) (((uint8_t *) stats) + table[i].offset); vp->vp_integer = counter; } }
/* * See if we have access to the huntgroup. */ static int huntgroup_access(REQUEST *request, PAIR_LIST *huntgroups) { PAIR_LIST *i; int r = RLM_MODULE_OK; VALUE_PAIR *request_pairs = request->packet->vps; /* * We're not controlling access by huntgroups: * Allow them in. */ if (!huntgroups) { return RLM_MODULE_OK; } for (i = huntgroups; i; i = i->next) { /* * See if this entry matches. */ if (paircompare(request, request_pairs, i->check, NULL) != 0) { continue; } /* * Now check for access. */ r = RLM_MODULE_REJECT; if (hunt_paircmp(request, request_pairs, i->reply) == 0) { VALUE_PAIR *vp; /* * We've matched the huntgroup, so add it in * to the list of request pairs. */ vp = pairfind(request_pairs, PW_HUNTGROUP_NAME, 0, TAG_ANY); if (!vp) { vp = radius_paircreate(request->packet, &request->packet->vps, PW_HUNTGROUP_NAME, 0); pairstrcpy(vp, i->name); } r = RLM_MODULE_OK; } break; } return r; }
/* * Check if account has expired, and if user may login now. */ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request) { VALUE_PAIR *vp, *check_item = NULL; check_item = pairfind(request->config, PW_EXPIRATION, 0, TAG_ANY); if (check_item != NULL) { char date[50]; /* * Has this user's password expired? * * If so, remove ALL reply attributes, * and add our own Reply-Message, saying * why they're being rejected. */ if (((time_t) check_item->vp_date) <= request->timestamp) { vp_prints_value(date, sizeof(date), check_item, 0); REDEBUG("Account expired at '%s'", date); return RLM_MODULE_USERLOCK; } else { if (RDEBUG_ENABLED) { vp_prints_value(date, sizeof(date), check_item, 0); RDEBUG("Account will expire at '%s'", date); } } /* * Else the account hasn't expired, but it may do so * in the future. Set Session-Timeout. */ vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY); if (!vp) { vp = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0); vp->vp_date = (uint32_t) (((time_t) check_item->vp_date) - request->timestamp); } else if (vp->vp_date > ((uint32_t) (((time_t) check_item->vp_date) - request->timestamp))) { vp->vp_date = (uint32_t) (((time_t) check_item->vp_date) - request->timestamp); } } else { return RLM_MODULE_NOOP; } return RLM_MODULE_OK; }
/* * Access-Requests can have the CHAP-Challenge implicitly taken * from the request authenticator. If the NAS has done that, * then we need to copy the data to a real CHAP-Challenge * attribute when proxying. Otherwise when we proxy the request, * the new authenticator is different, and the CHAP calculations * will fail. */ static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(UNUSED void *instance, REQUEST *request) { VALUE_PAIR *vp; /* * For Access-Requests, which have CHAP-Password, * and no CHAP-Challenge, copy it over from the request. */ if (request->packet->code != PW_CODE_AUTHENTICATION_REQUEST) return RLM_MODULE_NOOP; if (!pairfind(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) return RLM_MODULE_NOOP; vp = radius_paircreate(request, &request->proxy->vps, PW_CHAP_CHALLENGE, 0); if (!vp) return RLM_MODULE_FAIL; pairmemcpy(vp, request->packet->vector, sizeof(request->packet->vector)); return RLM_MODULE_OK; }
/* assign new IP address, if required */ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) { VALUE_PAIR *vp; char const *pname; /* name of requested IP pool */ uint32_t nasip; /* NAS IP in host byte order */ struct in_addr ip = {0}; /* reserved IP for client (net. byte order) */ rlm_sql_handle_t *sqlsock; unsigned long s_gid, /* _s_elected in sql result set */ s_prio, /* as above */ s_pid, /* as above */ gid, /* real integer value */ pid, /* as above */ weights_sum, used_sum, ip_start, ip_stop, connid; long prio; rlm_sqlhpwippool_t *inst = (rlm_sqlhpwippool_t *) instance; /* if IP is already there, then nothing to do */ vp = pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY); if (vp) { nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): IP address " "already in the reply packet - exiting"); return RLM_MODULE_NOOP; } /* if no pool name, we don't need to do anything */ vp = pairfind(request->reply->vps, ASN_IP_POOL_NAME, VENDORPEC_ASN, TAG_ANY); if (vp) { pname = vp->vp_strvalue; nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): pool name = '%s'", pname); } else { nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): no IP pool name - exiting"); return RLM_MODULE_NOOP; } /* if no NAS IP address, assign 0 */ vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY); if (vp) { nasip = ntohl(vp->vp_ipaddr); } else { nasip = 0; nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): no NAS IP address in " "the request packet - using \"0.0.0.0/0\" (any)"); } /* get our database connection */ sqlsock = sql_get_socket(inst->sqlinst); if (!sqlsock) { nvp_log(__LINE__, inst, L_ERR, "mod_post_auth(): error while requesting an SQL socket"); return RLM_MODULE_FAIL; } /* get connection id as temporary unique integer */ if (nvp_select(__LINE__, inst, sqlsock, "SELECT CONNECTION_ID()") < 1) { nvp_log(__LINE__, inst, L_ERR, "mod_post_auth(): WTF ;-)!"); nvp_select_finish(inst, sqlsock); sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } connid = strtoul(sqlsock->row[0], (char **) NULL, 10); nvp_select_finish(inst, sqlsock); /* synchronize with radacct db, if needed */ if (++inst->sincesync >= inst->sync_after #ifdef HAVE_PTHREAD_D && (pthread_mutex_trylock(&inst->mutex)) == 0 #endif ) { int r; inst->sincesync = 0; nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): syncing with radacct table"); r = (nvp_freeclosed(inst, sqlsock) && nvp_syncfree(inst, sqlsock)); #ifdef HAVE_PTHREAD_D pthread_mutex_unlock(&inst->mutex); #endif if (!r) { nvp_log(__LINE__, inst, L_ERR, "mod_post_auth(): synchronization failed"); sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } } for (s_gid = 0; s_gid < RLM_NETVIM_MAX_ROWS && !(ip.s_addr); s_gid++) { nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): selecting gid on position %lu", s_gid); /* find the most specific group which NAS belongs to */ switch (nvp_select(__LINE__, inst, sqlsock, "SELECT `host_groups`.`gid` " "FROM " "`%s`.`host_groups`, " "`%1$s`.`gid_ip`, " "`%1$s`.`ids` " "WHERE " "`host_groups`.`gid` = `ids`.`id` AND " "`ids`.`enabled` = 1 AND " "`host_groups`.`gid` = `gid_ip`.`gid` AND " "%lu BETWEEN `gid_ip`.`ip_start` AND `gid_ip`.`ip_stop` " "ORDER BY (`gid_ip`.`ip_stop` - `gid_ip`.`ip_start`) ASC " "LIMIT %lu, 1", inst->db_name, nasip, s_gid)) { case -1: nvp_log(__LINE__, inst, L_ERR, "mod_post_auth(): couldn't find " "any more matching host groups"); goto end_gid; /* exit the main loop */ case 0: sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } /* store the group ID and free memory occupied by results */ gid = strtoul(sqlsock->row[0], (char **) NULL, 10); nvp_select_finish(inst, sqlsock); for (s_prio = 0; s_prio < RLM_NETVIM_MAX_ROWS && !(ip.s_addr); s_prio++) { nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): selecting prio on position %lu", s_prio); /* prepare to search for best fit pool */ switch (nvp_select(__LINE__, inst, sqlsock, "SELECT " "`ip_pools`.`prio`, " "SUM(`ip_pools`.`weight`) AS `weights_sum`, " "(SUM(`ip_pools`.`total`) - " "SUM(`ip_pools`.`free`)) AS `used_sum` " "FROM " "`%s`.`ip_pools`, " "`%1$s`.`ids`, " "`%1$s`.`pool_names` " "WHERE " "`ip_pools`.`gid` = %lu AND " "`ids`.`id` = `ip_pools`.`pid` AND " "`ids`.`enabled` = 1 AND " "`pool_names`.`pnid` = `ip_pools`.`pnid` AND " "`pool_names`.`name` = '%s' AND " "`ip_pools`.`free` > 0 " "GROUP BY `prio` " "ORDER BY `prio` ASC " "LIMIT %lu, 1", inst->db_name, gid, pname, s_prio)) { case -1: nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): couldn't find " "any more matching pools for gid = %u", gid); goto end_prio; /* select next gid */ case 0: sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } /* store the prio and weights sum */ prio = strtol(sqlsock->row[0], (char **) NULL, 10); weights_sum = strtoul(sqlsock->row[1], (char **) NULL, 10); used_sum = strtoul(sqlsock->row[2], (char **) NULL, 10); /* free memory */ nvp_select_finish(inst, sqlsock); for (s_pid = 0; s_pid < RLM_NETVIM_MAX_ROWS && !(ip.s_addr); s_pid++) { nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): selecting PID on position %lu", s_pid); /* search for best fit pool */ switch (nvp_select(__LINE__, inst, sqlsock, "SELECT " "`ip_pools`.`pid`, " "`ip_pools`.`ip_start`, " "`ip_pools`.`ip_stop` " "FROM " "`%s`.`ip_pools`, " "`%1$s`.`ids`, " "`%1$s`.`pool_names` " "WHERE " "`ip_pools`.`gid` = %lu AND " "`ids`.`id` = `ip_pools`.`pid` AND " "`ids`.`enabled` = 1 AND " "`pool_names`.`pnid` = `ip_pools`.`pnid` AND " "`pool_names`.`name` = '%s' AND " "`ip_pools`.`free` > 0 AND " "`prio` = %ld " "ORDER BY (`weight`/%lu.0000 - (`total` - `free`)/%lu) DESC " "LIMIT %lu, 1", inst->db_name, gid, pname, prio, weights_sum, used_sum, s_pid)) { case -1: nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): couldn't find any more " "matching pools of prio = %ld for gid = %lu", prio, gid); goto end_pid; /* select next prio */ case 0: sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } /* store the inst and free memory occupied by results */ pid = strtoul(sqlsock->row[0], (char **) NULL, 10); ip_start = strtoul(sqlsock->row[1], (char **) NULL, 10); ip_stop = strtoul(sqlsock->row[2], (char **) NULL, 10); nvp_select_finish(inst, sqlsock); /* reserve an IP address */ if (!nvp_query(__LINE__, inst, sqlsock, "UPDATE `%s`.`ips` " "SET " "`pid` = %lu, " "`rsv_since` = NOW(), " "`rsv_by` = '" RLM_NETVIM_TMP_PREFIX "%lu', " "`rsv_until` = NOW() + INTERVAL %d SECOND " "WHERE " "`ip` BETWEEN %lu AND %lu AND " "(" "`pid` IS NULL OR " "(`rsv_until` > 0 AND `rsv_until` < NOW())" ") " "ORDER BY RAND() " "LIMIT 1", inst->db_name, pid, connid, inst->free_after, ip_start, ip_stop)) { sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } else { nvp_finish(inst, sqlsock); } /* select assigned IP address */ switch (nvp_select(__LINE__, inst, sqlsock, "SELECT `ip` " "FROM `%s`.`ips` " "WHERE `rsv_by` = '" RLM_NETVIM_TMP_PREFIX "%lu' " "ORDER BY `rsv_since` DESC " "LIMIT 1", inst->db_name, connid)) { case -1: nvp_log(__LINE__, inst, L_ERR, "mod_post_auth(): couldn't reserve an IP address " "from pool of pid = %lu (prio = %ld, gid = %lu)", pid, prio, gid); continue; /* select next pid */ case 0: sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } /* update free IPs count */ if (!nvp_query(__LINE__, inst, sqlsock, "UPDATE `%s`.`ip_pools` " "SET " "`free` = `free` - 1 " "WHERE " "`pid` = %lu " "LIMIT 1", inst->db_name, pid)) { sql_release_socket(inst->sqlinst, sqlsock); return RLM_MODULE_FAIL; } else { nvp_finish(inst, sqlsock); } /* get assigned IP and free memory */ ip.s_addr = htonl(strtoul(sqlsock->row[0], (char **) NULL, 10)); nvp_select_finish(inst, sqlsock); } /* pid */ end_pid: continue; /* stupid */ } /* prio */ end_prio: continue; /* stupid */ } /* gid */ end_gid: /* release SQL socket */ sql_release_socket(inst->sqlinst, sqlsock); /* no free IP address found */ if (!ip.s_addr) { nvp_log(__LINE__, inst, L_INFO, "mod_post_auth(): no free IP address found!"); if (inst->no_free_fail) { nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): rejecting user"); return RLM_MODULE_REJECT; } else { nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): exiting"); return RLM_MODULE_NOOP; } } /* add IP address to reply packet */ vp = radius_paircreate(request, &request->reply->vps, PW_FRAMED_IP_ADDRESS, 0); vp->vp_ipaddr = ip.s_addr; nvp_log(__LINE__, inst, L_DBG, "mod_post_auth(): returning %s", inet_ntoa(ip)); return RLM_MODULE_OK; }
static int dhcp_process(REQUEST *request) { int 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 = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) { VALUE_PAIR *relay; /* DHCP-Relay-IP-Address */ relay = radius_paircreate(request->reply, &request->reply->vps, 272, DHCP_MAGIC_VENDOR); if (relay) relay->vp_ipaddr = vp->vp_ipaddr; } vp = pairfind(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */ if (vp) { DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_integer); DEBUG("Trying sub-section dhcp %s {...}", dv->name ? dv->name : "<unknown>"); rcode = process_post_auth(vp->vp_integer, request); } else { DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } vp = pairfind(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */ if (vp) { request->reply->code = vp->vp_integer; if ((request->reply->code != 0) && (request->reply->code < PW_DHCP_OFFSET)) { request->reply->code += PW_DHCP_OFFSET; } } else switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: if (request->packet->code == PW_DHCP_DISCOVER) { request->reply->code = PW_DHCP_OFFER; break; } else if (request->packet->code == PW_DHCP_REQUEST) { request->reply->code = PW_DHCP_ACK; break; } request->reply->code = PW_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 == PW_DHCP_DISCOVER) { request->reply->code = 0; /* ignore the packet */ } else { request->reply->code = PW_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 = pairfind(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */ if (!vp) { RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!"); return 1; } /* BOOTREPLY received on port 67 (i.e. from a server) */ if (vp->vp_integer == 2) { return dhcprelay_process_server_reply(request); } /* Packet from client, and we have DHCP-Relay-To-IP-Address */ if (pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) { return dhcprelay_process_client_request(request); } /* else it's a packet from a client, without relaying */ rad_assert(vp->vp_integer == 1); /* BOOTREQUEST */ sock = request->listener->data; /* * Handle requests when acting as a DHCP server */ /* * Releases don't get replies. */ if (request->packet->code == PW_DHCP_RELEASE) { request->reply->code = 0; } if (request->reply->code == 0) { return 1; } 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 (pairfind(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue; vp = pairfind(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY); if (vp) { pairadd(&request->reply->vps, paircopyvp(request->reply, vp)); } } vp = pairfind(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */ rad_assert(vp != NULL); vp->vp_integer = 2; /* BOOTREPLY */ /* * Allow NAKs to be delayed for a short period of time. */ if (request->reply->code == PW_DHCP_NAK) { vp = pairfind(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY); if (vp) { if (vp->vp_integer <= 10) { request->response_delay = vp->vp_integer; } else { request->response_delay = 10; } } } /* * 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.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr; /* * They didn't set a proper src_ipaddr, but we want to * send the packet with a source IP. If there's a server * identifier, use it. */ if (request->reply->src_ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) { vp = pairfind(request->reply->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Server-IP-Address */ if (!vp) vp = pairfind(request->reply->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-DHCP-Server-Identifier */ if (vp) { request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; } } 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 = pairfind(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */ if (vp && (vp->vp_ipaddr != ntohl(INADDR_ANY))) { RDEBUG("DHCP: Reply will be unicast to giaddr from original packet"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; return 1; } /* * 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 = pairfind(request->reply->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) { RDEBUG("DHCP: Reply will be unicast to giaddr"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->reply->dst_port = request->packet->dst_port; return 1; } /* * 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 == PW_DHCP_NAK) || ((vp = pairfind(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */ (vp->vp_integer & 0x8000) && ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipaddr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG("DHCP: Reply will be broadcast"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); return 1; } /* * RFC 2131, page 23 * * Unicast to ciaddr if present, otherwise to yiaddr. */ if ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipaddr != htonl(INADDR_ANY))) { RDEBUG("DHCP: Reply will be sent unicast to client-ip-address"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; return 1; } vp = pairfind(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { RDEBUG("DHCP: Failed to find DHCP-Client-IP-Address or DHCP-Your-IP-Address for request; " "not responding."); /* * There is nowhere to send the response to, so don't bother. */ request->reply->code = 0; return -1; } #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\""); RDEBUG("DHCP: Reply will be broadcast as no interface was defined"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); return 1; } RDEBUG("DHCP: Reply will be unicast to your-ip-address"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * 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 == PW_DHCP_OFFER) { VALUE_PAIR *hwvp = pairfind(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (!hwvp) return -1; if (fr_dhcp_add_arp_entry(request->reply->sockfd, sock->src_interface, hwvp, vp) < 0) { RDEBUG("Failed adding arp entry: %s", fr_strerror()); return -1; } } #else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != ntohl(INADDR_NONE)) { RDEBUG("DHCP: Request will be unicast to the unicast source IP address"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr; } else { RDEBUG("DHCP: Reply will be broadcast as this system does not support ARP updates"); request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); } #endif return 1; }
/* * 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) { VALUE_PAIR *namepair; #ifdef WITH_SESSION_MGMT VALUE_PAIR *check_item; #endif VALUE_PAIR *auth_item = NULL; VALUE_PAIR *module_msg; VALUE_PAIR *tmp = NULL; int result; const char *password; char autz_retry = 0; int autz_type = 0; password = ""; #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_AUTHENTICATION_ACK: tmp = radius_paircreate(request, &request->config_items, PW_AUTH_TYPE, PW_TYPE_INTEGER); if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT; #ifdef WITH_POST_PROXY_AUTHORIZE if (mainconfig.post_proxy_authorize) break; #endif goto authenticate; /* * Challenges are punted back to the NAS without any * further processing. */ case PW_ACCESS_CHALLENGE: request->reply->code = PW_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_AUTHENTICATION_REJECT: rad_authlog("Login incorrect (Home Server says so)", request, 0); request->reply->code = PW_AUTHENTICATION_REJECT; return RLM_MODULE_REJECT; default: rad_authlog("Login incorrect (Home Server failed to respond)", request, 0); return RLM_MODULE_REJECT; } } #endif /* * Get the username from the request. * * Note that namepair MAY be NULL, in which case there * is no User-Name attribute in the request. */ namepair = request->username; /* * Look for, and cache, passwords. */ if (!request->password) { request->password = pairfind(request->packet->vps, PW_USER_PASSWORD); } /* * Discover which password we want to use. */ auth_item = request->password; if (auth_item) { password = (const char *)auth_item->vp_strvalue; } else { /* * Maybe there's a CHAP-Password? */ if ((auth_item = pairfind(request->packet->vps, PW_CHAP_PASSWORD)) != NULL) { password = "******"; } else { /* * No password we recognize. */ password = "******"; } } request->password = auth_item; /* * Get the user's authorization information from the database */ autz_redo: result = module_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)) != 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_AUTHENTICATION_REJECT; return result; } if (!autz_retry) { tmp = pairfind(request->config_items, PW_AUTZ_TYPE); if (tmp) { autz_type = tmp->vp_integer; RDEBUG2("Using Autz-Type %s", dict_valnamebyattr(PW_AUTZ_TYPE, 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)) != 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)) { RDEBUG2("WARNING: You set Proxy-To-Realm = %s, but it is a LOCAL realm! Cancelling proxy request.", realm->name); } if (!realm) { RDEBUG2("WARNING: You set Proxy-To-Realm = %s, but the realm does not exist! Cancelling invalid proxy request.", tmp->vp_strvalue); } } #ifdef WITH_PROXY authenticate: #endif /* * Perhaps there is a Stripped-User-Name now. */ namepair = request->username; /* * 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_AUTHENTICATION_REJECT; if ((module_msg = pairfind(request->packet->vps,PW_MODULE_FAILURE_MESSAGE)) != 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); } /* double check: maybe the secret is wrong? */ if ((debug_flag > 1) && (auth_item != NULL) && (auth_item->attribute == PW_USER_PASSWORD)) { uint8_t *p; p = (uint8_t *) auth_item->vp_strvalue; while (*p) { int size; size = fr_utf8_char(p); if (!size) { log_debug(" WARNING: 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)) != NULL) { int r, session_type = 0; char logstr[1024]; char umsg[MAX_STRING_LEN + 1]; const char *user_msg = NULL; tmp = pairfind(request->config_items, PW_SESSION_TYPE); if (tmp) { session_type = tmp->vp_integer; RDEBUG2("Using Session-Type %s", dict_valnamebyattr(PW_SESSION_TYPE, session_type)); } /* * User authenticated O.K. Now we have to check * for the Simultaneous-Use parameter. */ if (namepair && (r = module_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)) != 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_AUTHENTICATION_REJECT; /* * They're trying to log in too many times. * Remove ALL reply attributes. */ pairfree(&request->reply->vps); radius_pairmake(request, &request->reply->vps, "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; } /* * Add the port number to the Framed-IP-Address if * vp->addport is set. */ if (((tmp = pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS)) != NULL) && (tmp->flags.addport != 0)) { VALUE_PAIR *vpPortId; /* * Find the NAS port ID. */ if ((vpPortId = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL) { unsigned long tvalue = ntohl(tmp->vp_integer); tmp->vp_integer = htonl(tvalue + vpPortId->vp_integer); tmp->flags.addport = 0; ip_ntoa(tmp->vp_strvalue, tmp->vp_integer); } else { RDEBUG2("WARNING: No NAS-Port attribute in request. CANNOT return a Framed-IP-Address + NAS-Port.\n"); pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS); } } /* * 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_AUTHENTICATION_ACK; if ((module_msg = pairfind(request->packet->vps,PW_MODULE_SUCCESS_MESSAGE)) != 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); } /* * Run the modules in the 'post-auth' section. */ result = rad_postauth(request); return result; }
static void request_stats_reply(REQUEST *request) { VALUE_PAIR *flag, *vp; /* * Statistics are available ONLY on a "status" port. */ rad_assert(request->packet->code == PW_STATUS_SERVER); rad_assert(request->listener->type == RAD_LISTEN_NONE); flag = pairfind(request->packet->vps, FR2ATTR(127)); if (!flag || (flag->vp_integer == 0)) return; /* * Authentication. */ if (((flag->vp_integer & 0x01) != 0) && ((flag->vp_integer & 0xc0) == 0)) { request_stats_addvp(request, authvp, &radius_auth_stats); } #ifdef WITH_ACCOUNTING /* * Accounting */ if (((flag->vp_integer & 0x02) != 0) && ((flag->vp_integer & 0xc0) == 0)) { request_stats_addvp(request, acctvp, &radius_acct_stats); } #endif #ifdef WITH_PROXY /* * Proxied authentication requests. */ if (((flag->vp_integer & 0x04) != 0) && ((flag->vp_integer & 0x20) == 0)) { request_stats_addvp(request, proxy_authvp, &proxy_auth_stats); } #ifdef WITH_ACCOUNTING /* * Proxied accounting requests. */ if (((flag->vp_integer & 0x08) != 0) && ((flag->vp_integer & 0x20) == 0)) { request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats); } #endif #endif /* * Internal server statistics */ if ((flag->vp_integer & 0x10) != 0) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(176), PW_TYPE_DATE); if (vp) vp->vp_date = radius_start_time.tv_sec; vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(177), PW_TYPE_DATE); if (vp) vp->vp_date = radius_hup_time.tv_sec; #ifdef HAVE_PTHREAD_H int i, array[RAD_LISTEN_MAX]; thread_pool_queue_stats(array); for (i = 0; i <= RAD_LISTEN_DETAIL; i++) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(162 + i), PW_TYPE_INTEGER); if (!vp) continue; vp->vp_integer = array[i]; } #endif } /* * For a particular client. */ if ((flag->vp_integer & 0x20) != 0) { fr_ipaddr_t ipaddr; VALUE_PAIR *server_ip, *server_port = NULL; RADCLIENT *client = NULL; RADCLIENT_LIST *cl = NULL; /* * See if we need to look up the client by server * socket. */ server_ip = pairfind(request->packet->vps, FR2ATTR(170)); if (server_ip) { server_port = pairfind(request->packet->vps, FR2ATTR(171)); if (server_port) { ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; cl = listener_find_client_list(&ipaddr, server_port->vp_integer); /* * Not found: don't do anything */ if (!cl) return; } } vp = pairfind(request->packet->vps, FR2ATTR(167)); if (vp) { ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; client = client_find(cl, &ipaddr); /* * Else look it up by number. */ } else if ((vp = pairfind(request->packet->vps, FR2ATTR(168))) != NULL) { client = client_findbynumber(cl, vp->vp_integer); } if (client) { /* * If found, echo it back, along with * the requested statistics. */ pairadd(&request->reply->vps, paircopyvp(vp)); /* * When retrieving client by number, also * echo back it's IP address. */ if ((vp->type == PW_TYPE_INTEGER) && (client->ipaddr.af == AF_INET)) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(167), PW_TYPE_IPADDR); if (vp) { vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr; } if (client->prefix != 32) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(169), PW_TYPE_INTEGER); if (vp) { vp->vp_integer = client->prefix; } } } if (server_ip) { pairadd(&request->reply->vps, paircopyvp(server_ip)); pairadd(&request->reply->vps, paircopyvp(server_port)); } if (client->auth && ((flag->vp_integer & 0x01) != 0)) { request_stats_addvp(request, client_authvp, client->auth); } #ifdef WITH_ACCOUNTING if (client->acct && ((flag->vp_integer & 0x01) != 0)) { request_stats_addvp(request, client_acctvp, client->acct); } #endif } /* else client wasn't found, don't echo it back */ } /* * For a particular "listen" socket. */ if (((flag->vp_integer & 0x40) != 0) && ((flag->vp_integer & 0x03) != 0)) { rad_listen_t *this; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; /* * See if we need to look up the server by socket * socket. */ server_ip = pairfind(request->packet->vps, FR2ATTR(170)); if (!server_ip) return; server_port = pairfind(request->packet->vps, FR2ATTR(171)); if (!server_port) return; ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; this = listener_find_byipaddr(&ipaddr, server_port->vp_integer); /* * Not found: don't do anything */ if (!this) return; pairadd(&request->reply->vps, paircopyvp(server_ip)); pairadd(&request->reply->vps, paircopyvp(server_port)); if (((flag->vp_integer & 0x01) != 0) && ((request->listener->type == RAD_LISTEN_AUTH) || (request->listener->type == RAD_LISTEN_NONE))) { request_stats_addvp(request, authvp, &this->stats); } #ifdef WITH_ACCOUNTING if (((flag->vp_integer & 0x02) != 0) && ((request->listener->type == RAD_LISTEN_ACCT) || (request->listener->type == RAD_LISTEN_NONE))) { request_stats_addvp(request, acctvp, &this->stats); } #endif } /* * Home servers. */ if (((flag->vp_integer & 0x80) != 0) && ((flag->vp_integer & 0x03) != 0)) { home_server *home; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; /* * See if we need to look up the server by socket * socket. */ server_ip = pairfind(request->packet->vps, FR2ATTR(170)); if (!server_ip) return; server_port = pairfind(request->packet->vps, FR2ATTR(171)); if (!server_port) return; ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; home = home_server_find(&ipaddr, server_port->vp_integer); /* * Not found: don't do anything */ if (!home) return; pairadd(&request->reply->vps, paircopyvp(server_ip)); pairadd(&request->reply->vps, paircopyvp(server_port)); vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(172), PW_TYPE_INTEGER); if (vp) vp->vp_integer = home->currently_outstanding; vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(173), PW_TYPE_INTEGER); if (vp) vp->vp_integer = home->state; if ((home->state == HOME_STATE_ALIVE) && (home->revive_time.tv_sec != 0)) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(175), PW_TYPE_DATE); if (vp) vp->vp_date = home->revive_time.tv_sec; } if ((home->state == HOME_STATE_ALIVE) && (home->ema.window > 0)) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(178), PW_TYPE_INTEGER); if (vp) vp->vp_integer = home->ema.window; vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(179), PW_TYPE_INTEGER); if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE; vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(180), PW_TYPE_INTEGER); if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE; } if (home->state == HOME_STATE_IS_DEAD) { vp = radius_paircreate(request, &request->reply->vps, FR2ATTR(174), PW_TYPE_DATE); if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period; } if (((flag->vp_integer & 0x01) != 0) && (home->type == HOME_TYPE_AUTH)) { request_stats_addvp(request, proxy_authvp, &home->stats); } #ifdef WITH_ACCOUNTING if (((flag->vp_integer & 0x02) != 0) && (home->type == HOME_TYPE_ACCT)) { request_stats_addvp(request, proxy_acctvp, &home->stats); } #endif } }
/* * Check if account has expired, and if user may login now. */ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) { rlm_logintime_t *inst = instance; VALUE_PAIR *ends, *timeout; int left; ends = pairfind(request->config_items, PW_LOGIN_TIME, 0, TAG_ANY); if (!ends) { return RLM_MODULE_NOOP; } /* * Authentication is OK. Now see if this user may login at this time of the day. */ RDEBUG("Checking Login-Time"); /* * Compare the time the request was received with the current Login-Time value */ left = timestr_match(ends->vp_strvalue, request->timestamp); /* * Do nothing, login time is not controlled (unendsed). */ if (left == 0) { return RLM_MODULE_OK; } /* * The min_time setting is to deal with NAS that won't allow Session-Timeout values below a certain value * For example some Alcatel Lucent products won't allow a Session-Timeout < 300 (5 minutes). * * We don't know were going to get another chance to lock out the user, so we need to do it now. */ if (left < inst->min_time) { RDEBUGE("Login outside of allowed time-slot (session end %s, with lockout %i seconds before)", ends->vp_strvalue, inst->min_time); return RLM_MODULE_USERLOCK; } /* else left > inst->min_time */ /* * There's time left in the users session, inform the NAS by including a Session-Timeout * attribute in the reply, or modifying the existing one. */ RDEBUG("Login within allowed time-slot, %i seconds left in this session", left); timeout = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY); if (timeout) { /* just update... */ if (timeout->vp_integer > (unsigned int) left) { timeout->vp_integer = left; } } else { timeout = radius_paircreate(request, &request->reply->vps, PW_SESSION_TIMEOUT, 0); timeout->vp_integer = left; } RDEBUG("reply:Session-Timeout set to %i", left); return RLM_MODULE_OK; }
/* * The main guy. */ int main(int argc, char *argv[]) { int rcode = EXIT_SUCCESS; int argval; const char *input_file = NULL; const char *output_file = NULL; const char *filter_file = NULL; FILE *fp; REQUEST *request = NULL; VALUE_PAIR *vp; VALUE_PAIR *filter_vps = NULL; /* * If the server was built with debugging enabled always install * the basic fatal signal handlers. */ #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("unittest"); exit(EXIT_FAILURE); } #endif if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) progname = argv[0]; else progname++; debug_flag = 0; set_radius_dir(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&main_config, 0, sizeof(main_config)); main_config.myip.af = AF_UNSPEC; main_config.port = 0; main_config.name = "radiusd"; /* * The tests should have only IPs, not host names. */ fr_hostname_lookups = false; /* * We always log to stdout. */ fr_log_fp = stdout; default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; /* Process the options. */ while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:xX")) != EOF) { switch (argval) { case 'd': set_radius_dir(NULL, optarg); break; case 'D': main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filter_file = optarg; break; case 'h': usage(0); break; case 'i': input_file = optarg; break; case 'm': main_config.debug_memory = true; break; case 'M': memory_report = true; main_config.debug_memory = true; break; case 'n': main_config.name = optarg; break; case 'o': output_file = optarg; break; case 'X': debug_flag += 2; main_config.log_auth = true; main_config.log_auth_badpass = true; main_config.log_auth_goodpass = true; break; case 'x': debug_flag++; break; default: usage(1); break; } } if (debug_flag) { version(); } fr_debug_flag = debug_flag; /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radiusd"); exit(EXIT_FAILURE); } /* Read the configuration files, BEFORE doing anything else. */ if (main_config_init() < 0) { rcode = EXIT_FAILURE; goto finish; } /* * Load the modules */ if (modules_init(main_config.config) < 0) { rcode = EXIT_FAILURE; goto finish; } fr_state_init(); /* Set the panic action (if required) */ if (main_config.panic_action && #ifndef NDEBUG !getenv("PANIC_ACTION") && #endif (fr_fault_setup(main_config.panic_action, argv[0]) < 0)) { rcode = EXIT_FAILURE; goto finish; } setlinebuf(stdout); /* unbuffered output */ if (!input_file || (strcmp(input_file, "-") == 0)) { fp = stdin; } else { fp = fopen(input_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", input_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } /* * Grab the VPs from stdin, or from the file. */ request = request_setup(fp); if (!request) { fprintf(stderr, "Failed reading input: %s\n", fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * No filter file, OR there's no more input, OR we're * reading from a file, and it's different from the * filter file. */ if (!filter_file || filedone || ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) { if (output_file) { fclose(fp); fp = NULL; } filedone = false; } /* * There is a filter file. If necessary, open it. If we * already are reading it via "input_file", then we don't * need to re-open it. */ if (filter_file) { if (!fp) { fp = fopen(filter_file, "r"); if (!fp) { fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno)); rcode = EXIT_FAILURE; goto finish; } } if (readvp2(request, &filter_vps, fp, &filedone) < 0) { fprintf(stderr, "Failed reading attributes from %s: %s\n", filter_file, fr_strerror()); rcode = EXIT_FAILURE; goto finish; } /* * FIXME: loop over input packets. */ fclose(fp); } rad_virtual_server(request); if (!output_file || (strcmp(output_file, "-") == 0)) { fp = stdout; } else { fp = fopen(output_file, "w"); if (!fp) { fprintf(stderr, "Failed writing %s: %s\n", output_file, strerror(errno)); exit(EXIT_FAILURE); } } print_packet(fp, request->reply); if (output_file) fclose(fp); /* * Update the list with the response type. */ vp = radius_paircreate(request->reply, &request->reply->vps, PW_RESPONSE_PACKET_TYPE, 0); vp->vp_integer = request->reply->code; { VALUE_PAIR const *failed[2]; if (filter_vps && !pairvalidate(failed, filter_vps, request->reply->vps)) { pairvalidate_debug(request, failed); fr_perror("Output file %s does not match attributes in filter %s", output_file ? output_file : input_file, filter_file); rcode = EXIT_FAILURE; goto finish; } } INFO("Exiting normally"); finish: talloc_free(request); /* * Detach any modules. */ modules_free(); xlat_free(); /* modules may have xlat's */ fr_state_delete(); /* * Free the configuration items. */ main_config_free(); if (memory_report) { INFO("Allocated memory at time of report:"); fr_log_talloc_report(NULL); } return rcode; }
/* * Check if account has expired, and if user may login now. */ static rlm_rcode_t logintime_authorize(void *instance, REQUEST *request) { rlm_logintime_t *data = (rlm_logintime_t *)instance; VALUE_PAIR *check_item = NULL; int r; if ((check_item = pairfind(request->config_items, PW_LOGIN_TIME, 0, TAG_ANY)) != NULL) { /* * Authentication is OK. Now see if this * user may login at this time of the day. */ DEBUG("rlm_logintime: Checking Login-Time: '%s'",check_item->vp_strvalue); r = timestr_match((char *)check_item->vp_strvalue, request->timestamp); if (r == 0) { /* unlimited */ /* * Do nothing: login-time is OK. */ /* * Session-Timeout needs to be at least * 60 seconds, some terminal servers * ignore smaller values. */ DEBUG("rlm_logintime: timestr returned unlimited"); } else if (r < data->min_time) { char logstr[MAX_STRING_LEN]; VALUE_PAIR *module_fmsg_vp; /* * User called outside allowed time interval. */ DEBUG("rlm_logintime: timestr returned reject"); if (data->msg && data->msg[0]){ char msg[MAX_STRING_LEN]; VALUE_PAIR *tmp; if (!radius_xlat(msg, sizeof(msg), data->msg, request, NULL, NULL)) { radlog(L_ERR, "rlm_logintime: xlat failed."); return RLM_MODULE_FAIL; } pairfree(&request->reply->vps); tmp = pairmake("Reply-Message", msg, T_OP_SET); request->reply->vps = tmp; } snprintf(logstr, sizeof(logstr), "Outside allowed timespan (time allowed %s)", check_item->vp_strvalue); module_fmsg_vp = pairmake("Module-Failure-Message", logstr, T_OP_EQ); pairadd(&request->packet->vps, module_fmsg_vp); return RLM_MODULE_REJECT; } else if (r > 0) { VALUE_PAIR *reply_item; /* * User is allowed, but set Session-Timeout. */ DEBUG("rlm_logintime: timestr returned accept"); if ((reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY)) != NULL) { if (reply_item->vp_integer > (unsigned) r) reply_item->vp_integer = r; } else { reply_item = radius_paircreate(request, &request->reply->vps, PW_SESSION_TIMEOUT, 0, PW_TYPE_INTEGER); reply_item->vp_integer = r; } DEBUG("rlm_logintime: Session-Timeout set to: %d",r); } } else return RLM_MODULE_NOOP; return RLM_MODULE_OK; }
/* * Find the named user in this modules database. Create the set * of attribute-value pairs to check and reply with for this user * from the database. The authentication code only needs to check * the password, the rest is done here. */ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_sqlcounter_t *inst = instance; int rcode = RLM_MODULE_NOOP; uint64_t counter, res; DICT_ATTR const *da; VALUE_PAIR *key_vp, *limit; VALUE_PAIR *reply_item; char msg[128]; char query[MAX_QUERY_LEN], subst[MAX_QUERY_LEN]; char *expanded = NULL; size_t len; /* * Before doing anything else, see if we have to reset * the counters. */ if (inst->reset_time && (inst->reset_time <= request->timestamp)) { /* * Re-set the next time and prev_time for this counters range */ inst->last_reset = inst->reset_time; find_next_reset(inst,request->timestamp); } /* * Look for the key. User-Name is special. It means * The REAL username, after stripping. */ if ((inst->key_attr->vendor == 0) && (inst->key_attr->attr == PW_USER_NAME)) { key_vp = request->username; } else { key_vp = pairfind(request->packet->vps, inst->key_attr->attr, inst->key_attr->vendor, TAG_ANY); } if (!key_vp) { RWDEBUG2("Couldn't find key attribute 'request:%s'", inst->key_attr->name); return rcode; } /* * Look for the check item */ if ((da = dict_attrbyname(inst->limit_name)) == NULL) { return rcode; } limit = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY); if (limit == NULL) { RWDEBUG2("Couldn't find control attribute 'control:%s'", inst->limit_name); return rcode; } /* First, expand %k, %b and %e in query */ if (sqlcounter_expand(subst, sizeof(subst), inst->query, inst) <= 0) { REDEBUG("Insufficient query buffer space"); return RLM_MODULE_FAIL; } /* Then combine that with the name of the module were using to do the query */ len = snprintf(query, sizeof(query), "%%{%s:%s}", inst->sqlmod_inst, subst); if (len >= (sizeof(query) - 1)) { REDEBUG("Insufficient query buffer space"); return RLM_MODULE_FAIL; } /* Finally, xlat resulting SQL query */ if (radius_axlat(&expanded, request, query, NULL, NULL) < 0) { return RLM_MODULE_FAIL; } talloc_free(expanded); if (sscanf(expanded, "%" PRIu64, &counter) != 1) { RDEBUG2("No integer found in result string \"%s\". May be first session, setting counter to 0", expanded); counter = 0; } /* * Check if check item > counter */ if (limit->vp_integer64 <= counter) { /* User is denied access, send back a reply message */ snprintf(msg, sizeof(msg), "Your maximum %s usage time has been reached", inst->reset); pairmake_reply("Reply-Message", msg, T_OP_EQ); REDEBUG2("Maximum %s usage time reached", inst->reset); REDEBUG2("Rejecting user, control:%s value (%" PRIu64 ") is less than counter value (%" PRIu64 ")", inst->limit_name, limit->vp_integer64, counter); return RLM_MODULE_REJECT; } res = limit->vp_integer64 - counter; RDEBUG2("Allowing user, control:%s value (%" PRIu64 ") is greater than counter value (%" PRIu64 ")", inst->limit_name, limit->vp_integer64, counter); /* * We are assuming that simultaneous-use=1. But * even if that does not happen then our user * could login at max for 2*max-usage-time Is * that acceptable? */ /* * If we are near a reset then add the next * limit, so that the user will not need to login * again. Do this only for Session-Timeout. */ if (((inst->reply_attr->vendor == 0) && (inst->reply_attr->attr == PW_SESSION_TIMEOUT)) && inst->reset_time && ((int) res >= (inst->reset_time - request->timestamp))) { res = (inst->reset_time - request->timestamp); res += limit->vp_integer; } /* * Limit the reply attribute to the minimum of the existing value, or this new one. */ reply_item = pairfind(request->reply->vps, inst->reply_attr->attr, inst->reply_attr->vendor, TAG_ANY); if (reply_item) { if (reply_item->vp_integer64 <= res) { RDEBUG2("Leaving existing reply:%s value of %" PRIu64, inst->reply_attr->name, reply_item->vp_integer64); return RLM_MODULE_OK; } } else { reply_item = radius_paircreate(request->reply, &request->reply->vps, inst->reply_attr->attr, inst->reply_attr->vendor); } reply_item->vp_integer64 = res; RDEBUG2("Setting reply:%s value to %" PRIu64, inst->reply_name, reply_item->vp_integer64); return RLM_MODULE_OK; }
/* * Find the named user in this modules database. Create the set * of attribute-value pairs to check and reply with for this user * from the database. The authentication code only needs to check * the password, the rest is done here. */ static int sqlcounter_authorize(void *instance, REQUEST *request) { rlm_sqlcounter_t *data = (rlm_sqlcounter_t *) instance; int ret=RLM_MODULE_NOOP; unsigned int counter; DICT_ATTR *dattr; VALUE_PAIR *key_vp, *check_vp; VALUE_PAIR *reply_item; char msg[128]; char querystr[MAX_QUERY_LEN]; char sqlxlat[MAX_QUERY_LEN]; /* quiet the compiler */ instance = instance; request = request; /* * Before doing anything else, see if we have to reset * the counters. */ if (data->reset_time && (data->reset_time <= request->timestamp)) { /* * Re-set the next time and prev_time for this counters range */ data->last_reset = data->reset_time; find_next_reset(data,request->timestamp); } /* * Look for the key. User-Name is special. It means * The REAL username, after stripping. */ DEBUG2("rlm_sqlcounter: Entering module authorize code"); key_vp = ((data->key_attr->vendor == 0) && (data->key_attr->attr == PW_USER_NAME)) ? request->username : pairfind(request->packet->vps, data->key_attr->attr, data->key_attr->vendor); if (key_vp == NULL) { DEBUG2("rlm_sqlcounter: Could not find Key value pair"); return ret; } /* * Look for the check item */ if ((dattr = dict_attrbyname(data->check_name)) == NULL) { return ret; } /* DEBUG2("rlm_sqlcounter: Found Check item attribute %d", dattr->attr); */ if ((check_vp= pairfind(request->config_items, dattr->attr, dattr->vendor)) == NULL) { DEBUG2("rlm_sqlcounter: Could not find Check item value pair"); return ret; } /* first, expand %k, %b and %e in query */ sqlcounter_expand(querystr, MAX_QUERY_LEN, data->query, instance); /* next, wrap query with sql module & expand */ snprintf(sqlxlat, sizeof(sqlxlat), "%%{%s:%s}", data->sqlmod_inst, querystr); /* Finally, xlat resulting SQL query */ radius_xlat(querystr, MAX_QUERY_LEN, sqlxlat, request, NULL, NULL); if (sscanf(querystr, "%u", &counter) != 1) { DEBUG2("rlm_sqlcounter: No integer found in string \"%s\"", querystr); return RLM_MODULE_NOOP; } /* * Check if check item > counter */ if (check_vp->vp_integer > counter) { unsigned int res = check_vp->vp_integer - counter; DEBUG2("rlm_sqlcounter: Check item is greater than query result"); /* * We are assuming that simultaneous-use=1. But * even if that does not happen then our user * could login at max for 2*max-usage-time Is * that acceptable? */ /* * If we are near a reset then add the next * limit, so that the user will not need to login * again. Do this only for Session-Timeout. */ if ((data->reply_attr->attr == PW_SESSION_TIMEOUT) && data->reset_time && (res >= (data->reset_time - request->timestamp))) { res = data->reset_time - request->timestamp; res += check_vp->vp_integer; } /* * Limit the reply attribute to the minimum of * the existing value, or this new one. */ reply_item = pairfind(request->reply->vps, data->reply_attr->attr, data->reply_attr->vendor); if (reply_item) { if (reply_item->vp_integer > res) reply_item->vp_integer = res; } else { reply_item = radius_paircreate(request, &request->reply->vps, data->reply_attr->attr, data->reply_attr->vendor, PW_TYPE_INTEGER); reply_item->vp_integer = res; } ret=RLM_MODULE_OK; DEBUG2("rlm_sqlcounter: Authorized user %s, check_item=%u, counter=%u", key_vp->vp_strvalue,check_vp->vp_integer,counter); DEBUG2("rlm_sqlcounter: Sent Reply-Item for user %s, Type=%s, value=%u", key_vp->vp_strvalue,data->reply_name,reply_item->vp_integer); } else{ char module_fmsg[MAX_STRING_LEN]; VALUE_PAIR *module_fmsg_vp; DEBUG2("rlm_sqlcounter: (Check item - counter) is less than zero"); /* * User is denied access, send back a reply message */ snprintf(msg, sizeof(msg), "Your maximum %s usage time has been reached", data->reset); reply_item=pairmake("Reply-Message", msg, T_OP_EQ); pairadd(&request->reply->vps, reply_item); snprintf(module_fmsg, sizeof(module_fmsg), "rlm_sqlcounter: Maximum %s usage time reached", data->reset); module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ); pairadd(&request->packet->vps, module_fmsg_vp); ret=RLM_MODULE_REJECT; DEBUG2("rlm_sqlcounter: Rejected user %s, check_item=%u, counter=%u", key_vp->vp_strvalue,check_vp->vp_integer,counter); } return ret; }
/* * Write accounting information to this modules database. */ static int replicate_packet(void *instance, REQUEST *request) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; instance = instance; /* -Wunused */ last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2("ERROR: Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2("ERROR: Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2(" WARNING: Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2("ERROR: Failed to find live home server for realm %s", realm->name); continue; } if (!packet) { packet = rad_alloc(1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = request->packet->code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUG("ERROR: Failed opening socket: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } packet->vps = paircopy(request->packet->vps); if (!packet->vps) { RDEBUG("ERROR: Out of memory!"); cleanup(packet); return RLM_MODULE_FAIL; } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((request->packet->code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0, PW_TYPE_OCTETS); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating packet to Realm %s", realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUG("ERROR: Failed replicating packet: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } cleanup(packet); return rcode; }
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; int ldap_errno; int i; ldap_instance_t *inst = instance; struct berval **values; VALUE_PAIR *vp; ldap_handle_t *conn; LDAPMessage *result, *entry; char const *dn = NULL; rlm_ldap_map_xlat_t expanded; /* faster than mallocing every time */ if (!request->username) { RDEBUG2("Attribute \"User-Name\" is required for authorization"); return RLM_MODULE_NOOP; } /* * Check for valid input, zero length names not permitted */ if (request->username->vp_length == 0) { RDEBUG2("Zero length username not permitted"); return RLM_MODULE_INVALID; } if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) { return RLM_MODULE_FAIL; } conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; /* * Add any additional attributes we need for checking access, memberships, and profiles */ if (inst->userobj_access_attr) { expanded.attrs[expanded.count++] = inst->userobj_access_attr; } if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) { expanded.attrs[expanded.count++] = inst->userobj_membership_attr; } if (inst->profile_attr) { expanded.attrs[expanded.count++] = inst->profile_attr; } if (inst->valuepair_attr) { expanded.attrs[expanded.count++] = inst->valuepair_attr; } expanded.attrs[expanded.count] = NULL; dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode); if (!dn) { goto finish; } entry = ldap_first_entry(conn->handle, result); if (!entry) { ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); goto finish; } /* * Check for access. */ if (inst->userobj_access_attr) { rcode = rlm_ldap_check_access(inst, request, conn, entry); if (rcode != RLM_MODULE_OK) { goto finish; } } /* * Check if we need to cache group memberships */ if (inst->cacheable_group_dn || inst->cacheable_group_name) { if (inst->userobj_membership_attr) { rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr); if (rcode != RLM_MODULE_OK) { goto finish; } } rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn); if (rcode != RLM_MODULE_OK) { goto finish; } } #ifdef WITH_EDIR /* * We already have a Cleartext-Password. Skip edir. */ if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) { goto skip_edir; } /* * Retrieve Universal Password if we use eDirectory */ if (inst->edir) { int res = 0; char password[256]; size_t pass_size = sizeof(password); /* * Retrive universal password */ res = nmasldap_get_password(conn->handle, dn, password, &pass_size); if (res != 0) { REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res)); rcode = RLM_MODULE_FAIL; goto finish; } /* * Add Cleartext-Password attribute to the request */ vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0); pairstrcpy(vp, password); vp->vp_length = pass_size; if (RDEBUG_ENABLED3) { RDEBUG3("Added eDirectory password. control:%s += '%s'", vp->da->name, vp->vp_strvalue); } else { RDEBUG2("Added eDirectory password"); } if (inst->edir_autz) { RDEBUG2("Binding as user for eDirectory authorization checks"); /* * Bind as the user */ conn->rebound = true; status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; RDEBUG("Bind as user '%s' was successful", dn); break; case LDAP_PROC_NOT_PERMITTED: rcode = RLM_MODULE_USERLOCK; goto finish; case LDAP_PROC_REJECT: rcode = RLM_MODULE_REJECT; goto finish; case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; goto finish; case LDAP_PROC_NO_RESULT: rcode = RLM_MODULE_NOTFOUND; goto finish; default: rcode = RLM_MODULE_FAIL; goto finish; }; } } skip_edir: #endif /* * Apply ONE user profile, or a default user profile. */ if (inst->default_profile) { char profile[1024]; if (radius_xlat(profile, sizeof(profile), request, inst->default_profile, NULL, NULL) < 0) { REDEBUG("Failed creating default profile string"); rcode = RLM_MODULE_INVALID; goto finish; } rlm_ldap_map_profile(inst, request, &conn, profile, &expanded); } /* * Apply a SET of user profiles. */ if (inst->profile_attr) { values = ldap_get_values_len(conn->handle, entry, inst->profile_attr); if (values != NULL) { for (i = 0; values[i] != NULL; i++) { char *value; value = rlm_ldap_berval_to_string(request, values[i]); rlm_ldap_map_profile(inst, request, &conn, value, &expanded); talloc_free(value); } ldap_value_free_len(values); } } if (inst->user_map || inst->valuepair_attr) { RDEBUG("Processing user attributes"); RINDENT(); rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry); REXDENT(); rlm_ldap_check_reply(inst, request); } finish: rlm_ldap_map_xlat_free(&expanded); if (result) ldap_msgfree(result); mod_conn_release(inst, conn); return rcode; }
/* * Preprocess a request before accounting */ static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request) { int r; VALUE_PAIR *vp; rlm_preprocess_t *inst = instance; /* * Ensure that we have the SAME user name for both * authentication && accounting. */ rad_mangle(inst, request); if (inst->with_cisco_vsa_hack) { /* * We need to run this hack because the h323-conf-id * attribute should be used. */ cisco_vsa_hack(request); } if (inst->with_alvarion_vsa_hack) { /* * We need to run this hack because the Alvarion * people are crazy. */ alvarion_vsa_hack(request->packet->vps); } if (inst->with_cablelabs_vsa_hack) { /* * We need to run this hack because the Cablelabs * people are crazy. */ cablelabs_vsa_hack(&request->packet->vps); } /* * Ensure that we log the NAS IP Address in the packet. */ if (add_nas_attr(request) < 0) { return RLM_MODULE_FAIL; } hints_setup(inst->hints, request); /* * Add an event timestamp. This means that the rest of * the server can use it, rather than various error-prone * manual calculations. */ vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY); if (!vp) { VALUE_PAIR *delay; vp = radius_paircreate(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0); vp->vp_date = request->packet->timestamp.tv_sec; delay = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY); if (delay) { vp->vp_date -= delay->vp_integer; } } if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) { char buf[1024]; RIDEBUG("No huntgroup access: [%s] (%s)", request->username ? request->username->vp_strvalue : "<NO User-Name>", auth_name(buf, sizeof(buf), request, 1)); return r; } return r; }
/* * Preprocess a request. */ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { int r; rlm_preprocess_t *inst = instance; /* * Mangle the username, to get rid of stupid implementation * bugs. */ rad_mangle(inst, request); if (inst->with_ascend_hack) { /* * If we're using Ascend systems, hack the NAS-Port-Id * in place, to go from Ascend's weird values to something * approaching rationality. */ ascend_nasport_hack(pairfind(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY), inst->ascend_channels_per_line); } if (inst->with_cisco_vsa_hack) { /* * We need to run this hack because the h323-conf-id * attribute should be used. */ cisco_vsa_hack(request); } if (inst->with_alvarion_vsa_hack) { /* * We need to run this hack because the Alvarion * people are crazy. */ alvarion_vsa_hack(request->packet->vps); } if (inst->with_cablelabs_vsa_hack) { /* * We need to run this hack because the Cablelabs * people are crazy. */ cablelabs_vsa_hack(&request->packet->vps); } /* * Note that we add the Request-Src-IP-Address to the request * structure BEFORE checking huntgroup access. This allows * the Request-Src-IP-Address to be used for huntgroup * comparisons. */ if (add_nas_attr(request) < 0) { return RLM_MODULE_FAIL; } hints_setup(inst->hints, request); /* * If there is a PW_CHAP_PASSWORD attribute but there * is PW_CHAP_CHALLENGE we need to add it so that other * modules can use it as a normal attribute. */ if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) && pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) { VALUE_PAIR *vp; vp = radius_paircreate(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0); pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN); } if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) { char buf[1024]; RIDEBUG("No huntgroup access: [%s] (%s)", request->username ? request->username->vp_strvalue : "<NO User-Name>", auth_name(buf, sizeof(buf), request, 1)); return r; } return RLM_MODULE_OK; /* Meaning: try next authorization module */ }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no * replications succeeded, else ok. */ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, **vps, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2E("Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2E("Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2E("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (!packet) { packet = rad_alloc(NULL, 1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUGE("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } vps = radius_list(request, list); if (!vps) { RDEBUGW("List '%s' doesn't exist for " "this packet", fr_int2str(pair_lists, list, "?unknown?")); rcode = RLM_MODULE_INVALID; goto done; } /* * Don't assume the list actually contains any * attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto done; } } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "¿unknown?"),realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUGE("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: cleanup(packet); return rcode; }
/* * Internal function to cut down on duplicated code. * * Returns -1 on failure, 0 on no failure. returnrealm * is NULL on don't proxy, realm otherwise. */ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm) { char *namebuf; char *username; char const *realmname = NULL; char *ptr; VALUE_PAIR *vp; REALM *realm; struct realm_config_t *inst = instance; /* initiate returnrealm */ *returnrealm = NULL; /* * If the request has a proxy entry, then it's a proxy * reply, and we're walking through the module list again. * * In that case, don't bother trying to proxy the request * again. * * Also, if there's no User-Name attribute, we can't * proxy it, either. */ if ((!request->username) #ifdef WITH_PROXY || (request->proxy != NULL) #endif ) { RDEBUG2("Proxy reply, or no User-Name. Ignoring."); return RLM_MODULE_OK; } /* * Check for 'Realm' attribute. If it exists, then we've proxied * it already ( via another rlm_realm instance ) and should return. */ if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL ) { RDEBUG2("Request already proxied. Ignoring."); return RLM_MODULE_OK; } /* * We will be modifing this later, so we want our own copy * of it. */ namebuf = talloc_strdup(request, request->username->vp_strvalue); username = namebuf; switch(inst->format) { case REALM_FORMAT_SUFFIX: /* DEBUG2(" rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */ ptr = strrchr(username, inst->delim[0]); if (ptr) { *ptr = '\0'; realmname = ptr + 1; } break; case REALM_FORMAT_PREFIX: /* DEBUG2(" rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */ ptr = strchr(username, inst->delim[0]); if (ptr) { *ptr = '\0'; ptr++; realmname = username; username = ptr; } break; default: realmname = NULL; break; } /* * Print out excruciatingly descriptive debugging messages * for the people who find it too difficult to think about * what's going on. */ if (realmname) { RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"", realmname, request->username->vp_strvalue); } else { if (inst->ignore_null ) { RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.", inst->delim[0], request->username->vp_strvalue); talloc_free(namebuf); return RLM_MODULE_NOOP; } RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL", inst->delim[0], request->username->vp_strvalue); } /* * Allow DEFAULT realms unless told not to. */ realm = realm_find(realmname); if (!realm) { RDEBUG2("No such realm \"%s\"", (!realmname) ? "NULL" : realmname); talloc_free(namebuf); return RLM_MODULE_NOOP; } if( inst->ignore_default && (strcmp(realm->name, "DEFAULT")) == 0) { RDEBUG2("Found DEFAULT, but skipping due to config."); talloc_free(namebuf); return RLM_MODULE_NOOP; } RDEBUG2("Found realm \"%s\"", realm->name); /* * If we've been told to strip the realm off, then do so. */ if (realm->striprealm) { /* * Create the Stripped-User-Name attribute, if it * doesn't exist. * */ if (request->username->da->attr != PW_STRIPPED_USER_NAME) { vp = radius_paircreate(request, &request->packet->vps, PW_STRIPPED_USER_NAME, 0); RDEBUG2("Adding Stripped-User-Name = \"%s\"", username); } else { vp = request->username; RDEBUG2("Setting Stripped-User-Name = \"%s\"", username); } pairstrcpy(vp, username); request->username = vp; } /* * Add the realm name to the request. * If the realm is a regex, the use the realm as entered * by the user. Otherwise, use the configured realm name, * as realm name comparison is case insensitive. We want * to use the configured name, rather than what the user * entered. */ if (realm->name[0] != '~') realmname = realm->name; pairmake_packet("Realm", realmname, T_OP_EQ); RDEBUG2("Adding Realm = \"%s\"", realmname); talloc_free(namebuf); realmname = username = NULL; /* * Figure out what to do with the request. */ switch (request->packet->code) { default: RDEBUG2("Unknown packet code %d\n", request->packet->code); return RLM_MODULE_OK; /* don't do anything */ /* * Perhaps accounting proxying was turned off. */ case PW_ACCOUNTING_REQUEST: if (!realm->acct_pool) { RDEBUG2("Accounting realm is LOCAL."); return RLM_MODULE_OK; } break; /* * Perhaps authentication proxying was turned off. */ case PW_AUTHENTICATION_REQUEST: if (!realm->auth_pool) { RDEBUG2("Authentication realm is LOCAL."); return RLM_MODULE_OK; } break; } #ifdef WITH_PROXY RDEBUG2("Proxying request from user %s to realm %s", request->username->vp_strvalue, realm->name); /* * Skip additional checks if it's not an accounting * request. */ if (request->packet->code != PW_ACCOUNTING_REQUEST) { *returnrealm = realm; return RLM_MODULE_UPDATED; } /* * FIXME: Each server should have a unique server key, * and put it in the accounting packet. Every server * should know about the keys, and NOT proxy requests to * a server with key X if the packet already contains key * X. */ /* * If this request has arrived from another freeradius server * that has already proxied the request, we don't need to do * it again. */ vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY); if (vp && (request->packet->src_ipaddr.af == AF_INET)) { int i; fr_ipaddr_t my_ipaddr; my_ipaddr.af = AF_INET; my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * Loop over the home accounting servers for this * realm. If one of them has the same IP as the * FreeRADIUS-Proxied-To attribute, then the * packet has already been sent there. Don't * send it there again. */ for (i = 0; i < realm->acct_pool->num_home_servers; i++) { if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr, &my_ipaddr) == 0) { RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To"); return RLM_MODULE_OK; } } /* * See detail_recv() in src/main/listen.c for the * additional checks. */ #ifdef WITH_DETAIL } else if ((request->listener->type == RAD_LISTEN_DETAIL) && !fr_inaddr_any(&request->packet->src_ipaddr)) { int i; /* * Loop over the home accounting servers for this * realm. If one of them has the same IP as the * FreeRADIUS-Proxied-To attribute, then the * packet has already been sent there. Don't * send it there again. */ for (i = 0; i < realm->acct_pool->num_home_servers; i++) { if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr, &request->packet->src_ipaddr) == 0) && (realm->acct_pool->servers[i]->port == request->packet->src_port)) { RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm"); return RLM_MODULE_OK; } } #endif /* WITH_DETAIL */ } #endif /* WITH_PROXY */ /* * We got this far, which means we have a realm, set returnrealm */ *returnrealm = realm; return RLM_MODULE_UPDATED; }
void request_stats_reply(REQUEST *request) { VALUE_PAIR *flag, *vp; /* * Statistics are available ONLY on a "status" port. */ rad_assert(request->packet->code == PW_CODE_STATUS_SERVER); rad_assert(request->listener->type == RAD_LISTEN_NONE); flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS, TAG_ANY); if (!flag || (flag->vp_integer == 0)) return; /* * Authentication. */ if (((flag->vp_integer & 0x01) != 0) && ((flag->vp_integer & 0xc0) == 0)) { request_stats_addvp(request, authvp, &radius_auth_stats); } #ifdef WITH_ACCOUNTING /* * Accounting */ if (((flag->vp_integer & 0x02) != 0) && ((flag->vp_integer & 0xc0) == 0)) { request_stats_addvp(request, acctvp, &radius_acct_stats); } #endif #ifdef WITH_PROXY /* * Proxied authentication requests. */ if (((flag->vp_integer & 0x04) != 0) && ((flag->vp_integer & 0x20) == 0)) { request_stats_addvp(request, proxy_authvp, &proxy_auth_stats); } #ifdef WITH_ACCOUNTING /* * Proxied accounting requests. */ if (((flag->vp_integer & 0x08) != 0) && ((flag->vp_integer & 0x20) == 0)) { request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats); } #endif #endif /* * Internal server statistics */ if ((flag->vp_integer & 0x10) != 0) { vp = radius_paircreate(request->reply, &request->reply->vps, 176, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = start_time.tv_sec; vp = radius_paircreate(request->reply, &request->reply->vps, 177, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = hup_time.tv_sec; #ifdef HAVE_PTHREAD_H int i, array[RAD_LISTEN_MAX], pps[2]; thread_pool_queue_stats(array, pps); for (i = 0; i <= 4; i++) { vp = radius_paircreate(request->reply, &request->reply->vps, 162 + i, VENDORPEC_FREERADIUS); if (!vp) continue; vp->vp_integer = array[i]; } for (i = 0; i < 2; i++) { vp = radius_paircreate(request->reply, &request->reply->vps, 181 + i, VENDORPEC_FREERADIUS); if (!vp) continue; vp->vp_integer = pps[i]; } #endif } /* * For a particular client. */ if ((flag->vp_integer & 0x20) != 0) { fr_ipaddr_t ipaddr; VALUE_PAIR *server_ip, *server_port = NULL; RADCLIENT *client = NULL; RADCLIENT_LIST *cl = NULL; /* * See if we need to look up the client by server * socket. */ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY); if (server_ip) { server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY); if (server_port) { ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; cl = listener_find_client_list(&ipaddr, server_port->vp_integer, IPPROTO_UDP); /* * Not found: don't do anything */ if (!cl) return; } } vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS, TAG_ANY); if (vp) { memset(&ipaddr, 0, sizeof(ipaddr)); ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; client = client_find(cl, &ipaddr, IPPROTO_UDP); #ifdef WITH_TCP if (!client) { client = client_find(cl, &ipaddr, IPPROTO_TCP); } #endif /* * Else look it up by number. */ } else if ((vp = pairfind(request->packet->vps, 168, VENDORPEC_FREERADIUS, TAG_ANY)) != NULL) { client = client_findbynumber(cl, vp->vp_integer); } if (client) { /* * If found, echo it back, along with * the requested statistics. */ pairadd(&request->reply->vps, paircopyvp(request->reply, vp)); /* * When retrieving client by number, also * echo back it's IP address. */ if ((vp->da->type == PW_TYPE_INTEGER) && (client->ipaddr.af == AF_INET)) { vp = radius_paircreate(request->reply, &request->reply->vps, 167, VENDORPEC_FREERADIUS); if (vp) { vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr; } if (client->ipaddr.prefix != 32) { vp = radius_paircreate(request->reply, &request->reply->vps, 169, VENDORPEC_FREERADIUS); if (vp) { vp->vp_integer = client->ipaddr.prefix; } } } if (server_ip) { pairadd(&request->reply->vps, paircopyvp(request->reply, server_ip)); } if (server_port) { pairadd(&request->reply->vps, paircopyvp(request->reply, server_port)); } if ((flag->vp_integer & 0x01) != 0) { request_stats_addvp(request, client_authvp, &client->auth); } #ifdef WITH_ACCOUNTING if ((flag->vp_integer & 0x01) != 0) { request_stats_addvp(request, client_acctvp, &client->acct); } #endif } /* else client wasn't found, don't echo it back */ } /* * For a particular "listen" socket. */ if (((flag->vp_integer & 0x40) != 0) && ((flag->vp_integer & 0x03) != 0)) { rad_listen_t *this; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; /* * See if we need to look up the server by socket * socket. */ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_ip) return; server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_port) return; ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; this = listener_find_byipaddr(&ipaddr, server_port->vp_integer, IPPROTO_UDP); /* * Not found: don't do anything */ if (!this) return; pairadd(&request->reply->vps, paircopyvp(request->reply, server_ip)); pairadd(&request->reply->vps, paircopyvp(request->reply, server_port)); if (((flag->vp_integer & 0x01) != 0) && ((request->listener->type == RAD_LISTEN_AUTH) || (request->listener->type == RAD_LISTEN_NONE))) { request_stats_addvp(request, authvp, &this->stats); } #ifdef WITH_ACCOUNTING if (((flag->vp_integer & 0x02) != 0) && ((request->listener->type == RAD_LISTEN_ACCT) || (request->listener->type == RAD_LISTEN_NONE))) { request_stats_addvp(request, acctvp, &this->stats); } #endif } #ifdef WITH_PROXY /* * Home servers. */ if (((flag->vp_integer & 0x80) != 0) && ((flag->vp_integer & 0x03) != 0)) { home_server_t *home; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; /* * See if we need to look up the server by socket * socket. */ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_ip) return; server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_port) return; #ifndef NDEBUG memset(&ipaddr, 0, sizeof(ipaddr)); #endif ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; home = home_server_find(&ipaddr, server_port->vp_integer, IPPROTO_UDP); /* * Not found: don't do anything */ if (!home) return; pairadd(&request->reply->vps, paircopyvp(request->reply, server_ip)); pairadd(&request->reply->vps, paircopyvp(request->reply, server_port)); vp = radius_paircreate(request->reply, &request->reply->vps, 172, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->currently_outstanding; vp = radius_paircreate(request->reply, &request->reply->vps, 173, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->state; if ((home->state == HOME_STATE_ALIVE) && (home->revive_time.tv_sec != 0)) { vp = radius_paircreate(request->reply, &request->reply->vps, 175, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->revive_time.tv_sec; } if ((home->state == HOME_STATE_ALIVE) && (home->ema.window > 0)) { vp = radius_paircreate(request->reply, &request->reply->vps, 178, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->ema.window; vp = radius_paircreate(request->reply, &request->reply->vps, 179, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE; vp = radius_paircreate(request->reply, &request->reply->vps, 180, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE; } if (home->state == HOME_STATE_IS_DEAD) { vp = radius_paircreate(request->reply, &request->reply->vps, 174, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period; } /* * Show more information... * * FIXME: do this for clients, too! */ vp = radius_paircreate(request->reply, &request->reply->vps, 184, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->last_packet_recv; vp = radius_paircreate(request->reply, &request->reply->vps, 185, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->last_packet_sent; if (((flag->vp_integer & 0x01) != 0) && (home->type == HOME_TYPE_AUTH)) { request_stats_addvp(request, proxy_authvp, &home->stats); } #ifdef WITH_ACCOUNTING if (((flag->vp_integer & 0x02) != 0) && (home->type == HOME_TYPE_ACCT)) { request_stats_addvp(request, proxy_acctvp, &home->stats); } #endif } #endif /* WITH_PROXY */ }
/* * Allocate an IP number from the pool. */ static int sqlippool_postauth(void *instance, REQUEST * request) { rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance; char allocation[MAX_STRING_LEN]; int allocation_len; uint32_t ip_allocation; VALUE_PAIR * vp; SQLSOCK * sqlsocket; fr_ipaddr_t ipaddr; char logstr[MAX_STRING_LEN]; char sqlusername[MAX_STRING_LEN]; /* * If there is a Framed-IP-Address attribute in the reply do nothing */ if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS, 0) != NULL) { /* We already have a Framed-IP-Address */ radius_xlat(logstr, sizeof(logstr), data->log_exists, request, NULL, NULL); RDEBUG("Framed-IP-Address already exists"); return do_logging(logstr, RLM_MODULE_NOOP); } if (pairfind(request->config_items, PW_POOL_NAME, 0) == NULL) { RDEBUG("No Pool-Name defined."); radius_xlat(logstr, sizeof(logstr), data->log_nopool, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOOP); } sqlsocket = data->sql_inst->sql_get_socket(data->sql_inst); if (sqlsocket == NULL) { RDEBUG("cannot allocate sql connection"); return RLM_MODULE_FAIL; } if (data->sql_inst->sql_set_user(data->sql_inst, request, sqlusername, NULL) < 0) { return RLM_MODULE_FAIL; } /* * BEGIN */ sqlippool_command(data->allocate_begin, sqlsocket, data, request, (char *) NULL, 0); /* * CLEAR */ sqlippool_command(data->allocate_clear, sqlsocket, data, request, (char *) NULL, 0); /* * FIND */ allocation_len = sqlippool_query1(allocation, sizeof(allocation), data->allocate_find, sqlsocket, data, request, (char *) NULL, 0); /* * Nothing found... */ if (allocation_len == 0) { /* * COMMIT */ sqlippool_command(data->allocate_commit, sqlsocket, instance, request, (char *) NULL, 0); /* * Should we perform pool-check ? */ if (data->pool_check && *data->pool_check) { /* * Ok, so the allocate-find query found nothing ... * Let's check if the pool exists at all */ allocation_len = sqlippool_query1(allocation, sizeof(allocation), data->pool_check, sqlsocket, data, request, (char *) NULL, 0); data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); if (allocation_len) { /* * Pool exists after all... So, * the failure to allocate the IP * address was most likely due to * the depletion of the pool. In * that case, we should return * NOTFOUND */ RDEBUG("pool appears to be full"); radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOTFOUND); } /* * Pool doesn't exist in the table. It * may be handled by some other instance of * sqlippool, so we should just ignore this * allocation failure and return NOOP */ RDEBUG("IP address could not be allocated as no pool exists with that name."); return RLM_MODULE_NOOP; } data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); RDEBUG("IP address could not be allocated."); radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOOP); } /* * FIXME: Make it work with the ipv6 addresses */ if ((ip_hton(allocation, AF_INET, &ipaddr) < 0) || ((ip_allocation = ipaddr.ipaddr.ip4addr.s_addr) == INADDR_NONE)) { /* * COMMIT */ sqlippool_command(data->allocate_commit, sqlsocket, instance, request, (char *) NULL, 0); RDEBUG("Invalid IP number [%s] returned from database query.", allocation); data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOOP); } /* * UPDATE */ sqlippool_command(data->allocate_update, sqlsocket, data, request, allocation, allocation_len); RDEBUG("Allocated IP %s [%08x]", allocation, ip_allocation); vp = radius_paircreate(request, &request->reply->vps, PW_FRAMED_IP_ADDRESS, 0, PW_TYPE_IPADDR); vp->vp_ipaddr = ip_allocation; /* * COMMIT */ sqlippool_command(data->allocate_commit, sqlsocket, data, request, (char *) NULL, 0); data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); radius_xlat(logstr, sizeof(logstr), data->log_success, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_OK); }
/* * 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. * * request->proxy gets set only AFTER authorization, so * it's safe to check it here. If it exists, it means * we're doing a second pass through rad_authenticate(). */ if (request->proxy) { int code = 0; if (request->proxy_reply) code = request->proxy_reply->code; switch (code) { /* * Reply of ACCEPT means accept, thus set Auth-Type * accordingly. */ case PW_CODE_ACCESS_ACCEPT: tmp = radius_paircreate(request, &request->config, 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; fr_state_put_vps(request, request->packet, request->reply); 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_ACCESS_REJECT: rad_authlog("Login incorrect (Home Server says so)", request, 0); request->reply->code = PW_CODE_ACCESS_REJECT; fr_state_discard(request, request->packet); return RLM_MODULE_REJECT; default: rad_authlog("Login incorrect (Home Server failed to respond)", request, 0); fr_state_discard(request, request->packet); 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); } /* * Grab the VPS associated with the State attribute. */ fr_state_get_vps(request, request->packet); /* * 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_ACCESS_REJECT; return result; } if (!autz_retry) { tmp = pairfind(request->config, 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, 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) { 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_ACCESS_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, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) { int r, session_type = 0; char logstr[1024]; char umsg[MAX_STRING_LEN + 1]; tmp = pairfind(request->config, 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), "%s (%u)", main_config.denied_msg, check_item->vp_integer); } else { strlcpy(umsg, main_config.denied_msg, sizeof(umsg)); } request->reply->code = PW_CODE_ACCESS_REJECT; /* * They're trying to log in too many times. * Remove ALL reply attributes. */ pairfree(&request->reply->vps); pairmake_reply("Reply-Message", umsg, 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_ACCESS_ACCEPT; 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; }
/* * The main thread handler for requests. * * Wait on the semaphore until we have it, and process the request. */ static void *request_handler_thread(void *arg) { THREAD_HANDLE *self = (THREAD_HANDLE *) arg; /* * Loop forever, until told to exit. */ do { /* * Wait to be signalled. */ DEBUG2("Thread %d waiting to be assigned a request", self->thread_num); re_wait: if (sem_wait(&thread_pool.semaphore) != 0) { /* * Interrupted system call. Go back to * waiting, but DON'T print out any more * text. */ if (errno == EINTR) { DEBUG2("Re-wait %d", self->thread_num); goto re_wait; } radlog(L_ERR, "Thread %d failed waiting for semaphore: %s: Exiting\n", self->thread_num, strerror(errno)); break; } DEBUG2("Thread %d got semaphore", self->thread_num); #ifdef HAVE_OPENSSL_ERR_H /* * Clear the error queue for the current thread. */ ERR_clear_error (); #endif /* * The server is exiting. Don't dequeue any * requests. */ if (thread_pool.stop_flag) break; /* * Try to grab a request from the queue. * * It may be empty, in which case we fail * gracefully. */ if (!request_dequeue(&self->request)) continue; self->request->child_pid = self->pthread_id; self->request_count++; DEBUG2("Thread %d handling request %d, (%d handled so far)", self->thread_num, self->request->number, self->request_count); if ((self->request->packet->code == PW_ACCOUNTING_REQUEST) && thread_pool.auto_limit_acct) { VALUE_PAIR *vp; REQUEST *request = self->request; vp = radius_paircreate(request, &request->config_items, 181, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = thread_pool.pps_in.pps; vp = radius_paircreate(request, &request->config_items, 182, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = thread_pool.pps_in.pps; vp = radius_paircreate(request, &request->config_items, 183, VENDORPEC_FREERADIUS); if (vp) { vp->vp_integer = thread_pool.max_queue_size - thread_pool.num_queued; vp->vp_integer *= 100; vp->vp_integer /= thread_pool.max_queue_size; } } self->request->process(self->request, FR_ACTION_RUN); self->request = NULL; /* * Update the active threads. */ pthread_mutex_lock(&thread_pool.queue_mutex); rad_assert(thread_pool.active_threads > 0); thread_pool.active_threads--; pthread_mutex_unlock(&thread_pool.queue_mutex); } while (self->status != THREAD_CANCELLED); DEBUG2("Thread %d exiting...", self->thread_num); #ifdef HAVE_OPENSSL_ERR_H /* * If we linked with OpenSSL, the application * must remove the thread's error queue before * exiting to prevent memory leaks. */ ERR_remove_state(0); #endif /* * Do this as the LAST thing before exiting. */ self->request = NULL; self->status = THREAD_EXITED; exec_trigger(NULL, NULL, "server.thread.stop", TRUE); return NULL; }
/* * Mangle username if needed, IN PLACE. */ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request) { int num_proxy_state; VALUE_PAIR *namepair; VALUE_PAIR *request_pairs; VALUE_PAIR *tmp; vp_cursor_t cursor; /* * Get the username from the request * If it isn't there, then we can't mangle the request. */ request_pairs = request->packet->vps; namepair = pairfind(request_pairs, PW_USER_NAME, 0, TAG_ANY); if (!namepair || (namepair->length == 0)) { return; } if (inst->with_ntdomain_hack) { char *ptr; char newname[MAX_STRING_LEN]; /* * Windows NT machines often authenticate themselves as * NT_DOMAIN\username. Try to be smart about this. * * FIXME: should we handle this as a REALM ? */ if ((ptr = strchr(namepair->vp_strvalue, '\\')) != NULL) { strlcpy(newname, ptr + 1, sizeof(newname)); /* Same size */ pairstrcpy(namepair, newname); } } if (inst->with_specialix_jetstream_hack) { /* * Specialix Jetstream 8500 24 port access server. * If the user name is 10 characters or longer, a "/" * and the excess characters after the 10th are * appended to the user name. * * Reported by Lucas Heise <*****@*****.**> */ if ((strlen(namepair->vp_strvalue) > 10) && (namepair->vp_strvalue[10] == '/')) { pairstrcpy(namepair, namepair->vp_strvalue + 11); } } /* * Small check: if Framed-Protocol present but Service-Type * is missing, add Service-Type = Framed-User. */ if (pairfind(request_pairs, PW_FRAMED_PROTOCOL, 0, TAG_ANY) != NULL && pairfind(request_pairs, PW_SERVICE_TYPE, 0, TAG_ANY) == NULL) { tmp = radius_paircreate(request->packet, &request->packet->vps, PW_SERVICE_TYPE, 0); tmp->vp_integer = PW_FRAMED_USER; } num_proxy_state = 0; for (tmp = fr_cursor_init(&cursor, &request->packet->vps); tmp; tmp = fr_cursor_next(&cursor)) { if (tmp->da->vendor != 0) { continue; } if (tmp->da->attr != PW_PROXY_STATE) { continue; } num_proxy_state++; } if (num_proxy_state > 10) { RWDEBUG("There are more than 10 Proxy-State attributes in the request"); RWDEBUG("You have likely configured an infinite proxy loop"); } }
/* * Generate the keys after the user has been authenticated. */ static int wimax_postauth(void *instance, REQUEST *request) { rlm_wimax_t *inst = instance; VALUE_PAIR *msk, *emsk, *vp; VALUE_PAIR *mn_nai, *ip, *fa_rk; HMAC_CTX hmac; unsigned int rk1_len, rk2_len, rk_len; int rk_lifetime = 3600; /* ? */ uint32_t mip_spi; uint8_t usage_data[24]; uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE]; uint8_t mip_rk[2 * EVP_MAX_MD_SIZE]; msk = pairfind(request->reply->vps, 1129); emsk = pairfind(request->reply->vps, 1130); if (!msk || !emsk) { RDEBUG("No EAP-MSK or EAP-EMSK. Cannot create WiMAX keys."); return RLM_MODULE_NOOP; } /* * If we delete the MS-MPPE-*-Key attributes, then add in * the WiMAX-MSK so that the client has a key available. */ if (inst->delete_mppe_keys) { pairdelete(&request->reply->vps, ((311 << 16) | 16)); pairdelete(&request->reply->vps, ((311 << 16) | 17)); vp = radius_pairmake(request, &request->reply->vps, "WiMAX-MSK", "0x00", T_OP_EQ); if (vp) { memcpy(vp->vp_octets, msk->vp_octets, msk->length); vp->length = msk->length; } } /* * Initialize usage data. */ memcpy(usage_data, "*****@*****.**", 21); /* with trailing \0 */ usage_data[21] = 0x02; usage_data[22] = 0x00; usage_data[23] = 0x01; /* * MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01) */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL); HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data)); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01) */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL); HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len); HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data)); HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len); vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT); if (vp) rk_lifetime = vp->vp_integer; memcpy(mip_rk, mip_rk_1, rk1_len); memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len); rk_len = rk1_len + rk2_len; /* * MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP"); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL); HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Take the 4 most significant octets. * If less than 256, add 256. */ mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) | (mip_rk_1[2] << 8) | mip_rk_1[3]); if (mip_spi < 256) mip_spi += 256; if (debug_flag) { int len = rk_len; char buffer[512]; if (len > 128) len = 128; /* buffer size */ fr_bin2hex(mip_rk, buffer, len); radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer); radlog_request(L_DBG, 0, request, "MIP-SPI = %08x", ntohl(mip_spi)); } /* * FIXME: Perform SPI collision prevention */ /* * Calculate mobility keys */ mn_nai = pairfind(request->packet->vps, 1900); if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900); if (!mn_nai) { RDEBUG("WARNING: WiMAX-MN-NAI was not found in the request or in the reply."); RDEBUG("WARNING: We cannot calculate MN-HA keys."); } /* * WiMAX-IP-Technology */ vp = NULL; if (mn_nai) vp = pairfind(request->reply->vps, WIMAX2ATTR(23)); if (!vp) { RDEBUG("WARNING: WiMAX-IP-Technology not found in reply."); RDEBUG("WARNING: Not calculating MN-HA keys"); } if (vp) switch (vp->vp_integer) { case 2: /* PMIP4 */ /* * Look for WiMAX-hHA-IP-MIP4 */ ip = pairfind(request->reply->vps, WIMAX2ATTR(6)); if (!ip) { RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-PMIP4 key"); break; } /* * MN-HA-PMIP4 = * H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key */ vp = pairfind(request->reply->vps, WIMAX2ATTR(10)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(10), PW_TYPE_OCTETS); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI */ vp = pairfind(request->reply->vps, WIMAX2ATTR(11)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(11), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI"); break; } vp->vp_integer = mip_spi + 1; break; case 3: /* CMIP4 */ /* * Look for WiMAX-hHA-IP-MIP4 */ ip = pairfind(request->reply->vps, WIMAX2ATTR(6)); if (!ip) { RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-CMIP4 key"); break; } /* * MN-HA-CMIP4 = * H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key */ vp = pairfind(request->reply->vps, WIMAX2ATTR(10)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(10), PW_TYPE_OCTETS); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI */ vp = pairfind(request->reply->vps, WIMAX2ATTR(11)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(11), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI"); break; } vp->vp_integer = mip_spi; break; case 4: /* CMIP6 */ /* * Look for WiMAX-hHA-IP-MIP6 */ ip = pairfind(request->reply->vps, WIMAX2ATTR(7)); if (!ip) { RDEBUG("WARNING: WiMAX-hHA-IP-MIP6 not found. Cannot calculate MN-HA-CMIP6 key"); break; } /* * MN-HA-CMIP6 = * H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key */ vp = pairfind(request->reply->vps, WIMAX2ATTR(12)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(12), PW_TYPE_OCTETS); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI */ vp = pairfind(request->reply->vps, WIMAX2ATTR(13)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(13), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-SPI"); break; } vp->vp_integer = mip_spi + 2; break; default: break; /* do nothing */ } /* * Generate FA-RK, if requested. * * FA-RK= H(MIP-RK, "FA-RK") */ fa_rk = pairfind(request->reply->vps, WIMAX2ATTR(14)); if (fa_rk && (fa_rk->length == 0)) { HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len); fa_rk->length = rk1_len; } /* * Create FA-RK-SPI, which is really SPI-CMIP4, which is * really MIP-SPI. Clear? Of course. This is WiMAX. */ if (fa_rk) { vp = pairfind(request->reply->vps, WIMAX2ATTR(61)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(61), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-FA-RK-SPI"); } else { vp->vp_integer = mip_spi; } } /* * Generate MN-FA = H(FA-RK, "MN FA" | FA-IP | MN-NAI) */ ip = pairfind(request->reply->vps, 1901); if (fa_rk && ip && mn_nai) { HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, fa_rk->vp_octets, fa_rk->length, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "MN FA", 5); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); vp = radius_paircreate(request, &request->reply->vps, 1902, PW_TYPE_OCTETS); if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-FA"); } else { memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; } } /* * Give additional information about requests && responses * * WiMAX-RRQ-MN-HA-SPI */ vp = pairfind(request->packet->vps, WIMAX2ATTR(20)); if (vp) { RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage."); if (!mn_nai) { RDEBUG("WARNING: MN-NAI was not found!"); } /* * WiMAX-RRQ-HA-IP */ if (!pairfind(request->packet->vps, WIMAX2ATTR(18))) { RDEBUG("WARNING: HA-IP was not found!"); } /* * WiMAX-HA-RK-Key-Requested */ vp = pairfind(request->packet->vps, WIMAX2ATTR(58)); if (vp && (vp->vp_integer == 1)) { RDEBUG("Client requested HA-RK: Should use IP to look it up from storage."); } } /* * Wipe the context of all sensitive information. */ HMAC_CTX_cleanup(&hmac); return RLM_MODULE_UPDATED; }
/* * Allocate an IP number from the pool. */ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request) { rlm_sqlippool_t *inst = (rlm_sqlippool_t *) instance; char allocation[MAX_STRING_LEN]; int allocation_len; uint32_t ip_allocation; VALUE_PAIR *vp; rlm_sql_handle_t *handle; fr_ipaddr_t ipaddr; time_t now; /* * If there is a Framed-IP-Address attribute in the reply do nothing */ if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY) != NULL) { RDEBUG("Framed-IP-Address already exists"); return do_logging(request, inst->log_exists, RLM_MODULE_NOOP); } if (pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY) == NULL) { RDEBUG("No Pool-Name defined"); return do_logging(request, inst->log_nopool, RLM_MODULE_NOOP); } handle = inst->sql_inst->sql_get_socket(inst->sql_inst); if (!handle) { REDEBUG("cannot get sql connection"); return RLM_MODULE_FAIL; } if (inst->sql_inst->sql_set_user(inst->sql_inst, request, NULL) < 0) { return RLM_MODULE_FAIL; } /* * Limit the number of clears we do. There are minor * race conditions for the check, but so what. The * actual work is protected by a transaction. The idea * here is that if we're allocating 100 IPs a second, * we're only do 1 CLEAR per second. */ now = time(NULL); if (inst->last_clear < now) { inst->last_clear = now; DO(allocate_begin); DO(allocate_clear); DO(allocate_commit); } DO(allocate_begin); allocation_len = sqlippool_query1(allocation, sizeof(allocation), inst->allocate_find, handle, inst, request, (char *) NULL, 0); /* * Nothing found... */ if (allocation_len == 0) { DO(allocate_commit); /* *Should we perform pool-check ? */ if (inst->pool_check && *inst->pool_check) { /* *Ok, so the allocate-find query found nothing ... *Let's check if the pool exists at all */ allocation_len = sqlippool_query1(allocation, sizeof(allocation), inst->pool_check, handle, inst, request, (char *) NULL, 0); inst->sql_inst->sql_release_socket(inst->sql_inst, handle); if (allocation_len) { /* * Pool exists after all... So, * the failure to allocate the IP * address was most likely due to * the depletion of the pool. In * that case, we should return * NOTFOUND */ RDEBUG("pool appears to be full"); return do_logging(request, inst->log_failed, RLM_MODULE_NOTFOUND); } /* * Pool doesn't exist in the table. It * may be handled by some other instance of * sqlippool, so we should just ignore this * allocation failure and return NOOP */ RDEBUG("IP address could not be allocated as no pool exists with that name"); return RLM_MODULE_NOOP; } inst->sql_inst->sql_release_socket(inst->sql_inst, handle); RDEBUG("IP address could not be allocated"); return do_logging(request, inst->log_failed, RLM_MODULE_NOOP); } /* * FIXME: Make it work with the ipv6 addresses */ if ((ip_hton(&ipaddr, AF_INET, allocation, false) < 0) || ((ip_allocation = ipaddr.ipaddr.ip4addr.s_addr) == INADDR_NONE)) { DO(allocate_commit); RDEBUG("Invalid IP number [%s] returned from instbase query.", allocation); inst->sql_inst->sql_release_socket(inst->sql_inst, handle); return do_logging(request, inst->log_failed, RLM_MODULE_NOOP); } /* * UPDATE */ sqlippool_command(inst->allocate_update, handle, inst, request, allocation, allocation_len); RDEBUG("Allocated IP %s [%08x]", allocation, ip_allocation); vp = radius_paircreate(request->reply, &request->reply->vps, PW_FRAMED_IP_ADDRESS, 0); vp->vp_ipaddr = ip_allocation; vp->length = 4; DO(allocate_commit); inst->sql_inst->sql_release_socket(inst->sql_inst, handle); return do_logging(request, inst->log_success, RLM_MODULE_OK); }