r13519: Fix the credentials chaining across netlogon pipe disconnects.
authorJeremy Allison <jra@samba.org>
Wed, 15 Feb 2006 23:15:55 +0000 (23:15 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 16:10:09 +0000 (11:10 -0500)
I mean it this time :-).
Jeremy.

source/libsmb/credentials.c
source/passdb/secrets.c
source/rpc_server/srv_netlog_nt.c
source/rpc_server/srv_pipe.c

index 795c30d12d69433c2417f5189a28508cf07c8be3..5026f513ab795a0e039c67882f6f1e279613906a 100644 (file)
@@ -183,17 +183,30 @@ static void creds_reseed(struct dcinfo *dc)
 
 BOOL creds_server_step(struct dcinfo *dc, const DOM_CRED *received_cred, DOM_CRED *cred_out)
 {
-       dc->sequence = received_cred->timestamp.time;
+       BOOL ret;
+       struct dcinfo tmp_dc = *dc;
 
-       creds_step(dc);
+       /* Do all operations on a temporary copy of the dc,
+          which we throw away if the checks fail. */
+
+       tmp_dc.sequence = received_cred->timestamp.time;
+
+       creds_step(&tmp_dc);
 
        /* Create the outgoing credentials */
-       cred_out->timestamp.time = dc->sequence + 1;
-       cred_out->challenge = dc->srv_chal;
+       cred_out->timestamp.time = tmp_dc.sequence + 1;
+       cred_out->challenge = tmp_dc.srv_chal;
 
-       creds_reseed(dc);
+       creds_reseed(&tmp_dc);
 
-       return creds_server_check(dc, &received_cred->challenge);
+       ret = creds_server_check(&tmp_dc, &received_cred->challenge);
+       if (!ret) {
+               return False;
+       }
+
+       /* creds step succeeded - replace the current creds. */
+       *dc = tmp_dc;
+       return True;
 }
 
 /****************************************************************************
index 8f93bec9bc760f6b6612763b2ced8119b8a67a1f..6e46ea57febe49c45cb97dd97a08c787afe4b8ba 100644 (file)
@@ -997,7 +997,7 @@ BOOL secrets_store_schannel_session_info(TALLOC_CTX *mem_ctx, const struct dcinf
 
 BOOL secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx,
                                const char *remote_machine,
-                               struct dcinfo *pdc)
+                               struct dcinfo **ppdc)
 {
        TDB_CONTEXT *tdb_sc = NULL;
        TDB_DATA value;
@@ -1008,10 +1008,11 @@ BOOL secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx,
        unsigned char *pmach_pw = NULL;
        uint32 l1, l2, l3, l4, l5;
        int ret;
+       struct dcinfo *pdc = NULL;
        char *keystr = talloc_asprintf(mem_ctx, "%s/%s", SECRETS_SCHANNEL_STATE,
                                remote_machine);
 
-       ZERO_STRUCTP(pdc);
+       *ppdc = NULL;
 
        if (!keystr) {
                return False;
@@ -1035,6 +1036,8 @@ BOOL secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx,
 
        tdb_close(tdb_sc);
 
+       pdc = TALLOC_ZERO_P(mem_ctx, struct dcinfo);
+
        /* Retrieve the record. */
        ret = tdb_unpack(value.dptr, value.dsize, "dBBBBBfff",
                                &pdc->sequence,
@@ -1049,13 +1052,13 @@ BOOL secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx,
 
        if (ret == -1 || l1 != 8 || l2 != 8 || l3 != 8 || l4 != 8 || l5 != 16) {
                talloc_free(keystr);
+               talloc_free(pdc);
                SAFE_FREE(pseed_chal);
                SAFE_FREE(pclnt_chal);
                SAFE_FREE(psrv_chal);
                SAFE_FREE(psess_key);
                SAFE_FREE(pmach_pw);
                SAFE_FREE(value.dptr);
-               ZERO_STRUCTP(pdc);
                return False;
        }
 
@@ -1070,7 +1073,7 @@ BOOL secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx,
        pdc->challenge_sent = True;
        pdc->authenticated = True;
 
-       DEBUG(3,("secrets_store_schannel_session_info: restored schannel info key %s\n",
+       DEBUG(3,("secrets_restore_schannel_session_info: restored schannel info key %s\n",
                keystr ));
 
        SAFE_FREE(pseed_chal);
@@ -1081,5 +1084,8 @@ BOOL secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx,
 
        talloc_free(keystr);
        SAFE_FREE(value.dptr);
+
+       *ppdc = pdc;
+
        return True;
 }
index 2b98314722a1fea17d19393256dbea104fe63784..97e19e6cb78a9e111abf5f265ec8f778d7a15100 100644 (file)
@@ -281,6 +281,10 @@ NTSTATUS _net_req_chal(pipes_struct *p, NET_Q_REQ_CHAL *q_u, NET_R_REQ_CHAL *r_u
                        q_u->uni_logon_clnt.buffer,
                        sizeof(fstring),q_u->uni_logon_clnt.uni_str_len*2,0);
 
+       /* Remember the workstation name. This is what we'll use to look
+          up the secrets.tdb record later. */
+       fstrcpy(p->wks, p->dc->remote_machine);
+
        /* Save the client challenge to the server. */
        memcpy(p->dc->clnt_chal.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
 
@@ -464,10 +468,31 @@ NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *
        DOM_CRED cred_out;
        const uchar *old_pw;
 
+       DEBUG(5,("_net_srv_pwset: %d\n", __LINE__));
+
+       /* We need the workstation name for the creds lookup. */
+       rpcstr_pull(workstation,q_u->clnt_id.login.uni_comp_name.buffer,
+                   sizeof(workstation),q_u->clnt_id.login.uni_comp_name.uni_str_len*2,0);
+
+       if (!p->dc) {
+               /* Restore the saved state of the netlogon creds. */
+               become_root();
+               ret = secrets_restore_schannel_session_info(p->pipe_state_mem_ctx,
+                                                       workstation,
+                                                       &p->dc);
+               unbecome_root();
+               if (!ret) {
+                       return NT_STATUS_INVALID_HANDLE;
+               }
+       }
+
        if (!p->dc || !p->dc->authenticated) {
                return NT_STATUS_INVALID_HANDLE;
        }
 
+       DEBUG(3,("_net_srv_pwset: Server Password Set by Wksta:[%s] on account [%s]\n",
+                       workstation, p->dc->mach_acct));
+       
        /* Step the creds chain forward. */
        if (!creds_server_step(p->dc, &q_u->clnt_id.cred, &cred_out)) {
                DEBUG(2,("_net_srv_pwset: creds_server_step failed. Rejecting auth "
@@ -476,17 +501,10 @@ NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       DEBUG(5,("_net_srv_pwset: %d\n", __LINE__));
-
-       rpcstr_pull(workstation,q_u->clnt_id.login.uni_comp_name.buffer,
-                   sizeof(workstation),q_u->clnt_id.login.uni_comp_name.uni_str_len*2,0);
-
-       DEBUG(3,("_net_srv_pwset: Server Password Set by Wksta:[%s] on account [%s]\n",
-                       workstation, p->dc->mach_acct));
-       
-       pdb_init_sam(&sampass);
-
+       /* We must store the creds state after an update. */
        become_root();
+       secrets_store_schannel_session_info(p->pipe_state_mem_ctx, p->dc);
+       pdb_init_sam(&sampass);
        ret=pdb_getsampwnam(sampass, p->dc->mach_acct);
        unbecome_root();
 
@@ -559,9 +577,28 @@ NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *
 
 NTSTATUS _net_sam_logoff(pipes_struct *p, NET_Q_SAM_LOGOFF *q_u, NET_R_SAM_LOGOFF *r_u)
 {
+       fstring workstation;
+
        if (!get_valid_user_struct(p->vuid))
                return NT_STATUS_NO_SUCH_USER;
 
+       if (!p->dc) {
+               /* Restore the saved state of the netlogon creds. */
+               BOOL ret;
+
+               *workstation = '\0';
+               rpcstr_pull_unistr2_fstring(workstation, &q_u->sam_id.client.login.uni_comp_name);
+
+               become_root();
+               secrets_restore_schannel_session_info(p->pipe_state_mem_ctx,
+                                                       workstation,
+                                                       &p->dc);
+               unbecome_root();
+               if (!ret) {
+                       return NT_STATUS_INVALID_HANDLE;
+               }
+       }
+
        if (!p->dc || !p->dc->authenticated) {
                return NT_STATUS_INVALID_HANDLE;
        }
@@ -576,6 +613,11 @@ NTSTATUS _net_sam_logoff(pipes_struct *p, NET_Q_SAM_LOGOFF *q_u, NET_R_SAM_LOGOF
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       /* We must store the creds state after an update. */
+       become_root();
+       secrets_store_schannel_session_info(p->pipe_state_mem_ctx, p->dc);
+       unbecome_root();
+
        r_u->status = NT_STATUS_OK;
        return r_u->status;
 }
@@ -651,32 +693,7 @@ static NTSTATUS _net_sam_logon_internal(pipes_struct *p,
        if (!get_valid_user_struct(p->vuid))
                return NT_STATUS_NO_SUCH_USER;
 
-       if (process_creds) {
-               if (!p->dc || !p->dc->authenticated) {
-                       return NT_STATUS_INVALID_HANDLE;
-               }
-       }
-
-       if ( (lp_server_schannel() == True) && (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) ) {
-               /* 'server schannel = yes' should enforce use of
-                  schannel, the client did offer it in auth2, but
-                  obviously did not use it. */
-               DEBUG(0,("_net_sam_logon: client %s not using schannel for netlogon\n",
-                       p->dc->remote_machine ));
-               return NT_STATUS_ACCESS_DENIED;
-       }
-
-       if (process_creds) {
-               /* checks and updates credentials.  creates reply credentials */
-               if (!creds_server_step(p->dc, &q_u->sam_id.client.cred,  &r_u->srv_creds)) {
-                       DEBUG(2,("_net_sam_logon: creds_server_step failed. Rejecting auth "
-                               "request from client %s machine account %s\n",
-                               p->dc->remote_machine, p->dc->mach_acct ));
-                       return NT_STATUS_INVALID_PARAMETER;
-               }
-       }
-
-       /* find the username */
+       /* We need the workstation name for the creds lookup. */
     
        switch (q_u->sam_id.logon_level) {
        case INTERACTIVE_LOGON_TYPE:
@@ -703,9 +720,52 @@ static NTSTATUS _net_sam_logon_internal(pipes_struct *p,
        rpcstr_pull(nt_domain,uni_samlogon_domain->buffer,sizeof(nt_domain),uni_samlogon_domain->uni_str_len*2,0);
        rpcstr_pull(nt_workstation,uni_samlogon_workstation->buffer,sizeof(nt_workstation),uni_samlogon_workstation->uni_str_len*2,0);
 
-       DEBUG(3,("User:[%s@%s] Requested Domain:[%s]\n", nt_username, 
-                 nt_workstation, nt_domain));
-       
+       DEBUG(3,("User:[%s@%s] Requested Domain:[%s]\n", nt_username, nt_workstation, nt_domain));
+
+       if (process_creds) {
+               if (!p->dc) {
+                       /* Restore the saved state of the netlogon creds. */
+                       BOOL ret;
+
+                       become_root();
+                       secrets_restore_schannel_session_info(p->pipe_state_mem_ctx,
+                                                               nt_workstation,
+                                                               &p->dc);
+                       unbecome_root();
+                       if (!ret) {
+                               return NT_STATUS_INVALID_HANDLE;
+                       }
+               }
+
+               if (!p->dc || !p->dc->authenticated) {
+                       return NT_STATUS_INVALID_HANDLE;
+               }
+       }
+
+       if ( (lp_server_schannel() == True) && (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) ) {
+               /* 'server schannel = yes' should enforce use of
+                  schannel, the client did offer it in auth2, but
+                  obviously did not use it. */
+               DEBUG(0,("_net_sam_logon: client %s not using schannel for netlogon\n",
+                       p->dc->remote_machine ));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (process_creds) {
+               /* checks and updates credentials.  creates reply credentials */
+               if (!creds_server_step(p->dc, &q_u->sam_id.client.cred,  &r_u->srv_creds)) {
+                       DEBUG(2,("_net_sam_logon: creds_server_step failed. Rejecting auth "
+                               "request from client %s machine account %s\n",
+                               p->dc->remote_machine, p->dc->mach_acct ));
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               /* We must store the creds state after an update. */
+               become_root();
+               secrets_store_schannel_session_info(p->pipe_state_mem_ctx, p->dc);
+               unbecome_root();
+       }
+
        fstrcpy(current_user_info.smb_name, nt_username);
        sub_set_smb_name(nt_username);
      
@@ -822,8 +882,9 @@ static NTSTATUS _net_sam_logon_internal(pipes_struct *p,
                pstring my_name;
                fstring user_sid_string;
                fstring group_sid_string;
-               uchar user_session_key[16];
-               uchar lm_session_key[16];
+               unsigned char user_session_key[16];
+               unsigned char lm_session_key[16];
+               unsigned char pipe_session_key[16];
 
                sampw = server_info->sam_account;
 
@@ -870,14 +931,36 @@ static NTSTATUS _net_sam_logon_internal(pipes_struct *p,
                               server_info->user_session_key.data, 
                               MIN(sizeof(user_session_key),
                                   server_info->user_session_key.length));
-                       SamOEMhash(user_session_key, p->dc->sess_key, 16);
+                       if (process_creds) {
+                               /* Get the pipe session key from the creds. */
+                               memcpy(pipe_session_key, p->dc->sess_key, 16);
+                       } else {
+                               /* Get the pipe session key from the schannel. */
+                               if (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL || p->auth.a_u.schannel_auth == NULL) {
+                                       return NT_STATUS_INVALID_HANDLE;
+                               }
+                               memcpy(pipe_session_key, p->auth.a_u.schannel_auth->sess_key, 16);
+                       }
+                       SamOEMhash(user_session_key, pipe_session_key, 16);
+                       memset(pipe_session_key, '\0', 16);
                }
                if (server_info->lm_session_key.length) {
                        memcpy(lm_session_key,
                               server_info->lm_session_key.data, 
                               MIN(sizeof(lm_session_key),
                                   server_info->lm_session_key.length));
-                       SamOEMhash(lm_session_key, p->dc->sess_key, 16);
+                       if (process_creds) {
+                               /* Get the pipe session key from the creds. */
+                               memcpy(pipe_session_key, p->dc->sess_key, 16);
+                       } else {
+                               /* Get the pipe session key from the schannel. */
+                               if (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL || p->auth.a_u.schannel_auth == NULL) {
+                                       return NT_STATUS_INVALID_HANDLE;
+                               }
+                               memcpy(pipe_session_key, p->auth.a_u.schannel_auth->sess_key, 16);
+                       }
+                       SamOEMhash(lm_session_key, pipe_session_key, 16);
+                       memset(pipe_session_key, '\0', 16);
                }
                
                init_net_user_info3(p->mem_ctx, usr_info, 
index 68b3a2d434c26a8a23e752efeb8afb43d92fbf7c..716654103aad2caf6aab822e6e11a51a2b29f1c0 100644 (file)
@@ -1284,7 +1284,7 @@ static BOOL pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p,
        RPC_AUTH_SCHANNEL_NEG neg;
        RPC_AUTH_VERIFIER auth_verifier;
        BOOL ret;
-       struct dcinfo stored_dcinfo;
+       struct dcinfo *pdcinfo;
        uint32 flags;
 
        if (!smb_io_rpc_auth_schannel_neg("", &neg, rpc_in_p, 0)) {
@@ -1292,10 +1292,8 @@ static BOOL pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p,
                return False;
        }
 
-       ZERO_STRUCT(stored_dcinfo);
-
        become_root();
-       ret = secrets_restore_schannel_session_info(p->mem_ctx, neg.myname, &stored_dcinfo);
+       ret = secrets_restore_schannel_session_info(p->mem_ctx, neg.myname, &pdcinfo);
        unbecome_root();
 
        if (!ret) {
@@ -1305,29 +1303,24 @@ static BOOL pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p,
 
        p->auth.a_u.schannel_auth = TALLOC_P(p->pipe_state_mem_ctx, struct schannel_auth_struct);
        if (!p->auth.a_u.schannel_auth) {
+               talloc_free(pdcinfo);
                return False;
        }
 
        memset(p->auth.a_u.schannel_auth->sess_key, 0, sizeof(p->auth.a_u.schannel_auth->sess_key));
-       memcpy(p->auth.a_u.schannel_auth->sess_key, stored_dcinfo.sess_key, sizeof(stored_dcinfo.sess_key));
+       memcpy(p->auth.a_u.schannel_auth->sess_key, pdcinfo->sess_key,
+                       sizeof(pdcinfo->sess_key));
+
+       talloc_free(pdcinfo);
 
        p->auth.a_u.schannel_auth->seq_num = 0;
 
        /*
         * JRA. Should we also copy the schannel session key into the pipe session key p->session_key
-        * here ? We do that for NTLMSPP, but the session key is already set up from the vuser
+        * here ? We do that for NTLMSSP, but the session key is already set up from the vuser
         * struct of the person who opened the pipe. I need to test this further. JRA.
         */
 
-       /* The client opens a second RPC NETLOGON pipe without
-               doing a auth2. The credentials for the schannel are
-               re-used from the auth2 the client did before. */
-       p->dc = TALLOC_ZERO_P(p->pipe_state_mem_ctx, struct dcinfo);
-       if (!p->dc) {
-               return False;
-       }
-       *p->dc = stored_dcinfo;
-
        init_rpc_hdr_auth(&auth_info, RPC_SCHANNEL_AUTH_TYPE, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1);
        if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) {
                DEBUG(0,("pipe_schannel_auth_bind: marshalling of RPC_HDR_AUTH failed.\n"));