summaryrefslogtreecommitdiffstats
path: root/sbin/iked/chap_ms.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/iked/chap_ms.c')
-rw-r--r--sbin/iked/chap_ms.c412
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);
+}