/* * Run a virtual server auth and postauth * */ int rad_virtual_server(REQUEST *request) { VALUE_PAIR *vp; int result; /* * We currently only handle AUTH packets here. * This could be expanded to handle other packets as well if required. */ rad_assert(request->packet->code == PW_AUTHENTICATION_REQUEST); result = rad_authenticate(request); if (request->reply->code == PW_AUTHENTICATION_REJECT) { pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY); vp = radius_pairmake(request, &request->config_items, "Post-Auth-Type", "Reject", T_OP_SET); if (vp) rad_postauth(request); } if (request->reply->code == PW_AUTHENTICATION_ACK) { rad_postauth(request); } return result; }
/* * Set the REDISN user name. * * We don't call the escape function here. The resulting string * will be escaped later in the queries xlat so we don't need to * escape it twice. (it will make things wrong if we have an * escape candidate character in the username) */ int redisn_set_user(REDIS_INST *inst, REQUEST *request, char *redisnusername, const char *username) { VALUE_PAIR *vp=NULL; char tmpuser[MAX_STRING_LEN]; tmpuser[0] = '\0'; redisnusername[0]= '\0'; /* Remove any user attr we added previously */ pairdelete(&request->packet->vps, PW_REDIS_USER_NAME, 0, TAG_ANY); if (username != NULL) { strlcpy(tmpuser, username, sizeof(tmpuser)); } else if (strlen(inst->query_user)) { radius_xlat(tmpuser, sizeof(tmpuser), inst->query_user, request, NULL, inst); } else { return 0; } strlcpy(redisnusername, tmpuser, MAX_STRING_LEN); RDEBUG2("redisn_set_user escaped user --> '%s'", redisnusername); vp = radius_pairmake(request, &request->packet->vps, "REDISN-User-Name", NULL, 0); if (!vp) { radlog(L_ERR, "%s", fr_strerror()); return -1; } strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); return 0; }
/* * Add MPPE attributes to the reply. */ static void mppe_add_reply(REQUEST *request, const char* name, const uint8_t * value, int len) { VALUE_PAIR *vp; vp = radius_pairmake(request, &request->reply->vps, name, "", T_OP_EQ); if (!vp) { RDEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, fr_strerror()); return; } memcpy(vp->vp_octets, value, len); vp->length = len; }
/* * mschap_authorize() - authorize user if we can authenticate * it later. Add Auth-Type attribute if present in module * configuration (usually Auth-Type must be "MS-CHAP") */ static int mschap_authorize(void * instance, REQUEST *request) { #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL, *response = NULL; challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!challenge) { return RLM_MODULE_NOOP; } response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); if (!response) response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE); /* * Nothing we recognize. Don't do anything. */ if (!response) { RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP-Response."); return RLM_MODULE_NOOP; } if (pairfind(request->config_items, PW_AUTH_TYPE)) { RDEBUG2("Found existing Auth-Type. Not changing it."); return RLM_MODULE_NOOP; } RDEBUG2("Found MS-CHAP attributes. Setting 'Auth-Type = %s'", inst->xlat_name); /* * Set Auth-Type to MS-CHAP. The authentication code * will take care of turning clear-text passwords into * NT/LM passwords. */ if (!radius_pairmake(request, &request->config_items, "Auth-Type", inst->auth_type, T_OP_EQ)) { return RLM_MODULE_FAIL; } return RLM_MODULE_OK; #undef inst }
static int eap_example_server_step(EAP_HANDLER *handler) { int res, process = 0; REQUEST *request = handler->request; res = eap_server_sm_step(handler->server_ctx.eap); if (handler->server_ctx.eap_if->eapReq) { DEBUG("==> Request"); process = 1; handler->server_ctx.eap_if->eapReq = 0; } if (handler->server_ctx.eap_if->eapSuccess) { DEBUG("==> Success"); process = 1; res = 0; if (handler->server_ctx.eap_if->eapKeyAvailable) { int length = handler->server_ctx.eap_if->eapKeyDataLen; VALUE_PAIR *vp; if (length > 64) { length = 32; } else { length /= 2; /* * FIXME: Length is zero? */ } vp = radius_pairmake(request, &request->reply->vps, "MS-MPPE-Recv-Key", "", T_OP_EQ); if (vp) { memcpy(vp->vp_octets, handler->server_ctx.eap_if->eapKeyData, length); vp->length = length; } vp = radius_pairmake(request, &request->reply->vps, "MS-MPPE-Send-Key", "", T_OP_EQ); if (vp) { memcpy(vp->vp_octets, handler->server_ctx.eap_if->eapKeyData + length, length); vp->length = length; } } } if (handler->server_ctx.eap_if->eapFail) { DEBUG("==> Fail"); process = 1; } if (process) { if (wpabuf_head(handler->server_ctx.eap_if->eapReqData)) { if (!eap_req2vp(handler)) return -1; } else { return -1; } } return res; }
/* * Before trusting a certificate, you must make sure that the * certificate is 'valid'. There are several steps that your * application can take in determining if a certificate is * valid. Commonly used steps are: * * 1.Verifying the certificate's signature, and verifying that * the certificate has been issued by a trusted Certificate * Authority. * * 2.Verifying that the certificate is valid for the present date * (i.e. it is being presented within its validity dates). * * 3.Verifying that the certificate has not been revoked by its * issuing Certificate Authority, by checking with respect to a * Certificate Revocation List (CRL). * * 4.Verifying that the credentials presented by the certificate * fulfill additional requirements specific to the application, * such as with respect to access control lists or with respect * to OCSP (Online Certificate Status Processing). * * NOTE: This callback will be called multiple times based on the * depth of the root certificate chain */ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) { char subject[1024]; /* Used for the subject name */ char issuer[1024]; /* Used for the issuer name */ char common_name[1024]; char cn_str[1024]; char buf[64]; EAP_HANDLER *handler = NULL; X509 *client_cert; X509 *issuer_cert; SSL *ssl; int err, depth, lookup; EAP_TLS_CONF *conf; int my_ok = ok; REQUEST *request; ASN1_INTEGER *sn = NULL; ASN1_TIME *asn_time = NULL; #ifdef HAVE_OPENSSL_OCSP_H X509_STORE *ocsp_store = NULL; #endif client_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); lookup = depth; /* * Log client/issuing cert. If there's an error, log * issuing cert. */ if ((lookup > 1) && !my_ok) lookup = 1; /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0); request = handler->request; conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1); #ifdef HAVE_OPENSSL_OCSP_H ocsp_store = (X509_STORE *)SSL_get_ex_data(ssl, 2); #endif /* * Get the Serial Number */ buf[0] = '\0'; sn = X509_get_serialNumber(client_cert); /* * For this next bit, we create the attributes *only* if * we're at the client or issuing certificate. */ if ((lookup <= 1) && sn && (sn->length < (sizeof(buf) / 2))) { char *p = buf; int i; for (i = 0; i < sn->length; i++) { sprintf(p, "%02x", (unsigned int)sn->data[i]); p += 2; } pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_SERIAL][lookup], buf, T_OP_SET)); } /* * Get the Expiration Date */ buf[0] = '\0'; asn_time = X509_get_notAfter(client_cert); if ((lookup <= 1) && asn_time && (asn_time->length < MAX_STRING_LEN)) { memcpy(buf, (char*) asn_time->data, asn_time->length); buf[asn_time->length] = '\0'; pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_EXPIRATION][lookup], buf, T_OP_SET)); } /* * Get the Subject & Issuer */ subject[0] = issuer[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(client_cert), subject, sizeof(subject)); subject[sizeof(subject) - 1] = '\0'; if ((lookup <= 1) && subject[0] && (strlen(subject) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_SUBJECT][lookup], subject, T_OP_SET)); } X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, sizeof(issuer)); issuer[sizeof(issuer) - 1] = '\0'; if ((lookup <= 1) && issuer[0] && (strlen(issuer) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_ISSUER][lookup], issuer, T_OP_SET)); } /* * Get the Common Name */ X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), NID_commonName, common_name, sizeof(common_name)); common_name[sizeof(common_name) - 1] = '\0'; if ((lookup <= 1) && common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_CN][lookup], common_name, T_OP_SET)); } /* * If the CRL has expired, that might still be OK. */ if (!my_ok && (conf->allow_expired_crl) && (err == X509_V_ERR_CRL_HAS_EXPIRED)) { my_ok = 1; X509_STORE_CTX_set_error( ctx, 0 ); } if (!my_ok) { const char *p = X509_verify_cert_error_string(err); radlog(L_ERR,"--> verify error:num=%d:%s\n",err, p); radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", p, T_OP_SET); return my_ok; } switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: radlog(L_ERR, "issuer= %s\n", issuer); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: radlog(L_ERR, "notBefore="); #if 0 ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert)); #endif break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: radlog(L_ERR, "notAfter="); #if 0 ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert)); #endif break; } /* * If we're at the actual client cert, apply additional * checks. */ if (depth == 0) { /* * If the conf tells us to, check cert issuer * against the specified value and fail * verification if they don't match. */ if (conf->check_cert_issuer && (strcmp(issuer, conf->check_cert_issuer) != 0)) { radlog(L_AUTH, "rlm_eap_tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer); my_ok = 0; } /* * If the conf tells us to, check the CN in the * cert against xlat'ed value, but only if the * previous checks passed. */ if (my_ok && conf->check_cert_cn) { if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, handler->request, NULL)) { radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.", conf->check_cert_cn); /* if this fails, fail the verification */ my_ok = 0; } else { RDEBUG2("checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str); if (strcmp(cn_str, common_name) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str); my_ok = 0; } } } /* check_cert_cn */ #ifdef HAVE_OPENSSL_OCSP_H if (my_ok && conf->ocsp_enable){ RDEBUG2("--> Starting OCSP Request"); if(X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert)!=1) { radlog(L_ERR, "Error: Couldn't get issuer_cert for %s", common_name); } my_ok = ocsp_check(ocsp_store, issuer_cert, client_cert, conf); } #endif while (conf->verify_client_cert_cmd) { char filename[256]; int fd; FILE *fp; snprintf(filename, sizeof(filename), "%s/%s.client.XXXXXXXX", conf->verify_tmp_dir, progname); fd = mkstemp(filename); if (fd < 0) { RDEBUG("Failed creating file in %s: %s", conf->verify_tmp_dir, strerror(errno)); break; } fp = fdopen(fd, "w"); if (!fp) { RDEBUG("Failed opening file %s: %s", filename, strerror(errno)); break; } if (!PEM_write_X509(fp, client_cert)) { fclose(fp); RDEBUG("Failed writing certificate to file"); goto do_unlink; } fclose(fp); if (!radius_pairmake(request, &request->packet->vps, "TLS-Client-Cert-Filename", filename, T_OP_SET)) { RDEBUG("Failed creating TLS-Client-Cert-Filename"); goto do_unlink; } RDEBUG("Verifying client certificate: %s", conf->verify_client_cert_cmd); if (radius_exec_program(conf->verify_client_cert_cmd, request, 1, NULL, 0, request->packet->vps, NULL, 1) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) fails external verification!", common_name); my_ok = 0; } else { RDEBUG("Client certificate CN %s passed external validation", common_name); } do_unlink: unlink(filename); break; } } /* depth == 0 */ if (debug_flag > 0) { RDEBUG2("chain-depth=%d, ", depth); RDEBUG2("error=%d", err); RDEBUG2("--> User-Name = %s", handler->identity); RDEBUG2("--> BUF-Name = %s", common_name); RDEBUG2("--> subject = %s", subject); RDEBUG2("--> issuer = %s", issuer); RDEBUG2("--> verify return:%d", my_ok); } return my_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) { VALUE_PAIR *namepair; #ifdef WITH_SESSION_MGMT VALUE_PAIR *check_item; #endif VALUE_PAIR *auth_item = NULL; VALUE_PAIR *module_msg; VALUE_PAIR *tmp = NULL; int result; const char *password; char autz_retry = 0; int autz_type = 0; password = ""; #ifdef WITH_PROXY /* * If this request got proxied to another server, we need * to check whether it authenticated the request or not. */ if (request->proxy_reply) { switch (request->proxy_reply->code) { /* * Reply of ACCEPT means accept, thus set Auth-Type * accordingly. */ case PW_AUTHENTICATION_ACK: tmp = radius_paircreate(request, &request->config_items, PW_AUTH_TYPE, PW_TYPE_INTEGER); if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT; #ifdef WITH_POST_PROXY_AUTHORIZE if (mainconfig.post_proxy_authorize) break; #endif goto authenticate; /* * Challenges are punted back to the NAS without any * further processing. */ case PW_ACCESS_CHALLENGE: request->reply->code = PW_ACCESS_CHALLENGE; return RLM_MODULE_OK; /* * ALL other replies mean reject. (this is fail-safe) * * Do NOT do any authorization or authentication. They * are being rejected, so we minimize the amount of work * done by the server, by rejecting them here. */ case PW_AUTHENTICATION_REJECT: rad_authlog("Login incorrect (Home Server says so)", request, 0); request->reply->code = PW_AUTHENTICATION_REJECT; return RLM_MODULE_REJECT; default: rad_authlog("Login incorrect (Home Server failed to respond)", request, 0); return RLM_MODULE_REJECT; } } #endif /* * Get the username from the request. * * Note that namepair MAY be NULL, in which case there * is no User-Name attribute in the request. */ namepair = request->username; /* * Look for, and cache, passwords. */ if (!request->password) { request->password = pairfind(request->packet->vps, PW_USER_PASSWORD); } /* * Discover which password we want to use. */ auth_item = request->password; if (auth_item) { password = (const char *)auth_item->vp_strvalue; } else { /* * Maybe there's a CHAP-Password? */ if ((auth_item = pairfind(request->packet->vps, PW_CHAP_PASSWORD)) != NULL) { password = "******"; } else { /* * No password we recognize. */ password = "******"; } } request->password = auth_item; /* * Get the user's authorization information from the database */ autz_redo: result = module_authorize(autz_type, request); switch (result) { case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: break; case RLM_MODULE_HANDLED: return result; case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE)) != NULL) { char msg[MAX_STRING_LEN + 16]; snprintf(msg, sizeof(msg), "Invalid user (%s)", module_msg->vp_strvalue); rad_authlog(msg,request,0); } else { rad_authlog("Invalid user", request, 0); } request->reply->code = PW_AUTHENTICATION_REJECT; return result; } if (!autz_retry) { tmp = pairfind(request->config_items, PW_AUTZ_TYPE); if (tmp) { autz_type = tmp->vp_integer; RDEBUG2("Using Autz-Type %s", dict_valnamebyattr(PW_AUTZ_TYPE, autz_type)); autz_retry = 1; goto autz_redo; } } /* * If we haven't already proxied the packet, then check * to see if we should. Maybe one of the authorize * modules has decided that a proxy should be used. If * so, get out of here and send the packet. */ if ( #ifdef WITH_PROXY (request->proxy == NULL) && #endif ((tmp = pairfind(request->config_items, PW_PROXY_TO_REALM)) != NULL)) { REALM *realm; realm = realm_find2(tmp->vp_strvalue); /* * Don't authenticate, as the request is going to * be proxied. */ if (realm && realm->auth_pool) { return RLM_MODULE_OK; } /* * Catch users who set Proxy-To-Realm to a LOCAL * realm (sigh). But don't complain if it is * *the* LOCAL realm. */ if (realm &&(strcmp(realm->name, "LOCAL") != 0)) { RDEBUG2("WARNING: You set Proxy-To-Realm = %s, but it is a LOCAL realm! Cancelling proxy request.", realm->name); } if (!realm) { RDEBUG2("WARNING: You set Proxy-To-Realm = %s, but the realm does not exist! Cancelling invalid proxy request.", tmp->vp_strvalue); } } #ifdef WITH_PROXY authenticate: #endif /* * Perhaps there is a Stripped-User-Name now. */ namepair = request->username; /* * Validate the user */ do { result = rad_check_password(request); if (result > 0) { /* don't reply! */ return RLM_MODULE_HANDLED; } } while(0); /* * Failed to validate the user. * * We PRESUME that the code which failed will clean up * request->reply->vps, to be ONLY the reply items it * wants to send back. */ if (result < 0) { RDEBUG2("Failed to authenticate the user."); request->reply->code = PW_AUTHENTICATION_REJECT; if ((module_msg = pairfind(request->packet->vps,PW_MODULE_FAILURE_MESSAGE)) != NULL){ char msg[MAX_STRING_LEN+19]; snprintf(msg, sizeof(msg), "Login incorrect (%s)", module_msg->vp_strvalue); rad_authlog(msg, request, 0); } else { rad_authlog("Login incorrect", request, 0); } /* double check: maybe the secret is wrong? */ if ((debug_flag > 1) && (auth_item != NULL) && (auth_item->attribute == PW_USER_PASSWORD)) { uint8_t *p; p = (uint8_t *) auth_item->vp_strvalue; while (*p) { int size; size = fr_utf8_char(p); if (!size) { log_debug(" WARNING: Unprintable characters in the password. Double-check the shared secret on the server and the NAS!"); break; } p += size; } } } #ifdef WITH_SESSION_MGMT if (result >= 0 && (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE)) != NULL) { int r, session_type = 0; char logstr[1024]; char umsg[MAX_STRING_LEN + 1]; const char *user_msg = NULL; tmp = pairfind(request->config_items, PW_SESSION_TYPE); if (tmp) { session_type = tmp->vp_integer; RDEBUG2("Using Session-Type %s", dict_valnamebyattr(PW_SESSION_TYPE, session_type)); } /* * User authenticated O.K. Now we have to check * for the Simultaneous-Use parameter. */ if (namepair && (r = module_checksimul(session_type, request, check_item->vp_integer)) != 0) { char mpp_ok = 0; if (r == 2){ /* Multilink attempt. Check if port-limit > simultaneous-use */ VALUE_PAIR *port_limit; if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT)) != NULL && port_limit->vp_integer > check_item->vp_integer){ RDEBUG2("MPP is OK"); mpp_ok = 1; } } if (!mpp_ok){ if (check_item->vp_integer > 1) { snprintf(umsg, sizeof(umsg), "\r\nYou are already logged in %d times - access denied\r\n\n", (int)check_item->vp_integer); user_msg = umsg; } else { user_msg = "\r\nYou are already logged in - access denied\r\n\n"; } request->reply->code = PW_AUTHENTICATION_REJECT; /* * They're trying to log in too many times. * Remove ALL reply attributes. */ pairfree(&request->reply->vps); radius_pairmake(request, &request->reply->vps, "Reply-Message", user_msg, T_OP_SET); snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s", check_item->vp_integer, r == 2 ? "[MPP attempt]" : ""); rad_authlog(logstr, request, 1); result = -1; } } } #endif /* * Result should be >= 0 here - if not, it means the user * is rejected, so we just process post-auth and return. */ if (result < 0) { return RLM_MODULE_REJECT; } /* * Add the port number to the Framed-IP-Address if * vp->addport is set. */ if (((tmp = pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS)) != NULL) && (tmp->flags.addport != 0)) { VALUE_PAIR *vpPortId; /* * Find the NAS port ID. */ if ((vpPortId = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL) { unsigned long tvalue = ntohl(tmp->vp_integer); tmp->vp_integer = htonl(tvalue + vpPortId->vp_integer); tmp->flags.addport = 0; ip_ntoa(tmp->vp_strvalue, tmp->vp_integer); } else { RDEBUG2("WARNING: No NAS-Port attribute in request. CANNOT return a Framed-IP-Address + NAS-Port.\n"); pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS); } } /* * Set the reply to Access-Accept, if it hasn't already * been set to something. (i.e. Access-Challenge) */ if (request->reply->code == 0) request->reply->code = PW_AUTHENTICATION_ACK; if ((module_msg = pairfind(request->packet->vps,PW_MODULE_SUCCESS_MESSAGE)) != NULL){ char msg[MAX_STRING_LEN+12]; snprintf(msg, sizeof(msg), "Login OK (%s)", module_msg->vp_strvalue); rad_authlog(msg, request, 1); } else { rad_authlog("Login OK", request, 1); } /* * Run the modules in the 'post-auth' section. */ result = rad_postauth(request); return result; }
/* * mschap_authenticate() - authenticate user based on given * attributes and configuration. * We will try to find out password in configuration * or in configured passwd file. * If one is found we will check paraneters given by NAS. * * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have * one of: * PAP: PW_USER_PASSWORD or * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE * In case of password mismatch or locked account we MAY return * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2 * If MS-CHAP2 succeeds we MUST return * PW_MSCHAP2_SUCCESS */ static int mschap_authenticate(void * instance, REQUEST *request) { #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL; VALUE_PAIR *response = NULL; VALUE_PAIR *password = NULL; VALUE_PAIR *lm_password, *nt_password, *smb_ctrl; VALUE_PAIR *username; uint8_t nthashhash[16]; char msch2resp[42]; char *username_string; int chap = 0; int do_ntlm_auth; /* * If we have ntlm_auth configured, use it unless told * otherwise */ do_ntlm_auth = (inst->ntlm_auth != NULL); /* * If we have an ntlm_auth configuration, then we may * want to suppress it. */ if (do_ntlm_auth) { VALUE_PAIR *vp = pairfind(request->config_items, PW_MS_CHAP_USE_NTLM_AUTH); if (vp) do_ntlm_auth = vp->vp_integer; } /* * Find the SMB-Account-Ctrl attribute, or the * SMB-Account-Ctrl-Text attribute. */ smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL); if (!smb_ctrl) { password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT); if (password) { smb_ctrl = radius_pairmake(request, &request->config_items, "SMB-Account-CTRL", "0", T_OP_SET); if (smb_ctrl) { smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue); } } } /* * We're configured to do MS-CHAP authentication. * and account control information exists. Enforce it. */ if (smb_ctrl) { /* * Password is not required. */ if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) { RDEBUG2("SMB-Account-Ctrl says no password is required."); return RLM_MODULE_OK; } } /* * Decide how to get the passwords. */ password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD); /* * We need an LM-Password. */ lm_password = pairfind(request->config_items, PW_LM_PASSWORD); if (lm_password) { /* * Allow raw octets. */ if ((lm_password->length == 16) || ((lm_password->length == 32) && (fr_hex2bin(lm_password->vp_strvalue, lm_password->vp_octets, 16) == 16))) { RDEBUG2("Found LM-Password"); lm_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid LM-Password"); lm_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create LM-Password."); } else { /* there is a configured Cleartext-Password */ lm_password = radius_pairmake(request, &request->config_items, "LM-Password", "", T_OP_EQ); if (!lm_password) { radlog_request(L_ERR, 0, request, "No memory"); } else { smbdes_lmpwdhash(password->vp_strvalue, lm_password->vp_octets); lm_password->length = 16; } } /* * We need an NT-Password. */ nt_password = pairfind(request->config_items, PW_NT_PASSWORD); if (nt_password) { if ((nt_password->length == 16) || ((nt_password->length == 32) && (fr_hex2bin(nt_password->vp_strvalue, nt_password->vp_octets, 16) == 16))) { RDEBUG2("Found NT-Password"); nt_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid NT-Password"); nt_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create NT-Password."); } else { /* there is a configured Cleartext-Password */ nt_password = radius_pairmake(request, &request->config_items, "NT-Password", "", T_OP_EQ); if (!nt_password) { radlog_request(L_ERR, 0, request, "No memory"); return RLM_MODULE_FAIL; } else { ntpwdhash(nt_password->vp_octets, password->vp_strvalue); nt_password->length = 16; } } challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!challenge) { RDEBUG2("No MS-CHAP-Challenge in the request"); return RLM_MODULE_REJECT; } /* * We also require an MS-CHAP-Response. */ response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); /* * MS-CHAP-Response, means MS-CHAPv1 */ if (response) { int offset; /* * MS-CHAPv1 challenges are 8 octets. */ if (challenge->length < 8) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We are doing MS-CHAP. Calculate the MS-CHAP * response */ if (response->vp_octets[1] & 0x01) { RDEBUG2("Told to do MS-CHAPv1 with NT-Password"); password = nt_password; offset = 26; } else { RDEBUG2("Told to do MS-CHAPv1 with LM-Password"); password = lm_password; offset = 2; } /* * Do the MS-CHAP authentication. */ if (do_mschap(inst, request, password, challenge->vp_octets, response->vp_octets + offset, nthashhash, do_ntlm_auth) < 0) { RDEBUG2("MS-CHAP-Response is incorrect."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_REJECT; } chap = 1; } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) { uint8_t mschapv1_challenge[16]; /* * MS-CHAPv2 challenges are 16 octets. */ if (challenge->length < 16) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We also require a User-Name */ username = pairfind(request->packet->vps, PW_USER_NAME); if (!username) { radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2"); return RLM_MODULE_INVALID; } /* * with_ntdomain_hack moved here */ if ((username_string = strchr(username->vp_strvalue, '\\')) != NULL) { if (inst->with_ntdomain_hack) { username_string++; } else { RDEBUG2(" NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); username_string = username->vp_strvalue; } } else { username_string = username->vp_strvalue; } #ifdef __APPLE__ /* * No "known good" NT-Password attribute. Try to do * OpenDirectory authentication. * * If OD determines the user is an AD user it will return noop, which * indicates the auth process should continue directly to AD. * Otherwise OD will determine auth success/fail. */ if (!nt_password && inst->open_directory) { RDEBUG2("No NT-Password configured. Trying OpenDirectory Authentication."); int odStatus = od_mschap_auth(request, challenge, username); if (odStatus != RLM_MODULE_NOOP) { return odStatus; } } #endif /* * The old "mschapv2" function has been moved to * here. * * MS-CHAPv2 takes some additional data to create an * MS-CHAPv1 challenge, and then does MS-CHAPv1. */ challenge_hash(response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ username_string, /* user name */ mschapv1_challenge); /* resulting challenge */ RDEBUG2("Told to do MS-CHAPv2 for %s with NT-Password", username_string); if (do_mschap(inst, request, nt_password, mschapv1_challenge, response->vp_octets + 26, nthashhash, do_ntlm_auth) < 0) { RDEBUG2("FAILED: MS-CHAP2-Response is incorrect"); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_REJECT; } /* * Get the NT-hash-hash, if necessary */ if (nt_password) { } auth_response(username_string, /* without the domain */ nthashhash, /* nt-hash-hash */ response->vp_octets + 26, /* peer response */ response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ msch2resp); /* calculated MPPE key */ mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP2-Success", msch2resp, 42); chap = 2; } else { /* Neither CHAPv1 or CHAPv2 response: die */ radlog_request(L_AUTH, 0, request, "No MS-CHAP response found"); return RLM_MODULE_INVALID; } /* * We have a CHAP response, but the account may be * disabled. Reject the user with the same error code * we use when their password is invalid. */ if (smb_ctrl) { /* * Account is disabled. * * They're found, but they don't exist, so we * return 'not found'. */ if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) || ((smb_ctrl->vp_integer & ACB_NORMAL) == 0)) { RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal account."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_NOTFOUND; } /* * User is locked out. */ if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) { RDEBUG2("SMB-Account-Ctrl says that the account is locked out."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=647 R=0", 9); return RLM_MODULE_USERLOCK; } } /* now create MPPE attributes */ if (inst->use_mppe) { uint8_t mppe_sendkey[34]; uint8_t mppe_recvkey[34]; if (chap == 1){ RDEBUG2("adding MS-CHAPv1 MPPE keys"); memset(mppe_sendkey, 0, 32); if (lm_password) { memcpy(mppe_sendkey, lm_password->vp_octets, 8); } /* * According to RFC 2548 we * should send NT hash. But in * practice it doesn't work. * Instead, we should send nthashhash * * This is an error on RFC 2548. */ /* * do_mschap cares to zero nthashhash if NT hash * is not available. */ memcpy(mppe_sendkey + 8, nthashhash, 16); mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32); } else if (chap == 2) { RDEBUG2("adding MS-CHAPv2 MPPE keys"); mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey); mppe_add_reply(request, "MS-MPPE-Recv-Key", mppe_recvkey, 16); mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16); } radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Policy", (inst->require_encryption)? "0x00000002":"0x00000001", T_OP_EQ); radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Types", (inst->require_strong)? "0x00000004":"0x00000006", T_OP_EQ); } /* else we weren't asked to use MPPE */ return RLM_MODULE_OK; #undef inst }
/* * Generate the keys after the user has been authenticated. */ static int wimax_postauth(void *instance, REQUEST *request) { rlm_wimax_t *inst = instance; VALUE_PAIR *msk, *emsk, *vp; VALUE_PAIR *mn_nai, *ip, *fa_rk; HMAC_CTX hmac; unsigned int rk1_len, rk2_len, rk_len; int rk_lifetime = 3600; /* ? */ uint32_t mip_spi; uint8_t usage_data[24]; uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE]; uint8_t mip_rk[2 * EVP_MAX_MD_SIZE]; msk = pairfind(request->reply->vps, 1129); emsk = pairfind(request->reply->vps, 1130); if (!msk || !emsk) { RDEBUG("No EAP-MSK or EAP-EMSK. Cannot create WiMAX keys."); return RLM_MODULE_NOOP; } /* * If we delete the MS-MPPE-*-Key attributes, then add in * the WiMAX-MSK so that the client has a key available. */ if (inst->delete_mppe_keys) { pairdelete(&request->reply->vps, ((311 << 16) | 16)); pairdelete(&request->reply->vps, ((311 << 16) | 17)); vp = radius_pairmake(request, &request->reply->vps, "WiMAX-MSK", "0x00", T_OP_EQ); if (vp) { memcpy(vp->vp_octets, msk->vp_octets, msk->length); vp->length = msk->length; } } /* * Initialize usage data. */ memcpy(usage_data, "*****@*****.**", 21); /* with trailing \0 */ usage_data[21] = 0x02; usage_data[22] = 0x00; usage_data[23] = 0x01; /* * MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01) */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL); HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data)); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01) */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL); HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len); HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data)); HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len); vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT); if (vp) rk_lifetime = vp->vp_integer; memcpy(mip_rk, mip_rk_1, rk1_len); memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len); rk_len = rk1_len + rk2_len; /* * MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP"); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL); HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Take the 4 most significant octets. * If less than 256, add 256. */ mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) | (mip_rk_1[2] << 8) | mip_rk_1[3]); if (mip_spi < 256) mip_spi += 256; if (debug_flag) { int len = rk_len; char buffer[512]; if (len > 128) len = 128; /* buffer size */ fr_bin2hex(mip_rk, buffer, len); radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer); radlog_request(L_DBG, 0, request, "MIP-SPI = %08x", ntohl(mip_spi)); } /* * FIXME: Perform SPI collision prevention */ /* * Calculate mobility keys */ mn_nai = pairfind(request->packet->vps, 1900); if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900); if (!mn_nai) { RDEBUG("WARNING: WiMAX-MN-NAI was not found in the request or in the reply."); RDEBUG("WARNING: We cannot calculate MN-HA keys."); } /* * WiMAX-IP-Technology */ vp = NULL; if (mn_nai) vp = pairfind(request->reply->vps, WIMAX2ATTR(23)); if (!vp) { RDEBUG("WARNING: WiMAX-IP-Technology not found in reply."); RDEBUG("WARNING: Not calculating MN-HA keys"); } if (vp) switch (vp->vp_integer) { case 2: /* PMIP4 */ /* * Look for WiMAX-hHA-IP-MIP4 */ ip = pairfind(request->reply->vps, WIMAX2ATTR(6)); if (!ip) { RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-PMIP4 key"); break; } /* * MN-HA-PMIP4 = * H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key */ vp = pairfind(request->reply->vps, WIMAX2ATTR(10)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(10), PW_TYPE_OCTETS); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI */ vp = pairfind(request->reply->vps, WIMAX2ATTR(11)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(11), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI"); break; } vp->vp_integer = mip_spi + 1; break; case 3: /* CMIP4 */ /* * Look for WiMAX-hHA-IP-MIP4 */ ip = pairfind(request->reply->vps, WIMAX2ATTR(6)); if (!ip) { RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-CMIP4 key"); break; } /* * MN-HA-CMIP4 = * H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key */ vp = pairfind(request->reply->vps, WIMAX2ATTR(10)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(10), PW_TYPE_OCTETS); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI */ vp = pairfind(request->reply->vps, WIMAX2ATTR(11)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(11), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI"); break; } vp->vp_integer = mip_spi; break; case 4: /* CMIP6 */ /* * Look for WiMAX-hHA-IP-MIP6 */ ip = pairfind(request->reply->vps, WIMAX2ATTR(7)); if (!ip) { RDEBUG("WARNING: WiMAX-hHA-IP-MIP6 not found. Cannot calculate MN-HA-CMIP6 key"); break; } /* * MN-HA-CMIP6 = * H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI); */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key */ vp = pairfind(request->reply->vps, WIMAX2ATTR(12)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(12), PW_TYPE_OCTETS); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI */ vp = pairfind(request->reply->vps, WIMAX2ATTR(13)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(13), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-SPI"); break; } vp->vp_integer = mip_spi + 2; break; default: break; /* do nothing */ } /* * Generate FA-RK, if requested. * * FA-RK= H(MIP-RK, "FA-RK") */ fa_rk = pairfind(request->reply->vps, WIMAX2ATTR(14)); if (fa_rk && (fa_rk->length == 0)) { HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len); fa_rk->length = rk1_len; } /* * Create FA-RK-SPI, which is really SPI-CMIP4, which is * really MIP-SPI. Clear? Of course. This is WiMAX. */ if (fa_rk) { vp = pairfind(request->reply->vps, WIMAX2ATTR(61)); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, WIMAX2ATTR(61), PW_TYPE_INTEGER); } if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-FA-RK-SPI"); } else { vp->vp_integer = mip_spi; } } /* * Generate MN-FA = H(FA-RK, "MN FA" | FA-IP | MN-NAI) */ ip = pairfind(request->reply->vps, 1901); if (fa_rk && ip && mn_nai) { HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, fa_rk->vp_octets, fa_rk->length, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "MN FA", 5); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); vp = radius_paircreate(request, &request->reply->vps, 1902, PW_TYPE_OCTETS); if (!vp) { RDEBUG("WARNING: Failed creating WiMAX-MN-FA"); } else { memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; } } /* * Give additional information about requests && responses * * WiMAX-RRQ-MN-HA-SPI */ vp = pairfind(request->packet->vps, WIMAX2ATTR(20)); if (vp) { RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage."); if (!mn_nai) { RDEBUG("WARNING: MN-NAI was not found!"); } /* * WiMAX-RRQ-HA-IP */ if (!pairfind(request->packet->vps, WIMAX2ATTR(18))) { RDEBUG("WARNING: HA-IP was not found!"); } /* * WiMAX-HA-RK-Key-Requested */ vp = pairfind(request->packet->vps, WIMAX2ATTR(58)); if (vp && (vp->vp_integer == 1)) { RDEBUG("Client requested HA-RK: Should use IP to look it up from storage."); } } /* * Wipe the context of all sensitive information. */ HMAC_CTX_cleanup(&hmac); return RLM_MODULE_UPDATED; }
/* * mschap_authenticate() - authenticate user based on given * attributes and configuration. * We will try to find out password in configuration * or in configured passwd file. * If one is found we will check paraneters given by NAS. * * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have * one of: * PAP: PW_USER_PASSWORD or * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE * In case of password mismatch or locked account we MAY return * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2 * If MS-CHAP2 succeeds we MUST return * PW_MSCHAP2_SUCCESS */ static int mschap_authenticate(void * instance, REQUEST *request) { #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL; VALUE_PAIR *response = NULL; VALUE_PAIR *password = NULL; VALUE_PAIR *lm_password, *nt_password, *smb_ctrl; VALUE_PAIR *username; uint8_t nthashhash[16]; char msch2resp[42]; char *username_string; int chap = 0; int do_ntlm_auth; /* * If we have ntlm_auth configured, use it unless told * otherwise */ do_ntlm_auth = (inst->ntlm_auth != NULL); /* * If we have an ntlm_auth configuration, then we may * want to suppress it. */ if (do_ntlm_auth) { VALUE_PAIR *vp = pairfind(request->config_items, PW_MS_CHAP_USE_NTLM_AUTH); if (vp) do_ntlm_auth = vp->vp_integer; } /* * Find the SMB-Account-Ctrl attribute, or the * SMB-Account-Ctrl-Text attribute. */ smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL); if (!smb_ctrl) { password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT); if (password) { smb_ctrl = radius_pairmake(request, &request->config_items, "SMB-Account-CTRL", "0", T_OP_SET); if (smb_ctrl) { smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue); } } } /* * We're configured to do MS-CHAP authentication. * and account control information exists. Enforce it. */ if (smb_ctrl) { /* * Password is not required. */ if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) { RDEBUG2("SMB-Account-Ctrl says no password is required."); return RLM_MODULE_OK; } } /* * Decide how to get the passwords. */ password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD); /* * We need an LM-Password. */ lm_password = pairfind(request->config_items, PW_LM_PASSWORD); if (lm_password) { /* * Allow raw octets. */ if ((lm_password->length == 16) || ((lm_password->length == 32) && (fr_hex2bin(lm_password->vp_strvalue, lm_password->vp_octets, 16) == 16))) { RDEBUG2("Found LM-Password"); lm_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid LM-Password"); lm_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create LM-Password."); } else { /* there is a configured Cleartext-Password */ lm_password = radius_pairmake(request, &request->config_items, "LM-Password", "", T_OP_EQ); if (!lm_password) { radlog_request(L_ERR, 0, request, "No memory"); } else { smbdes_lmpwdhash(password->vp_strvalue, lm_password->vp_octets); lm_password->length = 16; } } /* * We need an NT-Password. */ nt_password = pairfind(request->config_items, PW_NT_PASSWORD); if (nt_password) { if ((nt_password->length == 16) || ((nt_password->length == 32) && (fr_hex2bin(nt_password->vp_strvalue, nt_password->vp_octets, 16) == 16))) { RDEBUG2("Found NT-Password"); nt_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid NT-Password"); nt_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create NT-Password."); } else { /* there is a configured Cleartext-Password */ nt_password = radius_pairmake(request, &request->config_items, "NT-Password", "", T_OP_EQ); if (!nt_password) { radlog_request(L_ERR, 0, request, "No memory"); return RLM_MODULE_FAIL; } else { mschap_ntpwdhash(nt_password->vp_octets, password->vp_strvalue); nt_password->length = 16; } } challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!challenge) { RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!"); return RLM_MODULE_REJECT; } /* * We also require an MS-CHAP-Response. */ response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); /* * MS-CHAP-Response, means MS-CHAPv1 */ if (response) { int offset; /* * MS-CHAPv1 challenges are 8 octets. */ if (challenge->length < 8) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We are doing MS-CHAP. Calculate the MS-CHAP * response */ if (response->vp_octets[1] & 0x01) { RDEBUG2("Client is using MS-CHAPv1 with NT-Password"); password = nt_password; offset = 26; } else { RDEBUG2("Client is using MS-CHAPv1 with LM-Password"); password = lm_password; offset = 2; } /* * Do the MS-CHAP authentication. */ if (do_mschap(inst, request, password, challenge->vp_octets, response->vp_octets + offset, nthashhash, do_ntlm_auth) < 0) { RDEBUG2("MS-CHAP-Response is incorrect."); goto do_error; } chap = 1; } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) { uint8_t mschapv1_challenge[16]; VALUE_PAIR *name_attr, *response_name; /* * MS-CHAPv2 challenges are 16 octets. */ if (challenge->length < 16) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We also require a User-Name */ username = pairfind(request->packet->vps, PW_USER_NAME); if (!username) { radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2"); return RLM_MODULE_INVALID; } /* * Check for MS-CHAP-User-Name and if found, use it * to construct the MSCHAPv1 challenge. This is * set by rlm_eap_mschap to the MS-CHAP Response * packet Name field. * * We prefer this to the User-Name in the * packet. */ response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME); if (response_name) { name_attr = response_name; } else { name_attr = username; } /* * with_ntdomain_hack moved here, too. */ if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) { if (inst->with_ntdomain_hack) { username_string++; } else { RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); username_string = name_attr->vp_strvalue; } } else { username_string = name_attr->vp_strvalue; } if (response_name && ((username->length != response_name->length) || (strncasecmp(username->vp_strvalue, response_name->vp_strvalue, username->length) != 0))) { RDEBUG("ERROR: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", username->vp_strvalue, response_name->vp_strvalue); return RLM_MODULE_REJECT; } /* * The old "mschapv2" function has been moved to * here. * * MS-CHAPv2 takes some additional data to create an * MS-CHAPv1 challenge, and then does MS-CHAPv1. */ RDEBUG2("Creating challenge hash with username: %s", username_string); mschap_challenge_hash(response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ username_string, /* user name */ mschapv1_challenge); /* resulting challenge */ RDEBUG2("Client is using MS-CHAPv2 for %s, we need NT-Password", username_string); #ifdef __APPLE__ if (inst->open_directory) { return do_od_mschap(request, response, challenge, username_string); } #endif if (do_mschap(inst, request, nt_password, mschapv1_challenge, response->vp_octets + 26, nthashhash, do_ntlm_auth) < 0) { int i; char buffer[128]; RDEBUG2("FAILED: MS-CHAP2-Response is incorrect"); do_error: snprintf(buffer, sizeof(buffer), "E=691 R=%d", inst->allow_retry); if (inst->retry_msg) { snprintf(buffer + 9, sizeof(buffer) - 9, " C="); for (i = 0; i < 16; i++) { snprintf(buffer + 12 + i*2, sizeof(buffer) - 12 - i*2, "%02x", fr_rand() & 0xff); } snprintf(buffer + 44, sizeof(buffer) - 44, " V=3 M=%s", inst->retry_msg); } mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", buffer, strlen(buffer)); return RLM_MODULE_REJECT; } mschap_auth_response(username_string, /* without the domain */ nthashhash, /* nt-hash-hash */ response->vp_octets + 26, /* peer response */ response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ msch2resp); /* calculated MPPE key */ mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP2-Success", msch2resp, 42); chap = 2; } else { /* Neither CHAPv1 or CHAPv2 response: die */ RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!"); return RLM_MODULE_INVALID; } /* * We have a CHAP response, but the account may be * disabled. Reject the user with the same error code * we use when their password is invalid. */ if (smb_ctrl) { /* * Account is disabled. * * They're found, but they don't exist, so we * return 'not found'. */ if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) || ((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0)) { RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal or workstatin trust account."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_NOTFOUND; } /* * User is locked out. */ if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) { RDEBUG2("SMB-Account-Ctrl says that the account is locked out."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=647 R=0", 9); return RLM_MODULE_USERLOCK; } } /* now create MPPE attributes */ if (inst->use_mppe) { uint8_t mppe_sendkey[34]; uint8_t mppe_recvkey[34]; if (chap == 1){ RDEBUG2("adding MS-CHAPv1 MPPE keys"); memset(mppe_sendkey, 0, 32); if (lm_password) { memcpy(mppe_sendkey, lm_password->vp_octets, 8); } /* * According to RFC 2548 we * should send NT hash. But in * practice it doesn't work. * Instead, we should send nthashhash * * This is an error on RFC 2548. */ /* * do_mschap cares to zero nthashhash if NT hash * is not available. */ memcpy(mppe_sendkey + 8, nthashhash, 16); mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32); } else if (chap == 2) { RDEBUG2("adding MS-CHAPv2 MPPE keys"); mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey); mppe_add_reply(request, "MS-MPPE-Recv-Key", mppe_recvkey, 16); mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16); } radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Policy", (inst->require_encryption)? "0x00000002":"0x00000001", T_OP_EQ); radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Types", (inst->require_strong)? "0x00000004":"0x00000006", T_OP_EQ); } /* else we weren't asked to use MPPE */ return RLM_MODULE_OK; #undef inst }
/* * member of the radius group? */ static int od_authorize(UNUSED void *instance, REQUEST *request) { char *name = NULL; struct passwd *userdata = NULL; struct group *groupdata = NULL; int ismember = 0; RADCLIENT *rad_client = NULL; uuid_t uuid; uuid_t guid_sacl; uuid_t guid_nasgroup; int err; char host_ipaddr[128] = {0}; if (!request || !request->username) { RDEBUG("OpenDirectory requires a User-Name attribute."); return RLM_MODULE_NOOP; } /* resolve SACL */ uuid_clear(guid_sacl); groupdata = getgrnam(kRadiusSACLName); if (groupdata != NULL) { err = mbr_gid_to_uuid(groupdata->gr_gid, guid_sacl); if (err != 0) { radlog(L_ERR, "rlm_opendirectory: The group \"%s\" does not have a GUID.", kRadiusSACLName); return RLM_MODULE_FAIL; } } else { RDEBUG("The SACL group \"%s\" does not exist on this system.", kRadiusSACLName); } /* resolve client access list */ uuid_clear(guid_nasgroup); rad_client = request->client; #if 0 if (rad_client->community[0] != '\0' ) { /* * The "community" can be a GUID (Globally Unique ID) or * a group name */ if (uuid_parse(rad_client->community, guid_nasgroup) != 0) { /* attempt to resolve the name */ groupdata = getgrnam(rad_client->community); if (groupdata == NULL) { radlog(L_AUTH, "rlm_opendirectory: The group \"%s\" does not exist on this system.", rad_client->community); return RLM_MODULE_FAIL; } err = mbr_gid_to_uuid(groupdata->gr_gid, guid_nasgroup); if (err != 0) { radlog(L_AUTH, "rlm_opendirectory: The group \"%s\" does not have a GUID.", rad_client->community); return RLM_MODULE_FAIL; } } } else #endif { if (rad_client == NULL) { RDEBUG("The client record could not be found for host %s.", ip_ntoh(&request->packet->src_ipaddr, host_ipaddr, sizeof(host_ipaddr))); } else { RDEBUG("The host %s does not have an access group.", ip_ntoh(&request->packet->src_ipaddr, host_ipaddr, sizeof(host_ipaddr))); } } if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) { RDEBUG("no access control groups, all users allowed."); if (pairfind(request->config_items, PW_AUTH_TYPE) == NULL) { pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ)); RDEBUG("Setting Auth-Type = %s", kAuthType); } return RLM_MODULE_OK; } /* resolve user */ uuid_clear(uuid); name = (char *)request->username->vp_strvalue; rad_assert(name != NULL); userdata = getpwnam(name); if (userdata != NULL) { err = mbr_uid_to_uuid(userdata->pw_uid, uuid); if (err != 0) uuid_clear(uuid); } if (uuid_is_null(uuid)) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "Could not get the user's uuid", T_OP_EQ); return RLM_MODULE_NOTFOUND; } if (!uuid_is_null(guid_sacl)) { err = mbr_check_service_membership(uuid, kRadiusServiceName, &ismember); if (err != 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "Failed to check group membership", T_OP_EQ); return RLM_MODULE_FAIL; } if (ismember == 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "User is not authorized", T_OP_EQ); return RLM_MODULE_USERLOCK; } } if (!uuid_is_null(guid_nasgroup)) { err = mbr_check_membership_refresh(uuid, guid_nasgroup, &ismember); if (err != 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "Failed to check group membership", T_OP_EQ); return RLM_MODULE_FAIL; } if (ismember == 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "User is not authorized", T_OP_EQ); return RLM_MODULE_USERLOCK; } } if (pairfind(request->config_items, PW_AUTH_TYPE) == NULL) { pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ)); RDEBUG("Setting Auth-Type = %s", kAuthType); } return RLM_MODULE_OK; }