diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-29 10:37:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-29 10:37:27 -0700 |
commit | b4020c1b198c0f0c0b0ff0cfdd824a26b93edd6f (patch) | |
tree | f2d0dcca7bae865606e90ffc2599c993da1a888f /fs | |
parent | 435f49a518c78eec8e2edbbadd912737246cbe20 (diff) | |
parent | d3686d54c7902a303bd65d751226aa1647319863 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
cifs: Cleanup and thus reduce smb session structure and fields used during authentication
NTLM auth and sign - Use appropriate server challenge
cifs: add kfree() on error path
NTLM auth and sign - minor error corrections and cleanup
NTLM auth and sign - Use kernel crypto apis to calculate hashes and smb signatures
NTLM auth and sign - Define crypto hash functions and create and send keys needed for key exchange
cifs: cifs_convert_address() returns zero on error
NTLM auth and sign - Allocate session key/client response dynamically
cifs: update comments - [s/GlobalSMBSesLock/cifs_file_list_lock/g]
cifs: eliminate cifsInodeInfo->write_behind_rc (try #6)
[CIFS] Fix checkpatch warnings and bump cifs version number
cifs: wait for writeback to complete in cifs_flush
cifs: convert cifsFileInfo->count to non-atomic counter
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/Kconfig | 3 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 427 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 1 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 55 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 13 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 14 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 4 | ||||
-rw-r--r-- | fs/cifs/connect.c | 51 | ||||
-rw-r--r-- | fs/cifs/file.c | 57 | ||||
-rw-r--r-- | fs/cifs/inode.c | 15 | ||||
-rw-r--r-- | fs/cifs/misc.c | 2 | ||||
-rw-r--r-- | fs/cifs/sess.c | 166 | ||||
-rw-r--r-- | fs/cifs/transport.c | 6 |
14 files changed, 526 insertions, 290 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 917b7d449bb..0ed213970ce 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -2,6 +2,9 @@ config CIFS tristate "CIFS support (advanced network filesystem, SMBFS successor)" depends on INET select NLS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_ARC4 help This is the client VFS module for the Common Internet File System (CIFS) protocol which is the successor to the Server Message Block diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 7ac0056294c..f856732161a 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -43,18 +43,32 @@ extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24); static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, - const struct session_key *key, char *signature) + struct TCP_Server_Info *server, char *signature) { - struct MD5Context context; + int rc; - if ((cifs_pdu == NULL) || (signature == NULL) || (key == NULL)) + if (cifs_pdu == NULL || signature == NULL || server == NULL) return -EINVAL; - cifs_MD5_init(&context); - cifs_MD5_update(&context, (char *)&key->data, key->len); - cifs_MD5_update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length); + if (!server->secmech.sdescmd5) { + cERROR(1, "%s: Can't generate signature\n", __func__); + return -1; + } + + rc = crypto_shash_init(&server->secmech.sdescmd5->shash); + if (rc) { + cERROR(1, "%s: Oould not init md5\n", __func__); + return rc; + } + + crypto_shash_update(&server->secmech.sdescmd5->shash, + server->session_key.response, server->session_key.len); + + crypto_shash_update(&server->secmech.sdescmd5->shash, + cifs_pdu->Protocol, cifs_pdu->smb_buf_length); + + rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); - cifs_MD5_final(signature, &context); return 0; } @@ -79,8 +93,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, server->sequence_number++; spin_unlock(&GlobalMid_Lock); - rc = cifs_calculate_signature(cifs_pdu, &server->session_key, - smb_signature); + rc = cifs_calculate_signature(cifs_pdu, server, smb_signature); if (rc) memset(cifs_pdu->Signature.SecuritySignature, 0, 8); else @@ -90,16 +103,28 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, } static int cifs_calc_signature2(const struct kvec *iov, int n_vec, - const struct session_key *key, char *signature) + struct TCP_Server_Info *server, char *signature) { - struct MD5Context context; int i; + int rc; - if ((iov == NULL) || (signature == NULL) || (key == NULL)) + if (iov == NULL || signature == NULL || server == NULL) return -EINVAL; - cifs_MD5_init(&context); - cifs_MD5_update(&context, (char *)&key->data, key->len); + if (!server->secmech.sdescmd5) { + cERROR(1, "%s: Can't generate signature\n", __func__); + return -1; + } + + rc = crypto_shash_init(&server->secmech.sdescmd5->shash); + if (rc) { + cERROR(1, "%s: Oould not init md5\n", __func__); + return rc; + } + + crypto_shash_update(&server->secmech.sdescmd5->shash, + server->session_key.response, server->session_key.len); + for (i = 0; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; @@ -112,18 +137,18 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, if (i == 0) { if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ break; /* nothing to sign or corrupt header */ - cifs_MD5_update(&context, iov[0].iov_base+4, - iov[0].iov_len-4); + crypto_shash_update(&server->secmech.sdescmd5->shash, + iov[i].iov_base + 4, iov[i].iov_len - 4); } else - cifs_MD5_update(&context, iov[i].iov_base, iov[i].iov_len); + crypto_shash_update(&server->secmech.sdescmd5->shash, + iov[i].iov_base, iov[i].iov_len); } - cifs_MD5_final(signature, &context); + rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); - return 0; + return rc; } - int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, __u32 *pexpected_response_sequence_number) { @@ -146,8 +171,7 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, server->sequence_number++; spin_unlock(&GlobalMid_Lock); - rc = cifs_calc_signature2(iov, n_vec, &server->session_key, - smb_signature); + rc = cifs_calc_signature2(iov, n_vec, server, smb_signature); if (rc) memset(cifs_pdu->Signature.SecuritySignature, 0, 8); else @@ -157,14 +181,14 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, } int cifs_verify_signature(struct smb_hdr *cifs_pdu, - const struct session_key *session_key, + struct TCP_Server_Info *server, __u32 expected_sequence_number) { unsigned int rc; char server_response_sig[8]; char what_we_think_sig_should_be[20]; - if (cifs_pdu == NULL || session_key == NULL) + if (cifs_pdu == NULL || server == NULL) return -EINVAL; if (cifs_pdu->Command == SMB_COM_NEGOTIATE) @@ -193,7 +217,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, cpu_to_le32(expected_sequence_number); cifs_pdu->Signature.Sequence.Reserved = 0; - rc = cifs_calculate_signature(cifs_pdu, session_key, + rc = cifs_calculate_signature(cifs_pdu, server, what_we_think_sig_should_be); if (rc) @@ -209,18 +233,28 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, } -/* We fill in key by putting in 40 byte array which was allocated by caller */ -int cifs_calculate_session_key(struct session_key *key, const char *rn, - const char *password) +/* first calculate 24 bytes ntlm response and then 16 byte session key */ +int setup_ntlm_response(struct cifsSesInfo *ses) { - char temp_key[16]; - if ((key == NULL) || (rn == NULL)) + unsigned int temp_len = CIFS_SESS_KEY_SIZE + CIFS_AUTH_RESP_SIZE; + char temp_key[CIFS_SESS_KEY_SIZE]; + + if (!ses) return -EINVAL; - E_md4hash(password, temp_key); - mdfour(key->data.ntlm, temp_key, 16); - memcpy(key->data.ntlm+16, rn, CIFS_SESS_KEY_SIZE); - key->len = 40; + ses->auth_key.response = kmalloc(temp_len, GFP_KERNEL); + if (!ses->auth_key.response) { + cERROR(1, "NTLM can't allocate (%u bytes) memory", temp_len); + return -ENOMEM; + } + ses->auth_key.len = temp_len; + + SMBNTencrypt(ses->password, ses->server->cryptkey, + ses->auth_key.response + CIFS_SESS_KEY_SIZE); + + E_md4hash(ses->password, temp_key); + mdfour(ses->auth_key.response, temp_key, CIFS_SESS_KEY_SIZE); + return 0; } @@ -294,15 +328,15 @@ build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp) * two times the unicode length of a server name + * size of a timestamp (which is 8 bytes). */ - ses->tilen = size + 2 * (2 * dlen) + 2 * (2 * wlen) + 8; - ses->tiblob = kzalloc(ses->tilen, GFP_KERNEL); - if (!ses->tiblob) { - ses->tilen = 0; + 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->tiblob; + blobptr = ses->auth_key.response; attrptr = (struct ntlmssp2_name *) blobptr; attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); @@ -357,7 +391,7 @@ build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp) * about target string i.e. for some, just user name might suffice. */ static int -find_domain_name(struct cifsSesInfo *ses) +find_domain_name(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { unsigned int attrsize; unsigned int type; @@ -366,11 +400,11 @@ find_domain_name(struct cifsSesInfo *ses) unsigned char *blobend; struct ntlmssp2_name *attrptr; - if (!ses->tilen || !ses->tiblob) + if (!ses->auth_key.len || !ses->auth_key.response) return 0; - blobptr = ses->tiblob; - blobend = ses->tiblob + ses->tilen; + blobptr = ses->auth_key.response; + blobend = blobptr + ses->auth_key.len; while (blobptr + onesize < blobend) { attrptr = (struct ntlmssp2_name *) blobptr; @@ -386,16 +420,13 @@ find_domain_name(struct cifsSesInfo *ses) if (!attrsize) break; if (!ses->domainName) { - struct nls_table *default_nls; ses->domainName = kmalloc(attrsize + 1, GFP_KERNEL); if (!ses->domainName) return -ENOMEM; - default_nls = load_nls_default(); cifs_from_ucs2(ses->domainName, (__le16 *)blobptr, attrsize, attrsize, - default_nls, false); - unload_nls(default_nls); + nls_cp, false); break; } } @@ -405,82 +436,136 @@ find_domain_name(struct cifsSesInfo *ses) return 0; } -static int calc_ntlmv2_hash(struct cifsSesInfo *ses, +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[16]; - struct HMACMD5Context *pctxt; + char nt_hash[CIFS_NTHASH_SIZE]; wchar_t *user; wchar_t *domain; + wchar_t *server; - pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL); - - if (pctxt == NULL) - return -ENOMEM; + 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); - /* convert Domainname to unicode and uppercase */ - hmac_md5_init_limK_to_64(nt_hash, 16, pctxt); + 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) + 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); - hmac_md5_update((char *)user, 2*len, pctxt); + + 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) + 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); - /* 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); - + 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: - /* BB FIXME what about bytes 24 through 40 of the signing key? - compare with the NTLM example */ - hmac_md5_final(ses->ntlmv2_hash, pctxt); + return rc; +} + +static int +CalcNTLMv2_response(const struct cifsSesInfo *ses, char *ntlmv2_hash) +{ + int rc; + unsigned int offset = CIFS_SESS_KEY_SIZE + 8; + + if (!ses->server->secmech.sdeschmacmd5) { + cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); + return -1; + } + + crypto_shash_setkey(ses->server->secmech.hmacmd5, + ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + + rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); + if (rc) { + cERROR(1, "CalcNTLMv2_response: could not init hmacmd5"); + return rc; + } + + if (ses->server->secType == RawNTLMSSP) + memcpy(ses->auth_key.response + offset, + ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); + else + memcpy(ses->auth_key.response + offset, + ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); + crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, + ses->auth_key.response + offset, ses->auth_key.len - offset); + + rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, + ses->auth_key.response + CIFS_SESS_KEY_SIZE); - kfree(pctxt); return rc; } + int -setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, - const struct nls_table *nls_cp) +setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { int rc; - struct ntlmv2_resp *buf = (struct ntlmv2_resp *)resp_buf; - struct HMACMD5Context context; - - buf->blob_signature = cpu_to_le32(0x00000101); - buf->reserved = 0; - buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); - buf->reserved2 = 0; + int baselen; + unsigned int tilen; + struct ntlmv2_resp *buf; + char ntlmv2_hash[16]; + unsigned char *tiblob = NULL; /* target info blob */ if (ses->server->secType == RawNTLMSSP) { if (!ses->domainName) { - rc = find_domain_name(ses); + rc = find_domain_name(ses, nls_cp); if (rc) { cERROR(1, "error %d finding domain name", rc); goto setup_ntlmv2_rsp_ret; @@ -490,51 +575,179 @@ setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, rc = build_avpair_blob(ses, nls_cp); if (rc) { cERROR(1, "error %d building av pair blob", rc); - return rc; + goto setup_ntlmv2_rsp_ret; } } - /* calculate buf->ntlmv2_hash */ - rc = calc_ntlmv2_hash(ses, nls_cp); + baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp); + tilen = ses->auth_key.len; + tiblob = ses->auth_key.response; + + ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL); + if (!ses->auth_key.response) { + rc = ENOMEM; + ses->auth_key.len = 0; + cERROR(1, "%s: Can't allocate auth blob", __func__); + goto setup_ntlmv2_rsp_ret; + } + ses->auth_key.len += baselen; + + buf = (struct ntlmv2_resp *) + (ses->auth_key.response + CIFS_SESS_KEY_SIZE); + buf->blob_signature = cpu_to_le32(0x00000101); + buf->reserved = 0; + buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); + get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); + buf->reserved2 = 0; + + memcpy(ses->auth_key.response + baselen, tiblob, tilen); + + /* calculate ntlmv2_hash */ + rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); if (rc) { cERROR(1, "could not get v2 hash rc %d", rc); goto setup_ntlmv2_rsp_ret; } - CalcNTLMv2_response(ses, resp_buf); + + /* calculate first part of the client response (CR1) */ + rc = CalcNTLMv2_response(ses, ntlmv2_hash); + if (rc) { + cERROR(1, "Could not calculate CR1 rc: %d", rc); + goto setup_ntlmv2_rsp_ret; + } /* now calculate the session key for NTLMv2 */ - hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context); - hmac_md5_update(resp_buf, 16, &context); - hmac_md5_final(ses->auth_key.data.ntlmv2.key, &context); + crypto_shash_setkey(ses->server->secmech.hmacmd5, + ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + + rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); + if (rc) { + cERROR(1, "%s: Could not init hmacmd5\n", __func__); + goto setup_ntlmv2_rsp_ret; + } - memcpy(&ses->auth_key.data.ntlmv2.resp, resp_buf, - sizeof(struct ntlmv2_resp)); - ses->auth_key.len = 16 + sizeof(struct ntlmv2_resp); + crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, + ses->auth_key.response + CIFS_SESS_KEY_SIZE, + CIFS_HMAC_MD5_HASH_SIZE); - return 0; + rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, + ses->auth_key.response); setup_ntlmv2_rsp_ret: - kfree(ses->tiblob); - ses->tiblob = NULL; - ses->tilen = 0; + kfree(tiblob); return rc; } -void CalcNTLMv2_response(const struct cifsSesInfo *ses, - char *v2_session_response) +int +calc_seckey(struct cifsSesInfo *ses) { - struct HMACMD5Context context; - /* rest of v2 struct already generated */ - memcpy(v2_session_response + 8, ses->cryptKey, 8); - hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context); + int rc; + struct crypto_blkcipher *tfm_arc4; + struct scatterlist sgin, sgout; + struct blkcipher_desc desc; + unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ + + get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); + + tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); + if (!tfm_arc4 || IS_ERR(tfm_arc4)) { + cERROR(1, "could not allocate crypto API arc4\n"); + return PTR_ERR(tfm_arc4); + } - hmac_md5_update(v2_session_response+8, - sizeof(struct ntlmv2_resp) - 8, &context); + desc.tfm = tfm_arc4; - if (ses->tilen) - hmac_md5_update(ses->tiblob, ses->tilen, &context); + crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response, + CIFS_SESS_KEY_SIZE); - hmac_md5_final(v2_session_response, &context); -/* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */ + sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE); + sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); + + rc = crypto_blkcipher_encrypt(&desc, &sgout, &sgin, CIFS_CPHTXT_SIZE); + if (rc) { + cERROR(1, "could not encrypt session key rc: %d\n", rc); + crypto_free_blkcipher(tfm_arc4); + return rc; + } + + /* 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; + + crypto_free_blkcipher(tfm_arc4); + + return 0; +} + +void +cifs_crypto_shash_release(struct TCP_Server_Info *server) +{ + if (server->secmech.md5) + crypto_free_shash(server->secmech.md5); + + if (server->secmech.hmacmd5) + crypto_free_shash(server->secmech.hmacmd5); + + kfree(server->secmech.sdeschmacmd5); + + kfree(server->secmech.sdescmd5); +} + +int +cifs_crypto_shash_allocate(struct TCP_Server_Info *server) +{ + int rc; + unsigned int size; + + server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0); + if (!server->secmech.hmacmd5 || + IS_ERR(server->secmech.hmacmd5)) { + cERROR(1, "could not allocate crypto hmacmd5\n"); + return PTR_ERR(server->secmech.hmacmd5); + } + + server->secmech.md5 = crypto_alloc_shash("md5", 0, 0); + if (!server->secmech.md5 || IS_ERR(server->secmech.md5)) { + cERROR(1, "could not allocate crypto md5\n"); + rc = PTR_ERR(server->secmech.md5); + goto crypto_allocate_md5_fail; + } + + size = sizeof(struct shash_desc) + + crypto_shash_descsize(server->secmech.hmacmd5); + server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL); + if (!server->secmech.sdeschmacmd5) { + cERROR(1, "cifs_crypto_shash_allocate: can't alloc hmacmd5\n"); + rc = -ENOMEM; + goto crypto_allocate_hmacmd5_sdesc_fail; + } + server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5; + server->secmech.sdeschmacmd5->shash.flags = 0x0; + + + size = sizeof(struct shash_desc) + + crypto_shash_descsize(server->secmech.md5); + server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL); + if (!server->secmech.sdescmd5) { + cERROR(1, "cifs_crypto_shash_allocate: can't alloc md5\n"); + rc = -ENOMEM; + goto crypto_allocate_md5_sdesc_fail; + } + server->secmech.sdescmd5->shash.tfm = server->secmech.md5; + server->secmech.sdescmd5->shash.flags = 0x0; + + return 0; + +crypto_allocate_md5_sdesc_fail: + kfree(server->secmech.sdeschmacmd5); + +crypto_allocate_hmacmd5_sdesc_fail: + crypto_free_shash(server->secmech.md5); + +crypto_allocate_md5_fail: + crypto_free_shash(server->secmech.hmacmd5); + + return rc; } diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8bd5c2c243a..75c4eaa7958 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -318,7 +318,6 @@ cifs_alloc_inode(struct super_block *sb) return NULL; cifs_inode->cifsAttrs = 0x20; /* default */ cifs_inode->time = 0; - cifs_inode->write_behind_rc = 0; /* Until the file is open and we have gotten oplock info back from the server, can not assume caching of file data or metadata */ diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index f35795a16b4..897b2b2b28b 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -112,5 +112,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.67" +#define CIFS_VERSION "1.68" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 3365e77f6f2..f259e4d7612 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -25,6 +25,9 @@ #include <linux/workqueue.h> #include "cifs_fs_sb.h" #include "cifsacl.h" +#include <crypto/internal/hash.h> +#include <linux/scatterlist.h> + /* * The sizes of various internal tables and strings */ @@ -74,7 +77,7 @@ * CIFS vfs client Status information (based on what we know.) */ - /* associated with each tcp and smb session */ +/* associated with each tcp and smb session */ enum statusEnum { CifsNew = 0, CifsGood, @@ -99,14 +102,29 @@ enum protocolEnum { struct session_key { unsigned int len; - union { - char ntlm[CIFS_SESS_KEY_SIZE + 16]; - char krb5[CIFS_SESS_KEY_SIZE + 16]; /* BB: length correct? */ - struct { - char key[16]; - struct ntlmv2_resp resp; - } ntlmv2; - } data; + char *response; +}; + +/* crypto security descriptor definition */ +struct sdesc { + struct shash_desc shash; + char ctx[]; +}; + +/* crypto hashing related structure/fields, not specific to a sec mech */ +struct cifs_secmech { + struct crypto_shash *hmacmd5; /* hmac-md5 hash function */ + struct crypto_shash *md5; /* md5 hash function */ + struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */ + struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ +}; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */ + __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlmssp */ }; struct cifs_cred { @@ -179,12 +197,14 @@ struct TCP_Server_Info { int capabilities; /* allow selective disabling of caps by smb sess */ int timeAdj; /* Adjust for difference in server time zone in sec */ __u16 CurrentMid; /* multiplex id - rotating counter */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ struct session_key session_key; unsigned long lstrp; /* when we got last response from this server */ u16 dialect; /* dialect index that server chose */ + struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ /* extended security flavors that server supports */ bool sec_kerberos; /* supports plain Kerberos */ bool sec_mskerberos; /* supports legacy MS Kerberos */ @@ -222,11 +242,8 @@ struct cifsSesInfo { char userName[MAX_USERNAME_SIZE + 1]; char *domainName; char *password; - char cryptKey[CIFS_CRYPTO_KEY_SIZE]; struct session_key auth_key; - char ntlmv2_hash[16]; - unsigned int tilen; /* length of the target info blob */ - unsigned char *tiblob; /* target info blob in challenge response */ + struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ bool need_reconnect:1; /* connection reset, uid now invalid */ }; /* no more than one of the following three session flags may be set */ @@ -395,16 +412,19 @@ struct cifsFileInfo { struct list_head llist; /* list of byte range locks we have. */ bool invalidHandle:1; /* file closed via session abend */ bool oplock_break_cancelled:1; - atomic_t count; /* reference count */ + int count; /* refcount protected by cifs_file_list_lock */ struct mutex fh_mutex; /* prevents reopen race after dead ses*/ struct cifs_search_info srch_inf; struct work_struct oplock_break; /* work for oplock breaks */ }; -/* Take a reference on the file private data */ +/* + * Take a reference on the file private data. Must be called with + * cifs_file_list_lock held. + */ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) { - atomic_inc(&cifs_file->count); + ++cifs_file->count; } void cifsFileInfo_put(struct cifsFileInfo *cifs_file); @@ -417,7 +437,6 @@ struct cifsInodeInfo { struct list_head lockList; /* BB add in lists for dirty pages i.e. write caching info for oplock */ struct list_head openFileList; - int write_behind_rc; __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ unsigned long time; /* jiffies of last update/check of inode */ bool clientCanCacheRead:1; /* read oplock */ @@ -668,7 +687,7 @@ require use of the stronger protocol */ * GlobalMid_Lock protects: * list operations on pending_mid_q and oplockQ * updates to XID counters, multiplex id and SMB sequence numbers - * GlobalSMBSesLock protects: + * cifs_file_list_lock protects: * list operations on tcp and SMB session lists and tCon lists * f_owner.lock protects certain per file struct operations * mapping->page_lock protects certain per page operations diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index b0f4b5656d4..de36b09763a 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -131,9 +131,20 @@ #define CIFS_CRYPTO_KEY_SIZE (8) /* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE (24) + +/* * Size of the session key (crypto key encrypted with the password */ -#define CIFS_SESS_KEY_SIZE (24) +#define CIFS_SESS_KEY_SIZE (16) + +#define CIFS_CLIENT_CHALLENGE_SIZE (8) +#define CIFS_SERVER_CHALLENGE_SIZE (8) +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) +#define CIFS_NTHASH_SIZE (16) /* * Maximum user name length diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index e593c40ba7b..edb6d90efdf 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -362,13 +362,15 @@ extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *, __u32 *); extern int cifs_verify_signature(struct smb_hdr *, - const struct session_key *session_key, + struct TCP_Server_Info *server, __u32 expected_sequence_number); -extern int cifs_calculate_session_key(struct session_key *key, const char *rn, - const char *pass); -extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *); -extern int setup_ntlmv2_rsp(struct cifsSesInfo *, char *, - const struct nls_table *); +extern void SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *); +extern int setup_ntlm_response(struct cifsSesInfo *); +extern int setup_ntlmv2_rsp(struct cifsSesInfo *, const struct nls_table *); +extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); +extern void cifs_crypto_shash_release(struct TCP_Server_Info *); +extern int calc_seckey(struct cifsSesInfo *); + #ifdef CONFIG_CIFS_WEAK_PW_HASH extern void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, char *lnm_session_key); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index e98f1f317b1..2f2632b6df5 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -503,7 +503,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) if (rsp->EncryptionKeyLength == cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { - memcpy(ses->cryptKey, rsp->EncryptionKey, + memcpy(ses->server->cryptkey, rsp->EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if (server->secMode & SECMODE_PW_ENCRYPT) { rc = -EIO; /* need cryptkey unless plain text */ @@ -574,7 +574,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj *= 60; if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { - memcpy(ses->cryptKey, pSMBr->u.EncryptionKey, + memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && (pSMBr->EncryptionKeyLength == 0)) { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 7e73176acb5..9eb327defa1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -175,6 +175,9 @@ cifs_reconnect(struct TCP_Server_Info *server) } server->sequence_number = 0; server->session_estab = false; + kfree(server->session_key.response); + server->session_key.response = NULL; + server->session_key.len = 0; spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { @@ -1064,7 +1067,7 @@ cifs_parse_mount_options(char *options, const char *devname, } i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, value, strlen(value)); - if (i < 0) { + if (i == 0) { printk(KERN_WARNING "CIFS: Could not parse" " srcaddr: %s\n", value); @@ -1560,8 +1563,13 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); + cifs_crypto_shash_release(server); cifs_fscache_release_client_cookie(server); + kfree(server->session_key.response); + server->session_key.response = NULL; + server->session_key.len = 0; + task = xchg(&server->tsk, NULL); if (task) force_sig(SIGKILL, task); @@ -1614,10 +1622,16 @@ cifs_get_tcp_session(struct smb_vol *volume_info) goto out_err; } + rc = cifs_crypto_shash_allocate(tcp_ses); + if (rc) { + cERROR(1, "could not setup hash structures rc %d", rc); + goto out_err; + } + tcp_ses->hostname = extract_hostname(volume_info->UNC); if (IS_ERR(tcp_ses->hostname)) { rc = PTR_ERR(tcp_ses->hostname); - goto out_err; + goto out_err_crypto_release; } tcp_ses->noblocksnd = volume_info->noblocksnd; @@ -1661,7 +1675,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) } if (rc < 0) { cERROR(1, "Error connecting to socket. Aborting operation"); - goto out_err; + goto out_err_crypto_release; } /* @@ -1675,7 +1689,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) rc = PTR_ERR(tcp_ses->tsk); cERROR(1, "error %d create cifsd thread", rc); module_put(THIS_MODULE); - goto out_err; + goto out_err_crypto_release; } /* thread spawned, put it on the list */ @@ -1687,6 +1701,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) return tcp_ses; +out_err_crypto_release: + cifs_crypto_shash_release(tcp_ses); + out_err: if (tcp_ses) { if (!IS_ERR(tcp_ses->hostname)) @@ -1801,8 +1818,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) if (ses == NULL) goto get_ses_fail; - ses->tilen = 0; - ses->tiblob = NULL; /* new SMB session uses our server ref */ ses->server = server; if (server->addr.sockAddr6.sin6_family == AF_INET6) @@ -1823,10 +1838,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) goto get_ses_fail; } if (volume_info->domainname) { - int len = strlen(volume_info->domainname); - ses->domainName = kmalloc(len + 1, GFP_KERNEL); - if (ses->domainName) - strcpy(ses->domainName, volume_info->domainname); + ses->domainName = kstrdup(volume_info->domainname, GFP_KERNEL); + if (!ses->domainName) + goto get_ses_fail; } ses->cred_uid = volume_info->cred_uid; ses->linux_uid = volume_info->linux_uid; @@ -2985,13 +2999,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((global_secflags & CIFSSEC_MAY_LANMAN) && (ses->server->secType == LANMAN)) - calc_lanman_hash(tcon->password, ses->cryptKey, + calc_lanman_hash(tcon->password, ses->server->cryptkey, ses->server->secMode & SECMODE_PW_ENCRYPT ? true : false, bcc_ptr); else #endif /* CIFS_WEAK_PW_HASH */ - SMBNTencrypt(tcon->password, ses->cryptKey, bcc_ptr); + SMBNTencrypt(tcon->password, ses->server->cryptkey, bcc_ptr); bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { @@ -3178,10 +3192,11 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, } else { mutex_lock(&ses->server->srv_mutex); if (!server->session_estab) { - memcpy(&server->session_key.data, - &ses->auth_key.data, ses->auth_key.len); + server->session_key.response = ses->auth_key.response; server->session_key.len = ses->auth_key.len; - ses->server->session_estab = true; + server->sequence_number = 0x2; + server->session_estab = true; + ses->auth_key.response = NULL; } mutex_unlock(&server->srv_mutex); @@ -3192,6 +3207,12 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); } + kfree(ses->auth_key.response); + ses->auth_key.response = NULL; + ses->auth_key.len = 0; + kfree(ses->ntlmssp); + ses->ntlmssp = NULL; + return rc; } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 45af003865d..ae82159cf7f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -131,8 +131,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, /* BB no need to lock inode until after invalidate since namei code should already have it locked? */ rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) - pCifsInode->write_behind_rc = rc; + mapping_set_error(inode->i_mapping, rc); } cFYI(1, "invalidating remote inode since open detected it " "changed"); @@ -232,6 +231,7 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, if (pCifsFile == NULL) return pCifsFile; + pCifsFile->count = 1; pCifsFile->netfid = fileHandle; pCifsFile->pid = current->tgid; pCifsFile->uid = current_fsuid(); @@ -242,7 +242,6 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, mutex_init(&pCifsFile->fh_mutex); mutex_init(&pCifsFile->lock_mutex); INIT_LIST_HEAD(&pCifsFile->llist); - atomic_set(&pCifsFile->count, 1); INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); spin_lock(&cifs_file_list_lock); @@ -267,7 +266,8 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, /* * Release a reference on the file private data. This may involve closing - * the filehandle out on the server. + * the filehandle out on the server. Must be called without holding + * cifs_file_list_lock. */ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) { @@ -276,7 +276,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) struct cifsLockInfo *li, *tmp; spin_lock(&cifs_file_list_lock); - if (!atomic_dec_and_test(&cifs_file->count)) { + if (--cifs_file->count > 0) { spin_unlock(&cifs_file_list_lock); return; } @@ -605,8 +605,7 @@ reopen_success: if (can_flush) { rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) - CIFS_I(inode)->write_behind_rc = rc; + mapping_set_error(inode->i_mapping, rc); pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheRead = false; @@ -1353,6 +1352,7 @@ static int cifs_writepages(struct address_space *mapping, if (!experimEnabled && tcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { cifsFileInfo_put(open_file); + kfree(iov); return generic_writepages(mapping, wbc); } cifsFileInfo_put(open_file); @@ -1478,12 +1478,7 @@ retry: if (rc || bytes_written < bytes_to_write) { cERROR(1, "Write2 ret %d, wrote %d", rc, bytes_written); - /* BB what if continued retry is - requested via mount flags? */ - if (rc == -ENOSPC) - set_bit(AS_ENOSPC, &mapping->flags); - else - set_bit(AS_EIO, &mapping->flags); + mapping_set_error(mapping, rc); } else { cifs_stats_bytes_written(tcon, bytes_written); } @@ -1628,11 +1623,10 @@ int cifs_fsync(struct file *file, int datasync) rc = filemap_write_and_wait(inode->i_mapping); if (rc == 0) { - rc = CIFS_I(inode)->write_behind_rc; - CIFS_I(inode)->write_behind_rc = 0; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + tcon = tlink_tcon(smbfile->tlink); - if (!rc && tcon && smbfile && - !(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); } @@ -1677,21 +1671,8 @@ int cifs_flush(struct file *file, fl_owner_t id) struct inode *inode = file->f_path.dentry->d_inode; int rc = 0; - /* Rather than do the steps manually: - lock the inode for writing - loop through pages looking for write behind data (dirty pages) - coalesce into contiguous 16K (or smaller) chunks to write to server - send to server (prefer in parallel) - deal with writebehind errors - unlock inode for writing - filemapfdatawrite appears easier for the time being */ - - rc = filemap_fdatawrite(inode->i_mapping); - /* reset wb rc if we were able to write out dirty pages */ - if (!rc) { - rc = CIFS_I(inode)->write_behind_rc; - CIFS_I(inode)->write_behind_rc = 0; - } + if (file->f_mode & FMODE_WRITE) + rc = filemap_write_and_wait(inode->i_mapping); cFYI(1, "Flush inode %p file %p rc %d", inode, file, rc); @@ -2270,7 +2251,7 @@ void cifs_oplock_break(struct work_struct *work) oplock_break); struct inode *inode = cfile->dentry->d_inode; struct cifsInodeInfo *cinode = CIFS_I(inode); - int rc, waitrc = 0; + int rc = 0; if (inode && S_ISREG(inode->i_mode)) { if (cinode->clientCanCacheRead) @@ -2279,13 +2260,10 @@ void cifs_oplock_break(struct work_struct *work) break_lease(inode, O_WRONLY); rc = filemap_fdatawrite(inode->i_mapping); if (cinode->clientCanCacheRead == 0) { - waitrc = filemap_fdatawait(inode->i_mapping); + rc = filemap_fdatawait(inode->i_mapping); + mapping_set_error(inode->i_mapping, rc); invalidate_remote_inode(inode); } - if (!rc) - rc = waitrc; - if (rc) - cinode->write_behind_rc = rc; cFYI(1, "Oplock flush inode %p rc %d", inode, rc); } @@ -2304,7 +2282,7 @@ void cifs_oplock_break(struct work_struct *work) /* * We might have kicked in before is_valid_oplock_break() * finished grabbing reference for us. Make sure it's done by - * waiting for GlobalSMSSeslock. + * waiting for cifs_file_list_lock. */ spin_lock(&cifs_file_list_lock); spin_unlock(&cifs_file_list_lock); @@ -2312,6 +2290,7 @@ void cifs_oplock_break(struct work_struct *work) cifs_oplock_break_put(cfile); } +/* must be called while holding cifs_file_list_lock */ void cifs_oplock_break_get(struct cifsFileInfo *cfile) { cifs_sb_active(cfile->dentry->d_sb); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 94979309698..39869c3c3ef 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1682,8 +1682,7 @@ cifs_invalidate_mapping(struct inode *inode) /* write back any cached data */ if (inode->i_mapping && inode->i_mapping->nrpages != 0) { rc = filemap_write_and_wait(inode->i_mapping); - if (rc) - cifs_i->write_behind_rc = rc; + mapping_set_error(inode->i_mapping, rc); } invalidate_remote_inode(inode); cifs_fscache_reset_inode_cookie(inode); @@ -1943,10 +1942,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) * the flush returns error? */ rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) { - cifsInode->write_behind_rc = rc; - rc = 0; - } + mapping_set_error(inode->i_mapping, rc); + rc = 0; if (attrs->ia_valid & ATTR_SIZE) { rc = cifs_set_file_size(inode, attrs, xid, full_path); @@ -2087,10 +2084,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) * the flush returns error? */ rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) { - cifsInode->write_behind_rc = rc; - rc = 0; - } + mapping_set_error(inode->i_mapping, rc); + rc = 0; if (attrs->ia_valid & ATTR_SIZE) { rc = cifs_set_file_size(inode, attrs, xid, full_path); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 1c681f6a680..c4e296fe351 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -577,7 +577,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) * cifs_oplock_break_put() can't be called * from here. Get reference after queueing * succeeded. cifs_oplock_break() will - * synchronize using GlobalSMSSeslock. + * synchronize using cifs_file_list_lock. */ if (queue_work(system_nrt_wq, &netfile->oplock_break)) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 2a11efd9659..7b01d3f6eed 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -32,9 +32,6 @@ #include <linux/slab.h> #include "cifs_spnego.h" -extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, - unsigned char *p24); - /* * Checks if this is the first smb session to be reconnected after * the socket has been reestablished (so we know whether to use vc 0). @@ -402,23 +399,22 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, return -EINVAL; } - memcpy(ses->cryptKey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); + memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); /* BB we could decode pblob->NegotiateFlags; some may be useful */ /* In particular we can examine sign flags */ /* BB spec says that if AvId field of MsvAvTimestamp is populated then we must set the MIC field of the AUTHENTICATE_MESSAGE */ - + ses->ntlmssp->server_flags = le32_to_cpu(pblob->NegotiateFlags); tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset); tilen = cpu_to_le16(pblob->TargetInfoArray.Length); - ses->tilen = tilen; - if (ses->tilen) { - ses->tiblob = kmalloc(tilen, GFP_KERNEL); - if (!ses->tiblob) { + if (tilen) { + ses->auth_key.response = kmalloc(tilen, GFP_KERNEL); + if (!ses->auth_key.response) { cERROR(1, "Challenge target info allocation failure"); - ses->tilen = 0; return -ENOMEM; } - memcpy(ses->tiblob, bcc_ptr + tioffset, ses->tilen); + memcpy(ses->auth_key.response, bcc_ptr + tioffset, tilen); + ses->auth_key.len = tilen; } return 0; @@ -443,10 +439,12 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM; if (ses->server->secMode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { flags |= NTLMSSP_NEGOTIATE_SIGN; - if (ses->server->secMode & SECMODE_SIGN_REQUIRED) - flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + if (!ses->server->session_estab) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH | + NTLMSSP_NEGOTIATE_EXTENDED_SEC; + } sec_blob->NegotiateFlags |= cpu_to_le32(flags); @@ -469,11 +467,9 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, const struct nls_table *nls_cp) { int rc; - unsigned int size; AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; __u32 flags; unsigned char *tmp; - struct ntlmv2_resp ntlmv2_response = {}; memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); sec_blob->MessageType = NtLmAuthenticate; @@ -497,25 +493,19 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, sec_blob->LmChallengeResponse.MaximumLength = 0; sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); - rc = setup_ntlmv2_rsp(ses, (char *)&ntlmv2_response, nls_cp); + rc = setup_ntlmv2_rsp(ses, nls_cp); if (rc) { cERROR(1, "Error %d during NTLMSSP authentication", rc); goto setup_ntlmv2_ret; } - size = sizeof(struct ntlmv2_resp); - memcpy(tmp, (char *)&ntlmv2_response, size); - tmp += size; - if (ses->tilen > 0) { - memcpy(tmp, ses->tiblob, ses->tilen); - tmp += ses->tilen; - } + memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - sec_blob->NtChallengeResponse.Length = cpu_to_le16(size + ses->tilen); + sec_blob->NtChallengeResponse.Length = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); sec_blob->NtChallengeResponse.MaximumLength = - cpu_to_le16(size + ses->tilen); - kfree(ses->tiblob); - ses->tiblob = NULL; - ses->tilen = 0; + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); if (ses->domainName == NULL) { sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); @@ -554,9 +544,19 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, sec_blob->WorkstationName.MaximumLength = 0; tmp += 2; - sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); - sec_blob->SessionKey.Length = 0; - sec_blob->SessionKey.MaximumLength = 0; + if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && + !calc_seckey(ses)) { + memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); + sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); + sec_blob->SessionKey.MaximumLength = + cpu_to_le16(CIFS_CPHTXT_SIZE); + tmp += CIFS_CPHTXT_SIZE; + } else { + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); + sec_blob->SessionKey.Length = 0; + sec_blob->SessionKey.MaximumLength = 0; + } setup_ntlmv2_ret: *buflen = tmp - pbuffer; @@ -600,8 +600,16 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, return -EINVAL; type = ses->server->secType; - cFYI(1, "sess setup type %d", type); + if (type == RawNTLMSSP) { + /* if memory allocation is successful, caller of this function + * frees it. + */ + ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); + if (!ses->ntlmssp) + return -ENOMEM; + } + ssetup_ntlmssp_authenticate: if (phase == NtLmChallenge) phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ @@ -666,10 +674,14 @@ ssetup_ntlmssp_authenticate: /* no capabilities flags in old lanman negotiation */ pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); - /* BB calculate hash with password */ - /* and copy into bcc */ - calc_lanman_hash(ses->password, ses->cryptKey, + /* Calculate hash with password and copy into bcc_ptr. + * Encryption Key (stored as in cryptkey) gets used if the + * security mode bit in Negottiate Protocol response states + * to use challenge/response method (i.e. Password bit is 1). + */ + + calc_lanman_hash(ses->password, ses->server->cryptkey, ses->server->secMode & SECMODE_PW_ENCRYPT ? true : false, lnm_session_key); @@ -687,24 +699,27 @@ ssetup_ntlmssp_authenticate: ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); #endif } else if (type == NTLM) { - char ntlm_session_key[CIFS_SESS_KEY_SIZE]; - pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); pSMB->req_no_secext.CaseInsensitivePasswordLength = - cpu_to_le16(CIFS_SESS_KEY_SIZE); + cpu_to_le16(CIFS_AUTH_RESP_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(CIFS_SESS_KEY_SIZE); + cpu_to_le16(CIFS_AUTH_RESP_SIZE); + + /* calculate ntlm response and session key */ + rc = setup_ntlm_response(ses); + if (rc) { + cERROR(1, "Error %d during NTLM authentication", rc); + goto ssetup_exit; + } - /* calculate session key */ - SMBNTencrypt(ses->password, ses->cryptKey, ntlm_session_key); + /* copy ntlm response */ + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + CIFS_AUTH_RESP_SIZE); + bcc_ptr += CIFS_AUTH_RESP_SIZE; + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + CIFS_AUTH_RESP_SIZE); + bcc_ptr += CIFS_AUTH_RESP_SIZE; - cifs_calculate_session_key(&ses->auth_key, - ntlm_session_key, ses->password); - /* copy session key */ - memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); - bcc_ptr += CIFS_SESS_KEY_SIZE; - memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); - bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { /* unicode strings must be word aligned */ if (iov[0].iov_len % 2) { @@ -715,47 +730,26 @@ ssetup_ntlmssp_authenticate: } else ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); } else if (type == NTLMv2) { - char *v2_sess_key = - kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL); - - /* BB FIXME change all users of v2_sess_key to - struct ntlmv2_resp */ - - if (v2_sess_key == NULL) { - rc = -ENOMEM; - goto ssetup_exit; - } - pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); /* LM2 password would be here if we supported it */ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; - /* cpu_to_le16(LM2_SESS_KEY_SIZE); */ - /* calculate session key */ - rc = setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); + /* calculate nlmv2 response and session key */ + rc = setup_ntlmv2_rsp(ses, nls_cp); if (rc) { cERROR(1, "Error %d during NTLMv2 authentication", rc); - kfree(v2_sess_key); goto ssetup_exit; } - memcpy(bcc_ptr, (char *)v2_sess_key, - sizeof(struct ntlmv2_resp)); - bcc_ptr += sizeof(struct ntlmv2_resp); - kfree(v2_sess_key); + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + /* set case sensitive password length after tilen may get * assigned, tilen is 0 otherwise. */ pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(sizeof(struct ntlmv2_resp) + ses->tilen); - if (ses->tilen > 0) { - memcpy(bcc_ptr, ses->tiblob, ses->tilen); - bcc_ptr += ses->tilen; - /* we never did allocate ses->domainName to free */ - kfree(ses->tiblob); - ses->tiblob = NULL; - ses->tilen = 0; - } + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); if (ses->capabilities & CAP_UNICODE) { if (iov[0].iov_len % 2) { @@ -768,6 +762,7 @@ ssetup_ntlmssp_authenticate: } else if (type == Kerberos) { #ifdef CONFIG_CIFS_UPCALL struct cifs_spnego_msg *msg; + spnego_key = cifs_get_spnego_key(ses); if (IS_ERR(spnego_key)) { rc = PTR_ERR(spnego_key); @@ -785,16 +780,17 @@ ssetup_ntlmssp_authenticate: rc = -EKEYREJECTED; goto ssetup_exit; } - /* bail out if key is too long */ - if (msg->sesskey_len > - sizeof(ses->auth_key.data.krb5)) { - cERROR(1, "Kerberos signing key too long (%u bytes)", - msg->sesskey_len); - rc = -EOVERFLOW; + + ses->auth_key.response = kmalloc(msg->sesskey_len, GFP_KERNEL); + if (!ses->auth_key.response) { + cERROR(1, "Kerberos can't allocate (%u bytes) memory", + msg->sesskey_len); + rc = -ENOMEM; goto ssetup_exit; } + memcpy(ses->auth_key.response, msg->data, msg->sesskey_len); ses->auth_key.len = msg->sesskey_len; - memcpy(ses->auth_key.data.krb5, msg->data, msg->sesskey_len); + pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; capabilities |= CAP_EXTENDED_SECURITY; pSMB->req.Capabilities = cpu_to_le32(capabilities); @@ -897,8 +893,6 @@ ssetup_ntlmssp_authenticate: CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); /* SMB request buf freed in SendReceive2 */ - cFYI(1, "ssetup rc from sendrecv2 is %d", rc); - pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base; smb_buf = (struct smb_hdr *)iov[0].iov_base; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index a66c91eb6eb..e0588cdf4cc 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -543,7 +543,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(midQ->resp_buf, - &ses->server->session_key, + ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); @@ -731,7 +731,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, - &ses->server->session_key, + ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); @@ -981,7 +981,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, - &ses->server->session_key, + ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); |