/* Does the NTLMv2 owfs of a user's password */ #if 0 /* function not needed yet - but will be soon */ static void ntv2_owf_gen(const unsigned char owf[16], const char *user_n, const char *domain_n, unsigned char kr_buf[16], const struct nls_table *nls_codepage) { wchar_t *user_u; wchar_t *dom_u; int user_l, domain_l; struct HMACMD5Context ctx; /* might as well do one alloc to hold both (user_u and dom_u) */ user_u = kmalloc(2048 * sizeof(wchar_t), GFP_KERNEL); if (user_u == NULL) return; dom_u = user_u + 1024; /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ /* BB user and domain may need to be uppercased */ user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage); domain_l = cifs_strtoUCS(dom_u, domain_n, 511, nls_codepage); user_l++; /* trailing null */ domain_l++; hmac_md5_init_limK_to_64(owf, 16, &ctx); hmac_md5_update((const unsigned char *) user_u, user_l * 2, &ctx); hmac_md5_update((const unsigned char *) dom_u, domain_l * 2, &ctx); hmac_md5_final(kr_buf, &ctx); kfree(user_u); }
static int calc_ntlmv2_hash(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { int rc = 0; int len; char nt_hash[16]; struct HMACMD5Context *pctxt; wchar_t *user; wchar_t *domain; pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL); if (pctxt == NULL) return -ENOMEM; /* calculate md4 hash of password */ E_md4hash(ses->password, nt_hash); /* convert Domainname to unicode and uppercase */ hmac_md5_init_limK_to_64(nt_hash, 16, pctxt); /* convert ses->userName to unicode and uppercase */ len = strlen(ses->userName); user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) goto calc_exit_2; len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); UniStrupr(user); hmac_md5_update((char *)user, 2*len, pctxt); /* convert ses->domainName to unicode and uppercase */ if (ses->domainName) { len = strlen(ses->domainName); domain = kmalloc(2 + (len * 2), GFP_KERNEL); if (domain == NULL) goto calc_exit_1; len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, nls_cp); /* the following line was removed since it didn't work well with lower cased domain name that passed as an option. Maybe converting the domain name earlier makes sense */ /* UniStrupr(domain); */ hmac_md5_update((char *)domain, 2*len, pctxt); kfree(domain); } calc_exit_1: kfree(user); calc_exit_2: /* BB FIXME what about bytes 24 through 40 of the signing key? compare with the NTLM example */ hmac_md5_final(ses->server->ntlmv2_hash, pctxt); kfree(pctxt); return rc; }
int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *ses, const struct nls_table *nls_info) { char temp_hash[16]; struct HMACMD5Context ctx; char *ucase_buf; __le16 *unicode_buf; unsigned int i, user_name_len, dom_name_len; if (ses == NULL) return -EINVAL; E_md4hash(ses->password, temp_hash); hmac_md5_init_limK_to_64(temp_hash, 16, &ctx); user_name_len = strlen(ses->userName); if (user_name_len > MAX_USERNAME_SIZE) return -EINVAL; if (ses->domainName == NULL) return -EINVAL; /* BB should we use CIFS_LINUX_DOM */ dom_name_len = strlen(ses->domainName); if (dom_name_len > MAX_USERNAME_SIZE) return -EINVAL; ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL); if (ucase_buf == NULL) return -ENOMEM; unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL); if (unicode_buf == NULL) { kfree(ucase_buf); return -ENOMEM; } for (i = 0; i < user_name_len; i++) ucase_buf[i] = nls_info->charset2upper[(int)ses->userName[i]]; ucase_buf[i] = 0; user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); unicode_buf[user_name_len] = 0; user_name_len++; for (i = 0; i < dom_name_len; i++) ucase_buf[i] = nls_info->charset2upper[(int)ses->domainName[i]]; ucase_buf[i] = 0; dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); unicode_buf[user_name_len + dom_name_len] = 0; hmac_md5_update((const unsigned char *) unicode_buf, (user_name_len+dom_name_len)*2, &ctx); hmac_md5_final(ses->server->ntlmv2_hash, &ctx); kfree(ucase_buf); kfree(unicode_buf); return 0; }
static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { char *bcc_ptr = *pbcc_area; int bytes_ret = 0; /* BB FIXME add check that strings total less than 335 or will need to send them as arrays */ /* unicode strings, must be word aligned before the call */ /* if ((long) bcc_ptr % 2) { *bcc_ptr = 0; bcc_ptr++; } */ /* copy user */ if (ses->userName == NULL) { /* null user mount */ *bcc_ptr = 0; *(bcc_ptr+1) = 0; } else { /* 300 should be long enough for any conceivable user name */ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName, 300, nls_cp); } bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null termination */ unicode_domain_string(&bcc_ptr, ses, nls_cp); unicode_oslm_strings(&bcc_ptr, nls_cp); *pbcc_area = bcc_ptr; }
static void unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) { char *bcc_ptr = *pbcc_area; int bytes_ret = 0; /* Copy OS version */ bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32, nls_cp); bcc_ptr += 2 * bytes_ret; bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, init_utsname()->release, 32, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* trailing null */ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, 32, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* trailing null */ *pbcc_area = bcc_ptr; }
static void unicode_domain_string(char **pbcc_area, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { char *bcc_ptr = *pbcc_area; int bytes_ret = 0; /* copy domain */ if (ses->domainName == NULL) { /* Sending null domain better than using a bogus domain name (as we did briefly in 2.6.18) since server will use its default */ *bcc_ptr = 0; *(bcc_ptr+1) = 0; bytes_ret = 0; } else bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, 256, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null terminator */ *pbcc_area = bcc_ptr; }
static int calc_ntlmv2_hash(struct cifsSesInfo *ses, char *ntlmv2_hash, const struct nls_table *nls_cp) { int rc = 0; int len; char nt_hash[CIFS_NTHASH_SIZE]; wchar_t *user; wchar_t *domain; wchar_t *server; if (!ses->server->secmech.sdeschmacmd5) { cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); return -1; } /* calculate md4 hash of password */ E_md4hash(ses->password, nt_hash); crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash, CIFS_NTHASH_SIZE); rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); if (rc) { cERROR(1, "calc_ntlmv2_hash: could not init hmacmd5\n"); return rc; } /* convert ses->userName to unicode and uppercase */ len = strlen(ses->userName); user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) { cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n"); rc = -ENOMEM; goto calc_exit_2; } len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); UniStrupr(user); crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, (char *)user, 2 * len); /* convert ses->domainName to unicode and uppercase */ if (ses->domainName) { len = strlen(ses->domainName); domain = kmalloc(2 + (len * 2), GFP_KERNEL); if (domain == NULL) { cERROR(1, "calc_ntlmv2_hash: domain mem alloc failure"); rc = -ENOMEM; goto calc_exit_1; } len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, nls_cp); crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, (char *)domain, 2 * len); kfree(domain); } else if (ses->serverName) { len = strlen(ses->serverName); server = kmalloc(2 + (len * 2), GFP_KERNEL); if (server == NULL) { cERROR(1, "calc_ntlmv2_hash: server mem alloc failure"); rc = -ENOMEM; goto calc_exit_1; } len = cifs_strtoUCS((__le16 *)server, ses->serverName, len, nls_cp); crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, (char *)server, 2 * len); kfree(server); } rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, ntlmv2_hash); calc_exit_1: kfree(user); calc_exit_2: return rc; }
/* Build a proper attribute value/target info pairs blob. * Fill in netbios and dns domain name and workstation name * and client time (total five av pairs and + one end of fields indicator. * Allocate domain name which gets freed when session struct is deallocated. */ static int build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { unsigned int dlen; unsigned int wlen; unsigned int size = 6 * sizeof(struct ntlmssp2_name); __le64 curtime; char *defdmname = "WORKGROUP"; unsigned char *blobptr; struct ntlmssp2_name *attrptr; if (!ses->domainName) { ses->domainName = kstrdup(defdmname, GFP_KERNEL); if (!ses->domainName) return -ENOMEM; } dlen = strlen(ses->domainName); wlen = strlen(ses->server->hostname); /* The length of this blob is a size which is * six times the size of a structure which holds name/size + * two times the unicode length of a domain name + * two times the unicode length of a server name + * size of a timestamp (which is 8 bytes). */ ses->auth_key.len = size + 2 * (2 * dlen) + 2 * (2 * wlen) + 8; ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); if (!ses->auth_key.response) { ses->auth_key.len = 0; cERROR(1, "Challenge target info allocation failure"); return -ENOMEM; } blobptr = ses->auth_key.response; attrptr = (struct ntlmssp2_name *) blobptr; attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); attrptr->length = cpu_to_le16(2 * dlen); blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); blobptr += 2 * dlen; attrptr = (struct ntlmssp2_name *) blobptr; attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_COMPUTER_NAME); attrptr->length = cpu_to_le16(2 * wlen); blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); blobptr += 2 * wlen; attrptr = (struct ntlmssp2_name *) blobptr; attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_DOMAIN_NAME); attrptr->length = cpu_to_le16(2 * dlen); blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); blobptr += 2 * dlen; attrptr = (struct ntlmssp2_name *) blobptr; attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_COMPUTER_NAME); attrptr->length = cpu_to_le16(2 * wlen); blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); blobptr += 2 * wlen; attrptr = (struct ntlmssp2_name *) blobptr; attrptr->type = cpu_to_le16(NTLMSSP_AV_TIMESTAMP); attrptr->length = cpu_to_le16(sizeof(__le64)); blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); curtime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); memcpy(blobptr, &curtime, sizeof(__le64)); return 0; }
/* * Convert 16 bit Unicode pathname to wire format from string in current code * page. Conversion may involve remapping up the six characters that are * only legal in POSIX-like OS (if they are present in the string). Path * names are little endian 16 bit Unicode on the wire */ int cifsConvertToUCS(__le16 *target, const char *source, int srclen, const struct nls_table *cp, int mapChars) { int i, j, charlen; char src_char; __le16 dst_char; wchar_t tmp; if (!mapChars) return cifs_strtoUCS(target, source, PATH_MAX, cp); for (i = 0, j = 0; i < srclen; j++) { src_char = source[i]; charlen = 1; switch (src_char) { case 0: put_unaligned(0, &target[j]); goto ctoUCS_out; case ':': dst_char = cpu_to_le16(UNI_COLON); break; case '*': dst_char = cpu_to_le16(UNI_ASTERISK); break; case '?': dst_char = cpu_to_le16(UNI_QUESTION); break; case '<': dst_char = cpu_to_le16(UNI_LESSTHAN); break; case '>': dst_char = cpu_to_le16(UNI_GRTRTHAN); break; case '|': dst_char = cpu_to_le16(UNI_PIPE); break; #ifdef SYNO_CIFS_SPECIAL_CHAR_CONVER case '"': dst_char = cpu_to_le16(UNI_DQUOT); break; #endif /* * FIXME: We can not handle remapping backslash (UNI_SLASH) * until all the calls to build_path_from_dentry are modified, * as they use backslash as separator. */ default: charlen = cp->char2uni(source + i, srclen - i, &tmp); dst_char = cpu_to_le16(tmp); /* * if no match, use question mark, which at least in * some cases serves as wild card */ if (charlen < 1) { dst_char = cpu_to_le16(0x003f); charlen = 1; } } /* * character may take more than one byte in the source string, * but will take exactly two bytes in the target string */ i += charlen; put_unaligned(dst_char, &target[j]); } ctoUCS_out: return i; }