📄 rlm_mschap.c
字号:
/* * rlm_mschap.c * * Version: $Id: rlm_mschap.c,v 1.59.2.2 2005/08/24 14:37:52 nbk Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2000,2001 The FreeRADIUS server project *//* * mschap.c MS-CHAP module * * This implements MS-CHAP, as described in RFC 2548 * * http://www.freeradius.org/rfc/rfc2548.txt * *//* * If you have any questions on NTLM (Samba) passwords * support, LM authentication and MS-CHAP v2 support * please contact * * Vladimir Dubrovin vlad@sandy.ru * aka * ZARAZA 3APA3A@security.nnov.ru *//* MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */#include "autoconf.h"#include "libradius.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include "radiusd.h"#include "modules.h"#include "md4.h"#include "md5.h"#include "sha1.h"#include "rad_assert.h"#include "smbdes.h"static const char rcsid[] = "$Id: rlm_mschap.c,v 1.59.2.2 2005/08/24 14:37:52 nbk Exp $";static const char *letters = "0123456789ABCDEF";/* * hex2bin converts hexadecimal strings into binary */static int hex2bin (const char *szHex, unsigned char* szBin, int len){ char * c1, * c2; int i; for (i = 0; i < len; i++) { if( !(c1 = memchr(letters, toupper((int) szHex[i << 1]), 16)) || !(c2 = memchr(letters, toupper((int) szHex[(i << 1) + 1]), 16))) break; szBin[i] = ((c1-letters)<<4) + (c2-letters); } return i;}/* * bin2hex creates hexadecimal presentation * of binary data */static void bin2hex (const unsigned char *szBin, char *szHex, int len){ int i; for (i = 0; i < len; i++) { szHex[i<<1] = letters[szBin[i] >> 4]; szHex[(i<<1) + 1] = letters[szBin[i] & 0x0F]; }}/* Allowable account control bits */#define ACB_DISABLED 0x0001 /* 1 = User account disabled */#define ACB_HOMDIRREQ 0x0002 /* 1 = Home directory required */#define ACB_PWNOTREQ 0x0004 /* 1 = User password not required */#define ACB_TEMPDUP 0x0008 /* 1 = Temporary duplicate account */#define ACB_NORMAL 0x0010 /* 1 = Normal user account */#define ACB_MNS 0x0020 /* 1 = MNS logon user account */#define ACB_DOMTRUST 0x0040 /* 1 = Interdomain trust account */#define ACB_WSTRUST 0x0080 /* 1 = Workstation trust account */#define ACB_SVRTRUST 0x0100 /* 1 = Server trust account */#define ACB_PWNOEXP 0x0200 /* 1 = User password does not expire */#define ACB_AUTOLOCK 0x0400 /* 1 = Account auto locked */static int pdb_decode_acct_ctrl(const char *p){ int acct_ctrl = 0; int finished = 0; /* * Check if the account type bits have been encoded after the * NT password (in the form [NDHTUWSLXI]). */ if (*p != '[') return 0; for (p++; *p && !finished; p++) { switch (*p) { case 'N': /* 'N'o password. */ acct_ctrl |= ACB_PWNOTREQ; break; case 'D': /* 'D'isabled. */ acct_ctrl |= ACB_DISABLED ; break; case 'H': /* 'H'omedir required. */ acct_ctrl |= ACB_HOMDIRREQ; break; case 'T': /* 'T'emp account. */ acct_ctrl |= ACB_TEMPDUP; break; case 'U': /* 'U'ser account (normal). */ acct_ctrl |= ACB_NORMAL; break; case 'M': /* 'M'NS logon user account. What is this? */ acct_ctrl |= ACB_MNS; break; case 'W': /* 'W'orkstation account. */ acct_ctrl |= ACB_WSTRUST; break; case 'S': /* 'S'erver account. */ acct_ctrl |= ACB_SVRTRUST; break; case 'L': /* 'L'ocked account. */ acct_ctrl |= ACB_AUTOLOCK; break; case 'X': /* No 'X'piry on password */ acct_ctrl |= ACB_PWNOEXP; break; case 'I': /* 'I'nterdomain trust account. */ acct_ctrl |= ACB_DOMTRUST; break; case ' ': /* ignore spaces */ break; case ':': case '\n': case '\0': case ']': default: finished = 1; break; } } return acct_ctrl;}/* * ntpwdhash converts Unicode password to 16-byte NT hash * with MD4 */static void ntpwdhash (unsigned char *szHash, const char *szPassword){ char szUnicodePass[513]; int nPasswordLen; int i; /* * NT passwords are unicode. Convert plain text password * to unicode by inserting a zero every other byte */ nPasswordLen = strlen(szPassword); for (i = 0; i < nPasswordLen; i++) { szUnicodePass[i << 1] = szPassword[i]; szUnicodePass[(i << 1) + 1] = 0; } /* Encrypt Unicode password to a 16-byte MD4 hash */ md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );}/* * challenge_hash() is used by mschap2() and auth_response() * implements RFC2759 ChallengeHash() * generates 64 bit challenge */static void challenge_hash( const char *peer_challenge, const char *auth_challenge, const char *user_name, char *challenge ){ SHA1_CTX Context; unsigned char hash[20]; SHA1Init(&Context); SHA1Update(&Context, peer_challenge, 16); SHA1Update(&Context, auth_challenge, 16); SHA1Update(&Context, user_name, strlen(user_name)); SHA1Final(hash, &Context); memcpy(challenge, hash, 8);}/* * auth_response() generates MS-CHAP v2 SUCCESS response * according to RFC 2759 GenerateAuthenticatorResponse() * returns 42-octet response string */static void auth_response(const char *username, const unsigned char *nt_hash_hash, unsigned char *ntresponse, char *peer_challenge, char *auth_challenge, char *response){ SHA1_CTX Context; const unsigned char magic1[39] = {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74}; const unsigned char magic2[41] = {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E}; char challenge[8]; unsigned char digest[20]; SHA1Init(&Context); SHA1Update(&Context, nt_hash_hash, 16); SHA1Update(&Context, ntresponse, 24); SHA1Update(&Context, magic1, 39); SHA1Final(digest, &Context); challenge_hash(peer_challenge, auth_challenge, username, challenge); SHA1Init(&Context); SHA1Update(&Context, digest, 20); SHA1Update(&Context, challenge, 8); SHA1Update(&Context, magic2, 41); SHA1Final(digest, &Context); /* * Encode the value of 'Digest' as "S=" followed by * 40 ASCII hexadecimal digits and return it in * AuthenticatorResponse. * For example, * "S=0123456789ABCDEF0123456789ABCDEF01234567" */ response[0] = 'S'; response[1] = '='; bin2hex(digest, response + 2, 20);}typedef struct rlm_mschap_t { int use_mppe; int require_encryption; int require_strong; int with_ntdomain_hack; /* this should be in another module */ char *passwd_file; char *xlat_name; char *auth_type; /* I don't think this is needed... */ char *ntlm_auth;} rlm_mschap_t;/* * Does dynamic translation of strings. * * Pulls NT-Response, LM-Response, or Challenge from MSCHAP * attributes. */static int mschap_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, RADIUS_ESCAPE_STRING func){ int i, data_len; uint8_t *data = NULL; uint8_t buffer[8]; VALUE_PAIR *user_name; VALUE_PAIR *chap_challenge, *response; rlm_mschap_t *inst = instance; chap_challenge = response = NULL; func = func; /* -Wunused */ /* * Challenge means MS-CHAPv1 challenge, or * hash of MS-CHAPv2 challenge, and peer challenge. */ if (strcasecmp(fmt, "Challenge") == 0) { chap_challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!chap_challenge) { DEBUG2(" rlm_mschap: No MS-CHAP-Challenge in the request."); return 0; } /* * MS-CHAP-Challenges are 8 octets, * for MS-CHAPv2 */ if (chap_challenge->length == 8) { DEBUG2(" mschap1: %02x", chap_challenge->strvalue[0]); data = chap_challenge->strvalue; data_len = 8; /* * MS-CHAP-Challenges are 16 octets, * for MS-CHAPv2. */ } else if (chap_challenge->length == 16) { char *username_string; DEBUG2(" mschap2: %02x", chap_challenge->strvalue[0]); response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE); if (!response) { DEBUG2(" rlm_mschap: MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge."); return 0; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format."); return 0; } user_name = pairfind(request->packet->vps, PW_USER_NAME); if (!user_name) { DEBUG2(" rlm_mschap: User-Name is required to calculateMS-CHAPv1 Challenge."); return 0; } /* * with_ntdomain_hack moved here, too. */ if ((username_string = strchr(user_name->strvalue, '\\')) != NULL) { if (inst->with_ntdomain_hack) { username_string++; } else { DEBUG2(" rlm_mschap: NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); username_string = user_name->strvalue; } } else { username_string = user_name->strvalue; } /* * Get the MS-CHAPv1 challenge * from the MS-CHAPv2 peer challenge, * our challenge, and the user name. */ challenge_hash(response->strvalue + 2, chap_challenge->strvalue, username_string, buffer); data = buffer; data_len = 8; } else { DEBUG2(" rlm_mschap: Invalid MS-CHAP challenge length"); return 0; } /* * Get the MS-CHAPv1 response, or the MS-CHAPv2 * response. */ } else if (strcasecmp(fmt, "NT-Response") == 0) { response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); if (!response) response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE); if (!response) { DEBUG2(" rlm_mschap: No MS-CHAP-Response or MS-CHAP2-Response was found in the request."); return 0; } /* * For MS-CHAPv1, the NT-Response exists only * if the second octet says so. */ if ((response->attribute == PW_MSCHAP_RESPONSE) && ((response->strvalue[1] & 0x01) == 0)) { DEBUG2(" rlm_mschap: No NT-Response in MS-CHAP-Response"); return 0; } /* * MS-CHAP-Response and MS-CHAP2-Response have * the NT-Response at the same offset, and are * the same length. */ data = response->strvalue + 26; data_len = 24; /* * LM-Response is deprecated, and exists only * in MS-CHAPv1, and not often there. */ } else if (strcasecmp(fmt, "LM-Response") == 0) { response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); if (!response) { DEBUG2(" rlm_mschap: No MS-CHAP-Response was found in the request."); return 0; } /* * For MS-CHAPv1, the NT-Response exists only * if the second octet says so. */ if ((response->strvalue[1] & 0x01) != 0) { DEBUG2(" rlm_mschap: No LM-Response in MS-CHAP-Response");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -