lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 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,v 1.1 2008-08-04 06:11:51 winfred Exp $" |
| 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 <openssl/sha.h> |
| 53 | #include "fsm.h" |
| 54 | #include "lcp.h" |
| 55 | #ifdef MPPE |
| 56 | #include "mppe.h" |
| 57 | #endif |
| 58 | #include "extra_crypto.h" |
| 59 | |
| 60 | static const char rcsid[] = RCSID; |
| 61 | |
| 62 | typedef struct { |
| 63 | u_char LANManResp[24]; |
| 64 | u_char NTResp[24]; |
| 65 | u_char UseNT; /* If 1, ignore the LANMan response field */ |
| 66 | } MS_ChapResponse; |
| 67 | /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), |
| 68 | in case this struct gets padded. */ |
| 69 | |
| 70 | typedef struct { |
| 71 | u_char PeerChallenge[16]; |
| 72 | u_char Reserved[8]; |
| 73 | u_char NTResp[24]; |
| 74 | u_char Flags; |
| 75 | } MS_ChapResponse_v2; |
| 76 | |
| 77 | static void ChallengeResponse __P((u_char *, u_char *, u_char *)); |
| 78 | static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *)); |
| 79 | #ifdef MSLANMAN |
| 80 | static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *)); |
| 81 | #endif |
| 82 | |
| 83 | #ifdef MSLANMAN |
| 84 | bool ms_lanman = 0; /* Use LanMan password instead of NT */ |
| 85 | /* Has meaning only with MS-CHAP challenges */ |
| 86 | #endif |
| 87 | |
| 88 | static void |
| 89 | ChallengeResponse(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 | 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 | static void |
| 108 | ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response) |
| 109 | char *rchallenge; |
| 110 | int rchallenge_len; |
| 111 | char *secret; |
| 112 | int secret_len; |
| 113 | MS_ChapResponse *response; |
| 114 | { |
| 115 | u_char hash[MD4_SIGNATURE_SIZE]; |
| 116 | |
| 117 | NtPasswordHash(secret, secret_len, hash); |
| 118 | ChallengeResponse(rchallenge, hash, response->NTResp); |
| 119 | } |
| 120 | |
| 121 | #ifdef MSLANMAN |
| 122 | static void |
| 123 | ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response) |
| 124 | char *rchallenge; |
| 125 | int rchallenge_len; |
| 126 | char *secret; |
| 127 | int secret_len; |
| 128 | MS_ChapResponse *response; |
| 129 | { |
| 130 | u_char PasswordHash[MD4_SIGNATURE_SIZE]; |
| 131 | |
| 132 | LmPasswordHash(secret, secret_len, PasswordHash); |
| 133 | ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); |
| 134 | } |
| 135 | #endif |
| 136 | |
| 137 | void |
| 138 | ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len) |
| 139 | chap_state *cstate; |
| 140 | char *rchallenge; |
| 141 | int rchallenge_len; |
| 142 | char *secret; |
| 143 | int secret_len; |
| 144 | { |
| 145 | MS_ChapResponse response; |
| 146 | |
| 147 | BZERO(&response, sizeof(response)); |
| 148 | |
| 149 | /* Calculate both always */ |
| 150 | ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); |
| 151 | |
| 152 | #ifdef MSLANMAN |
| 153 | ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); |
| 154 | |
| 155 | /* prefered method is set by option */ |
| 156 | response.UseNT = !ms_lanman; |
| 157 | #else |
| 158 | response.UseNT = 1; |
| 159 | #endif |
| 160 | |
| 161 | #ifdef MPPE |
| 162 | mppe_gen_master_key(secret, secret_len, rchallenge); |
| 163 | #endif |
| 164 | BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); |
| 165 | cstate->resp_length = MS_CHAP_RESPONSE_LEN; |
| 166 | } |
| 167 | |
| 168 | int |
| 169 | ChapMS_Resp(cstate, secret, secret_len, remmd) |
| 170 | chap_state *cstate; |
| 171 | char *secret; |
| 172 | int secret_len; |
| 173 | u_char *remmd; |
| 174 | { |
| 175 | MS_ChapResponse local; |
| 176 | MS_ChapResponse *response = (MS_ChapResponse *)remmd; |
| 177 | int i; |
| 178 | |
| 179 | BZERO(&local, sizeof(response)); |
| 180 | |
| 181 | if(response->UseNT) |
| 182 | { |
| 183 | ChapMS_NT(cstate->challenge,cstate->chal_len, secret, secret_len, &local); |
| 184 | i = memcmp(local.NTResp, response->NTResp, sizeof(local.NTResp)); |
| 185 | |
| 186 | #ifdef MPPE |
| 187 | if(i == 0) |
| 188 | mppe_gen_master_key(secret, secret_len, cstate->challenge); |
| 189 | #endif |
| 190 | return(i); |
| 191 | } |
| 192 | |
| 193 | #ifdef MSLANMAN |
| 194 | ChapMS_LANMan(cstate->challenge, cstate->chal_len, secret, secret_len, |
| 195 | &local); |
| 196 | if(memcmp(local.LANManResp, response->LANManResp, |
| 197 | sizeof(local.LANManResp)) == 0) { |
| 198 | #ifdef MPPE |
| 199 | mppe_gen_master_key(secret, secret_len, cstate->challenge); |
| 200 | #endif |
| 201 | return(0); |
| 202 | } |
| 203 | #endif /* MSLANMAN */ |
| 204 | return(1); |
| 205 | } |
| 206 | |
| 207 | void |
| 208 | ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge) |
| 209 | char *PeerChallenge; |
| 210 | char *AuthenticatorChallenge; |
| 211 | char *UserName; |
| 212 | char *Challenge; |
| 213 | { |
| 214 | SHA_CTX Context; |
| 215 | u_char Digest[SHA_DIGEST_LENGTH]; |
| 216 | char *username; |
| 217 | |
| 218 | if((username = strrchr(UserName, '\\')) != (char *)NULL) |
| 219 | ++username; |
| 220 | else |
| 221 | username = UserName; |
| 222 | SHA1_Init(&Context); |
| 223 | SHA1_Update(&Context, PeerChallenge, 16); |
| 224 | SHA1_Update(&Context, AuthenticatorChallenge, 16); |
| 225 | SHA1_Update(&Context, username, strlen(username)); |
| 226 | SHA1_Final(Digest, &Context); |
| 227 | BCOPY(Digest, Challenge, 8); |
| 228 | } |
| 229 | |
| 230 | void |
| 231 | ChapMS_v2(cstate, AuthenticatorChallenge, AuthenticatorChallengeLen, Password, PasswordLen) |
| 232 | chap_state *cstate; |
| 233 | char *AuthenticatorChallenge; |
| 234 | int AuthenticatorChallengeLen; |
| 235 | char *Password; |
| 236 | int PasswordLen; |
| 237 | { |
| 238 | u_char Challenge[8]; |
| 239 | u_char PasswordHash[MD4_SIGNATURE_SIZE]; |
| 240 | MS_ChapResponse_v2 response; |
| 241 | |
| 242 | BZERO(&response, sizeof(response)); |
| 243 | ChapGenChallenge(cstate); |
| 244 | BCOPY(cstate->challenge, response.PeerChallenge, |
| 245 | sizeof(response.PeerChallenge)); |
| 246 | ChallengeHash(response.PeerChallenge, AuthenticatorChallenge, |
| 247 | cstate->resp_name, Challenge); |
| 248 | NtPasswordHash(Password, PasswordLen, PasswordHash); |
| 249 | ChallengeResponse(Challenge, PasswordHash, response.NTResp); |
| 250 | BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); |
| 251 | cstate->resp_length = MS_CHAP_RESPONSE_LEN; |
| 252 | #ifdef MPPE |
| 253 | mppe_gen_master_key_v2(Password, PasswordLen, response.NTResp, 0); |
| 254 | #endif |
| 255 | } |
| 256 | |
| 257 | int |
| 258 | ChapMS_v2_Resp(cstate, Password, PasswordLen, remmd, UserName) |
| 259 | chap_state *cstate; |
| 260 | char *Password; |
| 261 | int PasswordLen; |
| 262 | u_char *remmd; |
| 263 | char *UserName; |
| 264 | { |
| 265 | u_char Challenge[8]; |
| 266 | u_char PasswordHash[MD4_SIGNATURE_SIZE]; |
| 267 | MS_ChapResponse_v2 response, response1; |
| 268 | int i; |
| 269 | |
| 270 | BCOPY(remmd, &response, MS_CHAP_RESPONSE_LEN); |
| 271 | ChallengeHash(response.PeerChallenge,cstate->challenge,UserName,Challenge); |
| 272 | NtPasswordHash(Password, PasswordLen, PasswordHash); |
| 273 | ChallengeResponse(Challenge, PasswordHash, response1.NTResp); |
| 274 | i = memcmp(response.NTResp, response1.NTResp, sizeof(response.NTResp)); |
| 275 | #ifdef MPPE |
| 276 | if(i == 0) |
| 277 | mppe_gen_master_key_v2(Password, PasswordLen, response1.NTResp, 1); |
| 278 | #endif |
| 279 | return(i); |
| 280 | } |
| 281 | |
| 282 | void |
| 283 | ChapMS_v2_Auth(cstate, Password, PasswordLen, remmd, UserName) |
| 284 | chap_state *cstate; |
| 285 | char *Password; |
| 286 | int PasswordLen; |
| 287 | u_char *remmd; |
| 288 | char *UserName; |
| 289 | { |
| 290 | u_char PasswordHash[MD4_SIGNATURE_SIZE]; |
| 291 | u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; |
| 292 | u_char Challenge[8]; |
| 293 | static char Magic1[] = "Magic server to client signing constant"; |
| 294 | static char Magic2[] = "Pad to make it do more than one iteration"; |
| 295 | |
| 296 | SHA_CTX Context; |
| 297 | u_char Digest[SHA_DIGEST_LENGTH]; |
| 298 | MS_ChapResponse_v2 *response = (MS_ChapResponse_v2 *)remmd; |
| 299 | char StrResponse[SHA_DIGEST_LENGTH * 2 + 3], *s; |
| 300 | int i; |
| 301 | static char HexDigs[] = "0123456789ABCDEF"; |
| 302 | |
| 303 | NtPasswordHash(Password, PasswordLen, PasswordHash); |
| 304 | md4(PasswordHash, sizeof(PasswordHash), PasswordHashHash); |
| 305 | |
| 306 | SHA1_Init(&Context); |
| 307 | SHA1_Update(&Context, PasswordHashHash, 16); |
| 308 | SHA1_Update(&Context, response->NTResp, 24); |
| 309 | SHA1_Update(&Context, Magic1, sizeof(Magic1) - 1); |
| 310 | SHA1_Final(Digest, &Context); |
| 311 | |
| 312 | ChallengeHash(response->PeerChallenge,cstate->challenge,UserName,Challenge); |
| 313 | |
| 314 | SHA1_Init(&Context); |
| 315 | SHA1_Update(&Context, Digest, SHA_DIGEST_LENGTH); |
| 316 | SHA1_Update(&Context, Challenge, 8); |
| 317 | SHA1_Update(&Context, Magic2, sizeof(Magic2) - 1); |
| 318 | SHA1_Final(Digest, &Context); |
| 319 | s = stpcpy(StrResponse, "S="); |
| 320 | for(i = 0; i < SHA_DIGEST_LENGTH; ++i) { |
| 321 | *s++ = HexDigs[Digest[i] >> 4]; |
| 322 | *s++ = HexDigs[Digest[i] & 0x0F]; |
| 323 | } |
| 324 | *s = '\0'; |
| 325 | BCOPY(StrResponse, cstate->response, sizeof(StrResponse)); |
| 326 | cstate->resp_length = sizeof(StrResponse) - 1; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | * functions called from config options |
| 331 | */ |
| 332 | int |
| 333 | reqchapms(char **argv) |
| 334 | { |
| 335 | lcp_wantoptions[0].neg_chap = 1; |
| 336 | lcp_wantoptions[0].use_chapms = 1; |
| 337 | auth_required = 1; |
| 338 | |
| 339 | return 1; |
| 340 | } |
| 341 | |
| 342 | int |
| 343 | nochapms(char **argv) |
| 344 | { |
| 345 | lcp_wantoptions[0].use_chapms = 0; |
| 346 | return 1; |
| 347 | } |
| 348 | |
| 349 | int |
| 350 | reqchapms_v2(char **argv) |
| 351 | { |
| 352 | lcp_wantoptions[0].neg_chap = 1; |
| 353 | lcp_wantoptions[0].use_chapms_v2 = 1; |
| 354 | auth_required = 1; |
| 355 | |
| 356 | return 1; |
| 357 | } |
| 358 | |
| 359 | int |
| 360 | nochapms_v2(char **argv) |
| 361 | { |
| 362 | lcp_wantoptions[0].use_chapms_v2 = 0; |
| 363 | return 1; |
| 364 | } |
| 365 | |
| 366 | #endif /* CHAPMS */ |