Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 29 Oct 2010 17:37:27 +0000 (10:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 29 Oct 2010 17:37:27 +0000 (10:37 -0700)
* 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

14 files changed:
fs/cifs/Kconfig
fs/cifs/cifsencrypt.c
fs/cifs/cifsfs.c
fs/cifs/cifsfs.h
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/misc.c
fs/cifs/sess.c
fs/cifs/transport.c

index 917b7d449bb2a6248c28c23284cf82d9c032f285..0ed213970cede228e097a04eec99c94ce97a6146 100644 (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
index 7ac0056294cf65357d6c45b0c3409e69d4110931..f856732161aba416234842ed8d5a496df6336823 100644 (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);
+       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;
 }
index 8bd5c2c243a5829fe869856ce2ed7efdb1d7395f..75c4eaa7958821483c6829658ab6469a8ad8c152 100644 (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 */
index f35795a16b425b4c3b6fa84c6c9ff9030338a239..897b2b2b28b539cb0c74847a1690d6e84929ab84 100644 (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 */
index 3365e77f6f240825a68ff3f577c11e064d691620..f259e4d7612d5f2a50f70f6b65802b4fff01621c 100644 (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
index b0f4b5656d4c2008379f720194e2a294350a06bc..de36b09763a84e7fb6016b93222fc74ee263d540 100644 (file)
  */
 #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
index e593c40ba7baf925f447f9d246f663fcc1c498aa..edb6d90efdf27ac2272c961c7269fbbfab2c0aa7 100644 (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);
index e98f1f317b159f56fe39f7dda35a9ab15881e32d..2f2632b6df5a1efd960bf44502d280e0e9499908 100644 (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)) {
index 7e73176acb58d700a73ef41de51f1aec45a8b945..9eb327defa1d39fc16d1c8f0837de36a65e43c92 100644 (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;
 }
 
index 45af003865d2607da7eb0f9ffdc43eed18f82b4d..ae82159cf7fa6921bb3a27a1fc5e56fbb1045a5c 100644 (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 @@ 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);
index 94979309698a1a2459c765153d80277886826c74..39869c3c3efbc2a08b87ba6661e48ace7e9789eb 100644 (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);
index 1c681f6a68039ce14f2ac832c90eb17226198247..c4e296fe351876252a48210f81f6cbae165562f8 100644 (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))
index 2a11efd965928964adbedf57e8eb3dd6a33b2830..7b01d3f6eed6e76cff6317f16c43b366c1af595d 100644 (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 @@ 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;
 
index a66c91eb6eb4e13cdc611786ad44dae2172ede1a..e0588cdf4cc5d5a1e8a73c1190c21f2d6cbe4986 100644 (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");