/* * Copyright (c) 2000-2001, Boris Popov * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. * * $Id: smb_crypt.c,v 1.13 2005/01/26 23:50:50 lindak Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static u_char N8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; static void smb_E(const u_char *key, u_char *data, u_char *dest) { des_key_schedule *ksp; u_char kk[8]; kk[0] = key[0] & 0xfe; kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe); kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe); kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe); kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe); kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe); kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe); kk[7] = key[6] << 1; ksp = malloc(sizeof(des_key_schedule), M_SMBTEMP, M_WAITOK); des_set_key((des_cblock*)kk, *ksp); des_ecb_encrypt((des_cblock*)data, (des_cblock*)dest, *ksp, 1); free(ksp, M_SMBTEMP); } /* * Compute an LM response given the ASCII password and a challenge. */ PRIVSYM int smb_lmresponse(const u_char *apwd, u_char *C8, u_char *RN) { u_char *p, *P14, *S21; p = malloc(14 + 21, M_SMBTEMP, M_WAITOK); bzero(p, 14 + 21); P14 = p; S21 = p + 14; bcopy(apwd, P14, min(14, strlen(apwd))); /* * S21 = concat(Ex(P14, N8), zeros(5)); */ smb_E(P14, N8, S21); smb_E(P14 + 7, N8, S21 + 8); smb_E(S21, C8, RN); smb_E(S21 + 7, C8, RN + 8); smb_E(S21 + 14, C8, RN + 16); free(p, M_SMBTEMP); return 0; } /* * Compute the NTLMv1 hash, which is used to compute both NTLMv1 and * NTLMv2 responses. */ static void smb_ntlmv1hash(const u_char *apwd, u_char *v1hash) { u_int16_t *unipwd; MD4_CTX *ctxp; size_t alen, unilen; alen = strlen(apwd); unipwd = malloc(alen * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK); /* * v1hash = concat(MD4(U(apwd)), zeros(5)); */ unilen = smb_strtouni(unipwd, apwd, alen, UTF_PRECOMPOSED|UTF_NO_NULL_TERM); ctxp = malloc(sizeof(MD4_CTX), M_SMBTEMP, M_WAITOK); MD4Init(ctxp); MD4Update(ctxp, (u_char*)unipwd, unilen); free(unipwd, M_SMBTEMP); bzero(v1hash, 21); MD4Final(v1hash, ctxp); free(ctxp, M_SMBTEMP); } /* * Compute an NTLM response given the Unicode password (as an ASCII string, * not a Unicode string!) and a challenge. */ PRIVSYM int smb_ntlmresponse(const u_char *apwd, u_char *C8, u_char *RN) { u_char S21[21]; smb_ntlmv1hash(apwd, S21); smb_E(S21, C8, RN); smb_E(S21 + 7, C8, RN + 8); smb_E(S21 + 14, C8, RN + 16); return 0; } static void HMACT64(const u_char *key, size_t key_len, const u_char *data, size_t data_len, u_char *digest) { MD5_CTX context; u_char k_ipad[64]; /* inner padding - key XORd with ipad */ u_char k_opad[64]; /* outer padding - key XORd with opad */ int i; /* if key is longer than 64 bytes use only the first 64 bytes */ if (key_len > 64) key_len = 64; /* * The HMAC-MD5 (and HMACT64) transform looks like: * * MD5(K XOR opad, MD5(K XOR ipad, data)) * * where K is an n byte key * ipad is the byte 0x36 repeated 64 times * opad is the byte 0x5c repeated 64 times * and data is the data being protected. */ /* start out by storing key in pads */ bzero(k_ipad, sizeof k_ipad); bzero(k_opad, sizeof k_opad); bcopy(key, k_ipad, key_len); bcopy(key, k_opad, key_len); /* XOR key with ipad and opad values */ for (i = 0; i < 64; i++) { k_ipad[i] ^= 0x36; k_opad[i] ^= 0x5c; } /* * perform inner MD5 */ MD5Init(&context); /* init context for 1st pass */ MD5Update(&context, k_ipad, 64); /* start with inner pad */ MD5Update(&context, data, data_len); /* then data of datagram */ MD5Final(digest, &context); /* finish up 1st pass */ /* * perform outer MD5 */ MD5Init(&context); /* init context for 2nd pass */ MD5Update(&context, k_opad, 64); /* start with outer pad */ MD5Update(&context, digest, 16); /* then results of 1st hash */ MD5Final(digest, &context); /* finish up 2nd pass */ } /* * Compute an NTLMv2 response given the Unicode password (as an ASCII string, * not a Unicode string!), the user name, the destination workgroup/domain * name, a challenge, and the blob. */ PRIVSYM int smb_ntlmv2response(const u_char *apwd, const u_char *user, const u_char *destination, u_char *C8, const u_char *blob, size_t bloblen, u_char **RN, size_t *RNlen) { u_char v1hash[21]; u_int16_t *uniuser, *unidest; size_t uniuserlen, unidestlen; u_char v2hash[16]; int len; size_t datalen; u_char *data; size_t v2resplen; u_char *v2resp; smb_ntlmv1hash(apwd, v1hash); /* * v2hash = HMACT64(v1hash, 16, concat(upcase(user), upcase(destination)) * We assume that user and destination are supplied to us as * upper-case UTF-8. */ len = strlen(user); uniuser = malloc(len * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK); uniuserlen = smb_strtouni(uniuser, user, len, UTF_PRECOMPOSED|UTF_NO_NULL_TERM); len = strlen(destination); unidest = malloc(len * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK); unidestlen = smb_strtouni(unidest, destination, len, UTF_PRECOMPOSED|UTF_NO_NULL_TERM); datalen = uniuserlen + unidestlen; data = malloc(datalen, M_SMBTEMP, M_WAITOK); bcopy(uniuser, data, uniuserlen); bcopy(unidest, data + uniuserlen, unidestlen); free(uniuser, M_SMBTEMP); free(unidest, M_SMBTEMP); HMACT64(v1hash, 16, data, datalen, v2hash); free(data, M_SMBTEMP); datalen = 8 + bloblen; data = malloc(datalen, M_SMBTEMP, M_WAITOK); bcopy(C8, data, 8); bcopy(blob, data + 8, bloblen); v2resplen = 16 + bloblen; v2resp = malloc(v2resplen, M_SMBTEMP, M_WAITOK); HMACT64(v2hash, 16, data, datalen, v2resp); free(data, M_SMBTEMP); bcopy(blob, v2resp + 16, bloblen); *RN = v2resp; *RNlen = v2resplen; return 0; }