// SPDX-License-Identifier: LGPL-2.1 /* * * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP * for more detailed information * * Copyright (C) International Business Machines Corp., 2005,2013 * Author(s): Steve French (sfrench@us.ibm.com) *
*/
ret = crypto_shash_update(shash, iter_base, len); if (ret < 0) {
*pret = ret; return len;
} return 0;
}
/* * Pass the data from an iterator into a hash.
*/ staticint cifs_shash_iter(conststruct iov_iter *iter, size_t maxsize, struct shash_desc *shash)
{ struct iov_iter tmp_iter = *iter; int err = -EIO;
int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, char *signature, struct shash_desc *shash)
{ int i;
ssize_t rc; struct kvec *iov = rqst->rq_iov; int n_vec = rqst->rq_nvec;
/* iov[0] is actual data and not the rfc1002 length for SMB2+ */ if (!is_smb1(server)) { if (iov[0].iov_len <= 4) return -EIO;
i = 0;
} else { if (n_vec < 2 || iov[0].iov_len != 4) return -EIO;
i = 1; /* skip rfc1002 length */
}
for (; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; if (iov[i].iov_base == NULL) {
cifs_dbg(VFS, "null iovec entry\n"); return -EIO;
}
rc = crypto_shash_update(shash,
iov[i].iov_base, iov[i].iov_len); if (rc) {
cifs_dbg(VFS, "%s: Could not update with payload\n",
__func__); return rc;
}
}
rc = crypto_shash_final(shash, signature); if (rc)
cifs_dbg(VFS, "%s: Could not generate hash\n", __func__);
return rc;
}
/* * Calculate and return the CIFS signature based on the mac key and SMB PDU. * The 16 byte signature must be allocated by the caller. Note we only use the * 1st eight bytes and that the smb header signature field on input contains * the sequence number before this function is called. Also, this function * should be called with the server->srv_mutex held.
*/ staticint cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, char *signature)
{ int rc;
if (!rqst->rq_iov || !signature || !server) return -EINVAL;
rc = cifs_alloc_hash("md5", &server->secmech.md5); if (rc) return -1;
rc = crypto_shash_init(server->secmech.md5); if (rc) {
cifs_dbg(VFS, "%s: Could not init md5\n", __func__); return rc;
}
rc = crypto_shash_update(server->secmech.md5,
server->session_key.response, server->session_key.len); if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__); return rc;
}
/* must be called with server->srv_mutex held */ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
__u32 *pexpected_response_sequence_number)
{ int rc = 0; char smb_signature[20]; struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
/* must be called with server->srv_mutex held */ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
__u32 *pexpected_response_sequence_number)
{ struct kvec iov[2];
if (cifs_pdu == NULL || server == NULL) return -EINVAL;
if (!server->session_estab) return 0;
if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { struct smb_com_lock_req *pSMB =
(struct smb_com_lock_req *)cifs_pdu; if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) return 0;
}
/* BB what if signatures are supposed to be on for session but
server does not send one? BB */
/* Do not need to verify session setups with signature "BSRSPYL " */ if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0)
cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n",
cifs_pdu->Command);
/* save off the original signature so we can modify the smb and check
its signature against what the server sent */
memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8);
/* cifs_dump_mem("what we think it should be: ",
what_we_think_sig_should_be, 16); */
if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) return -EACCES; else return 0;
}
/* 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.
*/ staticint
build_avpair_blob(struct cifs_ses *ses, conststruct nls_table *nls_cp)
{ unsignedint dlen; unsignedint size = 2 * sizeof(struct ntlmssp2_name); char *defdmname = "WORKGROUP"; unsignedchar *blobptr; struct ntlmssp2_name *attrptr;
if (!ses->domainName) {
ses->domainName = kstrdup(defdmname, GFP_KERNEL); if (!ses->domainName) return -ENOMEM;
}
dlen = strlen(ses->domainName);
/* * The length of this blob is two times the size of a * structure (av pair) which holds name/size * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) + * unicode length of a netbios domain name
*/
kfree_sensitive(ses->auth_key.response);
ses->auth_key.len = size + 2 * dlen;
ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); if (!ses->auth_key.response) {
ses->auth_key.len = 0; return -ENOMEM;
}
/* * As defined in MS-NTLM 3.3.2, just this av pair field * is sufficient as part of the temp
*/
attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME);
attrptr->length = cpu_to_le16(2 * dlen);
blobptr = (unsignedchar *)attrptr + sizeof(struct ntlmssp2_name);
cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp);
end = (u8 *)ses->auth_key.response + ses->auth_key.len; if (!av) { if (unlikely(!ses->auth_key.response || !ses->auth_key.len)) return NULL;
av = (void *)ses->auth_key.response;
} else {
av = (void *)((u8 *)av + sizeof(*av) + AV_LEN(av));
}
if ((u8 *)av + sizeof(*av) > end) return NULL;
len = AV_LEN(av); if (AV_TYPE(av) == NTLMSSP_AV_EOL) return NULL; if ((u8 *)av + sizeof(*av) + len > end) return NULL; return av;
}
/* * Check if server has provided av pair of @type in the NTLMSSP * CHALLENGE_MESSAGE blob.
*/ staticint find_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)
{ conststruct nls_table *nlsc = ses->local_nls; struct ntlmssp2_name *av;
u16 len, nlen;
if (*name) return 0;
av_for_each_entry(ses, av) {
len = AV_LEN(av); if (AV_TYPE(av) != type || !len) continue; if (!IS_ALIGNED(len, sizeof(__le16))) {
cifs_dbg(VFS | ONCE, "%s: bad length(%u) for type %u\n",
__func__, len, type); continue;
}
nlen = len / sizeof(__le16); if (nlen <= maxlen) {
++nlen;
*name = kmalloc(nlen, GFP_KERNEL); if (!*name) return -ENOMEM;
cifs_from_utf16(*name, AV_DATA_PTR(av), nlen,
len, nlsc, NO_MAP_UNI_RSVD); break;
}
} return 0;
}
/* Server has provided av pairs/target info in the type 2 challenge * packet and we have plucked it and stored within smb session. * We parse that blob here to find the server given timestamp * as part of ntlmv2 authentication (or local current time as * default in case of failure)
*/ static __le64 find_timestamp(struct cifs_ses *ses)
{ struct ntlmssp2_name *av; struct timespec64 ts;
/* calculate md4 hash of password */
E_md4hash(ses->password, nt_hash, nls_cp);
rc = crypto_shash_setkey(hmacmd5->tfm, nt_hash, CIFS_NTHASH_SIZE); if (rc) {
cifs_dbg(VFS, "%s: Could not set NT hash as a key, rc=%d\n", __func__, rc); return rc;
}
rc = crypto_shash_init(hmacmd5); if (rc) {
cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc); return rc;
}
/* convert ses->user_name to unicode */
len = ses->user_name ? strlen(ses->user_name) : 0;
user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) return -ENOMEM;
if (len) {
len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp);
UniStrupr(user);
} else {
*(u16 *)user = 0;
}
rc = crypto_shash_update(hmacmd5, (char *)user, 2 * len);
kfree(user); if (rc) {
cifs_dbg(VFS, "%s: Could not update with user, rc=%d\n", __func__, rc); return rc;
}
/* convert ses->domainName to unicode and uppercase */ if (ses->domainName) {
len = strlen(ses->domainName);
len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
nls_cp);
rc = crypto_shash_update(hmacmd5, (char *)domain, 2 * len);
kfree(domain); if (rc) {
cifs_dbg(VFS, "%s: Could not update with domain, rc=%d\n", __func__, rc); return rc;
}
} else { /* We use ses->ip_addr if no domain name available */
len = strlen(ses->ip_addr);
server = kmalloc(2 + (len * 2), GFP_KERNEL); if (server == NULL) return -ENOMEM;
len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, nls_cp);
rc = crypto_shash_update(hmacmd5, (char *)server, 2 * len);
kfree(server); if (rc) {
cifs_dbg(VFS, "%s: Could not update with server, rc=%d\n", __func__, rc); return rc;
}
}
rc = crypto_shash_final(hmacmd5, ntlmv2_hash); if (rc)
cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
/* The MD5 hash starts at challenge_key.key */
hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE +
offsetof(struct ntlmv2_resp, challenge.key[0]));
rc = crypto_shash_setkey(hmacmd5->tfm, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); if (rc) {
cifs_dbg(VFS, "%s: Could not set NTLMv2 hash as a key, rc=%d\n", __func__, rc); return rc;
}
rc = crypto_shash_init(hmacmd5); if (rc) {
cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc); return rc;
}
rc = crypto_shash_update(hmacmd5, ntlmv2->challenge.key, hash_len); if (rc) {
cifs_dbg(VFS, "%s: Could not update with response, rc=%d\n", __func__, rc); return rc;
}
/* Note that the MD5 digest over writes anon.challenge_key.key */
rc = crypto_shash_final(hmacmd5, ntlmv2->ntlmv2_hash); if (rc)
cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
return rc;
}
/* * Set up NTLMv2 response blob with SPN (cifs/<hostname>) appended to the * existing list of AV pairs.
*/ staticint set_auth_key_response(struct cifs_ses *ses)
{
size_t baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
size_t len, spnlen, tilen = 0, num_avs = 2 /* SPN + EOL */; struct TCP_Server_Info *server = ses->server; char *spn __free(kfree) = NULL; struct ntlmssp2_name *av; char *rsp = NULL; int rc;
if (nls_cp == NULL) {
cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__); return -EINVAL;
}
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { if (!ses->domainName) { if (ses->domainAuto) { /* * Domain (workgroup) hasn't been specified in * mount options, so try to find it in * CHALLENGE_MESSAGE message and then use it as * part of NTLMv2 authentication.
*/
rc = find_av_name(ses, NTLMSSP_AV_NB_DOMAIN_NAME,
&ses->domainName,
CIFS_MAX_DOMAINNAME_LEN); if (rc) goto setup_ntlmv2_rsp_ret;
} else {
ses->domainName = kstrdup("", GFP_KERNEL); if (!ses->domainName) {
rc = -ENOMEM; goto setup_ntlmv2_rsp_ret;
}
}
}
rc = find_av_name(ses, NTLMSSP_AV_DNS_DOMAIN_NAME,
&ses->dns_dom, CIFS_MAX_DOMAINNAME_LEN); if (rc) goto setup_ntlmv2_rsp_ret;
} else {
rc = build_avpair_blob(ses, nls_cp); if (rc) {
cifs_dbg(VFS, "error %d building av pair blob\n", rc); goto setup_ntlmv2_rsp_ret;
}
}
/* Must be within 5 minutes of the server (or in range +/-2h * in case of Mac OS X), so simply carry over server timestamp * (as Windows 7 does)
*/
rsp_timestamp = find_timestamp(ses);
get_random_bytes(&cc, sizeof(cc));
rc = cifs_alloc_hash("hmac(md5)", &hmacmd5); if (rc) {
cifs_dbg(VFS, "Could not allocate HMAC-MD5, rc=%d\n", rc); goto unlock;
}
/* calculate ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp, hmacmd5); if (rc) {
cifs_dbg(VFS, "Could not get NTLMv2 hash, rc=%d\n", rc); goto unlock;
}
/* calculate first part of the client response (CR1) */
rc = CalcNTLMv2_response(ses, ntlmv2_hash, hmacmd5); if (rc) {
cifs_dbg(VFS, "Could not calculate CR1, rc=%d\n", rc); goto unlock;
}
/* now calculate the session key for NTLMv2 */
rc = crypto_shash_setkey(hmacmd5->tfm, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); if (rc) {
cifs_dbg(VFS, "%s: Could not set NTLMv2 hash as a key, rc=%d\n", __func__, rc); goto unlock;
}
rc = crypto_shash_init(hmacmd5); if (rc) {
cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc); goto unlock;
}
rc = crypto_shash_update(hmacmd5, ntlmv2->ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); if (rc) {
cifs_dbg(VFS, "%s: Could not update with response, rc=%d\n", __func__, rc); goto unlock;
}
rc = crypto_shash_final(hmacmd5, ses->auth_key.response); if (rc)
cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
unlock:
cifs_server_unlock(ses->server);
cifs_free_hash(&hmacmd5);
setup_ntlmv2_rsp_ret:
kfree_sensitive(tiblob);
return rc;
}
int
calc_seckey(struct cifs_ses *ses)
{ unsignedchar sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ struct arc4_ctx *ctx_arc4;
if (fips_enabled) return -ENODEV;
get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE);
ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); if (!ctx_arc4) {
cifs_dbg(VFS, "Could not allocate arc4 context\n"); return -ENOMEM;
}
/* make secondary_key/nonce as session key */
memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); /* and make len as that of session key only */
ses->auth_key.len = CIFS_SESS_KEY_SIZE;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.