1/* 2 * chap_ms.c - Microsoft MS-CHAP compatible implementation. 3 * 4 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. 5 * http://www.strataware.com/ 6 * 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms are permitted 10 * provided that the above copyright notice and this paragraph are 11 * duplicated in all such forms and that any documentation, 12 * advertising materials, and other materials related to such 13 * distribution and use acknowledge that the software was developed 14 * by Eric Rosenquist. The name of the author may not be used to 15 * endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 20 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 21 */ 22 23/* 24 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 25 * 26 * Implemented LANManager type password response to MS-CHAP challenges. 27 * Now pppd provides both NT style and LANMan style blocks, and the 28 * prefered is set by option "ms-lanman". Default is to use NT. 29 * The hash text (StdText) was taken from Win95 RASAPI32.DLL. 30 * 31 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 32 */ 33 34#define RCSID "$Id: chap_ms.c 241182 2011-02-17 21:50:03Z $" 35 36#ifdef CHAPMS 37 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <ctype.h> 42#include <sys/types.h> 43#include <sys/time.h> 44#include <unistd.h> 45#ifdef HAVE_CRYPT_H 46#include <crypt.h> 47#endif 48 49#include "pppd.h" 50#include "chap.h" 51#include "chap_ms.h" 52#include "md4.h" 53 54#ifndef USE_CRYPT 55#include <des.h> 56#endif 57 58static const char rcsid[] = RCSID; 59 60typedef struct { 61 u_char LANManResp[24]; 62 u_char NTResp[24]; 63 u_char UseNT; /* If 1, ignore the LANMan response field */ 64} MS_ChapResponse; 65/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), 66 in case this struct gets padded. */ 67 68 69static void ChallengeResponse __P((u_char *, u_char *, u_char *)); 70static void DesEncrypt __P((u_char *, u_char *, u_char *)); 71static void MakeKey __P((u_char *, u_char *)); 72static u_char Get7Bits __P((u_char *, int)); 73static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *)); 74#ifdef MSLANMAN 75static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *)); 76#endif 77 78#ifdef USE_CRYPT 79static void Expand __P((u_char *, u_char *)); 80static void Collapse __P((u_char *, u_char *)); 81#endif 82 83#ifdef MSLANMAN 84bool ms_lanman = 0; /* Use LanMan password instead of NT */ 85 /* Has meaning only with MS-CHAP challenges */ 86#endif 87 88static void 89ChallengeResponse(challenge, pwHash, response) 90 u_char *challenge; /* IN 8 octets */ 91 u_char *pwHash; /* IN 16 octets */ 92 u_char *response; /* OUT 24 octets */ 93{ 94 unsigned char ZPasswordHash[21]; 95 96 BZERO(ZPasswordHash, sizeof(ZPasswordHash)); 97 BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE); 98 99 100 DesEncrypt(challenge, ZPasswordHash + 0, response + 0); 101 DesEncrypt(challenge, ZPasswordHash + 7, response + 8); 102 DesEncrypt(challenge, ZPasswordHash + 14, response + 16); 103 104} 105 106 107#ifdef USE_CRYPT 108static void 109DesEncrypt(clear, key, cipher) 110 u_char *clear; /* IN 8 octets */ 111 u_char *key; /* IN 7 octets */ 112 u_char *cipher; /* OUT 8 octets */ 113{ 114 u_char des_key[8]; 115 u_char crypt_key[66]; 116 u_char des_input[66]; 117 118 MakeKey(key, des_key); 119 120 Expand(des_key, crypt_key); 121 setkey((char *)crypt_key); 122 123 124 Expand(clear, des_input); 125 encrypt((char *)des_input, 0); 126 Collapse(des_input, cipher); 127 128} 129 130#else /* USE_CRYPT */ 131 132static void 133DesEncrypt(clear, key, cipher) 134 u_char *clear; /* IN 8 octets */ 135 u_char *key; /* IN 7 octets */ 136 u_char *cipher; /* OUT 8 octets */ 137{ 138 des_cblock des_key; 139 des_key_schedule key_schedule; 140 141 MakeKey(key, des_key); 142 143 des_set_key(&des_key, key_schedule); 144 145 146 des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); 147 148} 149 150#endif /* USE_CRYPT */ 151 152 153static u_char Get7Bits(input, startBit) 154 u_char *input; 155 int startBit; 156{ 157 register unsigned int word; 158 159 word = (unsigned)input[startBit / 8] << 8; 160 word |= (unsigned)input[startBit / 8 + 1]; 161 162 word >>= 15 - (startBit % 8 + 7); 163 164 return word & 0xFE; 165} 166 167#ifdef USE_CRYPT 168 169/* in == 8-byte string (expanded version of the 56-bit key) 170 * out == 64-byte string where each byte is either 1 or 0 171 * Note that the low-order "bit" is always ignored by by setkey() 172 */ 173static void Expand(in, out) 174 u_char *in; 175 u_char *out; 176{ 177 int j, c; 178 int i; 179 180 for(i = 0; i < 64; in++){ 181 c = *in; 182 for(j = 7; j >= 0; j--) 183 *out++ = (c >> j) & 01; 184 i += 8; 185 } 186} 187 188/* The inverse of Expand 189 */ 190static void Collapse(in, out) 191 u_char *in; 192 u_char *out; 193{ 194 int j; 195 int i; 196 unsigned int c; 197 198 for (i = 0; i < 64; i += 8, out++) { 199 c = 0; 200 for (j = 7; j >= 0; j--, in++) 201 c |= *in << j; 202 *out = c & 0xff; 203 } 204} 205#endif 206 207static void MakeKey(key, des_key) 208 u_char *key; /* IN 56 bit DES key missing parity bits */ 209 u_char *des_key; /* OUT 64 bit DES key with parity bits added */ 210{ 211 des_key[0] = Get7Bits(key, 0); 212 des_key[1] = Get7Bits(key, 7); 213 des_key[2] = Get7Bits(key, 14); 214 des_key[3] = Get7Bits(key, 21); 215 des_key[4] = Get7Bits(key, 28); 216 des_key[5] = Get7Bits(key, 35); 217 des_key[6] = Get7Bits(key, 42); 218 des_key[7] = Get7Bits(key, 49); 219 220#ifndef USE_CRYPT 221 des_set_odd_parity((des_cblock *)des_key); 222#endif 223 224} 225 226static void 227ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response) 228 char *rchallenge; 229 int rchallenge_len; 230 char *secret; 231 int secret_len; 232 MS_ChapResponse *response; 233{ 234 int i; 235#ifdef __NetBSD__ 236 /* NetBSD uses the libc md4 routines which take bytes instead of bits */ 237 int mdlen = secret_len * 2; 238#else 239 int mdlen = secret_len * 2 * 8; 240#endif 241 MD4_CTX md4Context; 242 u_char hash[MD4_SIGNATURE_SIZE]; 243 u_char unicodePassword[MAX_NT_PASSWORD * 2]; 244 245 /* Initialize the Unicode version of the secret (== password). */ 246 /* This implicitly supports 8-bit ISO8859/1 characters. */ 247 BZERO(unicodePassword, sizeof(unicodePassword)); 248 for (i = 0; i < secret_len; i++) 249 unicodePassword[i * 2] = (u_char)secret[i]; 250 251 MD4Init(&md4Context); 252 MD4Update(&md4Context, unicodePassword, mdlen); 253 254 MD4Final(hash, &md4Context); /* Tell MD4 we're done */ 255 256 ChallengeResponse((unsigned char *)rchallenge, hash, response->NTResp); 257} 258 259#ifdef MSLANMAN 260static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ 261 262static void 263ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response) 264 char *rchallenge; 265 int rchallenge_len; 266 char *secret; 267 int secret_len; 268 MS_ChapResponse *response; 269{ 270 int i; 271 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ 272 u_char PasswordHash[MD4_SIGNATURE_SIZE]; 273 274 /* LANMan password is case insensitive */ 275 BZERO(UcasePassword, sizeof(UcasePassword)); 276 for (i = 0; i < secret_len; i++) 277 UcasePassword[i] = (u_char)toupper(secret[i]); 278 DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); 279 DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); 280 ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); 281} 282#endif 283 284void 285ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len) 286 chap_state *cstate; 287 char *rchallenge; 288 int rchallenge_len; 289 char *secret; 290 int secret_len; 291{ 292 MS_ChapResponse response; 293 294 BZERO(&response, sizeof(response)); 295 296 /* Calculate both always */ 297 ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); 298 299#ifdef MSLANMAN 300 ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); 301 302 /* prefered method is set by option */ 303 response.UseNT = !ms_lanman; 304#else 305 response.UseNT = 1; 306#endif 307 308 BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); 309 cstate->resp_length = MS_CHAP_RESPONSE_LEN; 310} 311 312#endif /* CHAPMS */ 313