/************************************************************************ * 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=<msg>" * - pppd on Mac OS 10.4 hungs up if it received a failure packet * with "M=<msg>". * - 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); }
/** * sending packet via MPPE. */ void mppe_pkt_output(mppe *_this, uint16_t proto, u_char *pktp, int len) { int encrypt, flushed; uint16_t coher_cnt; u_char *outp, *outp0; MPPE_ASSERT(proto == PPP_PROTO_IP); flushed = 0; encrypt = 1; outp = ppp_packetbuf(_this->ppp, PPP_PROTO_MPPE); outp0 = outp; if (_this->send.stateless != 0) { flushed = 1; mppe_key_change(_this, &_this->send); } else { if ((_this->send.coher_cnt % 0x100) == 0xff) { flushed = 1; mppe_key_change(_this, &_this->send); } else if (_this->send.resetreq != 0) { flushed = 1; _this->send.resetreq = 0; } } if (flushed) { mppe_rc4_setkey(_this, &_this->send); } MPPE_DBG((_this, DEBUG_LEVEL_2, "out coher_cnt=%03x %s%s", _this->send.coher_cnt, (flushed)? "[flushed]" : "", (encrypt)? "[encrypt]" : "")); coher_cnt = _this->send.coher_cnt & COHERENCY_CNT_MASK; if (flushed) coher_cnt |= 0x8000; if (encrypt) coher_cnt |= 0x1000; PUTSHORT(coher_cnt, outp); proto = htons(proto); mppe_rc4_encrypt(_this, &_this->send, 2, (u_char *)&proto, outp); mppe_rc4_encrypt(_this, &_this->send, len, pktp, outp + 2); ppp_output(_this->ppp, PPP_PROTO_MPPE, 0, 0, outp0, len + 4); _this->send.coher_cnt++; _this->send.coher_cnt &= COHERENCY_CNT_MASK; }
static void pap_response(pap *_this, int authok, const char *mes) { int lpktp, lmes; u_char *pktp, *pktp1; const char *realm; pktp = ppp_packetbuf(_this->ppp, PPP_PROTO_PAP) + HEADERLEN; lpktp = _this->ppp->mru - HEADERLEN; realm = npppd_ppp_get_realm_name(_this->ppp->pppd, _this->ppp); pktp1 = pktp; if (mes == NULL) lmes = 0; else lmes = strlen(mes); lmes = MINIMUM(lmes, lpktp - 1); PUTCHAR(lmes, pktp1); if (lmes > 0) memcpy(pktp1, mes, lmes); lpktp = lmes + 1; if (authok) ppp_output(_this->ppp, PPP_PROTO_PAP, AUTHACK, _this->auth_id, pktp, lpktp); else ppp_output(_this->ppp, PPP_PROTO_PAP, AUTHNAK, _this->auth_id, pktp, lpktp); if (!authok) { pap_log(_this, LOG_ALERT, "logtype=Failure username=\"%s\" realm=%s", _this->name, realm); pap_stop(_this); ppp_set_disconnect_cause(_this->ppp, PPP_DISCON_AUTH_FAILED, PPP_PROTO_PAP, 1 /* peer */, NULL); ppp_stop(_this->ppp, "Authentication Required"); } else { strlcpy(_this->ppp->username, _this->name, sizeof(_this->ppp->username)); pap_log(_this, LOG_INFO, "logtype=Success username=\"%s\" realm=%s", _this->name, realm); pap_stop(_this); ppp_auth_ok(_this->ppp); /* reset the state to response request of retransmision. */ _this->state = PAP_STATE_SENT_RESPONSE; } }
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); }
/** * receiving packets via MPPE. * len must be 4 at least. */ void mppe_input(mppe *_this, u_char *pktp, int len) { int pktloss, encrypt, flushed, m, n; uint16_t coher_cnt; u_char *pktp0, *opktp, *opktp0; uint16_t proto; int delayed = 0; encrypt = 0; flushed = 0; MPPE_ASSERT(len >= 4); pktp0 = pktp; GETSHORT(coher_cnt, pktp); flushed = (coher_cnt & 0x8000)? 1 : 0; encrypt = (coher_cnt & 0x1000)? 1 : 0; coher_cnt &= COHERENCY_CNT_MASK; pktloss = 0; MPPE_DBG((_this, DEBUG_LEVEL_2, "in coher_cnt=%03x/%03x %s%s", _this->recv.coher_cnt, coher_cnt, (flushed)? "[flushed]" : "", (encrypt)? "[encrypt]" : "")); if (encrypt == 0) { mppe_log(_this, LOG_WARNING, "Received unexpected MPPE packet. (no encrypt)"); return; } /* * In L2TP/IPsec implementation, in case that the ppp frame sequence * is not able to reconstruct and the ppp frame is out of sequence, it * is unable to identify with many packets losing. If it does so, MPPE * key is out of place. * To avoid this problem, when it seems that more than 4096-256 packets * drops, it assumes that the packet doesn't lose but the packet is out * of sequence. */ { int coher_cnt0; coher_cnt0 = coher_cnt; if (coher_cnt < _this->recv.coher_cnt) coher_cnt0 += 0x1000; if (coher_cnt0 - _this->recv.coher_cnt > 0x0f00) { if (!_this->recv.stateless || coher_cnt0 - _this->recv.coher_cnt <= 0x1000 - MPPE_NOLDKEY) { mppe_log(_this, LOG_INFO, "Workaround the out-of-sequence PPP framing problem: " "%d => %d", _this->recv.coher_cnt, coher_cnt); return; } delayed = 1; } } if (_this->recv.stateless != 0) { if (!delayed) { mppe_key_change(_this, &_this->recv); while (_this->recv.coher_cnt != coher_cnt) { _this->recv.coher_cnt++; _this->recv.coher_cnt &= COHERENCY_CNT_MASK; mppe_key_change(_this, &_this->recv); pktloss++; } } mppe_rc4_setoldkey(_this, &_this->recv, coher_cnt); flushed = 1; } else { if (flushed) { if (coher_cnt < _this->recv.coher_cnt) { /* in case of carrying up. */ coher_cnt += 0x1000; } pktloss += coher_cnt - _this->recv.coher_cnt; m = _this->recv.coher_cnt / 256; n = coher_cnt / 256; while (m++ < n) mppe_key_change(_this, &_this->recv); coher_cnt &= COHERENCY_CNT_MASK; _this->recv.coher_cnt = coher_cnt; } else if (_this->recv.coher_cnt != coher_cnt) { _this->recv.resetreq = 1; opktp0 = ppp_packetbuf(_this->ppp, PPP_PROTO_NCP | NCP_CCP); opktp = opktp0; PUTLONG(_this->ppp->ccp.mppe_p_bits, opktp); ppp_output(_this->ppp, PPP_PROTO_NCP | NCP_CCP, RESETREQ, _this->recv.resetreq, opktp0, opktp - opktp0); return; } if ((coher_cnt & 0xff) == 0xff) { mppe_key_change(_this, &_this->recv); flushed = 1; } if (flushed) { mppe_rc4_setkey(_this, &_this->recv); } } if (pktloss > 1000) { /* * In case of many packets losing or out of sequence. * The latter is not able to communicate because the key is * out of place soon. * */ mppe_log(_this, LOG_WARNING, "%d packets loss", pktloss); } mppe_rc4_encrypt(_this, &_this->recv, len - 2, pktp, pktp); if (!delayed) { _this->recv.coher_cnt++; _this->recv.coher_cnt &= COHERENCY_CNT_MASK; } if (pktp[0] & 1) proto = pktp[0]; else proto = pktp[0] << 8 | pktp[1]; /* * According to RFC3078 section 3, * MPPE only accept protocol number 0021-00FA. * If decrypted protocol number is out of range, * it indicates loss of coherency. */ if (!(proto & 1) || proto < 0x21 || proto > 0xfa) { mppe_log(_this, LOG_INFO, "MPPE coherency is lost"); return; /* drop frame */ } _this->ppp->recv_packet(_this->ppp, pktp, len - 2, PPP_IO_FLAGS_MPPE_ENCRYPTED); }
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; } /* * Authetication 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); /* No extra information */ chap_failure(_this, "FAILED", errorCode); }
/************************************************************************ * 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 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; }
/** 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"); } } }