/* $OpenBSD: chap.c,v 1.18 2024/02/26 08:47:28 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /**@file * This file provides CHAP (PPP Challenge Handshake Authentication Protocol, * RFC 1994) handlers. Currently this contains authenticator side * implementation only. *

* Supported authentication types: *

  • MD5-CHAP
  • *
  • MS-CHAP Version 2 (RFC 2759)
  • *

    */ /* RFC 1994, 2433 */ /* $Id: chap.c,v 1.18 2024/02/26 08:47:28 yasuoka Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "npppd.h" #include "ppp.h" #ifdef USE_NPPPD_RADIUS #include "radius_chap_const.h" #include "npppd_radius.h" #endif #include "npppd_defs.h" #include "debugutil.h" #include "chap_ms.h" #define HEADERLEN 4 #define CHAP_STATE_INITIAL 1 #define CHAP_STATE_SENT_CHALLENGE 2 #define CHAP_STATE_AUTHENTICATING 3 #define CHAP_STATE_SENT_RESPONSE 4 #define CHAP_STATE_STOPPED 5 #define CHAP_STATE_PROXY_AUTHENTICATION 6 /* retry intervals */ #define CHAP_TIMEOUT 3 #define CHAP_RETRY 10 #define CHAP_CHALLENGE 1 #define CHAP_RESPONSE 2 #define CHAP_SUCCESS 3 #define CHAP_FAILURE 4 /* from RFC 2433 */ #define ERROR_RESTRICTED_LOGIN_HOURS 646 #define ERROR_ACCT_DISABLED 647 #define ERROR_PASSWD_EXPIRED 648 #define ERROR_NO_DIALIN_PERMISSION 649 #define ERROR_AUTHENTICATION_FAILURE 691 #define ERROR_CHANGING_PASSWORD 709 /* MprError.h */ #define ERROR_AUTH_SERVER_TIMEOUT 930 #ifdef CHAP_DEBUG #define CHAP_DBG(x) chap_log x #define CHAP_ASSERT(cond) \ if (!(cond)) { \ fprintf(stderr, \ "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ , __func__, __FILE__, __LINE__); \ abort(); \ } #else #define CHAP_ASSERT(cond) #define CHAP_DBG(x) #endif static void chap_authenticate(chap *_this, u_char *, int); static void chap_failure(chap *, const char *, int); static void chap_response (chap *, int, u_char *, int); static void chap_create_challenge (chap *); static void chap_send_error (chap *, const char *); static void md5chap_authenticate (chap *, int, char *, u_char *, int, u_char *); static void mschapv2_send_error (chap *, int, int); static void mschapv2_authenticate (chap *, int, char *, u_char *, int, u_char *); #ifdef USE_NPPPD_RADIUS static void chap_radius_authenticate (chap *, int, char *, u_char *, int, u_char *); static void chap_radius_response (void *, RADIUS_PACKET *, int, RADIUS_REQUEST_CTX); #endif static char *strip_nt_domain (char *); static void chap_log (chap *, uint32_t, const char *, ...) __printflike(3,4); /** Initialize the CHAP */ void chap_init(chap *_this, npppd_ppp *ppp) { struct tunnconf *conf; CHAP_ASSERT(ppp != NULL); CHAP_ASSERT(_this != NULL); memset(_this, 0, sizeof(chap)); _this->ppp = ppp; conf = ppp_get_tunnconf(ppp); if (conf->chap_name == NULL) gethostname(_this->myname, sizeof(_this->myname)); else strlcpy(_this->myname, conf->chap_name, sizeof(_this->myname)); _this->timerctx.ctx = _this; _this->state = CHAP_STATE_INITIAL; _this->ntry = CHAP_RETRY; } /** Start CHAP as a authenticator. Send a challenge */ void chap_start(chap *_this) { u_char *challp, *challp0; int lmyname; CHAP_ASSERT(_this != NULL); CHAP_ASSERT(_this->ppp != NULL); if (_this->state == CHAP_STATE_PROXY_AUTHENTICATION) { _this->type = PPP_AUTH_CHAP_MD5; _this->state = CHAP_STATE_AUTHENTICATING; chap_authenticate(_this, _this->ppp->proxy_authen_resp, _this->ppp->lproxy_authen_resp); return; } if (_this->state == CHAP_STATE_INITIAL || _this->state == CHAP_STATE_SENT_CHALLENGE) { if (_this->ntry > 0) { _this->ntry--; _this->type = _this->ppp->peer_auth; /* The type is supported? */ if (_this->type != PPP_AUTH_CHAP_MS_V2 && _this->type != PPP_AUTH_CHAP_MD5) { chap_log(_this, LOG_ALERT, "Requested authentication type(0x%x) " "is not supported.", _this->type); ppp_set_disconnect_cause(_this->ppp, PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE, PPP_PROTO_CHAP, 2 /* local */, NULL); ppp_stop(_this->ppp, "Authentication Required"); return; } #ifdef USE_NPPPD_MPPE /* The peer must use MS-CHAP-V2 as the type */ if (MPPE_IS_REQUIRED(_this->ppp) && _this->type != PPP_AUTH_CHAP_MS_V2) { chap_log(_this, LOG_ALERT, "mppe is required but try to start chap " "type=0x%02x", _this->type); ppp_set_disconnect_cause(_this->ppp, PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE, PPP_PROTO_CHAP, 2 /* local */, NULL); ppp_stop(_this->ppp, "Authentication Required"); return; } #endif /* Generate a challenge packet and send it */ challp = ppp_packetbuf(_this->ppp, PPP_AUTH_CHAP); challp += HEADERLEN; challp0 = challp; chap_create_challenge(_this); PUTCHAR(_this->lchall, challp); memcpy(challp, &_this->chall, _this->lchall); challp += _this->lchall; lmyname = strlen(_this->myname); memcpy(challp, _this->myname, lmyname); challp += lmyname; _this->challid = ++_this->pktid; ppp_output(_this->ppp, PPP_PROTO_CHAP, CHAP_CHALLENGE, _this->challid, challp0, challp - challp0); _this->state = CHAP_STATE_SENT_CHALLENGE; TIMEOUT((void (*)(void *))chap_start, _this, CHAP_TIMEOUT); } else { chap_log(_this, LOG_INFO, "Client did't respond our challenage."); ppp_set_disconnect_cause(_this->ppp, PPP_DISCON_AUTH_FSM_TIMEOUT, PPP_PROTO_CHAP, 0, NULL); ppp_stop(_this->ppp, "Authentication Required"); } } } /** Stop the CHAP */ void chap_stop(chap *_this) { _this->state = CHAP_STATE_STOPPED; UNTIMEOUT(chap_start, _this); #ifdef USE_NPPPD_RADIUS if (_this->radctx != NULL) { radius_cancel_request(_this->radctx); _this->radctx = NULL; } #endif } /** Called when a CHAP packet is received. */ void chap_input(chap *_this, u_char *pktp, int len) { int code, id, length, lval, lname, authok; u_char *pktp1, *val, namebuf[MAX_USERNAME_LENGTH]; char *name; if (_this->state == CHAP_STATE_STOPPED || _this->state == CHAP_STATE_INITIAL) { chap_log(_this, LOG_INFO, "Received chap packet. But chap is " "not started"); return; } CHAP_ASSERT(_this != NULL); if (len < 4) { chap_log(_this, LOG_ERR, "%s: Received broken packet.", __func__); return; } pktp1 = pktp; GETCHAR(code, pktp1); GETCHAR(id, pktp1); GETSHORT(length, pktp1); if (len < length || len < 5) { chap_log(_this, LOG_ERR, "%s: Received broken packet.", __func__); return; } if (code != CHAP_RESPONSE) { chap_log(_this, LOG_ERR, "Received unknown code=%d", code); return; } /* Create a chap response */ if (id != _this->challid) { chap_log(_this, LOG_ERR, "Received challenge response has unknown id."); return; } if (_this->state == CHAP_STATE_AUTHENTICATING) return; authok = 0; UNTIMEOUT(chap_start, _this); /* pick the username */ GETCHAR(lval, pktp1); val = pktp1; pktp1 += lval; if (lval > length) { chap_log(_this, LOG_ERR, "Received challenge response has invalid Value-Size " "field. %d", lval); return; } name = pktp1; lname = len - (pktp1 - pktp); if (lname <= 0 || sizeof(namebuf) <= lname + 1) { chap_log(_this, LOG_ERR, "Received challenge response has invalid Name " "field."); return; } memcpy(namebuf, name, lname); namebuf[lname] = '\0'; name = namebuf; if (_this->state == CHAP_STATE_SENT_RESPONSE) { if (strcmp(_this->name, name) != 0) { /* * The peer requests us to resend, but the username * has been changed. */ chap_log(_this, LOG_ERR, "Received AuthReq is not same as before. " "%s != %s", name, _this->name); return; } } else if (_this->state != CHAP_STATE_SENT_CHALLENGE) { chap_log(_this, LOG_ERR, "Received AuthReq in illegal state. username=%s", name); return; } _this->state = CHAP_STATE_AUTHENTICATING; strlcpy(_this->name, name, sizeof(_this->name)); chap_authenticate(_this, val, lval); } static void chap_failure(chap *_this, const char *msg, int mschapv2err) { switch(_this->type) { case PPP_AUTH_CHAP_MD5: chap_send_error(_this, "FAILED"); break; case PPP_AUTH_CHAP_MS_V2: mschapv2_send_error(_this, mschapv2err, 0); break; } } static void chap_authenticate(chap *_this, u_char *response, int lresponse) { switch(_this->type) { case PPP_AUTH_CHAP_MD5: /* check the length */ if (lresponse != 16) { chap_log(_this, LOG_ERR, "Invalid response length %d != 16", lresponse); chap_failure(_this, "FAILED", ERROR_AUTHENTICATION_FAILURE); return; } break; case PPP_AUTH_CHAP_MS_V2: /* check the length */ if (lresponse < 49) { chap_log(_this, LOG_ERR, "Packet too short."); chap_failure(_this, "FAILED", ERROR_AUTHENTICATION_FAILURE); return; } break; } if (npppd_ppp_bind_realm(_this->ppp->pppd, _this->ppp, _this->name, 0) == 0) { if (!npppd_ppp_is_realm_ready(_this->ppp->pppd, _this->ppp)) { chap_log(_this, LOG_INFO, "username=\"%s\" realm is not ready.", _this->name); chap_failure(_this, "FAILED", ERROR_AUTH_SERVER_TIMEOUT); return; } #ifdef USE_NPPPD_RADIUS if (npppd_ppp_is_realm_radius(_this->ppp->pppd, _this->ppp)) { chap_radius_authenticate(_this, _this->challid, _this->name, _this->chall, _this->lchall, response); return; /* NOTREACHED */ } else #endif if (npppd_ppp_is_realm_local(_this->ppp->pppd, _this->ppp)) { switch(_this->type) { case PPP_AUTH_CHAP_MD5: md5chap_authenticate(_this, _this->challid, _this->name, _this->chall, _this->lchall, response); return; /* NOTREACHED */ case PPP_AUTH_CHAP_MS_V2: mschapv2_authenticate(_this, _this->challid, strip_nt_domain(_this->name), _this->chall, _this->lchall, response); return; /* NOTREACHED */ } } } chap_failure(_this, "FAILED", ERROR_AUTHENTICATION_FAILURE); return; } static void chap_response(chap *_this, int authok, u_char *pktp, int lpktp) { const char *realm_name; CHAP_ASSERT(_this != NULL); CHAP_ASSERT(pktp != NULL); CHAP_ASSERT(_this->type == PPP_AUTH_CHAP_MD5 || _this->type == PPP_AUTH_CHAP_MS_V2); ppp_output(_this->ppp, PPP_PROTO_CHAP, (authok)? 3 : 4, _this->challid, pktp, lpktp); realm_name = npppd_ppp_get_realm_name(_this->ppp->pppd, _this->ppp); if (!authok) { chap_log(_this, LOG_ALERT, "logtype=Failure username=\"%s\" realm=%s", _this->name, realm_name); chap_stop(_this); /* Stop the PPP if the authentication is failed. */ ppp_set_disconnect_cause(_this->ppp, PPP_DISCON_AUTH_FAILED, PPP_PROTO_CHAP, 1 /* peer */, NULL); ppp_stop(_this->ppp, "Authentication Required"); } else { strlcpy(_this->ppp->username, _this->name, sizeof(_this->ppp->username)); chap_log(_this, LOG_INFO, "logtype=Success username=\"%s\" " "realm=%s", _this->name, realm_name); chap_stop(_this); /* We change our state to prepare to resend requests. */ _this->state = CHAP_STATE_SENT_RESPONSE; ppp_auth_ok(_this->ppp); } } /** Generate a challenge */ static void chap_create_challenge(chap *_this) { CHAP_ASSERT(_this->ppp->peer_auth == PPP_AUTH_CHAP_MS_V2 || _this->ppp->peer_auth == PPP_AUTH_CHAP_MD5); _this->lchall = 16; arc4random_buf(_this->chall, _this->lchall); } /*********************************************************************** * Proxy Authentication ***********************************************************************/ int chap_proxy_authen_prepare(chap *_this, dialin_proxy_info *dpi) { CHAP_ASSERT(dpi->auth_type == PPP_AUTH_CHAP_MD5); CHAP_ASSERT(_this->state == CHAP_STATE_INITIAL); _this->pktid = dpi->auth_id; #ifdef USE_NPPPD_MPPE if (MPPE_IS_REQUIRED(_this->ppp) && _this->type != PPP_AUTH_CHAP_MS_V2) { chap_log(_this, LOG_ALERT, "mppe is required but try to start chap " "type=0x%02x", dpi->auth_type); return -1; } #endif /* authentication */ if (strlen(dpi->username) >= sizeof(_this->name)) { chap_log(_this, LOG_NOTICE, "\"Proxy Authen Name\" is too long."); return -1; } if (dpi->lauth_chall >= sizeof(_this->chall)) { chap_log(_this, LOG_NOTICE, "\"Proxy Authen Challenge\" is too long."); return -1; } /* copy the authentication properties */ CHAP_ASSERT(_this->ppp->proxy_authen_resp == NULL); if ((_this->ppp->proxy_authen_resp = malloc(dpi->lauth_resp)) == NULL) { chap_log(_this, LOG_ERR, "malloc() failed in %s(): %m", __func__); return -1; } memcpy(_this->ppp->proxy_authen_resp, dpi->auth_resp, dpi->lauth_resp); _this->ppp->lproxy_authen_resp = dpi->lauth_resp; _this->challid = dpi->auth_id; strlcpy(_this->name, dpi->username, sizeof(_this->name)); memcpy(_this->chall, dpi->auth_chall, dpi->lauth_chall); _this->lchall = dpi->lauth_chall; _this->state = CHAP_STATE_PROXY_AUTHENTICATION; return 0; } /************************************************************************ * Functions for MD5-CHAP(RFC1994) ************************************************************************/ static void md5chap_authenticate(chap *_this, int id, char *username, u_char *challenge, int lchallenge, u_char *response) { MD5_CTX md5ctx; int rval, passlen; u_char digest[16]; char *password, buf[MAX_PASSWORD_LENGTH + 1]; buf[0] = id; passlen = sizeof(buf) - 1; password = &buf[1]; rval = npppd_get_user_password(_this->ppp->pppd, _this->ppp, username, password, &passlen); if (rval != 0) { switch (rval) { case 1: chap_log(_this, LOG_INFO, "username=\"%s\" user unknown", username); break; default: chap_log(_this, LOG_ERR, "username=\"%s\" generic error", username); break; } goto auth_failed; } passlen = strlen(password); MD5Init(&md5ctx); MD5Update(&md5ctx, buf, passlen + 1); MD5Update(&md5ctx, challenge, lchallenge); MD5Final(digest, &md5ctx); if (memcmp(response, digest, 16) == 0) { chap_response(_this, 1, "OK", 2); return; } /* FALLTHROUGH. The password are not matched */ auth_failed: /* No extra information, just "FAILED" */ chap_send_error(_this, "FAILED"); return; } static void chap_send_error(chap *_this, const char *msg) { u_char *pkt, *challenge; int lpkt; challenge = _this->chall; pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; lpkt = _this->ppp->mru - HEADERLEN; strlcpy(pkt, msg, lpkt); lpkt = strlen(msg); chap_response(_this, 0, pkt, lpkt); } /************************************************************************ * Functions for MS-CHAP-V2(RFC 2759) ************************************************************************/ static void mschapv2_send_error(chap *_this, int error, int can_retry) { u_char *pkt, *challenge; int lpkt; challenge = _this->chall; pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; lpkt = _this->ppp->mru - HEADERLEN; /* * We don't use "M=" * - pppd on Mac OS 10.4 hungs up if it received a failure packet * with "M=". * - RRAS on windows server 2003 never uses "M=". */ snprintf(pkt, lpkt, "E=%d R=%d C=%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x V=3", error, can_retry, challenge[0], challenge[1], challenge[2], challenge[3], challenge[4], challenge[5], challenge[6], challenge[7], challenge[8], challenge[9], challenge[10], challenge[11], challenge[12], challenge[13], challenge[14], challenge[15] ); lpkt = strlen(pkt); chap_response(_this, 0, pkt, lpkt); } static void mschapv2_authenticate(chap *_this, int id, char *username, u_char *challenge, int lchallenge, u_char *response) { int i, rval, passlen, lpkt; u_char *pkt; char password[MAX_PASSWORD_LENGTH * 2], ntresponse[24]; #ifdef USE_NPPPD_MPPE char pwdhash[16], pwdhashhash[16]; #endif CHAP_DBG((_this, LOG_DEBUG, "%s()", __func__)); pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; lpkt = _this->ppp->mru - HEADERLEN; passlen = sizeof(password) / 2; rval = npppd_get_user_password(_this->ppp->pppd, _this->ppp, username, password, &passlen); if (rval != 0) { switch (rval) { case 1: chap_log(_this, LOG_INFO, "username=\"%s\" user unknown", username); break; default: chap_log(_this, LOG_ERR, "username=\"%s\" generic error", username); break; } goto auth_failed; } /* Convert the string charset from ASCII to UTF16-LE */ passlen = strlen(password); for (i = passlen - 1; i >= 0; i--) { password[i*2] = password[i]; password[i*2+1] = 0; } mschap_nt_response(challenge, response, username, strlen(username), password, passlen * 2, ntresponse); if (memcmp(ntresponse, response + 24, 24) != 0) { chap_log(_this, LOG_INFO, "username=\"%s\" password mismatch.", username); goto auth_failed; } /* * Authentication succeed */ CHAP_DBG((_this, LOG_DEBUG, "%s() OK", __func__)); mschap_auth_response(password, passlen * 2, ntresponse, challenge, response, username, strlen(username), pkt); lpkt = 42; #ifdef USE_NPPPD_MPPE if (_this->ppp->mppe.enabled != 0) { mschap_ntpassword_hash(password, passlen * 2, pwdhash); mschap_ntpassword_hash(pwdhash, sizeof(pwdhash), pwdhashhash); mschap_masterkey(pwdhashhash, ntresponse, _this->ppp->mppe.master_key); mschap_asymetric_startkey(_this->ppp->mppe.master_key, _this->ppp->mppe.recv.master_key, MPPE_KEYLEN, 0, 1); mschap_asymetric_startkey(_this->ppp->mppe.master_key, _this->ppp->mppe.send.master_key, MPPE_KEYLEN, 1, 1); } #endif chap_response(_this, 1, pkt, lpkt); return; auth_failed: /* No extra information */ mschapv2_send_error(_this, ERROR_AUTHENTICATION_FAILURE, 0); return; } #ifdef USE_NPPPD_RADIUS /************************************************************************ * Functions for RADIUS * RFC 2058: RADIUS * RFC 2548: Microsoft Vendor-specific RADIUS Attributes ************************************************************************/ static void chap_radius_authenticate(chap *_this, int id, char *username, u_char *challenge, int lchallenge, u_char *response) { void *radctx; RADIUS_PACKET *radpkt; radius_req_setting *rad_setting; int lpkt; u_char *pkt; char buf0[MAX_USERNAME_LENGTH]; radpkt = NULL; radctx = NULL; if ((rad_setting = npppd_get_radius_auth_setting(_this->ppp->pppd, _this->ppp)) == NULL) { goto fail; /* no radius server */ } pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; lpkt = _this->ppp->mru - HEADERLEN; if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST)) == NULL) goto fail; if (radius_prepare(rad_setting, _this, &radctx, chap_radius_response) != 0) { radius_delete_packet(radpkt); goto fail; } if (ppp_set_radius_attrs_for_authreq(_this->ppp, rad_setting, radpkt) != 0) goto fail; if (radius_put_string_attr(radpkt, RADIUS_TYPE_USER_NAME, npppd_ppp_get_username_for_auth(_this->ppp->pppd, _this->ppp, username, buf0)) != 0) goto fail; switch (_this->type) { case PPP_AUTH_CHAP_MD5: { u_char md5response[17]; md5response[0] = _this->challid; memcpy(&md5response[1], response, 16); if (radius_put_raw_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD, md5response, 17) != 0) goto fail; if (radius_put_raw_attr(radpkt, RADIUS_TYPE_CHAP_CHALLENGE, challenge, lchallenge) != 0) goto fail; break; } case PPP_AUTH_CHAP_MS_V2: { struct RADIUS_MS_CHAP2_RESPONSE msresponse; /* Preparing RADIUS_MS_CHAP2_RESPONSE */ memset(&msresponse, 0, sizeof(msresponse)); msresponse.ident = id; msresponse.flags = response[48]; memcpy(&msresponse.peer_challenge, response, 16); memcpy(&msresponse.response, response + 24, 24); if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MS_CHAP_CHALLENGE, challenge, 16) != 0) goto fail; if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MS_CHAP2_RESPONSE, &msresponse, sizeof(msresponse)) != 0) goto fail; break; } } radius_get_authenticator(radpkt, _this->authenticator); /* Cancel previous request */ if (_this->radctx != NULL) radius_cancel_request(_this->radctx); /* Send a request */ _this->radctx = radctx; radius_request(radctx, radpkt); return; fail: switch (_this->type) { case PPP_AUTH_CHAP_MD5: /* No extra information, just "FAILED" */ chap_send_error(_this, "FAILED"); break; case PPP_AUTH_CHAP_MS_V2: /* No extra information */ mschapv2_send_error(_this, ERROR_AUTHENTICATION_FAILURE, 0); break; } if (radctx != NULL) radius_cancel_request(radctx); } static void chap_radius_response(void *context, RADIUS_PACKET *pkt, int flags, RADIUS_REQUEST_CTX reqctx) { int code, lrespkt; const char *secret, *reason = ""; chap *_this; u_char *respkt, *respkt0; int errorCode; RADIUS_REQUEST_CTX radctx; CHAP_ASSERT(context != NULL); reason = ""; errorCode = ERROR_AUTH_SERVER_TIMEOUT; _this = context; secret = radius_get_server_secret(_this->radctx); radctx = _this->radctx; _this->radctx = NULL; /* IMPORTANT */ respkt = respkt0 = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; lrespkt = _this->ppp->mru - HEADERLEN; if (pkt == NULL) { if (flags & RADIUS_REQUEST_TIMEOUT) reason = "timeout"; else if (flags & RADIUS_REQUEST_ERROR) reason = strerror(errno); else reason = "error"; goto auth_failed; } code = radius_get_code(pkt); if (code == RADIUS_CODE_ACCESS_REJECT) { reason="reject"; errorCode = ERROR_AUTHENTICATION_FAILURE; /* Windows peer will reset the password by this error code */ goto auth_failed; } else if (code != RADIUS_CODE_ACCESS_ACCEPT) { reason="error"; goto auth_failed; } if ((flags & RADIUS_REQUEST_CHECK_AUTHENTICATOR_OK) == 0 && (flags & RADIUS_REQUEST_CHECK_AUTHENTICATOR_NO_CHECK) == 0) { reason="bad_authenticator"; goto auth_failed; } if ((flags & RADIUS_REQUEST_CHECK_MSG_AUTHENTICATOR_OK) == 0 && (flags & RADIUS_REQUEST_CHECK_NO_MSG_AUTHENTICATOR) == 0) { reason="bad_msg_authenticator"; goto auth_failed; } /* * Authentication OK */ switch (_this->type) { case PPP_AUTH_CHAP_MD5: chap_response(_this, 1, "OK", 2); break; case PPP_AUTH_CHAP_MS_V2: { struct RADIUS_MS_CHAP2_SUCCESS success; #ifdef USE_NPPPD_MPPE struct RADIUS_MPPE_KEY sendkey, recvkey; #endif size_t len; len = sizeof(success); if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MS_CHAP2_SUCCESS, &success, &len) != 0) { chap_log(_this, LOG_ERR, "no ms_chap2_success"); goto auth_failed; } #ifdef USE_NPPPD_MPPE if (_this->ppp->mppe.enabled != 0) { len = sizeof(sendkey); if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MPPE_SEND_KEY, &sendkey, &len) != 0) { chap_log(_this, LOG_ERR, "no mppe_send_key"); goto auth_failed; } len = sizeof(recvkey); if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MPPE_RECV_KEY, &recvkey, &len) != 0) { chap_log(_this, LOG_ERR, "no mppe_recv_key"); goto auth_failed; } mschap_radiuskey(_this->ppp->mppe.send.master_key, sendkey.salt, _this->authenticator, secret); mschap_radiuskey(_this->ppp->mppe.recv.master_key, recvkey.salt, _this->authenticator, secret); } #endif chap_response(_this, 1, success.str, sizeof(success.str)); break; } } ppp_process_radius_framed_ip(_this->ppp, pkt); return; auth_failed: chap_log(_this, LOG_WARNING, "Radius authentication request failed: %s", reason); /* log reply messages from radius server */ if (pkt != NULL) { char radmsg[255], vissed[1024]; size_t rmlen = 0; if ((radius_get_raw_attr(pkt, RADIUS_TYPE_REPLY_MESSAGE, radmsg, &rmlen)) == 0) { if (rmlen != 0) { strvisx(vissed, radmsg, rmlen, VIS_WHITE); chap_log(_this, LOG_WARNING, "Radius reply message: %s", vissed); } } } /* No extra information */ chap_failure(_this, "FAILED", errorCode); } #endif /************************************************************************ * Miscellaneous functions ************************************************************************/ static char * strip_nt_domain(char *username) { char *lastbackslash; if ((lastbackslash = strrchr(username, '\\')) != NULL) return lastbackslash + 1; return username; } static void chap_log(chap *_this, uint32_t prio, const char *fmt, ...) { const char *protostr; char logbuf[BUFSIZ]; va_list ap; CHAP_ASSERT(_this != NULL); CHAP_ASSERT(_this->ppp != NULL); switch (_this->type) { case PPP_AUTH_CHAP_MD5: protostr = "chap"; break; case PPP_AUTH_CHAP_MS_V2: protostr = "mschap_v2"; break; default: protostr = "unknown"; break; } va_start(ap, fmt); snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=chap proto=%s %s", _this->ppp->id, protostr, fmt); vlog_printf(prio, logbuf, ap); va_end(ap); }