NTLM authentication and signing - Calculate auth response per smb session
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>
Wed, 13 Oct 2010 23:15:00 +0000 (18:15 -0500)
committerSteve French <sfrench@us.ibm.com>
Thu, 14 Oct 2010 18:05:19 +0000 (18:05 +0000)
Start calculation auth response within a session.  Move/Add pertinet
data structures like session key, server challenge and ntlmv2_hash in
a session structure.  We should do the calculations within a session
before copying session key and response over to server data
structures because a session setup can fail.

Only after a very first smb session succeeds, it copies/makes its
session key, session key of smb connection.  This key stays with
the smb connection throughout its life.

Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
fs/cifs/cifsencrypt.c
fs/cifs/cifsglob.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/sess.c

index e3edd8a..7ac0056 100644 (file)
@@ -458,7 +458,7 @@ calc_exit_1:
 calc_exit_2:
        /* BB FIXME what about bytes 24 through 40 of the signing key?
           compare with the NTLM example */
-       hmac_md5_final(ses->server->ntlmv2_hash, pctxt);
+       hmac_md5_final(ses->ntlmv2_hash, pctxt);
 
        kfree(pctxt);
        return rc;
@@ -502,14 +502,14 @@ setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
        }
        CalcNTLMv2_response(ses, resp_buf);
 
-       /* now calculate the MAC key for NTLMv2 */
-       hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
+       /* 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->server->session_key.data.ntlmv2.key, &context);
+       hmac_md5_final(ses->auth_key.data.ntlmv2.key, &context);
 
-       memcpy(&ses->server->session_key.data.ntlmv2.resp, resp_buf,
+       memcpy(&ses->auth_key.data.ntlmv2.resp, resp_buf,
               sizeof(struct ntlmv2_resp));
-       ses->server->session_key.len = 16 + sizeof(struct ntlmv2_resp);
+       ses->auth_key.len = 16 + sizeof(struct ntlmv2_resp);
 
        return 0;
 
@@ -526,8 +526,8 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses,
 {
        struct HMACMD5Context context;
        /* rest of v2 struct already generated */
-       memcpy(v2_session_response + 8, ses->server->cryptKey, 8);
-       hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
+       memcpy(v2_session_response + 8, ses->cryptKey, 8);
+       hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context);
 
        hmac_md5_update(v2_session_response+8,
                        sizeof(struct ntlmv2_resp) - 8, &context);
index e2b760e..6c69bd7 100644 (file)
@@ -179,12 +179,10 @@ 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];
        /* 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;
-       char ntlmv2_hash[16];
        unsigned long lstrp; /* when we got last response from this server */
        u16 dialect; /* dialect index that server chose */
        /* extended security flavors that server supports */
