/* * Authenticate a previously sent challenge. */ static int mschapv2_authenticate(void *arg, EAP_HANDLER *handler) { int rcode, ccode; mschapv2_opaque_t *data; EAP_DS *eap_ds = handler->eap_ds; VALUE_PAIR *challenge, *response, *name; rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg; rad_assert(handler->request != NULL); rad_assert(handler->stage == AUTHENTICATE); data = (mschapv2_opaque_t *) handler->opaque; /* * Sanity check the response. */ if (eap_ds->response->length <= 5) { radlog(L_ERR, "rlm_eap_mschapv2: corrupted data"); return 0; } ccode = eap_ds->response->type.data[0]; switch (data->code) { case PW_EAP_MSCHAPV2_FAILURE: if (ccode == PW_EAP_MSCHAPV2_RESPONSE) { DEBUG2(" rlm_eap_mschapv2: authentication re-try from client after we sent a failure"); break; } /* * if we sent error 648 (password expired) to the client * we might get an MSCHAP-CPW packet here; turn it into a * regular MS-CHAP2-CPW packet and pass it to rlm_mschap * (or proxy it, I guess) */ if (ccode == PW_EAP_MSCHAPV2_CHGPASSWD) { VALUE_PAIR *cpw; int mschap_id = eap_ds->response->type.data[1]; int copied=0,seq=1; DEBUG2(" rlm_eap_mschapv2: password change packet received"); challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ); if (!challenge) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } challenge->length = MSCHAPV2_CHALLENGE_LEN; memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN); pairadd(&handler->request->packet->vps, challenge); cpw = pairmake("MS-CHAP2-CPW", "", T_OP_EQ); cpw->vp_octets[0] = 7; cpw->vp_octets[1] = mschap_id; memcpy(cpw->vp_octets+2, eap_ds->response->type.data + 520, 66); cpw->length = 68; pairadd(&handler->request->packet->vps, cpw); /* * break the encoded password into VPs (3 of them) */ while (copied < 516) { VALUE_PAIR *nt_enc; int to_copy = 516 - copied; if (to_copy > 243) to_copy = 243; nt_enc = pairmake("MS-CHAP-NT-Enc-PW", "", T_OP_ADD); nt_enc->vp_octets[0] = 6; nt_enc->vp_octets[1] = mschap_id; nt_enc->vp_octets[2] = 0; nt_enc->vp_octets[3] = seq++; memcpy(nt_enc->vp_octets + 4, eap_ds->response->type.data + 4 + copied, to_copy); copied += to_copy; nt_enc->length = 4 + to_copy; pairadd(&handler->request->packet->vps, nt_enc); } DEBUG2(" rlm_eap_mschapv2: built change password packet"); debug_pair_list(handler->request->packet->vps); /* * jump to "authentication" */ goto packet_ready; } /* * we sent a failure and are expecting a failure back */ if (ccode != PW_EAP_MSCHAPV2_FAILURE) { radlog(L_ERR, "rlm_eap_mschapv2: Sent FAILURE expecting FAILURE but got %d", ccode); return 0; } failure: handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; eap_ds->request->code = PW_EAP_FAILURE; return 1; case PW_EAP_MSCHAPV2_SUCCESS: /* * we sent a success to the client; some clients send a * success back as-per the RFC, some send an ACK. Permit * both, I guess... */ switch (ccode) { case PW_EAP_MSCHAPV2_SUCCESS: eap_ds->request->code = PW_EAP_SUCCESS; pairadd(&handler->request->reply->vps, data->mppe_keys); data->mppe_keys = NULL; /* fall through... */ case PW_EAP_MSCHAPV2_ACK: #ifdef WITH_PROXY /* * It's a success. Don't proxy it. */ handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; #endif pairadd(&handler->request->reply->vps, data->reply); data->reply = NULL; return 1; } radlog(L_ERR, "rlm_eap_mschapv2: Sent SUCCESS expecting SUCCESS (or ACK) but got %d", ccode); return 0; case PW_EAP_MSCHAPV2_CHALLENGE: if (ccode == PW_EAP_MSCHAPV2_FAILURE) goto failure; /* * we sent a challenge, expecting a response */ if (ccode != PW_EAP_MSCHAPV2_RESPONSE) { radlog(L_ERR, "rlm_eap_mschapv2: Sent CHALLENGE expecting RESPONSE but got %d", ccode); return 0; } /* authentication happens below */ break; default: /* should never happen */ radlog(L_ERR, "rlm_eap_mschapv2: unknown state %d", data->code); return 0; } /* * Ensure that we have at least enough data * to do the following checks. * * EAP header (4), EAP type, MS-CHAP opcode, * MS-CHAP ident, MS-CHAP data length (2), * MS-CHAP value length. */ if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) { radlog(L_ERR, "rlm_eap_mschapv2: Response is too short"); return 0; } /* * The 'value_size' is the size of the response, * which is supposed to be the response (48 * bytes) plus 1 byte of flags at the end. */ if (eap_ds->response->type.data[4] != 49) { radlog(L_ERR, "rlm_eap_mschapv2: Response is of incorrect length %d", eap_ds->response->type.data[4]); return 0; } /* * The MS-Length field is 5 + value_size + length * of name, which is put after the response. */ if (((eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3]) < (5 + 49)) { radlog(L_ERR, "rlm_eap_mschapv2: Response contains contradictory length %d %d", (eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3], 5 + 49); return 0; } /* * We now know that the user has sent us a response * to the challenge. Let's try to authenticate it. * * We do this by taking the challenge from 'data', * the response from the EAP packet, and creating VALUE_PAIR's * to pass to the 'mschap' module. This is a little wonky, * but it works. */ challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ); if (!challenge) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } challenge->length = MSCHAPV2_CHALLENGE_LEN; memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN); response = pairmake("MS-CHAP2-Response", "0x00", T_OP_EQ); if (!response) { pairfree(&challenge); radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } response->length = MSCHAPV2_RESPONSE_LEN; memcpy(response->vp_strvalue + 2, &eap_ds->response->type.data[5], MSCHAPV2_RESPONSE_LEN - 2); response->vp_strvalue[0] = eap_ds->response->type.data[1]; response->vp_strvalue[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN]; name = pairmake("NTLM-User-Name", "", T_OP_EQ); if (!name) { pairfree(&challenge); pairfree(&response); radlog(L_ERR, "rlm_eap_mschapv2: Failed creating NTLM-User-Name: %s", fr_strerror()); return 0; } /* * MS-Length - MS-Value - 5. */ name->length = (((eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3]) - eap_ds->response->type.data[4] - 5); if (name->length >= sizeof(name->vp_strvalue)) { name->length = sizeof(name->vp_strvalue) - 1; } memcpy(name->vp_strvalue, &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN], name->length); name->vp_strvalue[name->length] = '\0'; /* * Add the pairs to the request, and call the 'mschap' * module. */ pairadd(&handler->request->packet->vps, challenge); pairadd(&handler->request->packet->vps, response); pairadd(&handler->request->packet->vps, name); packet_ready: #ifdef WITH_PROXY /* * If this options is set, then we do NOT authenticate the * user here. Instead, now that we've added the MS-CHAP * attributes to the request, we STOP, and let the outer * tunnel code handle it. * * This means that the outer tunnel code will DELETE the * EAP attributes, and proxy the MS-CHAP attributes to a * home server. */ if (handler->request->options & RAD_REQUEST_OPTION_PROXY_EAP) { char *username = NULL; eap_tunnel_data_t *tunnel; DEBUG2("rlm_eap_mschapv2: cancelling authentication and letting it be proxied"); /* * Set up the callbacks for the tunnel */ tunnel = rad_malloc(sizeof(*tunnel)); memset(tunnel, 0, sizeof(*tunnel)); tunnel->tls_session = arg; tunnel->callback = mschap_postproxy; /* * Associate the callback with the request. */ rcode = request_data_add(handler->request, handler->request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK, tunnel, free); rad_assert(rcode == 0); /* * The State attribute is NOT supposed to * go into the proxied packet, it will confuse * other RADIUS servers, and they will discard * the request. * * The PEAP module will take care of adding * the State attribute back, before passing * the handler & request back into the tunnel. */ pairdelete(&handler->request->packet->vps, PW_STATE, 0, TAG_ANY); /* * Fix the User-Name when proxying, to strip off * the NT Domain, if we're told to, and a User-Name * exists, and there's a \\, meaning an NT-Domain * in the user name, THEN discard the user name. */ if (inst->with_ntdomain_hack && ((challenge = pairfind(handler->request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) && ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) { /* * Wipe out the NT domain. * * FIXME: Put it into MS-CHAP-Domain? */ username++; /* skip the \\ */ memmove(challenge->vp_strvalue, username, strlen(username) + 1); /* include \0 */ challenge->length = strlen(challenge->vp_strvalue); } /* * Remember that in the post-proxy stage, we've got * to do the work below, AFTER the call to MS-CHAP * authentication... */ return 1; } #endif /* * This is a wild & crazy hack. */ rcode = module_authenticate(PW_AUTHTYPE_MS_CHAP, handler->request); /* * Delete MPPE keys & encryption policy. We don't * want these here. */ fix_mppe_keys(handler, data); /* * Take the response from the mschap module, and * return success or failure, depending on the result. */ response = NULL; if (rcode == RLM_MODULE_OK) { pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY); data->code = PW_EAP_MSCHAPV2_SUCCESS; } else if (inst->send_error) { pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY); if (response) { int n,err,retry; char buf[34]; DEBUG2(" MSCHAP-Error: %s", response->vp_strvalue); /* * Pxarse the new challenge out of the * MS-CHAP-Error, so that if the client * issues a re-try, we will know which * challenge value that they used. */ n = sscanf(response->vp_strvalue, "%*cE=%d R=%d C=%32s", &err, &retry, &buf[0]); if (n == 3) { DEBUG2(" Found new challenge from MS-CHAP-Error: err=%d retry=%d challenge=%s", err, retry, buf); fr_hex2bin(buf, data->challenge, 16); } else { DEBUG2(" Could not parse new challenge from MS-CHAP-Error: %d", n); } } data->code = PW_EAP_MSCHAPV2_FAILURE; } else { eap_ds->request->code = PW_EAP_FAILURE; return 1; } /* * No response, die. */ if (!response) { radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found."); return 0; } /* * Compose the response (whatever it is), * and return it to the over-lying EAP module. */ eapmschapv2_compose(handler, response); pairfree(&response); return 1; }
static rlm_rcode_t rlm_sql_authorize(void *instance, REQUEST * request) { int ret = RLM_MODULE_NOTFOUND; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; int dofallthrough = 1; int rows; char querystr[MAX_QUERY_LEN]; /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) return RLM_MODULE_FAIL; /* * Reserve a socket * * After this point use goto error or goto release to cleanup sockets * temporary pairlists and temporary attributes. */ handle = sql_get_socket(inst); if (handle == NULL) goto error; /* * Query the check table to find any conditions associated with * this user/realm/whatever... */ if (inst->config->authorize_check_query && *inst->config->authorize_check_query) { if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); goto error; } rows = sql_getvpdata(inst, &handle, &check_tmp, querystr); if (rows < 0) { radlog_request(L_ERR, 0, request, "SQL query error; rejecting user"); goto error; } /* * Only do this if *some* check pairs were returned */ if ((rows > 0) && (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0)) { RDEBUG2("User found in radcheck table"); radius_xlat_move(request, &request->config_items, &check_tmp); ret = RLM_MODULE_OK; } /* * We only process reply table items if check conditions * were verified */ else goto skipreply; } if (inst->config->authorize_reply_query && *inst->config->authorize_reply_query) { /* * Now get the reply pairs since the paircompare matched */ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); goto error; } rows = sql_getvpdata(inst, &handle, &reply_tmp, querystr); if (rows < 0) { radlog_request(L_ERR, 0, request, "SQL query error; rejecting user"); goto error; } if (rows > 0) { if (!inst->config->read_groups) { dofallthrough = fallthrough(reply_tmp); } RDEBUG2("User found in radreply table"); radius_xlat_move(request, &request->reply->vps, &reply_tmp); ret = RLM_MODULE_OK; } } skipreply: /* * Clear out the pairlists */ pairfree(&check_tmp); pairfree(&reply_tmp); /* * dofallthrough is set to 1 by default so that if the user information * is not found, we will still process groups. If the user information, * however, *is* found, Fall-Through must be set in order to process * the groups as well. */ if (dofallthrough) { rows = rlm_sql_process_groups(inst, request, handle, &dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user"); goto error; } if (rows > 0) ret = RLM_MODULE_OK; } /* * Repeat the above process with the default profile or User-Profile */ if (dofallthrough) { /* * Check for a default_profile or for a User-Profile. */ user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY); const char *profile = user_profile ? user_profile->vp_strvalue : inst->config->default_profile; if (!profile || !*profile) goto release; RDEBUG("Checking profile %s", profile); if (sql_set_user(inst, request, profile) < 0) { radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user"); goto error; } rows = rlm_sql_process_groups(inst, request, handle, &dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user"); goto error; } if (rows > 0) ret = RLM_MODULE_OK; } goto release; error: ret = RLM_MODULE_FAIL; release: sql_release_socket(inst, handle); pairfree(&check_tmp); pairfree(&reply_tmp); return ret; }
/* * 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, 0, PW_TYPE_INTEGER); if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT; 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, 0); } /* * 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, 0)) != 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, 0)) != 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, 0); if (tmp) { RDEBUG2("Using Autz-Type %s", tmp->vp_strvalue); autz_type = tmp->vp_integer; 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, 0)) != 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, 0)) != 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)) { char *p; p = auth_item->vp_strvalue; while (*p != '\0') { if (!isprint((int) *p)) { log_debug(" WARNING: Unprintable characters in the password.\n\t Double-check the shared secret on the server and the NAS!"); break; } p++; } } } #ifdef WITH_SESSION_MGMT if (result >= 0 && (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE, 0)) != 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, 0); if (tmp) { RDEBUG2("Using Session-Type %s", tmp->vp_strvalue); session_type = tmp->vp_integer; } /* * 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, 0)) != 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, 0)) != NULL) && (tmp->flags.addport != 0)) { VALUE_PAIR *vpPortId; /* * Find the NAS port ID. */ if ((vpPortId = pairfind(request->packet->vps, PW_NAS_PORT, 0)) != 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, 0); } } /* * 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, 0)) != 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; }
/** Execute a program. * * @param cmd Command to execute. This is parsed into argv[] parts, * then each individual argv part is xlat'ed. * @param request current request. * @param exec_wait set to 1 if you want to read from or write to child * @param user_msg buffer to append plaintext (non valuepair) output. * @param msg_len length of user_msg buffer. * @param input_pairs list of value pairs - these will be put into * the environment variables of the child. * @param[out] output_pairs list of value pairs - child stdout will be * parsed and added into this list of value pairs. * @param shell_escape * @return 0 if exec_wait==0, exit code if exec_wait!=0, -1 on error. */ int radius_exec_program(const char *cmd, REQUEST *request, int exec_wait, char *user_msg, int msg_len, VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs, int shell_escape) { pid_t pid; int from_child; #ifndef __MINGW32__ VALUE_PAIR *vp; char *p; pid_t child_pid; int comma = 0; int status; int n, done; char answer[4096]; #endif pid = radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape); if (pid < 0) { return -1; } if (!exec_wait) return 0; #ifndef __MINGW32__ done = radius_readfrom_program(from_child, pid, 10, answer, sizeof(answer)); if (done < 0) { /* * failure - radius_readfrom_program will * have called close(from_child) for us */ DEBUG("failed to read from child output"); return 1; } answer[done] = 0; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(from_child); DEBUG2("Exec-Program output: %s", answer); /* * Parse the output, if any. */ if (done) { n = T_OP_INVALID; if (output_pairs) { /* * For backwards compatibility, first check * for plain text (user_msg). */ vp = NULL; n = userparse(answer, &vp); if (vp) { pairfree(&vp); } } if (n == T_OP_INVALID) { DEBUG("Exec-Program-Wait: plaintext: %s", answer); if (user_msg) { strlcpy(user_msg, answer, msg_len); } } else { /* * HACK: Replace '\n' with ',' so that * userparse() can parse the buffer in * one go (the proper way would be to * fix userparse(), but oh well). */ for (p = answer; *p; p++) { if (*p == '\n') { *p = comma ? ' ' : ','; p++; comma = 0; } if (*p == ',') comma++; } /* * Replace any trailing comma by a NUL. */ if (answer[strlen(answer) - 1] == ',') { answer[strlen(answer) - 1] = '\0'; } radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer); if (userparse(answer, &vp) == T_OP_INVALID) { radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd); } else { /* * Tell the caller about the value * pairs. */ *output_pairs = vp; } } /* else the answer was a set of VP's, not a text message */ } /* else we didn't read anything from the child */ /* * Call rad_waitpid (should map to waitpid on non-threaded * or single-server systems). */ child_pid = rad_waitpid(pid, &status); if (child_pid == 0) { radlog(L_DBG, "Exec-Program: Timeout waiting for child"); return 2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); radlog(L_DBG, "Exec-Program: returned: %d", status); return status; } } radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s", strerror(errno)); #endif /* __MINGW32__ */ return 1; }
/* * Call the function_name inside the module * Store all vps in hashes %RAD_CHECK %RAD_REPLY %RAD_REQUEST * */ static int rlmperl_call(void *instance, REQUEST *request, char *function_name) { PERL_INST *inst = instance; VALUE_PAIR *vp; int exitstatus=0, count; STRLEN n_a; HV *rad_reply_hv; HV *rad_check_hv; HV *rad_config_hv; HV *rad_request_hv; #ifdef WITH_PROXY HV *rad_request_proxy_hv; HV *rad_request_proxy_reply_hv; #endif #ifdef USE_ITHREADS pthread_mutex_lock(&inst->clone_mutex); PerlInterpreter *interp; interp = rlm_perl_clone(inst->perl,inst->thread_key); { dTHXa(interp); PERL_SET_CONTEXT(interp); } pthread_mutex_unlock(&inst->clone_mutex); #else PERL_SET_CONTEXT(inst->perl); #endif { dSP; ENTER; SAVETMPS; /* * Radius has told us to call this function, but none * is defined. */ if (!function_name) { return RLM_MODULE_FAIL; } rad_reply_hv = get_hv("RAD_REPLY",1); rad_check_hv = get_hv("RAD_CHECK",1); rad_config_hv = get_hv("RAD_CONFIG",1); rad_request_hv = get_hv("RAD_REQUEST",1); #ifdef WITH_PROXY rad_request_proxy_hv = get_hv("RAD_REQUEST_PROXY",1); rad_request_proxy_reply_hv = get_hv("RAD_REQUEST_PROXY_REPLY",1); #endif perl_store_vps(request->reply->vps, rad_reply_hv); perl_store_vps(request->config_items, rad_check_hv); perl_store_vps(request->packet->vps, rad_request_hv); perl_store_vps(request->config_items, rad_config_hv); #ifdef WITH_PROXY if (request->proxy != NULL) { perl_store_vps(request->proxy->vps, rad_request_proxy_hv); } else { hv_undef(rad_request_proxy_hv); } if (request->proxy_reply !=NULL) { perl_store_vps(request->proxy_reply->vps, rad_request_proxy_reply_hv); } else { hv_undef(rad_request_proxy_reply_hv); } #endif PUSHMARK(SP); /* * This way %RAD_xx can be pushed onto stack as sub parameters. * XPUSHs( newRV_noinc((SV *)rad_request_hv) ); * XPUSHs( newRV_noinc((SV *)rad_reply_hv) ); * XPUSHs( newRV_noinc((SV *)rad_check_hv) ); * PUTBACK; */ count = call_pv(function_name, G_SCALAR | G_EVAL | G_NOARGS); SPAGAIN; if (SvTRUE(ERRSV)) { radlog(L_ERR, "rlm_perl: perl_embed:: module = %s , func = %s exit status= %s\n", inst->module, function_name, SvPV(ERRSV,n_a)); (void)POPs; } if (count == 1) { exitstatus = POPi; if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; vp = NULL; if ((get_hv_content(rad_request_hv, &vp)) > 0 ) { pairfree(&request->packet->vps); request->packet->vps = vp; vp = NULL; /* * Update cached copies */ request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); 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); } if ((get_hv_content(rad_reply_hv, &vp)) > 0 ) { pairfree(&request->reply->vps); request->reply->vps = vp; vp = NULL; } if ((get_hv_content(rad_check_hv, &vp)) > 0 ) { pairfree(&request->config_items); request->config_items = vp; vp = NULL; } #ifdef WITH_PROXY if (request->proxy && (get_hv_content(rad_request_proxy_hv, &vp) > 0)) { pairfree(&request->proxy->vps); request->proxy->vps = vp; vp = NULL; } if (request->proxy_reply && (get_hv_content(rad_request_proxy_reply_hv, &vp) > 0)) { pairfree(&request->proxy_reply->vps); request->proxy_reply->vps = vp; vp = NULL; } #endif } return exitstatus; }
/* * Common attr_filter checks */ static rlm_rcode_t CC_HINT(nonnull(1,2)) attr_filter_common(void *instance, REQUEST *request, RADIUS_PACKET *packet) { rlm_attr_filter_t *inst = instance; VALUE_PAIR *vp; vp_cursor_t input, check, out; VALUE_PAIR *input_item, *check_item, *output; PAIR_LIST *pl; int found = 0; int pass, fail = 0; char const *keyname = NULL; char buffer[256]; if (!packet) return RLM_MODULE_NOOP; if (!inst->key) { VALUE_PAIR *namepair; namepair = pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY); if (!namepair) { return (RLM_MODULE_NOOP); } keyname = namepair->vp_strvalue; } else { int len; len = radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL); if (len < 0) { return RLM_MODULE_FAIL; } if (len == 0) { return RLM_MODULE_NOOP; } keyname = buffer; } /* * Head of the output list */ output = NULL; fr_cursor_init(&out, &output); /* * Find the attr_filter profile entry for the entry. */ for (pl = inst->attrs; pl; pl = pl->next) { int fall_through = 0; int relax_filter = inst->relaxed; /* * If the current entry is NOT a default, * AND the realm does NOT match the current entry, * then skip to the next entry. */ if ((strcmp(pl->name, "DEFAULT") != 0) && (strcmp(keyname, pl->name) != 0)) { continue; } RDEBUG2("Matched entry %s at line %d", pl->name, pl->lineno); found = 1; for (check_item = fr_cursor_init(&check, &pl->check); check_item; check_item = fr_cursor_next(&check)) { if (!check_item->da->vendor && (check_item->da->attr == PW_FALL_THROUGH) && (check_item->vp_integer == 1)) { fall_through = 1; continue; } else if (!check_item->da->vendor && check_item->da->attr == PW_RELAX_FILTER) { relax_filter = check_item->vp_integer; continue; } /* * If it is a SET operator, add the attribute to * the output list without checking it. */ if (check_item->op == T_OP_SET ) { vp = paircopyvp(packet, check_item); if (!vp) { goto error; } radius_xlat_do(request, vp); fr_cursor_insert(&out, vp); } } /* * Iterate through the input items, comparing * each item to every rule, then moving it to the * output list only if it matches all rules * for that attribute. IE, Idle-Timeout is moved * only if it matches all rules that describe an * Idle-Timeout. */ for (input_item = fr_cursor_init(&input, &packet->vps); input_item; input_item = fr_cursor_next(&input)) { pass = fail = 0; /* reset the pass,fail vars for each reply item */ /* * Reset the check_item pointer to beginning of the list */ for (check_item = fr_cursor_first(&check); check_item; check_item = fr_cursor_next(&check)) { /* * Vendor-Specific is special, and matches any VSA if the * comparison is always true. */ if ((check_item->da->attr == PW_VENDOR_SPECIFIC) && (input_item->da->vendor != 0) && (check_item->op == T_OP_CMP_TRUE)) { pass++; continue; } if (input_item->da == check_item->da) { check_pair(request, check_item, input_item, &pass, &fail); } } RDEBUG3("Attribute \"%s\" allowed by %i rules, disallowed by %i rules", input_item->da->name, pass, fail); /* * Only move attribute if it passed all rules, or if the config says we * should copy unmatched attributes ('relaxed' mode). */ if (fail == 0 && (pass > 0 || relax_filter)) { if (!pass) { RDEBUG3("Attribute \"%s\" allowed by relaxed mode", input_item->da->name); } vp = paircopyvp(packet, input_item); if (!vp) { goto error; } fr_cursor_insert(&out, vp); } } /* If we shouldn't fall through, break */ if (!fall_through) { break; } } /* * No entry matched. We didn't do anything. */ if (!found) { rad_assert(!output); return RLM_MODULE_NOOP; } /* * Replace the existing request list with our filtered one */ pairfree(&packet->vps); packet->vps = output; if (request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) { request->username = pairfind(request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY); if (!request->username) { request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); } request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); } return RLM_MODULE_UPDATED; error: pairfree(&output); return RLM_MODULE_FAIL; }
/** Evaluate a map * * @param[in] request the REQUEST * @param[in] modreturn the previous module return code * @param[in] depth of the recursion (only used for debugging) * @param[in] c the condition to evaluate * @return -1 on error, 0 for "no match", 1 for "match". */ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c) { int rcode; char *lhs, *rhs; value_pair_map_t *map; rad_assert(c->type == COND_TYPE_MAP); map = c->data.map; rad_assert(map->dst->type != VPT_TYPE_UNKNOWN); rad_assert(map->src->type != VPT_TYPE_UNKNOWN); rad_assert(map->dst->type != VPT_TYPE_LIST); rad_assert(map->src->type != VPT_TYPE_LIST); rad_assert(map->dst->type != VPT_TYPE_REGEX); rad_assert(map->dst->type != VPT_TYPE_REGEX_STRUCT); EVAL_DEBUG("Map %s ? %s", fr_int2str(template_names, map->dst->type, "???"), fr_int2str(template_names, map->src->type, "???")); /* * Verify regexes. */ if ((map->src->type == VPT_TYPE_REGEX) || (map->src->type == VPT_TYPE_REGEX_STRUCT)) { rad_assert(map->op == T_OP_REG_EQ); } else { rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE))); } /* * They're both attributes. Do attribute-specific work. * * LHS is DST. RHS is SRC <sigh> */ if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to ATTR"); if ((radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) || (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0)) { return -2; } return paircmp_op(lhs_vp, map->op, rhs_vp); } /* * LHS is a cast. Do type-specific comparisons, as if * the LHS was a real attribute. */ if (c->cast) { VALUE_PAIR *lhs_vp, *rhs_vp; /* * Try to copy data from the VP which is being * casted, instead of printing it to a string and * then re-parsing it. */ if (map->dst->type == VPT_TYPE_ATTR) { VALUE_PAIR *cast_vp; if (radius_vpt_get_vp(&cast_vp, request, map->dst) < 0) { return false; } lhs_vp = pairalloc(request, c->cast); if (!lhs_vp) return false; /* * In a separate function for clarity */ if (!do_cast_copy(lhs_vp, cast_vp)) { talloc_free(lhs_vp); return false; } } else { rcode = get_cast_vp(&lhs_vp, request, map->dst, c->cast); if (rcode < 0) { return rcode; } } rad_assert(lhs_vp); /* * Get either a real VP, or parse the RHS into a * VP, and return that. */ if (map->src->type == VPT_TYPE_ATTR) { if (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0) { return -2; } } else { rcode = get_cast_vp(&rhs_vp, request, map->src, c->cast); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); } if (!rhs_vp) return -2; EVAL_DEBUG("CAST to %s", fr_int2str(dict_attr_types, c->cast->type, "?Unknown?")); rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&lhs_vp); if (map->src->type != VPT_TYPE_ATTR) { pairfree(&rhs_vp); } return rcode; } /* * Might be a virtual comparison */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type != VPT_TYPE_REGEX) && (map->src->type != VPT_TYPE_REGEX_STRUCT) && (c->pass2_fixup == PASS2_PAIRCOMPARE)) { int ret; VALUE_PAIR *lhs_vp; EVAL_DEBUG("virtual ATTR to DATA"); rcode = get_cast_vp(&lhs_vp, request, map->src, map->dst->vpt_da); if (rcode < 0) { return rcode; } rad_assert(lhs_vp); /* * paircompare requires the operator be set for the * check attribute. */ lhs_vp->op = map->op; ret = paircompare(request, request->packet->vps, lhs_vp, NULL); talloc_free(lhs_vp); if (ret == 0) { return true; } return false; } rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE); /* * RHS has been pre-parsed into binary data. Go check * that. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_DATA)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to DATA"); if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) { return -2; } rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->vpt_da); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); #ifdef WITH_EVAL_DEBUG debug_pair(lhs_vp); debug_pair(rhs_vp); #endif rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&rhs_vp); return rcode; } rad_assert(map->src->type != VPT_TYPE_DATA); rad_assert(map->dst->type != VPT_TYPE_DATA); #ifdef HAVE_REGEX_H /* * Parse regular expressions. */ if ((map->src->type == VPT_TYPE_REGEX) || (map->src->type == VPT_TYPE_REGEX_STRUCT)) { return do_regex(request, map, c->regex_i); } #endif /* * The RHS now needs to be expanded into a string. */ rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } rad_assert(rhs != NULL); /* * User-Name == FOO * * Parse the RHS to be the same DA as the LHS. do * comparisons. So long as it's not a regex, which does * string comparisons. * * The LHS may be a virtual attribute, too. */ if (map->dst->type == VPT_TYPE_ATTR) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to non-REGEX"); /* * No LHS means no match */ if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) { /* * Not a real attr: might be a dynamic comparison. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->dst->vpt_da->vendor == 0) && radius_find_compare(map->dst->vpt_da)) { rhs_vp = pairalloc(request, map->dst->vpt_da); rad_assert(rhs_vp != NULL); if (!pairparsevalue(rhs_vp, rhs)) { talloc_free(rhs); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } talloc_free(rhs); rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0); pairfree(&rhs_vp); return rcode; } return -2; } /* * Get VP for RHS */ rhs_vp = pairalloc(request, map->dst->vpt_da); rad_assert(rhs_vp != NULL); if (!pairparsevalue(rhs_vp, rhs)) { talloc_free(rhs); pairfree(&rhs_vp); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rcode = paircmp_op(lhs_vp, map->op, rhs_vp); talloc_free(rhs); pairfree(&rhs_vp); return rcode; } /* * The LHS is a string. Expand it. */ rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } rad_assert(lhs != NULL); EVAL_DEBUG("LHS is %s", lhs); /* * Loop over the string, doing comparisons */ if (all_digits(lhs) && all_digits(rhs)) { int lint, rint; lint = strtoul(lhs, NULL, 0); rint = strtoul(rhs, NULL, 0); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (lint == rint); case T_OP_NE: return (lint != rint); case T_OP_LT: return (lint < rint); case T_OP_GT: return (lint > rint); case T_OP_LE: return (lint <= rint); case T_OP_GE: return (lint >= rint); default: break; } } else { rad_assert(lhs != NULL); rad_assert(rhs != NULL); rcode = strcmp(lhs, rhs); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (rcode == 0); case T_OP_NE: return (rcode != 0); case T_OP_LT: return (rcode < 0); case T_OP_GT: return (rcode > 0); case T_OP_LE: return (rcode <= 0); case T_OP_GE: return (rcode >= 0); default: break; } } EVAL_DEBUG("FAIL %d", __LINE__); return -1; }
/* * Common code called by everything below. */ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *filename, fr_hash_table_t *ht, VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs) { char const *name, *match; VALUE_PAIR *check_tmp; VALUE_PAIR *reply_tmp; PAIR_LIST const *user_pl, *default_pl; int found = 0; PAIR_LIST my_pl; char buffer[256]; if (!inst->key) { VALUE_PAIR *namepair; namepair = request->username; name = namepair ? namepair->vp_strvalue : "NONE"; } else { int len; len = radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL); if (len < 0) { return RLM_MODULE_FAIL; } name = len ? buffer : "NONE"; } if (!ht) return RLM_MODULE_NOOP; my_pl.name = name; user_pl = fr_hash_table_finddata(ht, &my_pl); my_pl.name = "DEFAULT"; default_pl = fr_hash_table_finddata(ht, &my_pl); /* * Find the entry for the user. */ while (user_pl || default_pl) { vp_cursor_t cursor; VALUE_PAIR *vp; PAIR_LIST const *pl; if (!default_pl && user_pl) { pl = user_pl; match = name; user_pl = user_pl->next; } else if (!user_pl && default_pl) { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } else if (user_pl->order < default_pl->order) { pl = user_pl; match = name; user_pl = user_pl->next; } else { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } check_tmp = paircopy(request, pl->check); for (vp = fr_cursor_init(&cursor, &check_tmp); vp; vp = fr_cursor_next(&cursor)) { if (radius_xlat_do(request, vp) < 0) { RWARN("Failed parsing expanded value for check item, skipping entry: %s", fr_strerror()); pairfree(&check_tmp); continue; } } if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) { RDEBUG2("%s: Matched entry %s at line %d", filename, match, pl->lineno); found = 1; /* ctx may be reply or proxy */ reply_tmp = paircopy(request, pl->reply); radius_xlat_move(request, reply_pairs, &reply_tmp); pairmove(request, &request->config_items, &check_tmp); /* Cleanup any unmoved valuepairs */ pairfree(&reply_tmp); pairfree(&check_tmp); /* * Fallthrough? */ if (!fallthrough(pl->reply)) break; } } /* * Remove server internal parameters. */ pairdelete(reply_pairs, PW_FALL_THROUGH, 0, TAG_ANY); /* * See if we succeeded. */ if (!found) return RLM_MODULE_NOOP; /* on to the next module */ return 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; }
/** Convert a map to a VALUE_PAIR. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc) * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST. * @param[in] ctx unused * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent */ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx) { int rcode = 0; VALUE_PAIR *vp = NULL, *found, **from = NULL; DICT_ATTR const *da; REQUEST *context; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse the value, just allocate an attribute with * the right operator. */ if (map->op == T_OP_CMP_FALSE) { vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; *out = vp; return 0; } /* * List to list found, this is a special case because we don't need * to allocate any attributes, just found the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { from = radius_list(request, map->src->list); if (!from) return -2; found = paircopy(request, *from); /* * List to list copy is invalid if the src list has no attributes. */ if (!found) return -2; for (vp = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list founding operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: case VPT_TYPE_LITERAL: case VPT_TYPE_DATA: vp = pairalloc(request, da); if (!vp) return -1; vp->op = map->op; break; default: break; } /* * And parse the RHS */ switch (map->src->type) { case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ /* * Don't call unnecessary expansions */ if (strchr(map->src->name, '%') != NULL) { ssize_t slen; char *str = NULL; slen = radius_axlat(&str, request, map->src->name, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; } /* FALL-THROUGH */ case VPT_TYPE_LITERAL: if (!pairparsevalue(vp, map->src->name)) { rcode = -2; goto error; } break; case VPT_TYPE_ATTR: rad_assert(!map->dst->da || (map->src->da->type == map->dst->da->type) || (map->src->da->type == PW_TYPE_OCTETS) || (map->dst->da->type == PW_TYPE_OCTETS)); context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } /* * Can't add the attribute if the list isn't * valid. */ if (!from) { rcode = -2; goto error; } /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } for (vp = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * FIXME: allow tag references? */ found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } /* * Copy the data over verbatim, assuming it's * actually data. */ // rad_assert(found->type == VT_DATA); vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: rad_assert(map->src->da->type == map->dst->da->type); memcpy(&vp->data, map->src->vpd, sizeof(vp->data)); vp->length = map->src->length; break; /* * This essentially does the same as rlm_exec xlat, except it's non-configurable. * It's only really here as a convenience for people who expect the contents of * backticks to be executed in a shell. * * exec string is xlat expanded and arguments are shell escaped. */ case VPT_TYPE_EXEC: return radius_mapexec(out, request, map); default: rad_assert(0); /* Should of been caught at parse time */ error: pairfree(&vp); return rcode; } *out = vp; return 0; }
/* * Dispatch an exec method */ static rlm_rcode_t CC_HINT(nonnull) mod_exec_dispatch(void *instance, REQUEST *request) { rlm_exec_t *inst = (rlm_exec_t *)instance; rlm_rcode_t rcode; int status; VALUE_PAIR **input_pairs = NULL, **output_pairs = NULL; VALUE_PAIR *answer = NULL; char out[1024]; /* * We need a program to execute. */ if (!inst->program) { ERROR("rlm_exec (%s): We require a program to execute", inst->xlat_name); return RLM_MODULE_FAIL; } /* * See if we're supposed to execute it now. */ if (!((inst->packet_code == 0) || (request->packet->code == inst->packet_code) || (request->reply->code == inst->packet_code) #ifdef WITH_PROXY || (request->proxy && (request->proxy->code == inst->packet_code)) || (request->proxy_reply && (request->proxy_reply->code == inst->packet_code)) #endif )) { RDEBUG2("Packet type is not %s. Not executing.", inst->packet_type); return RLM_MODULE_NOOP; } /* * Decide what input/output the program takes. */ if (inst->input) { input_pairs = radius_list(request, inst->input_list); if (!input_pairs) { return RLM_MODULE_INVALID; } } if (inst->output) { output_pairs = radius_list(request, inst->output_list); if (!output_pairs) { return RLM_MODULE_INVALID; } } /* * This function does it's own xlat of the input program * to execute. */ status = radius_exec_program(out, sizeof(out), inst->output ? &answer : NULL, request, inst->program, inst->input ? *input_pairs : NULL, inst->wait, inst->shell_escape, inst->timeout); rcode = rlm_exec_status2rcode(request, out, strlen(out), status); /* * Move the answer over to the output pairs. * * If we're not waiting, then there are no output pairs. */ if (inst->output) { pairmove(request, output_pairs, &answer); } pairfree(&answer); return rcode; }
/** Process map which has exec as a src * * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so * has been broken out into it's own function. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc). * @param[in] map the map. The LHS (dst) must be VPT_TYPE_ATTR or VPT_TYPE_LIST. The RHS (src) must be VPT_TYPE_EXEC. * @return -1 on failure, 0 on success. */ int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map) { int result; char *expanded = NULL; char answer[1024]; VALUE_PAIR **input_pairs = NULL; VALUE_PAIR **output_pairs = NULL; *out = NULL; rad_assert(map->src->type == VPT_TYPE_EXEC); rad_assert((map->dst->type == VPT_TYPE_ATTR) || (map->dst->type == VPT_TYPE_LIST)); /* * We always put the request pairs into the environment */ input_pairs = radius_list(request, PAIR_LIST_REQUEST); /* * Automagically switch output type depending on our destination * If dst is a list, then we create attributes from the output of the program * if dst is an attribute, then we create an attribute of that type and then * call pairparsevalue on the output of the script. */ out[0] = '\0'; result = radius_exec_program(request, map->src->name, true, true, answer, sizeof(answer), input_pairs ? *input_pairs : NULL, (map->dst->type == VPT_TYPE_LIST) ? output_pairs : NULL); talloc_free(expanded); if (result != 0) { REDEBUG("%s", answer); talloc_free(output_pairs); return -1; } switch (map->dst->type) { case VPT_TYPE_LIST: if (!output_pairs) { return -2; } *out = *output_pairs; return 0; case VPT_TYPE_ATTR: { VALUE_PAIR *vp; vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; if (!pairparsevalue(vp, answer)) { pairfree(&vp); return -2; } *out = vp; return 0; } default: rad_assert(0); } return -1; }
static void got_packet(UNUSED uint8_t *args, struct pcap_pkthdr const*header, uint8_t const *data) { static int count = 1; /* Packets seen */ /* * Define pointers for packet's attributes */ const struct ip_header *ip; /* The IP header */ const struct udp_header *udp; /* The UDP header */ const uint8_t *payload; /* Packet payload */ /* * And define the size of the structures we're using */ int size_ethernet = sizeof(struct ethernet_header); int size_ip = sizeof(struct ip_header); int size_udp = sizeof(struct udp_header); /* * For FreeRADIUS */ RADIUS_PACKET *packet, *original; struct timeval elapsed; /* * Define our packet's attributes */ if ((data[0] == 2) && (data[1] == 0) && (data[2] == 0) && (data[3] == 0)) { ip = (struct ip_header const *) (data + 4); } else { ip = (struct ip_header const *)(data + size_ethernet); } udp = (struct udp_header const *)(((uint8_t const *) ip) + size_ip); payload = (uint8_t const *)(((uint8_t const *) udp) + size_udp); packet = rad_alloc(NULL, 0); if (!packet) { fprintf(stderr, "Out of memory\n"); return; } packet->src_ipaddr.af = AF_INET; packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr; packet->src_port = ntohs(udp->udp_sport); packet->dst_ipaddr.af = AF_INET; packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; packet->dst_port = ntohs(udp->udp_dport); memcpy(&packet->data, &payload, sizeof(packet->data)); packet->data_len = header->len - (payload - data); if (!rad_packet_ok(packet, 0)) { DEBUG("Packet: %s\n", fr_strerror()); DEBUG(" From %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); DEBUG(" To: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); DEBUG(" Type: %s\n", fr_packet_codes[packet->code]); rad_free(&packet); return; } switch (packet->code) { case PW_CODE_COA_REQUEST: /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */ original = nullpacket; break; case PW_CODE_AUTHENTICATION_ACK: /* look for a matching request and use it for decoding */ original = rbtree_finddata(request_tree, packet); break; case PW_CODE_AUTHENTICATION_REQUEST: /* save the request for later matching */ original = rad_alloc_reply(NULL, packet); if (original) { /* just ignore allocation failures */ rbtree_deletebydata(request_tree, original); rbtree_insert(request_tree, original); } /* fallthrough */ default: /* don't attempt to decode any encrypted attributes */ original = NULL; } /* * Decode the data without bothering to check the signatures. */ if (rad_decode(packet, original, radius_secret) != 0) { rad_free(&packet); fr_perror("decode"); return; } /* * We've seen a successfull reply to this, so delete it now */ if (original) rbtree_deletebydata(request_tree, original); if (filter_vps && filter_packet(packet)) { rad_free(&packet); DEBUG("Packet number %d doesn't match\n", count++); return; } if (out) { pcap_dump((void *) out, header, data); goto check_filter; } INFO("%s Id %d\t", fr_packet_codes[packet->code], packet->id); /* * Print the RADIUS packet */ INFO("%s:%d -> %s:%d", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport), inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); DEBUG1("\t(%d packets)", count++); if (!start_pcap.tv_sec) { start_pcap = header->ts; } tv_sub(&header->ts, &start_pcap, &elapsed); INFO("\t+%u.%03u", (unsigned int) elapsed.tv_sec, (unsigned int) elapsed.tv_usec / 1000); if (fr_debug_flag > 1) { DEBUG("\n"); if (packet->vps) { if (do_sort) { pairsort(&packet->vps, true); } vp_printlist(log_dst, packet->vps); pairfree(&packet->vps); } } INFO("\n"); if (!to_stdout && (fr_debug_flag > 4)) { rad_print_hex(packet); } fflush(log_dst); check_filter: /* * If we're doing filtering, Access-Requests are cached in the * filter tree. */ if (!filter_vps || ((packet->code != PW_CODE_AUTHENTICATION_REQUEST) && (packet->code != PW_CODE_ACCOUNTING_REQUEST))) { rad_free(&packet); } }
static rlm_rcode_t rlm_redisn_authorize(void *instance, REQUEST * request) { VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; int found = 0; int dofallthrough = 1; int rows; REDISSOCK *redis_socket; REDIS_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char redisnusername[MAX_STRING_LEN]; /* * the profile username is used as the redisnusername during * profile checking so that we don't overwrite the orignal * redisnusername string */ char profileusername[MAX_STRING_LEN]; /* * Set, escape, and check the user attr here */ if (redisn_set_user(inst, request, redisnusername, NULL) < 0) return RLM_MODULE_FAIL; /* * reserve a socket */ redis_socket = redisn_get_socket(inst); if (redis_socket == NULL) { /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); return RLM_MODULE_FAIL; } /* * After this point, ALL 'return's MUST release the REDISN socket! */ /* * Alright, start by getting the specific entry for the user */ if (!radius_xlat(querystr, sizeof(querystr), inst->authorize_check_query, request, redisn_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); redisn_release_socket(inst, redis_socket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); return RLM_MODULE_FAIL; } rows = redisn_getvpdata(inst, redis_socket, &check_tmp, querystr); if (rows < 0) { radlog_request(L_ERR, 0, request, "REDISN query error; rejecting user"); redisn_release_socket(inst, redis_socket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); pairfree(&check_tmp); return RLM_MODULE_FAIL; } else if (rows > 0) { /* * Only do this if *some* check pairs were returned */ if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) { found = 1; RDEBUG2("User found in radcheck table"); if (inst->authorize_reply_query && *inst->authorize_reply_query) { /* * Now get the reply pairs since the paircompare matched */ if (!radius_xlat(querystr, sizeof(querystr), inst->authorize_reply_query, request, redisn_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); redisn_release_socket(inst, redis_socket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); pairfree(&check_tmp); return RLM_MODULE_FAIL; } if (redisn_getvpdata(inst, redis_socket, &reply_tmp, querystr) < 0) { radlog_request(L_ERR, 0, request, "REDISN query error; rejecting user"); redisn_release_socket(inst, redis_socket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); pairfree(&check_tmp); pairfree(&reply_tmp); return RLM_MODULE_FAIL; } if (!inst->read_groups) { dofallthrough = fallthrough(reply_tmp); DEBUG("rlm_redisn (%s) %d: dofallthrough: %d", inst->xlat_name, __LINE__,dofallthrough); } pairxlatmove(request, &request->reply->vps, &reply_tmp); } pairxlatmove(request, &request->config_items, &check_tmp); } } /* * Clear out the pairlists */ pairfree(&check_tmp); pairfree(&reply_tmp); /* * dofallthrough is set to 1 by default so that if the user information * is not found, we will still process groups. If the user information, * however, *is* found, Fall-Through must be set in order to process * the groups as well */ DEBUG("rlm_redisn (%s) %d: dofallthrough: %d", inst->xlat_name, __LINE__,dofallthrough); if (dofallthrough) { rows = rlm_redisn_process_groups(inst, request, redis_socket, &dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user"); redisn_release_socket(inst, redis_socket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); return RLM_MODULE_FAIL; } else if (rows > 0) { found = 1; } } /* * repeat the above process with the default profile or User-Profile */ DEBUG("rlm_redisn (%s) %d: dofallthrough: %d", inst->xlat_name, __LINE__,dofallthrough); if (dofallthrough) { int profile_found = 0; /* * Check for a default_profile or for a User-Profile. */ user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY); if (inst->default_profile[0] != 0 || user_profile != NULL){ char *profile = inst->default_profile; if (user_profile != NULL) profile = user_profile->vp_strvalue; if (profile && strlen(profile)){ RDEBUG("Checking profile %s", profile); if (redisn_set_user(inst, request, profileusername, profile) < 0) { radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user"); redisn_release_socket(inst, redis_socket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); return RLM_MODULE_FAIL; } else { profile_found = 1; } } } if (profile_found) { rows = rlm_redisn_process_groups(inst, request, redis_socket, &dofallthrough); DEBUG("rlm_redisn (%s) %d: dofallthrough: %d", inst->xlat_name, __LINE__,dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user"); redisn_release_socket(inst, redis_socket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); return RLM_MODULE_FAIL; } else if (rows > 0) { found = 1; } } } /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); redisn_release_socket(inst, redis_socket); if (!found) { RDEBUG("User %s not found", redisnusername); return RLM_MODULE_NOTFOUND; } else { return RLM_MODULE_OK; } }
static int rlm_sql_authorize(void *instance, REQUEST * request) { VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; int found = 0; SQLSOCK *sqlsocket; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; /* * They MUST have a user name to do SQL authorization. */ if ((request->username == NULL) || (request->username->length == 0)) { radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name); return RLM_MODULE_INVALID; } /* * Set, escape, and check the user attr here. */ if (sql_set_user(inst, request, sqlusername, NULL) < 0) return RLM_MODULE_FAIL; radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) { /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); return RLM_MODULE_FAIL; } /* * After this point, ALL 'return's MUST release the SQL socket! */ found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA); /* * Find the entry for the user. */ if (found > 0) { radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA); radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA); radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA); } else if (found < 0) { radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user", inst->config->xlat_name); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); pairfree(&check_tmp); return RLM_MODULE_FAIL; } else { radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck", inst->config->xlat_name, sqlusername); /* * We didn't find the user in radcheck, so we try looking * for radgroupcheck entry */ radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func); found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA); radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA); } if (!found) radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck", inst->config->xlat_name, sqlusername); if (found || (!found && inst->config->query_on_not_found)){ int def_found = 0; /* * Check for a default_profile or for a User-Profile. */ user_profile = pairfind(request->config_items, PW_USER_PROFILE); if (inst->config->default_profile[0] != 0 || user_profile != NULL){ char *profile = inst->config->default_profile; if (user_profile != NULL) profile = user_profile->strvalue; if (profile && strlen(profile)){ radlog(L_DBG, "rlm_sql (%s): Checking profile %s", inst->config->xlat_name, profile); if (sql_set_user(inst, request, sqlusername, profile) < 0) { sql_release_socket(inst, sqlsocket); pairfree(&reply_tmp); pairfree(&check_tmp); return RLM_MODULE_FAIL; } radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func); def_found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA); if (def_found) found = 1; radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA); } } } /* * We don't need the SQL socket anymore. */ sql_release_socket(inst, sqlsocket); if (!found) { radlog(L_DBG, "rlm_sql (%s): User not found", inst->config->xlat_name); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); pairfree(&reply_tmp); pairfree(&check_tmp); return RLM_MODULE_NOTFOUND; } /* * Uncomment these lines for debugging * Recompile, and run 'radiusd -X' */ /* DEBUG2("rlm_sql: check items"); vp_listdebug(check_tmp); DEBUG2("rlm_sql: reply items"); vp_listdebug(reply_tmp); */ if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) { radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]", inst->config->xlat_name, sqlusername); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); pairfree(&reply_tmp); pairfree(&check_tmp); return RLM_MODULE_NOTFOUND; } pairxlatmove(request, &request->reply->vps, &reply_tmp); pairxlatmove(request, &request->config_items, &check_tmp); pairfree(&reply_tmp); pairfree(&check_tmp); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); return RLM_MODULE_OK; }
static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle, sql_fall_through_t *do_fall_through) { rlm_rcode_t rcode = RLM_MODULE_NOOP; VALUE_PAIR *check_tmp = NULL, *reply_tmp = NULL, *sql_group = NULL; rlm_sql_grouplist_t *head = NULL, *entry = NULL; char *expanded = NULL; int rows; rad_assert(request->packet != NULL); /* * Get the list of groups this user is a member of */ rows = sql_get_grouplist(inst, handle, request, &head); if (rows < 0) { REDEBUG("Error retrieving group list"); return RLM_MODULE_FAIL; } if (rows == 0) { RDEBUG2("User not found in any groups"); rcode = RLM_MODULE_NOTFOUND; goto finish; } rad_assert(head); RDEBUG2("User found in the group table"); entry = head; do { /* * Add the Sql-Group attribute to the request list so we know * which group we're retrieving attributes for */ sql_group = pairmake_packet("Sql-Group", entry->name, T_OP_EQ); if (!sql_group) { REDEBUG("Error creating Sql-Group attribute"); rcode = RLM_MODULE_FAIL; goto finish; } if (inst->config->authorize_group_check_query && (*inst->config->authorize_group_check_query != '\0')) { vp_cursor_t cursor; VALUE_PAIR *vp; /* * Expand the group query */ if (radius_axlat(&expanded, request, inst->config->authorize_group_check_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto finish; } rows = sql_getvpdata(request, inst, handle, &check_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("Error retrieving check pairs for group %s", entry->name); rcode = RLM_MODULE_FAIL; goto finish; } /* * If we got check rows we need to process them before we decide to process the reply rows */ if ((rows > 0) && (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0)) { pairfree(&check_tmp); pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); continue; } RDEBUG2("Group \"%s\": Conditional check items matched", entry->name); rcode = RLM_MODULE_OK; RDEBUG2("Group \"%s\": Merging assignment check items", entry->name); RINDENT(); for (vp = fr_cursor_init(&cursor, &check_tmp); vp; vp = fr_cursor_next(&cursor)) { if (!fr_assignment_op[vp->op]) continue; rdebug_pair(2, request, vp); } REXDENT(); radius_pairmove(request, &request->config_items, check_tmp, true); check_tmp = NULL; } if (inst->config->authorize_group_reply_query && (*inst->config->authorize_group_reply_query != '\0')) { /* * Now get the reply pairs since the paircompare matched */ if (radius_axlat(&expanded, request, inst->config->authorize_group_reply_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto finish; } rows = sql_getvpdata(request->reply, inst, handle, &reply_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("Error retrieving reply pairs for group %s", entry->name); rcode = RLM_MODULE_FAIL; goto finish; } *do_fall_through = fall_through(reply_tmp); RDEBUG2("Group \"%s\": Merging reply items", entry->name); rcode = RLM_MODULE_OK; rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp); radius_pairmove(request, &request->reply->vps, reply_tmp, true); reply_tmp = NULL; } pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); entry = entry->next; } while (entry != NULL && (*do_fall_through == FALL_THROUGH_YES)); finish: talloc_free(head); pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); 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; }
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_rcode_t rcode = RLM_MODULE_NOOP; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; bool user_found = false; sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT; int rows; char *expanded = NULL; rad_assert(request->packet != NULL); rad_assert(request->reply != NULL); if (!inst->config->authorize_check_query && !inst->config->authorize_reply_query && !inst->config->read_groups && !inst->config->read_profiles) { RWDEBUG("No authorization checks configured, returning noop"); return RLM_MODULE_NOOP; } /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) { return RLM_MODULE_FAIL; } /* * Reserve a socket * * After this point use goto error or goto release to cleanup socket temporary pairlists and * temporary attributes. */ handle = sql_get_socket(inst); if (!handle) { rcode = RLM_MODULE_FAIL; goto error; } /* * Query the check table to find any conditions associated with this user/realm/whatever... */ if (inst->config->authorize_check_query) { vp_cursor_t cursor; VALUE_PAIR *vp; if (radius_axlat(&expanded, request, inst->config->authorize_check_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto error; } rows = sql_getvpdata(request, inst, &handle, &check_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("SQL query error"); rcode = RLM_MODULE_FAIL; goto error; } if (rows == 0) goto skipreply; /* Don't need to free VPs we don't have */ /* * Only do this if *some* check pairs were returned */ RDEBUG2("User found in radcheck table"); user_found = true; if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) { pairfree(&check_tmp); check_tmp = NULL; goto skipreply; } RDEBUG2("Conditional check items matched, merging assignment check items"); RINDENT(); for (vp = fr_cursor_init(&cursor, &check_tmp); vp; vp = fr_cursor_next(&cursor)) { if (!fr_assignment_op[vp->op]) continue; rdebug_pair(2, request, vp); } REXDENT(); radius_pairmove(request, &request->config_items, check_tmp, true); rcode = RLM_MODULE_OK; check_tmp = NULL; } if (inst->config->authorize_reply_query) { /* * Now get the reply pairs since the paircompare matched */ if (radius_axlat(&expanded, request, inst->config->authorize_reply_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto error; } rows = sql_getvpdata(request->reply, inst, &handle, &reply_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("SQL query error"); rcode = RLM_MODULE_FAIL; goto error; } if (rows == 0) goto skipreply; do_fall_through = fall_through(reply_tmp); RDEBUG2("User found in radreply table, merging reply items"); user_found = true; rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp); radius_pairmove(request, &request->reply->vps, reply_tmp, true); rcode = RLM_MODULE_OK; reply_tmp = NULL; } skipreply: if ((do_fall_through == FALL_THROUGH_YES) || (inst->config->read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) { rlm_rcode_t ret; RDEBUG3("... falling-through to group processing"); ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through); switch (ret) { /* * Nothing bad happened, continue... */ case RLM_MODULE_UPDATED: rcode = RLM_MODULE_UPDATED; /* FALL-THROUGH */ case RLM_MODULE_OK: if (rcode != RLM_MODULE_UPDATED) { rcode = RLM_MODULE_OK; } /* FALL-THROUGH */ case RLM_MODULE_NOOP: user_found = true; break; case RLM_MODULE_NOTFOUND: break; default: rcode = ret; goto release; } } /* * Repeat the above process with the default profile or User-Profile */ if ((do_fall_through == FALL_THROUGH_YES) || (inst->config->read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) { rlm_rcode_t ret; /* * Check for a default_profile or for a User-Profile. */ RDEBUG3("... falling-through to profile processing"); user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY); char const *profile = user_profile ? user_profile->vp_strvalue : inst->config->default_profile; if (!profile || !*profile) { goto release; } RDEBUG2("Checking profile %s", profile); if (sql_set_user(inst, request, profile) < 0) { REDEBUG("Error setting profile"); rcode = RLM_MODULE_FAIL; goto error; } ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through); switch (ret) { /* * Nothing bad happened, continue... */ case RLM_MODULE_UPDATED: rcode = RLM_MODULE_UPDATED; /* FALL-THROUGH */ case RLM_MODULE_OK: if (rcode != RLM_MODULE_UPDATED) { rcode = RLM_MODULE_OK; } /* FALL-THROUGH */ case RLM_MODULE_NOOP: user_found = true; break; case RLM_MODULE_NOTFOUND: break; default: rcode = ret; goto release; } } /* * At this point the key (user) hasn't be found in the check table, the reply table * or the group mapping table, and there was no matching profile. */ release: if (!user_found) { rcode = RLM_MODULE_NOTFOUND; } sql_release_socket(inst, handle); sql_unset_user(inst, request); return rcode; error: pairfree(&check_tmp); pairfree(&reply_tmp); sql_unset_user(inst, request); sql_release_socket(inst, handle); return rcode; }
static int dhcp_process(REQUEST *request) { int rcode; VALUE_PAIR *vp; vp = pairfind(request->packet->vps, 53, DHCP_MAGIC_VENDOR); /* 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 = module_post_auth(vp->vp_integer, request); } else { DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } /* * For messages from a client, look for Relay attribute, * and forward it if necessary. */ vp = NULL; if (request->packet->data[0] == 1) { vp = pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR); } if (vp) { VALUE_PAIR *giaddr; /* * Find the original giaddr. * FIXME: Maybe look in the original packet? * * It's invalid to have giaddr=0 AND a relay option */ giaddr = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR); if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY))) { if (pairfind(request->packet->vps, 82, DHCP_MAGIC_VENDOR)) { RDEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet"); return 1; } } if (request->packet->data[3] > 10) { RDEBUG("DHCP: Number of hops is greater than 10: not relaying"); return 1; } /* * Forward a reply... */ pairfree(&request->reply->vps); request->reply->vps = paircopy(request->packet->vps); request->reply->code = request->packet->code; request->reply->id = request->packet->id; request->reply->src_ipaddr = request->packet->dst_ipaddr; request->reply->src_port = request->packet->dst_port; request->reply->dst_ipaddr.af = AF_INET; request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * Don't change the destination port. It's the * server port. */ /* * Hop count goes up. */ vp = pairfind(request->reply->vps, 259, DHCP_MAGIC_VENDOR); if (vp) vp->vp_integer++; return 1; } /* * Responses from a server. Handle them differently. */ if (request->packet->data[0] == 2) { pairfree(&request->reply->vps); request->reply->vps = paircopy(request->packet->vps); request->reply->code = request->packet->code; request->reply->id = request->packet->id; /* * Delete any existing giaddr. If we received a * message from the server, then we're NOT the * server. So we must be the destination of the * giaddr field. */ pairdelete(&request->packet->vps, 266, DHCP_MAGIC_VENDOR, -1); /* * Search for client IP address. */ vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR); if (!vp) { request->reply->code = 0; RDEBUG("DHCP: No YIAddr in the reply. Discarding packet"); return 1; } /* * FROM us, TO the client's IP, OUR port + 1. */ request->reply->src_ipaddr = request->packet->dst_ipaddr; request->reply->src_port = request->packet->dst_port; request->reply->dst_ipaddr.af = AF_INET; request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->reply->dst_port = request->packet->dst_port + 1; /* * Hop count goes down. */ vp = pairfind(request->reply->vps, 259, DHCP_MAGIC_VENDOR); if (vp && (vp->vp_integer > 0)) vp->vp_integer--; /* * FIXME: Keep original somewhere? If the * broadcast flags are set, use them here? */ return 1; } vp = pairfind(request->reply->vps, 53, DHCP_MAGIC_VENDOR); /* 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: break; } /* * Releases don't get replies. */ if (request->packet->code == PW_DHCP_RELEASE) { request->reply->code = 0; } return 1; }
/* * Free a REQUEST struct. */ void request_free(REQUEST **request_ptr) { REQUEST *request; if ((request_ptr == NULL) || !*request_ptr) return; request = *request_ptr; rad_assert(!request->in_request_hash); #ifdef WITH_PROXY rad_assert(!request->in_proxy_hash); #endif rad_assert(!request->ev); if (request->packet) rad_free(&request->packet); #ifdef WITH_PROXY if (request->proxy) rad_free(&request->proxy); #endif if (request->reply) rad_free(&request->reply); #ifdef WITH_PROXY if (request->proxy_reply) rad_free(&request->proxy_reply); #endif if (request->config_items) pairfree(&request->config_items); request->username = NULL; request->password = NULL; if (request->data) { request_data_t *this, *next; for (this = request->data; this != NULL; this = next) { next = this->next; if (this->opaque && /* free it, if necessary */ this->free_opaque) this->free_opaque(this->opaque); free(this); } request->data = NULL; } if (request->root && (request->root->refcount > 0)) { request->root->refcount--; request->root = NULL; } #ifdef WITH_COA if (request->coa) { request->coa->parent = NULL; rad_assert(request->coa->ev == NULL); request_free(&request->coa); } if (request->parent && (request->parent->coa == request)) { request->parent->coa = NULL; } #endif #ifndef NDEBUG request->magic = 0x01020304; /* set the request to be nonsense */ #endif request->client = NULL; #ifdef WITH_PROXY request->home_server = NULL; #endif free(request); *request_ptr = NULL; }
/* * The pairmove() function in src/lib/valuepair.c does all sorts of * extra magic that we don't want here. * * FIXME: integrate this with the code calling it, so that we * only paircopy() those attributes that we're really going to * use. */ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) { int i, j, count, from_count, to_count, tailto; vp_cursor_t cursor; VALUE_PAIR *vp, *next, **last; VALUE_PAIR **from_list, **to_list; bool *edited = NULL; REQUEST *fixup = NULL; if (!request) return; /* * Set up arrays for editing, to remove some of the * O(N^2) dependencies. This also makes it easier to * insert and remove attributes. * * It also means that the operators apply ONLY to the * attributes in the original list. With the previous * implementation of pairmove(), adding two attributes * via "+=" and then "=" would mean that the second one * wasn't added, because of the existence of the first * one in the "to" list. This implementation doesn't * have that bug. * * Also, the previous implementation did NOT implement * "-=" correctly. If two of the same attributes existed * in the "to" list, and you tried to subtract something * matching the *second* value, then the pairdelete() * function was called, and the *all* attributes of that * number were deleted. With this implementation, only * the matching attributes are deleted. */ count = 0; for (vp = fr_cursor_init(&cursor, &from); vp; vp = fr_cursor_next(&cursor)) count++; from_list = talloc_array(request, VALUE_PAIR *, count); for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++; to_list = talloc_array(request, VALUE_PAIR *, count); /* * Move the lists to the arrays, and break the list * chains. */ from_count = 0; for (vp = from; vp != NULL; vp = next) { next = vp->next; from_list[from_count++] = vp; vp->next = NULL; } to_count = 0; for (vp = *to; vp != NULL; vp = next) { next = vp->next; to_list[to_count++] = vp; vp->next = NULL; } tailto = to_count; edited = talloc_zero_array(request, bool, to_count); RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count); /* * Now that we have the lists initialized, start working * over them. */ for (i = 0; i < from_count; i++) { int found; RDEBUG4("::: Examining %s", from_list[i]->da->name); /* * Attribute should be appended, OR the "to" list * is empty, and we're supposed to replace or * "add if not existing". */ if (from_list[i]->op == T_OP_ADD) goto append; found = false; for (j = 0; j < to_count; j++) { if (edited[j] || !to_list[j] || !from_list[i]) continue; /* * Attributes aren't the same, skip them. */ if (from_list[i]->da != to_list[j]->da) { continue; } /* * We don't use a "switch" statement here * because we want to break out of the * "for" loop over 'j' in most cases. */ /* * Over-write the FIRST instance of the * matching attribute name. We free the * one in the "to" list, and move over * the one in the "from" list. */ if (from_list[i]->op == T_OP_SET) { RDEBUG4("::: OVERWRITING %s FROM %d TO %d", to_list[j]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = true; break; } /* * Add the attribute only if it does not * exist... but it exists, so we stop * looking. */ if (from_list[i]->op == T_OP_EQ) { found = true; break; } /* * Delete every attribute, independent * of its value. */ if (from_list[i]->op == T_OP_CMP_FALSE) { goto delete; } /* * Delete all matching attributes from * "to" */ if ((from_list[i]->op == T_OP_SUB) || (from_list[i]->op == T_OP_CMP_EQ) || (from_list[i]->op == T_OP_LE) || (from_list[i]->op == T_OP_GE)) { int rcode; int old_op = from_list[i]->op; /* * Check for equality. */ from_list[i]->op = T_OP_CMP_EQ; /* * If equal, delete the one in * the "to" list. */ rcode = radius_compare_vps(NULL, from_list[i], to_list[j]); /* * We may want to do more * subtractions, so we re-set the * operator back to it's original * value. */ from_list[i]->op = old_op; switch (old_op) { case T_OP_CMP_EQ: if (rcode != 0) goto delete; break; case T_OP_SUB: if (rcode == 0) { delete: RDEBUG4("::: DELETING %s FROM %d TO %d", from_list[i]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = NULL; } break; /* * Enforce <=. If it's * >, replace it. */ case T_OP_LE: if (rcode > 0) { RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = true; } break; case T_OP_GE: if (rcode < 0) { RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->da->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; from_list[i] = NULL; edited[j] = true; } break; } continue; } rad_assert(0 == 1); /* panic! */ }
/* * Add a handler to the set of active sessions. * * Since we're adding it to the list, we guess that this means * the packet needs a State attribute. So add one. */ int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler) { int status = 0; VALUE_PAIR *state; REQUEST *request = handler->request; rad_assert(handler != NULL); rad_assert(request != NULL); /* * Generate State, since we've been asked to add it to * the list. */ state = pairmake_reply("State", NULL, T_OP_EQ); if (!state) return 0; /* * The time at which this request was made was the time * at which it was received by the RADIUS server. */ handler->timestamp = request->timestamp; handler->status = 1; handler->src_ipaddr = request->packet->src_ipaddr; handler->eap_id = handler->eap_ds->request->id; /* * Playing with a data structure shared among threads * means that we need a lock, to avoid conflict. */ PTHREAD_MUTEX_LOCK(&(inst->session_mutex)); /* * If we have a DoS attack, discard new sessions. */ if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) { status = -1; eaplist_expire(inst, request, handler->timestamp); goto done; } /* * Create a unique content for the State variable. * It will be modified slightly per round trip, but less so * than in 1.x. */ if (handler->trips == 0) { int i; for (i = 0; i < 4; i++) { uint32_t lvalue; lvalue = eap_rand(&inst->rand_pool); memcpy(handler->state + i * 4, &lvalue, sizeof(lvalue)); } } /* * Add some more data to distinguish the sessions. */ handler->state[4] = handler->trips ^ handler->state[0]; handler->state[5] = handler->eap_id ^ handler->state[1]; handler->state[6] = handler->type ^ handler->state[2]; pairmemcpy(state, handler->state, sizeof(handler->state)); /* * Big-time failure. */ status = rbtree_insert(inst->session_tree, handler); /* * Catch Access-Challenge without response. */ if (inst->handler_tree) { check_handler_t *check = rad_malloc(sizeof(*check)); check->inst = inst; check->handler = handler; check->trips = handler->trips; request_data_add(request, inst, 0, check, check_handler); } if (status) { eap_handler_t *prev; prev = inst->session_tail; if (prev) { prev->next = handler; handler->prev = prev; handler->next = NULL; inst->session_tail = handler; } else { inst->session_head = inst->session_tail = handler; handler->next = handler->prev = NULL; } } /* * Now that we've finished mucking with the list, * unlock it. */ done: /* * We don't need this any more. */ if (status > 0) handler->request = NULL; PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex)); if (status <= 0) { pairfree(&state); if (status < 0) { static time_t last_logged = 0; if (last_logged < handler->timestamp) { last_logged = handler->timestamp; ERROR("rlm_eap (%s): Too many open sessions. Try increasing \"max_sessions\" " "in the EAP module configuration", inst->xlat_name); } } else { ERROR("rlm_eap (%s): Failed to store handler", inst->xlat_name); } return 0; } RDEBUG("New EAP session, adding 'State' attribute to reply 0x%02x%02x%02x%02x%02x%02x%02x%02x", state->vp_octets[0], state->vp_octets[1], state->vp_octets[2], state->vp_octets[3], state->vp_octets[4], state->vp_octets[5], state->vp_octets[6], state->vp_octets[7]); return 1; }
/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */ static void perl_store_vps(VALUE_PAIR *vp, HV *rad_hv) { VALUE_PAIR *nvp, *vpa, *vpn; AV *av; const char *name; char namebuf[256]; char buffer[1024]; int len; hv_undef(rad_hv); /* * Copy the valuepair list so we can remove attributes we've * already processed. */ nvp = paircopy(vp); while (nvp != NULL) { /* * Tagged attributes are added to the hash with name * <attribute>:<tag>, others just use the normal attribute * name as the key. */ if (nvp->flags.has_tag && (nvp->flags.tag != 0)) { snprintf(namebuf, sizeof(namebuf), "%s:%d", nvp->name, nvp->flags.tag); name = namebuf; } else { name = nvp->name; } /* * Create a new list with all the attributes like this one * which are in the same tag group. */ vpa = paircopy2(nvp, nvp->attribute, nvp->vendor, nvp->flags.tag); /* * Attribute has multiple values */ if (vpa->next) { av = newAV(); for (vpn = vpa; vpn; vpn = vpn->next) { len = vp_prints_value(buffer, sizeof(buffer), vpn, FALSE); av_push(av, newSVpv(buffer, len)); } (void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0); /* * Attribute has a single value, so its value just gets * added to the hash. */ } else { len = vp_prints_value(buffer, sizeof(buffer), vpa, FALSE); (void)hv_store(rad_hv, name, strlen(name), newSVpv(buffer, len), 0); } pairfree(&vpa); /* * Find the next attribute which we won't have processed, * we need to do this so we know it won't be freed on * pairdelete. */ vpa = nvp->next; while ((vpa != NULL) && (vpa->attribute == nvp->attribute) && (vpa->vendor == nvp->vendor) && (vpa->flags.tag == nvp->flags.tag)) { vpa = vpa->next; } /* * Finally remove all the VPs we processed from our copy * of the list. */ pairdelete(&nvp, nvp->attribute, nvp->vendor, nvp->flags.tag); nvp = vpa; } }
int vqp_decode(RADIUS_PACKET *packet) { uint8_t *ptr, *end; int attribute, length; VALUE_PAIR *vp, **tail; if (!packet || !packet->data) return -1; if (packet->data_len < VQP_HDR_LEN) return -1; tail = &packet->vps; vp = paircreate(PW_VQP_PACKET_TYPE, 0, PW_TYPE_OCTETS); if (!vp) { fr_strerror_printf("No memory"); return -1; } vp->lvalue = packet->data[1]; debug_pair(vp); *tail = vp; tail = &(vp->next); vp = paircreate(PW_VQP_ERROR_CODE, 0, PW_TYPE_OCTETS); if (!vp) { fr_strerror_printf("No memory"); return -1; } vp->lvalue = packet->data[2]; debug_pair(vp); *tail = vp; tail = &(vp->next); vp = paircreate(PW_VQP_SEQUENCE_NUMBER, 0, PW_TYPE_OCTETS); if (!vp) { fr_strerror_printf("No memory"); return -1; } vp->lvalue = packet->id; /* already set by vqp_recv */ debug_pair(vp); *tail = vp; tail = &(vp->next); ptr = packet->data + VQP_HDR_LEN; end = packet->data + packet->data_len; /* * Note that vqp_recv() MUST ensure that the packet is * formatted in a way we expect, and that vqp_recv() MUST * be called before vqp_decode(). */ while (ptr < end) { attribute = (ptr[2] << 8) | ptr[3]; length = (ptr[4] << 8) | ptr[5]; ptr += 6; /* * Hack to get the dictionaries to work correctly. */ attribute |= 0x2000; vp = paircreate(attribute, 0, PW_TYPE_OCTETS); if (!vp) { pairfree(&packet->vps); fr_strerror_printf("No memory"); return -1; } switch (vp->type) { case PW_TYPE_IPADDR: if (length == 4) { memcpy(&vp->vp_ipaddr, ptr, 4); vp->length = 4; break; } vp->type = PW_TYPE_OCTETS; /* FALL-THROUGH */ default: case PW_TYPE_OCTETS: case PW_TYPE_STRING: vp->length = (length > MAX_VMPS_LEN) ? MAX_VMPS_LEN : length; memcpy(vp->vp_octets, ptr, vp->length); vp->vp_octets[vp->length] = '\0'; break; } ptr += length; debug_pair(vp); *tail = vp; tail = &(vp->next); } /* * FIXME: Map attributes to Calling-Station-Id, etc... */ return 0; }
static int rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t *handle, int *dofallthrough) { VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; rlm_sql_grouplist_t *group_list, *group_list_tmp; VALUE_PAIR *sql_group = NULL; char querystr[MAX_QUERY_LEN]; int found = 0; int rows; /* * Get the list of groups this user is a member of */ if (sql_get_grouplist(inst, handle, request, &group_list) < 0) { radlog_request(L_ERR, 0, request, "Error retrieving group list"); return -1; } for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) { /* * Add the Sql-Group attribute to the request list so we know * which group we're retrieving attributes for */ sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ); if (!sql_group) { radlog_request(L_ERR, 0, request, "Error creating Sql-Group attribute"); sql_grouplist_free(&group_list); return -1; } pairadd(&request->packet->vps, sql_group); if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); /* Remove the grouup we added above */ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); sql_grouplist_free(&group_list); return -1; } rows = sql_getvpdata(inst, &handle, &check_tmp, querystr); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s", group_list_tmp->groupname); /* Remove the grouup we added above */ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); pairfree(&check_tmp); sql_grouplist_free(&group_list); return -1; } else if (rows > 0) { /* * Only do this if *some* check pairs were returned */ if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) { found = 1; RDEBUG2("User found in group %s", group_list_tmp->groupname); /* * Now get the reply pairs since the paircompare matched */ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); /* Remove the grouup we added above */ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); pairfree(&check_tmp); sql_grouplist_free(&group_list); return -1; } if (sql_getvpdata(inst, &handle, &reply_tmp, querystr) < 0) { radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s", group_list_tmp->groupname); /* Remove the grouup we added above */ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); pairfree(&check_tmp); pairfree(&reply_tmp); sql_grouplist_free(&group_list); return -1; } *dofallthrough = fallthrough(reply_tmp); radius_xlat_move(request, &request->reply->vps, &reply_tmp); radius_xlat_move(request, &request->config_items, &check_tmp); } } else { /* * rows == 0. This is like having the username on a line * in the user's file with no check vp's. As such, we treat * it as found and add the reply attributes, so that we * match expected behavior */ found = 1; RDEBUG2("User found in group %s", group_list_tmp->groupname); /* * Now get the reply pairs since the paircompare matched */ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); /* Remove the grouup we added above */ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); pairfree(&check_tmp); sql_grouplist_free(&group_list); return -1; } if (sql_getvpdata(inst, &handle, &reply_tmp, querystr) < 0) { radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s", group_list_tmp->groupname); /* Remove the grouup we added above */ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); pairfree(&check_tmp); pairfree(&reply_tmp); sql_grouplist_free(&group_list); return -1; } *dofallthrough = fallthrough(reply_tmp); radius_xlat_move(request, &request->reply->vps, &reply_tmp); radius_xlat_move(request, &request->config_items, &check_tmp); } /* * Delete the Sql-Group we added above * And clear out the pairlists */ pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY); pairfree(&check_tmp); pairfree(&reply_tmp); } sql_grouplist_free(&group_list); return found; }
/* * 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 caching_authorize(void *instance, REQUEST *request) { rlm_caching_t *data = (rlm_caching_t *) instance; char key[MAX_STRING_LEN]; datum key_datum; datum data_datum; rlm_caching_data cache_data; VALUE_PAIR *reply_item; VALUE_PAIR *item; char *tmp; int len = 0; int delete_cache = 0; float hit_ratio = 0.0; /* quiet the compiler */ instance = instance; request = request; if (pairfind(request->packet->vps, PW_CACHE_NO_CACHING, 0) != NULL){ DEBUG("rlm_caching: Cache-No-Caching is set. Returning NOOP"); return RLM_MODULE_NOOP; } if (pairfind(request->packet->vps, PW_CACHE_DELETE_CACHE, 0) != NULL){ DEBUG("rlm_caching: Found Cache-Delete-Cache. Will delete record if found"); delete_cache = 1; } if (!radius_xlat(key,sizeof(key), data->key, request, NULL)){ radlog(L_ERR, "rlm_caching: xlat on key '%s' failed.",data->key); return RLM_MODULE_FAIL; } key_datum.dptr = key; key_datum.dsize = strlen(key); DEBUG("rlm_caching: Searching the database for key '%s'",key); pthread_mutex_lock(&data->mutex); data_datum = gdbm_fetch(data->gdbm, key_datum); pthread_mutex_unlock(&data->mutex); data->cache_queries++; if (data_datum.dptr != NULL){ DEBUG("rlm_caching: Key Found."); data->cache_hits++; hit_ratio = (float)data->cache_hits / data->cache_queries; hit_ratio *= 100.0; memcpy(&cache_data, data_datum.dptr, sizeof(rlm_caching_data)); free(data_datum.dptr); if (delete_cache == 0 && cache_data.creation + data->cache_ttl <= time(NULL)){ DEBUG("rlm_caching: Cache entry has expired"); DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%", data->cache_queries,data->cache_hits,hit_ratio); show_hit_ratio; delete_cache = 1; } if (delete_cache){ DEBUG("rlm_caching: Deleting record"); pthread_mutex_lock(&data->mutex); gdbm_delete(data->gdbm, key_datum); pthread_mutex_unlock(&data->mutex); return RLM_MODULE_NOOP; } tmp = cache_data.data; if (tmp){ pairfree(&request->reply->vps); while(tmp && len < cache_data.len){ reply_item = NULL; if (userparse(tmp, &reply_item) > 0 && reply_item != NULL) pairadd(&request->reply->vps, reply_item); len += (strlen(tmp) + 1); DEBUG("rlm_caching: VP='%s',VALUE='%s',lenth='%d',cache record length='%d'", reply_item->name,reply_item->vp_strvalue,reply_item->length,len); tmp = cache_data.data + len; } } else{ DEBUG("rlm_caching: No reply items found. Returning NOOP"); return RLM_MODULE_NOOP; } if (cache_data.auth_type){ DEBUG("rlm_caching: Adding Auth-Type '%s'",cache_data.auth_type); if ((item = pairfind(request->config_items, PW_AUTH_TYPE, 0)) == NULL){ item = pairmake("Auth-Type", cache_data.auth_type, T_OP_SET); pairadd(&request->config_items, item); } else{ strcmp(item->vp_strvalue, cache_data.auth_type); item->length = strlen(cache_data.auth_type); } } if (data->post_auth){ DEBUG("rlm_caching: Adding Post-Auth-Type '%s'",data->post_auth); if ((item = pairfind(request->config_items, PW_POST_AUTH_TYPE, 0)) == NULL){ item = pairmake("Post-Auth-Type", data->post_auth, T_OP_SET); pairadd(&request->config_items, item); } else{ strcmp(item->vp_strvalue, data->post_auth); item->length = strlen(data->post_auth); } } item = pairmake("Cache-No-Caching", "YES", T_OP_EQ); pairadd(&request->packet->vps, item); DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%", data->cache_queries,data->cache_hits,hit_ratio); show_hit_ratio; return RLM_MODULE_OK; } else{ DEBUG("rlm_caching: Could not find the requested key in the database."); DEBUG("rlm_caching: Cache Queries: %7d, Cache Hits: %7d, Hit Ratio: %.2f%%", data->cache_queries,data->cache_hits,hit_ratio); show_hit_ratio; } return RLM_MODULE_NOOP; }
/** Print a template to a string * * @param[out] buffer for the output string * @param[in] bufsize of the buffer * @param[in] vpt to print * @return the size of the string printed */ size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt) { char c; char const *p; char *q = buffer; char *end; switch (vpt->type) { default: return 0; case VPT_TYPE_REGEX: c = '/'; break; case VPT_TYPE_XLAT: c = '"'; break; case VPT_TYPE_LITERAL: /* single-quoted or bare word */ /* * Hack */ for (p = vpt->name; *p != '\0'; p++) { if (*p == ' ') break; if (*p == '\'') break; if (!dict_attr_allowed_chars[(int) *p]) break; } if (!*p) { strlcpy(buffer, vpt->name, bufsize); return strlen(buffer); } c = '\''; break; case VPT_TYPE_EXEC: c = '`'; break; case VPT_TYPE_ATTR: buffer[0] = '&'; if (vpt->request == REQUEST_CURRENT) { if (vpt->list == PAIR_LIST_REQUEST) { strlcpy(buffer + 1, vpt->da->name, bufsize - 1); } else { snprintf(buffer + 1, bufsize - 1, "%s:%s", fr_int2str(pair_lists, vpt->list, ""), vpt->da->name); } } else { snprintf(buffer + 1, bufsize - 1, "%s.%s:%s", fr_int2str(request_refs, vpt->request, ""), fr_int2str(pair_lists, vpt->list, ""), vpt->da->name); } return strlen(buffer); case VPT_TYPE_DATA: { VALUE_PAIR *vp; TALLOC_CTX *ctx; memcpy(&ctx, &vpt, sizeof(ctx)); /* hack */ vp = pairalloc(ctx, vpt->da); memcpy(&vp->data, vpt->vpd, sizeof(vp->data)); vp->length = vpt->length; q = vp_aprint(vp, vp); if ((vpt->da->type != PW_TYPE_STRING) && (vpt->da->type != PW_TYPE_DATE)) { strlcpy(buffer, q, bufsize); } else { /* * FIXME: properly escape the string... */ snprintf(buffer, bufsize, "\"%s\"", q); } talloc_free(q); pairfree(&vp); return strlen(buffer); } } if (bufsize <= 3) { no_room: *buffer = '\0'; return 0; } p = vpt->name; *(q++) = c; end = buffer + bufsize - 3; /* quotes + EOS */ while (*p && (q < end)) { if (*p == c) { if ((q - end) < 4) goto no_room; /* escape, char, quote, EOS */ *(q++) = '\\'; *(q++) = *(p++); continue; } switch (*p) { case '\\': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = *(p++); break; case '\r': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = 'r'; p++; break; case '\n': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = 'r'; p++; break; case '\t': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = 't'; p++; break; default: *(q++) = *(p++); break; } } *(q++) = c; *q = '\0'; return q - buffer; }
/* * *presult is "did comparison match or not" */ static int radius_do_cmp(REQUEST *request, int *presult, FR_TOKEN lt, const char *pleft, FR_TOKEN token, FR_TOKEN rt, const char *pright, int cflags, int modreturn) { int result; uint32_t lint, rint; VALUE_PAIR *vp = NULL; #ifdef HAVE_REGEX_H char buffer[8192]; #else cflags = cflags; /* -Wunused */ #endif rt = rt; /* -Wunused */ if (lt == T_BARE_WORD) { /* * Maybe check the last return code. */ if (token == T_OP_CMP_TRUE) { int isreturn; /* * Looks like a return code, treat is as such. */ isreturn = fr_str2int(modreturn_table, pleft, -1); if (isreturn != -1) { *presult = (modreturn == isreturn); return TRUE; } } /* * Bare words on the left can be attribute names. */ if (radius_get_vp(request, pleft, &vp)) { VALUE_PAIR myvp; /* * VP exists, and that's all we're looking for. */ if (token == T_OP_CMP_TRUE) { *presult = (vp != NULL); return TRUE; } if (!vp) { DICT_ATTR *da; /* * The attribute on the LHS may * have been a dynamically * registered callback. i.e. it * doesn't exist as a VALUE_PAIR. * If so, try looking for it. */ da = dict_attrbyname(pleft); if (da && (da->vendor == 0) && radius_find_compare(da->attr)) { VALUE_PAIR *check = pairmake(pleft, pright, token); *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0); RDEBUG3(" Callback returns %d", *presult); pairfree(&check); return TRUE; } RDEBUG2(" (Attribute %s was not found)", pleft); *presult = 0; return TRUE; } #ifdef HAVE_REGEX_H /* * Regex comparisons treat everything as * strings. */ if ((token == T_OP_REG_EQ) || (token == T_OP_REG_NE)) { vp_prints_value(buffer, sizeof(buffer), vp, 0); pleft = buffer; goto do_checks; } #endif memcpy(&myvp, vp, sizeof(myvp)); if (!pairparsevalue(&myvp, pright)) { RDEBUG2("Failed parsing \"%s\": %s", pright, fr_strerror()); return FALSE; } myvp.operator = token; *presult = paircmp(&myvp, vp); RDEBUG3(" paircmp -> %d", *presult); return TRUE; } /* else it's not a VP in a list */ } #ifdef HAVE_REGEX_H do_checks: #endif switch (token) { case T_OP_GE: case T_OP_GT: case T_OP_LE: case T_OP_LT: if (!all_digits(pright)) { RDEBUG2(" (Right field is not a number at: %s)", pright); return FALSE; } rint = strtoul(pright, NULL, 0); if (!all_digits(pleft)) { RDEBUG2(" (Left field is not a number at: %s)", pleft); return FALSE; } lint = strtoul(pleft, NULL, 0); break; default: lint = rint = 0; /* quiet the compiler */ break; } switch (token) { case T_OP_CMP_TRUE: /* * Check for truth or falsehood. */ if (all_digits(pleft)) { lint = strtoul(pleft, NULL, 0); result = (lint != 0); } else { result = (*pleft != '\0'); } break; case T_OP_CMP_EQ: result = (strcmp(pleft, pright) == 0); break; case T_OP_NE: result = (strcmp(pleft, pright) != 0); break; case T_OP_GE: result = (lint >= rint); break; case T_OP_GT: result = (lint > rint); break; case T_OP_LE: result = (lint <= rint); break; case T_OP_LT: result = (lint < rint); break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: { int i, compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); /* * Add new %{0}, %{1}, etc. */ if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) { char *r; free(request_data_get(request, request, REQUEST_DATA_REGEX | i)); /* * No %{i}, skip it. * We MAY have %{2} without %{1}. */ if (rxmatch[i].rm_so == -1) continue; /* * Copy substring into allocated buffer */ r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1); memcpy(r, pleft + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; request_data_add(request, request, REQUEST_DATA_REGEX | i, r, free); } result = (compare == 0); } break; case T_OP_REG_NE: { int compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); result = (compare != 0); } break; #endif default: DEBUG("ERROR: Comparison operator %s is not supported", fr_token_name(token)); result = FALSE; break; } *presult = result; return TRUE; }
int main(int argc, char **argv) { RADIUS_PACKET *req; char *p; int c; int port = 0; char *filename = NULL; FILE *fp; int id; int force_af = AF_UNSPEC; /* * We probably don't want to free the talloc autofree context * directly, so we'll allocate a new context beneath it, and * free that before any leak reports. */ TALLOC_CTX *autofree = talloc_init("main"); id = ((int)getpid() & 0xff); fr_debug_flag = 0; radlog_dest = L_DST_STDERR; set_radius_dir(autofree, RADIUS_DIR); while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF) { switch(c) { case '4': force_af = AF_INET; break; case '6': force_af = AF_INET6; break; case 'd': set_radius_dir(autofree, optarg); break; case 'D': mainconfig.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filename = optarg; break; case 'q': do_output = 0; break; case 'x': debug_flag++; fr_debug_flag++; break; case 'X': #if 0 sha1_data_problems = 1; /* for debugging only */ #endif break; case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); break; case 'i': if (!isdigit((int) *optarg)) usage(); id = atoi(optarg); if ((id < 0) || (id > 255)) { usage(); } break; case 's': do_summary = 1; break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': printf("radeapclient: $Id: 20c518ef414933fae3cdf564852c12e483f7c8c9 $ built on " __DATE__ " at " __TIME__ "\n"); exit(0); break; case 'S': fp = fopen(optarg, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", optarg, fr_syserror(errno)); exit(1); } if (fgets(filesecret, sizeof(filesecret), fp) == NULL) { fprintf(stderr, "radclient: Error reading %s: %s\n", optarg, fr_syserror(errno)); exit(1); } fclose(fp); /* truncate newline */ p = filesecret + strlen(filesecret) - 1; while ((p >= filesecret) && (*p < ' ')) { *p = '\0'; --p; } if (strlen(filesecret) < 2) { fprintf(stderr, "radclient: Secret in %s is too short\n", optarg); exit(1); } secret = filesecret; break; case 'h': default: usage(); break; } } argc -= (optind - 1); argv += (optind - 1); if ((argc < 3) || ((!secret) && (argc < 4))) { usage(); } if (!mainconfig.dictionary_dir) { mainconfig.dictionary_dir = DICTDIR; } /* * Read the distribution dictionaries first, then * the ones in raddb. */ DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY); if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) { ERROR("Errors reading dictionary: %s", fr_strerror()); exit(1); } /* * It's OK if this one doesn't exist. */ int rcode = dict_read(radius_dir, RADIUS_DICTIONARY); if (rcode == -1) { ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY, fr_strerror()); exit(1); } /* * We print this after reading it. That way if * it doesn't exist, it's OK, and we don't print * anything. */ if (rcode == 0) { DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY); } req = rad_alloc(NULL, 1); if (!req) { fr_perror("radclient"); exit(1); } #if 0 { FILE *randinit; if((randinit = fopen("/dev/urandom", "r")) == NULL) { perror("/dev/urandom"); } else { fread(randctx.randrsl, 256, 1, randinit); fclose(randinit); } } fr_randinit(&randctx, 1); #endif req->id = id; /* * Resolve hostname. */ if (force_af == AF_UNSPEC) force_af = AF_INET; req->dst_ipaddr.af = force_af; if (strcmp(argv[1], "-") != 0) { char const *hostname = argv[1]; char const *portname = argv[1]; char buffer[256]; if (*argv[1] == '[') { /* IPv6 URL encoded */ p = strchr(argv[1], ']'); if ((size_t) (p - argv[1]) >= sizeof(buffer)) { usage(); } memcpy(buffer, argv[1] + 1, p - argv[1] - 1); buffer[p - argv[1] - 1] = '\0'; hostname = buffer; portname = p + 1; } p = strchr(portname, ':'); if (p && (strchr(p + 1, ':') == NULL)) { *p = '\0'; portname = p + 1; } else { portname = NULL; } if (ip_hton(hostname, force_af, &req->dst_ipaddr) < 0) { fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno)); exit(1); } /* * Strip port from hostname if needed. */ if (portname) port = atoi(portname); } /* * See what kind of request we want to send. */ if (strcmp(argv[2], "auth") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_AUTHENTICATION_REQUEST; } else if (strcmp(argv[2], "acct") == 0) { if (port == 0) port = getport("radacct"); if (port == 0) port = PW_ACCT_UDP_PORT; req->code = PW_CODE_ACCOUNTING_REQUEST; do_summary = 0; } else if (strcmp(argv[2], "status") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_STATUS_SERVER; } else if (strcmp(argv[2], "disconnect") == 0) { if (port == 0) port = PW_POD_UDP_PORT; req->code = PW_CODE_DISCONNECT_REQUEST; } else if (isdigit((int) argv[2][0])) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = atoi(argv[2]); } else { usage(); } req->dst_port = port; /* * Add the secret. */ if (argv[3]) secret = argv[3]; /* * Read valuepairs. * Maybe read them, from stdin, if there's no * filename, or if the filename is '-'. */ if (filename && (strcmp(filename, "-") != 0)) { fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", filename, fr_syserror(errno)); exit(1); } } else { fp = stdin; } /* * Send request. */ if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("radclient: socket: "); exit(1); } while(!filedone) { if(req->vps) pairfree(&req->vps); if ((req->vps = readvp2(NULL, fp, &filedone, "radeapclient:")) == NULL) { break; } sendrecv_eap(req); } if(do_summary) { printf("\n\t Total approved auths: %d\n", totalapp); printf("\t Total denied auths: %d\n", totaldeny); } talloc_free(autofree); return 0; }
/* * Do post-proxy processing, * 0 = fail * 1 = OK. * * Called from rlm_eap.c, eap_postproxy(). */ static int mschap_postproxy(EAP_HANDLER *handler, void *tunnel_data) { VALUE_PAIR *response = NULL; mschapv2_opaque_t *data; data = (mschapv2_opaque_t *) handler->opaque; rad_assert(data != NULL); tunnel_data = tunnel_data; /* -Wunused */ DEBUG2(" rlm_eap_mschapv2: Passing reply from proxy back into the tunnel %p %d.", handler->request, handler->request->reply->code); /* * There is only a limited number of possibilities. */ switch (handler->request->reply->code) { case PW_AUTHENTICATION_ACK: DEBUG(" rlm_eap_mschapv2: Proxied authentication succeeded."); /* * Move the attribute, so it doesn't go into * the reply. */ pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY); break; default: case PW_AUTHENTICATION_REJECT: DEBUG(" rlm_eap_mschapv2: Proxied authentication did not succeed."); return 0; } /* * No response, die. */ if (!response) { radlog(L_ERR, "rlm_eap_mschapv2: Proxied reply contained no MS-CHAPv2-Success or MS-CHAP-Error"); return 0; } /* * Done doing EAP proxy stuff. */ handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; eapmschapv2_compose(handler, response); data->code = PW_EAP_MSCHAPV2_SUCCESS; /* * Delete MPPE keys & encryption policy * * FIXME: Use intelligent names... */ fix_mppe_keys(handler, data); /* * save any other attributes for re-use in the final * access-accept e.g. vlan, etc. This lets the PEAP * use_tunneled_reply code work */ data->reply = paircopy(handler->request->reply->vps); /* * And we need to challenge the user, not ack/reject them, * so we re-write the ACK to a challenge. Yuck. */ handler->request->reply->code = PW_ACCESS_CHALLENGE; pairfree(&response); return 1; }