/* * 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 = fr_pair_find_by_num(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY); if (!nas) { nas = radius_pair_create(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 = fr_pair_find_by_num(request->packet->vps, PW_NAS_IPV6_ADDRESS, 0, TAG_ANY); if (!nas) { nas = radius_pair_create(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; }
/* * 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 = fr_pair_find_by_num(request_pairs, PW_HUNTGROUP_NAME, 0, TAG_ANY); if (!vp) { vp = radius_pair_create(request->packet, &request->packet->vps, PW_HUNTGROUP_NAME, 0); fr_pair_value_strcpy(vp, i->name); } r = RLM_MODULE_OK; } break; } return r; }
/* * 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_ACCESS_REQUEST) return RLM_MODULE_NOOP; if (!fr_pair_find_by_num(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) return RLM_MODULE_NOOP; vp = radius_pair_create(request, &request->proxy->vps, PW_CHAP_CHALLENGE, 0); if (!vp) return RLM_MODULE_FAIL; fr_pair_value_memcpy(vp, request->packet->vector, sizeof(request->packet->vector)); return RLM_MODULE_OK; }
/* * 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; char date[50]; check_item = fr_pair_find_by_num(request->config, PW_EXPIRATION, 0, TAG_ANY); if (!check_item) return RLM_MODULE_NOOP; /* * 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 = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY); if (!vp) { vp = radius_pair_create(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); } return RLM_MODULE_OK; }
/* * 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 = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY); if (!vp) { VALUE_PAIR *delay; vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0); vp->vp_date = request->packet->timestamp.tv_sec; delay = fr_pair_find_by_num(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY); if (delay) { if ((delay->vp_integer >= vp->vp_date) || (delay->vp_integer == UINT32_MAX)) { RWARN("Ignoring invalid Acct-Delay-time of %u seconds", delay->vp_integer); } else { 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; VALUE_PAIR *vp; /* * 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(fr_pair_find_by_num(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); } /* * Add an event timestamp. Means Event-Timestamp can be used * consistently instead of one letter expansions. */ vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY); if (!vp) { vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0); vp->vp_date = request->packet->timestamp.tv_sec; } /* * 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 (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) && fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) { vp = radius_pair_create(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0); fr_pair_value_memcpy(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 */ }
/* * 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 = fr_pair_find_by_num(request_pairs, PW_USER_NAME, 0, TAG_ANY); if (!namepair || (namepair->vp_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 */ fr_pair_value_strcpy(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] == '/')) { fr_pair_value_strcpy(namepair, namepair->vp_strvalue + 11); } } /* * Small check: if Framed-Protocol present but Service-Type * is missing, add Service-Type = Framed-User. */ if (fr_pair_find_by_num(request_pairs, PW_FRAMED_PROTOCOL, 0, TAG_ANY) != NULL && fr_pair_find_by_num(request_pairs, PW_SERVICE_TYPE, 0, TAG_ANY) == NULL) { tmp = radius_pair_create(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"); } }
/* * 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; bool xlat_only = false; fr_state_tree_t *state = NULL; fr_talloc_fault_setup(); /* * 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 rad_debug_lvl = 0; set_radius_dir(NULL, RADIUS_DIR); /* * Ensure that the configuration is initialized. */ memset(&main_config, 0, sizeof(main_config)); main_config.name = "unittest"; /* * 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: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 'O': if (strcmp(optarg, "xlat_only") == 0) { xlat_only = true; break; } fprintf(stderr, "Unknown option '%s'\n", optarg); exit(EXIT_FAILURE); case 'X': rad_debug_lvl += 2; main_config.log_auth = true; main_config.log_auth_badpass = true; main_config.log_auth_goodpass = true; break; case 'x': rad_debug_lvl++; break; default: usage(1); break; } } if (rad_debug_lvl) version_print(); fr_debug_lvl = rad_debug_lvl; /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("%s", main_config.name); exit(EXIT_FAILURE); } /* * Initialising OpenSSL once, here, is safer than having individual modules do it. */ #ifdef HAVE_OPENSSL_CRYPTO_H if (tls_global_init() < 0) { rcode = EXIT_FAILURE; goto finish; } #endif if (xlat_register(NULL, "poke", xlat_poke, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN) < 0) { rcode = EXIT_FAILURE; goto finish; } if (map_proc_register(NULL, "test-fail", mod_map_proc, NULL, NULL, 0) < 0) { rcode = EXIT_FAILURE; goto finish; } /* Read the configuration files, BEFORE doing anything else. */ if (main_config_init() < 0) { exit_failure: rcode = EXIT_FAILURE; goto finish; } /* * Setup dummy virtual server */ cf_section_add(main_config.config, cf_section_alloc(main_config.config, "server", "unit_test")); /* * Initialize Auth-Type, etc. in the virtual servers * before loading the modules. Some modules need those * to be defined. */ if (virtual_servers_bootstrap(main_config.config) < 0) goto exit_failure; /* * Bootstrap the modules. This links to them, and runs * their "bootstrap" routines. * * After this step, all dynamic attributes, xlats, etc. are defined. */ if (modules_bootstrap(main_config.config) < 0) exit(EXIT_FAILURE); /* * Load the modules */ if (modules_init(main_config.config) < 0) goto exit_failure; /* * And then load the virtual servers. */ if (virtual_servers_init(main_config.config) < 0) goto exit_failure; state = fr_state_tree_init(NULL, main_config.max_requests * 2, 10); /* * Set the panic action (if required) */ { char const *panic_action = NULL; panic_action = getenv("PANIC_ACTION"); if (!panic_action) panic_action = main_config.panic_action; if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) { fr_perror("%s", main_config.name); exit(EXIT_FAILURE); } } 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; } } /* * For simplicity, read xlat's. */ if (xlat_only) { if (!do_xlats(input_file, fp)) rcode = EXIT_FAILURE; if (input_file) fclose(fp); 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 (fr_pair_list_afrom_file(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_pair_create(request->reply, &request->reply->vps, PW_RESPONSE_PACKET_TYPE, 0); vp->vp_integer = request->reply->code; { VALUE_PAIR const *failed[2]; if (filter_vps && !fr_pair_validate(failed, filter_vps, request->reply->vps)) { fr_pair_validate_debug(request, failed); fr_perror("Output file %s does not match attributes in filter %s (%s)", output_file ? output_file : input_file, filter_file, fr_strerror()); rcode = EXIT_FAILURE; goto finish; } } INFO("Exiting normally"); finish: talloc_free(request); talloc_free(state); /* * Free the configuration items. */ main_config_free(); /* * Detach any modules. */ modules_free(); xlat_unregister(NULL, "poke", xlat_poke); xlat_free(); /* modules may have xlat's */ if (memory_report) { INFO("Allocated memory at time of report:"); fr_log_talloc_report(NULL); } return rcode; }
/** 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; vp_cursor_t cursor; home_server_t *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; /* * Send as many packets as necessary to different * destinations. */ fr_cursor_init(&cursor, &request->config); while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) { realm = realm_find2(vp->vp_strvalue); if (!realm) { REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_CODE_ACCESS_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_CODE_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_CODE_COA_REQUEST: case PW_CODE_DISCONNECT_REQUEST: pool = realm->coa_pool; break; #endif } if (!pool) { RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { REDEBUG2("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(request, true); if (!packet) { return RLM_MODULE_FAIL; } packet->code = code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { REDEBUG("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } 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 done; } /* * Don't assume the list actually contains any * attributes. */ if (*vps) { packet->vps = fr_pair_list_copy(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_CODE_ACCESS_REQUEST) && (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { uint8_t *p; vp = radius_pair_create(packet, &packet->vps, PW_CHAP_CHALLENGE, 0); vp->length = AUTH_VECTOR_LEN; vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length); memcpy(p, 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++; TALLOC_FREE(packet->data); 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, "<INVALID>"), realm->name); if (rad_send(packet, NULL, home->secret) < 0) { REDEBUG("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; }
/* * 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_pair_create(request, &request->config, PW_AUTH_TYPE, 0); if (tmp) tmp->vp_integer = PW_AUTH_TYPE_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 = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); } if (!request->password) { request->password = fr_pair_find_by_num(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 = fr_pair_find_by_num(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 = fr_pair_find_by_num(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 = fr_pair_find_by_num(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 = fr_pair_find_by_num(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 ((rad_debug_lvl > 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, -1); 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 = fr_pair_find_by_num(request->config, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) { int r, session_type = 0; char logstr[1024]; char umsg[MAX_STRING_LEN + 1]; tmp = fr_pair_find_by_num(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 = fr_pair_find_by_num(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. */ fr_pair_list_free(&request->reply->vps); pair_make_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 = fr_pair_find_by_num(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; }