@@ -192,6 +190,7 @@ struct TCP_Server_Info {
        bool    sec_mskerberos;         /* supports legacy MS Kerberos */
        bool    sec_kerberosu2u;        /* supports U2U Kerberos */
        bool    sec_ntlmssp;            /* supports NTLMSSP */
+       bool session_estab; /* mark when very first sess is established */
 #ifdef CONFIG_CIFS_FSCACHE
        struct fscache_cookie   *fscache; /* client index cache cookie */
 #endif
@@ -223,6 +222,9 @@ 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 */
        bool need_reconnect:1; /* connection reset, uid now invalid */
index 54bd83a..a420c7b 100644 (file)
@@ -503,7 +503,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
 
                if (rsp->EncryptionKeyLength ==
                                cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
-                       memcpy(server->cryptKey, rsp->EncryptionKey,
+                       memcpy(ses->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(server->cryptKey, pSMBr->u.EncryptionKey,
+               memcpy(ses->cryptKey, pSMBr->u.EncryptionKey,
                       CIFS_CRYPTO_KEY_SIZE);
        } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
                        && (pSMBr->EncryptionKeyLength == 0)) {
index 4944fc8..019f003 100644 (file)
@@ -173,6 +173,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
                sock_release(server->ssocket);
                server->ssocket = NULL;
        }
+       server->sequence_number = 0;
+       server->session_estab = false;
 
        spin_lock(&GlobalMid_Lock);
        list_for_each(tmp, &server->pending_mid_q) {
@@ -205,7 +207,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
                        spin_lock(&GlobalMid_Lock);
                        if (server->tcpStatus != CifsExiting)
                                server->tcpStatus = CifsGood;
-                       server->sequence_number = 0;
                        spin_unlock(&GlobalMid_Lock);
        /*              atomic_set(&server->inFlight,0);*/
                        wake_up(&server->response_q);
@@ -1631,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
                volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
        memcpy(tcp_ses->server_RFC1001_name,
                volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+       tcp_ses->session_estab = false;
        tcp_ses->sequence_number = 0;
        INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
        INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
@@ -2983,14 +2985,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->server->cryptKey,
+                       calc_lanman_hash(tcon->password, ses->cryptKey,
                                         ses->server->secMode &
                                            SECMODE_PW_ENCRYPT ? true : false,
                                         bcc_ptr);
                else
 #endif /* CIFS_WEAK_PW_HASH */
-               SMBNTencrypt(tcon->password, ses->server->cryptKey,
-                            bcc_ptr);
+               SMBNTencrypt(tcon->password, ses->cryptKey, bcc_ptr);
 
                bcc_ptr += CIFS_SESS_KEY_SIZE;
                if (ses->capabilities & CAP_UNICODE) {
@@ -3175,6 +3176,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses,
        if (rc) {
                cERROR(1, "Send error in SessSetup = %d", rc);
        } 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.len = ses->auth_key.len;
+                       ses->server->session_estab = true;
+               }
+               mutex_unlock(&server->srv_mutex);
+
                cFYI(1, "CIFS Session Established successfully");
                spin_lock(&GlobalMid_Lock);
                ses->status = CifsGood;
index c926e6c..2111bed 100644 (file)
@@ -402,7 +402,7 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
                return -EINVAL;
        }
 
-       memcpy(ses->server->cryptKey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE);
+       memcpy(ses->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
@@ -591,17 +591,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
        int bytes_remaining;
        struct key *spnego_key = NULL;
        __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
-       bool first_time;
        int blob_len;
        char *ntlmsspblob = NULL;
 
        if (ses == NULL)
                return -EINVAL;
 
-       read_lock(&cifs_tcp_ses_lock);
-       first_time = is_first_ses_reconnect(ses);
-       read_unlock(&cifs_tcp_ses_lock);
-
        type = ses->server->secType;
 
        cFYI(1, "sess setup type %d", type);
@@ -672,7 +667,7 @@ ssetup_ntlmssp_authenticate:
                /* BB calculate hash with password */
                /* and copy into bcc */
 
-               calc_lanman_hash(ses->password, ses->server->cryptKey,
+               calc_lanman_hash(ses->password, ses->cryptKey,
                                 ses->server->secMode & SECMODE_PW_ENCRYPT ?
                                        true : false, lnm_session_key);
 
@@ -699,15 +694,11 @@ ssetup_ntlmssp_authenticate:
                        cpu_to_le16(CIFS_SESS_KEY_SIZE);
 
                /* calculate session key */
-               SMBNTencrypt(ses->password, ses->server->cryptKey,
-                            ntlm_session_key);
+               SMBNTencrypt(ses->password, ses->cryptKey, ntlm_session_key);
 
-               if (first_time) /* should this be moved into common code
-                                 with similar ntlmv2 path? */
-                       cifs_calculate_session_key(&ses->server->session_key,
-                               ntlm_session_key, ses->password);
+               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);
@@ -794,17 +785,14 @@ ssetup_ntlmssp_authenticate:
                }
                /* bail out if key is too long */
                if (msg->sesskey_len >
-                   sizeof(ses->server->session_key.data.krb5)) {
+                   sizeof(ses->auth_key.data.krb5)) {
                        cERROR(1, "Kerberos signing key too long (%u bytes)",
                                msg->sesskey_len);
                        rc = -EOVERFLOW;
                        goto ssetup_exit;
                }
-               if (first_time) {
-                       ses->server->session_key.len = msg->sesskey_len;
-                       memcpy(ses->server->session_key.data.krb5,
-                               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);