From 3e4cf56fa3f9d465d27dadaa6790bbcdea5d3cd9 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 15 Feb 2006 23:15:55 +0000 Subject: r13519: Fix the credentials chaining across netlogon pipe disconnects. I mean it this time :-). Jeremy. (This used to be commit 80f4868944d349015d2b64c2414b06466a8194aa) --- source3/libsmb/credentials.c | 25 ++++-- source3/passdb/secrets.c | 14 ++- source3/rpc_server/srv_netlog_nt.c | 169 +++++++++++++++++++++++++++---------- source3/rpc_server/srv_pipe.c | 23 ++--- 4 files changed, 163 insertions(+), 68 deletions(-) (limited to 'source3') diff --git a/source3/libsmb/credentials.c b/source3/libsmb/credentials.c index 795c30d12d..5026f513ab 100644 --- a/source3/libsmb/credentials.c +++ b/source3/libsmb/credentials.c @@ -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; } /**************************************************************************** diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c index 8f93bec9bc..6e46ea57fe 100644 --- a/source3/passdb/secrets.c +++ b/source3/passdb/secrets.c @@ -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; } diff --git a/source3/rpc_server/srv_netlog_nt.c b/source3/rpc_server/srv_netlog_nt.c index 2b98314722..97e19e6cb7 100644 --- a/source3/rpc_server/srv_netlog_nt.c +++ b/source3/rpc_server/srv_netlog_nt.c @@ -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, diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index 68b3a2d434..716654103a 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -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")); -- cgit