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
This commit is contained in:
Linus Torvalds 2010-10-29 10:37:27 -07:00
commit b4020c1b19
14 changed files with 527 additions and 291 deletions

View file

@ -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

View file

@ -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);
kfree(pctxt);
return rc;
}
int
setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
const struct nls_table *nls_cp)
static int
CalcNTLMv2_response(const struct cifsSesInfo *ses, char *ntlmv2_hash)
{
int rc;
struct ntlmv2_resp *buf = (struct ntlmv2_resp *)resp_buf;
struct HMACMD5Context context;
unsigned int offset = CIFS_SESS_KEY_SIZE + 8;
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;
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);
return rc;
}
int
setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp)
{
int rc;
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);
memcpy(&ses->auth_key.data.ntlmv2.resp, resp_buf,
sizeof(struct ntlmv2_resp));
ses->auth_key.len = 16 + sizeof(struct ntlmv2_resp);
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;
}
return 0;
crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + CIFS_SESS_KEY_SIZE,
CIFS_HMAC_MD5_HASH_SIZE);
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 */
hmac_md5_update(v2_session_response+8,
sizeof(struct ntlmv2_resp) - 8, &context);
get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE);
if (ses->tilen)
hmac_md5_update(ses->tiblob, ses->tilen, &context);
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_final(v2_session_response, &context);
/* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */
desc.tfm = tfm_arc4;
crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response,
CIFS_SESS_KEY_SIZE);
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;
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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

View file

@ -130,10 +130,21 @@
*/
#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

View file

@ -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);

View file

@ -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)) {

View file

@ -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;
}

View file

@ -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 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
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 @@ static int cifs_writepages(struct address_space *mapping,
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);

View file

@ -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);

View file

@ -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))

View file

@ -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 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
/* 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 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
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 session key */
SMBNTencrypt(ses->password, ses->cryptKey, ntlm_session_key);
/* calculate ntlm response and session key */
rc = setup_ntlm_response(ses);
if (rc) {
cERROR(1, "Error %d during NTLM authentication", rc);
goto ssetup_exit;
}
/* 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 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
} 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 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
} 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 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
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 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
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;

View file

@ -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");