6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000 The FreeRADIUS server project
25 * mschap.c MS-CHAP module
27 * Jay Miller jaymiller@socket.net
29 * This implements MS-CHAP, as described in RFC 2548
31 * http://www.freeradius.org/rfc/rfc2548.txt
36 #include "libradius.h"
48 #define PW_MSCHAP_RESPONSE ((311 << 16) | 1)
49 #define PW_MSCHAP_CHALLENGE ((311 << 16) | 11)
51 static void parity_key(char * szOut, const char * szIn);
52 static void des_encrypt(const char *szClear, const char *szKey, char *szOut);
53 static void mschap(const char *szChallenge, const char *szPassword, char *szResponse);
56 * parity_key takes a 7-byte string in szIn and returns an
57 * 8-byte string in szOut. It inserts a 1 into every 8th bit.
58 * DES just strips these back out.
60 static void parity_key(char * szOut, const char * szIn) {
62 unsigned char cNext = 0;
63 unsigned char cWorking = 0;
65 for (i = 0; i < 7; i++) {
66 /* Shift operator works in place. Copy the char out */
68 szOut[i] = (cWorking >> i) | cNext | 1;
70 cNext = (cWorking << (7 - i));
76 * des_encrypt takes an 8-byte string and a 7-byte key and
77 * returns an 8-byte DES encrypted string in szOut
79 static void des_encrypt(const char *szClear, const char *szKey, char *szOut) {
81 unsigned long ulK[16][2];
83 parity_key(szParityKey, szKey); /* Insert parity bits */
84 strncpy(szOut, szClear, 8); /* des encrypts in place */
85 deskey(ulK, (unsigned char *) szParityKey, 0); /* generate keypair */
86 des(ulK, szOut); /* encrypt */
90 * mschap takes an 8-byte challenge string and a plain text
91 * password (up to 253 bytes) and returns a 24-byte response
92 * string in szResponse
94 static void mschap(const char *szChallenge, const char *szPassword, char *szResponse) {
96 char szUnicodePass[513];
100 /* initialize hash string */
101 for (i = 0; i < 21; i++) {
106 * Microsoft passwords are unicode. Convert plain text password
107 * to unicode by inserting a zero every other byte
109 nPasswordLen = strlen(szPassword);
110 for (i = 0; i < nPasswordLen; i++) {
111 szUnicodePass[2 * i] = szPassword[i];
112 szUnicodePass[2 * i + 1] = 0;
115 /* Encrypt plain text password to a 16-byte MD4 hash */
116 md4_calc(szMD4, szUnicodePass, nPasswordLen * 2);
120 * challenge_response takes an 8-byte challenge string and a
121 * 21-byte hash (16-byte hash padded to 21 bytes with zeros) and
122 * returns a 24-byte response in szResponse
124 des_encrypt(szChallenge, szMD4, szResponse);
125 des_encrypt(szChallenge, szMD4 + 7, szResponse + 8);
126 des_encrypt(szChallenge, szMD4 + 14, szResponse + 16);
130 /* validate userid/passwd */
131 static int mschap_auth(void *instance, REQUEST *request)
133 VALUE_PAIR *challenge, *response;
134 uint8_t calculated[32];
136 instance = instance; /* -Wunused */
139 * We need an MS-CHAP-Challenge attribute to calculate
142 challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
144 radlog(L_AUTH, "rlm_mschap: Attribute \"MS-CHAP-Challenge\" is required for authentication.");
145 return RLM_MODULE_INVALID;
149 * We need an MS-CHAP-Challenge attribute to calculate
152 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
154 radlog(L_AUTH, "rlm_mschap: Attribute \"MS-CHAP-Response\" is required for authentication.");
155 return RLM_MODULE_INVALID;
159 * We can only authenticate user requests which HAVE
160 * a Password attribute.
162 if (!request->password) {
163 radlog(L_AUTH, "rlm_mschap: Attribute \"Password\" is required for authentication.");
164 return RLM_MODULE_INVALID;
168 * Ensure that we're being passed a plain-text password,
169 * and not anything else.
171 if (request->password->attribute != PW_PASSWORD) {
172 radlog(L_AUTH, "rlm_mschap: Attribute \"Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
173 return RLM_MODULE_INVALID;
177 * Calculate the MS-CHAP response
179 mschap(challenge->strvalue, request->password->strvalue, calculated);
180 if (memcmp(response->strvalue + 26, calculated, 24) == 0) {
181 return RLM_MODULE_OK;
184 return RLM_MODULE_REJECT;
187 module_t rlm_mschap = {
190 NULL, /* initialize */
191 NULL, /* instantiation */
193 mschap_auth, /* authenticate */
194 NULL, /* authorize */
195 NULL, /* pre-accounting */
196 NULL, /* accounting */
197 NULL /* checksimul */