diff options
Diffstat (limited to 'sbin/iked/chap_ms.c')
| -rw-r--r-- | sbin/iked/chap_ms.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/sbin/iked/chap_ms.c b/sbin/iked/chap_ms.c new file mode 100644 index 00000000000..50975c62864 --- /dev/null +++ b/sbin/iked/chap_ms.c @@ -0,0 +1,412 @@ +/* $OpenBSD: chap_ms.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */ +/* $vantronix: chap_ms.c,v 1.7 2010/06/02 12:22:58 reyk Exp $ */ + +/* + * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net> + * Copyright (c) 1997 - 2001 Brian Somers <brian@Awfulhak.org> + * Copyright (c) 1997 Gabor Kincses <gabor@acm.org> + * Copyright (c) 1995 Eric Rosenquist + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/cdefs.h> + +#include <ctype.h> +#include <string.h> +#include <stdio.h> + +#include <openssl/evp.h> +#include <openssl/des.h> +#include <openssl/md4.h> +#include <openssl/md5.h> +#include <openssl/sha.h> + +#include "chap_ms.h" + +/* + * Documentation & specifications: + * + * MS-CHAP (CHAP80) RFC2433 + * MS-CHAP-V2 (CHAP81) RFC2759 + * MPPE key management RFC3079 + * + * Security analysis: + * Schneier/Mudge/Wagner, "MS-CHAP-v2", Oct 99 + * "It is unclear to us why this protocol is so complicated." + */ + +static u_int8_t sha1_pad1[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static u_int8_t sha1_pad2[40] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 +}; + +u_int8_t get7bits(u_int8_t *, int); +void mschap_des_addparity(u_int8_t *, u_int8_t *); +void mschap_des_encrypt(u_int8_t *, u_int8_t *, u_int8_t *); +void mschap_challenge_response(u_int8_t *, u_int8_t *, u_int8_t *); + +u_int8_t +get7bits(u_int8_t *in, int start) +{ + u_int word; + + word = (u_int)in[start / 8] << 8; + word |= (u_int)in[start / 8 + 1]; + word >>= 15 - (start % 8 + 7); + + return (word & 0xfe); +} + +/* IN 56 bit DES key missing parity bits + OUT 64 bit DES key with parity bits added */ +void +mschap_des_addparity(u_int8_t *key, u_int8_t *des_key) +{ + des_key[0] = get7bits(key, 0); + des_key[1] = get7bits(key, 7); + des_key[2] = get7bits(key, 14); + des_key[3] = get7bits(key, 21); + des_key[4] = get7bits(key, 28); + des_key[5] = get7bits(key, 35); + des_key[6] = get7bits(key, 42); + des_key[7] = get7bits(key, 49); + + DES_set_odd_parity((des_cblock *)des_key); +} + +void +mschap_des_encrypt(u_int8_t *clear, u_int8_t *key, u_int8_t *cipher) +{ + des_cblock des_key; + des_key_schedule key_schedule; + + mschap_des_addparity(key, des_key); + + DES_set_key(&des_key, &key_schedule); + DES_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, + &key_schedule, 1); +} + +void +mschap_challenge_response(u_int8_t *challenge, u_int8_t *pwhash, + u_int8_t *response) +{ + u_int8_t padpwhash[21]; + + bzero(&padpwhash, sizeof(padpwhash)); + memcpy(padpwhash, pwhash, MSCHAP_HASH_SZ); + + mschap_des_encrypt(challenge, padpwhash + 0, response + 0); + mschap_des_encrypt(challenge, padpwhash + 7, response + 8); + mschap_des_encrypt(challenge, padpwhash + 14, response + 16); +} + +void +mschap_ntpassword_hash(u_int8_t *in, int inlen, u_int8_t *hash) +{ + EVP_MD_CTX ctx; + u_int mdlen; + + EVP_DigestInit(&ctx, EVP_md4()); + EVP_DigestUpdate(&ctx, in, inlen); + EVP_DigestFinal(&ctx, hash, &mdlen); +} + +void +mschap_challenge_hash(u_int8_t *peer_challenge, u_int8_t *auth_challenge, + u_int8_t *username, int usernamelen, u_int8_t *challenge) +{ + EVP_MD_CTX ctx; + u_int8_t md[SHA_DIGEST_LENGTH]; + u_int mdlen; + u_int8_t *name; + + if ((name = strrchr(username, '\\')) == NULL) + name = username; + else + name++; + + EVP_DigestInit(&ctx, EVP_sha1()); + EVP_DigestUpdate(&ctx, peer_challenge, MSCHAPV2_CHALLENGE_SZ); + EVP_DigestUpdate(&ctx, auth_challenge, MSCHAPV2_CHALLENGE_SZ); + EVP_DigestUpdate(&ctx, name, strlen(name)); + EVP_DigestFinal(&ctx, md, &mdlen); + + memcpy(challenge, md, MSCHAP_CHALLENGE_SZ); +} + +void +mschap_nt_response(u_int8_t *auth_challenge, u_int8_t *peer_challenge, + u_int8_t *username, int usernamelen, u_int8_t *password, int passwordlen, + u_int8_t *response) +{ + u_int8_t challenge[MSCHAP_CHALLENGE_SZ]; + u_int8_t password_hash[MSCHAP_HASH_SZ]; + + mschap_challenge_hash(peer_challenge, auth_challenge, + username, usernamelen, challenge); + + mschap_ntpassword_hash(password, passwordlen, password_hash); + mschap_challenge_response(challenge, password_hash, response); +} + +void +mschap_auth_response(u_int8_t *password, int passwordlen, + u_int8_t *ntresponse, u_int8_t *auth_challenge, u_int8_t *peer_challenge, + u_int8_t *username, int usernamelen, u_int8_t *auth_response) +{ + EVP_MD_CTX ctx; + u_int8_t password_hash[MSCHAP_HASH_SZ]; + u_int8_t password_hash2[MSCHAP_HASH_SZ]; + u_int8_t challenge[MSCHAP_CHALLENGE_SZ]; + u_int8_t md[SHA_DIGEST_LENGTH], *ptr; + u_int mdlen; + int i; + const u_int8_t hex[] = "0123456789ABCDEF"; + static u_int8_t 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 + }; + static u_int8_t 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 + }; + + mschap_ntpassword_hash(password, passwordlen, password_hash); + mschap_ntpassword_hash(password_hash, MSCHAP_HASH_SZ, password_hash2); + + EVP_DigestInit(&ctx, EVP_sha1()); + EVP_DigestUpdate(&ctx, password_hash2, sizeof(password_hash2)); + EVP_DigestUpdate(&ctx, ntresponse, 24); + EVP_DigestUpdate(&ctx, magic1, 39); + EVP_DigestFinal(&ctx, md, &mdlen); + + mschap_challenge_hash(peer_challenge, auth_challenge, + username, usernamelen, challenge); + + EVP_DigestInit(&ctx, EVP_sha1()); + EVP_DigestUpdate(&ctx, md, sizeof(md)); + EVP_DigestUpdate(&ctx, challenge, sizeof(challenge)); + EVP_DigestUpdate(&ctx, magic2, 41); + EVP_DigestFinal(&ctx, md, &mdlen); + + /* + * Encode the value of 'Digest' as "S=" followed by + * 40 ASCII hexadecimal digits and return it in + * AuthenticatorResponse. + * For example, + * "S=0123456789ABCDEF0123456789ABCDEF01234567" + */ + ptr = auth_response; + *ptr++ = 'S'; + *ptr++ = '='; + for (i = 0; i < SHA_DIGEST_LENGTH; i++) { + *ptr++ = hex[md[i] >> 4]; + *ptr++ = hex[md[i] & 0x0f]; + } +} + +void +mschap_masterkey(u_int8_t *password_hash2, u_int8_t *ntresponse, + u_int8_t *masterkey) +{ + u_int8_t md[SHA_DIGEST_LENGTH]; + u_int mdlen; + EVP_MD_CTX ctx; + static u_int8_t magic1[27] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 + }; + + EVP_DigestInit(&ctx, EVP_sha1()); + EVP_DigestUpdate(&ctx, password_hash2, MSCHAP_HASH_SZ); + EVP_DigestUpdate(&ctx, ntresponse, 24); + EVP_DigestUpdate(&ctx, magic1, 27); + EVP_DigestFinal(&ctx, md, &mdlen); + + memcpy(masterkey, md, 16); +} + +void +mschap_asymetric_startkey(u_int8_t *masterkey, u_int8_t *sessionkey, + int sessionkeylen, int issend, int isserver) +{ + EVP_MD_CTX ctx; + u_int8_t md[SHA_DIGEST_LENGTH]; + u_int mdlen; + u_int8_t *s; + static u_int8_t magic2[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static u_int8_t magic3[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + + if (issend) + s = isserver ? magic3 : magic2; + else + s = isserver ? magic2 : magic3; + + EVP_DigestInit(&ctx, EVP_sha1()); + EVP_DigestUpdate(&ctx, masterkey, 16); + EVP_DigestUpdate(&ctx, sha1_pad1, 40); + EVP_DigestUpdate(&ctx, s, 84); + EVP_DigestUpdate(&ctx, sha1_pad2, 40); + EVP_DigestFinal(&ctx, md, &mdlen); + + memcpy(sessionkey, md, sessionkeylen); +} + +void +mschap_msk(u_int8_t *password, int passwordlen, + u_int8_t *ntresponse, u_int8_t *msk) +{ + u_int8_t password_hash[MSCHAP_HASH_SZ]; + u_int8_t password_hash2[MSCHAP_HASH_SZ]; + u_int8_t masterkey[MSCHAP_MASTERKEY_SZ]; + u_int8_t sendkey[MSCHAP_MASTERKEY_SZ]; + u_int8_t recvkey[MSCHAP_MASTERKEY_SZ]; + + mschap_ntpassword_hash(password, passwordlen, password_hash); + mschap_ntpassword_hash(password_hash, MSCHAP_HASH_SZ, password_hash2); + + mschap_masterkey(password_hash2, ntresponse, masterkey); + mschap_asymetric_startkey(masterkey, recvkey, sizeof(recvkey), 0, 1); + mschap_asymetric_startkey(masterkey, sendkey, sizeof(sendkey), 1, 1); + + /* 16 bytes receive key + 16 bytes send key + 32 bytes 0 padding */ + bzero(msk, MSCHAP_MSK_SZ); + memcpy(msk, &recvkey, sizeof(recvkey)); + memcpy(msk + sizeof(recvkey), &sendkey, sizeof(sendkey)); +} + +void +mschap_newkey(u_int8_t *startkey, u_int8_t *sessionkey, + long sessionkeylen, u_int8_t *key) +{ + EVP_MD_CTX ctx; + u_int8_t md[SHA_DIGEST_LENGTH]; + u_int mdlen; + + EVP_DigestInit(&ctx, EVP_sha1()); + EVP_DigestUpdate(&ctx, startkey, sessionkeylen); + EVP_DigestUpdate(&ctx, sha1_pad1, sizeof(sha1_pad1)); + EVP_DigestUpdate(&ctx, sessionkey, sessionkeylen); + EVP_DigestUpdate(&ctx, sha1_pad2, sizeof(sha1_pad2)); + EVP_DigestFinal(&ctx, md, &mdlen); + + memcpy(key, md, sessionkeylen); +} + +void +mschap_nt(u_int8_t *password_hash, u_int8_t *challenge) +{ + u_int8_t response[24]; + + mschap_challenge_response(challenge, password_hash, response); + memcpy(password_hash, response, sizeof(response)); + password_hash[24] = 1; /* NT-style response */ +} + +void +mschap_lanman(u_int8_t *digest, u_int8_t *challenge, u_int8_t *secret) +{ + static u_int8_t salt[] = "KGS!@#$%"; /* RASAPI32.dll */ + u_int8_t SECRET[14], *ptr, *end; + u_int8_t hash[MSCHAP_HASH_SZ]; + + end = SECRET + sizeof(SECRET); + for (ptr = SECRET; *secret && ptr < end; ptr++, secret++) + *ptr = toupper(*secret); + if (ptr < end) + memset(ptr, '\0', end - ptr); + + mschap_des_encrypt(salt, SECRET, hash); + mschap_des_encrypt(salt, SECRET + 7, hash + 8); + + mschap_challenge_response(challenge, hash, digest); +} + +void +mschap_radiuskey(u_int8_t *plain, const u_int8_t *crypted, + const u_int8_t *authenticator, const u_int8_t *secret) +{ + EVP_MD_CTX ctx; + u_int8_t b[MD5_DIGEST_LENGTH], p[32]; + u_int i, mdlen; + + EVP_DigestInit(&ctx, EVP_md5()); + EVP_DigestUpdate(&ctx, secret, strlen(secret)); + EVP_DigestUpdate(&ctx, authenticator, 16); + EVP_DigestUpdate(&ctx, crypted, 2); + EVP_DigestFinal(&ctx, b, &mdlen); + + for(i = 0; i < mdlen; i++) { + p[i] = b[i] ^ crypted[i+2]; + } + + EVP_DigestInit(&ctx, EVP_md5()); + EVP_DigestUpdate(&ctx, secret, strlen(secret)); + EVP_DigestUpdate(&ctx, crypted + 2, mdlen); + EVP_DigestFinal(&ctx, b, &mdlen); + + for(i = 0; i < mdlen; i++) { + p[i+16] = b[i] ^ crypted[i+18]; + } + + memcpy(plain, p+1, 16); +} |
