diff options
Diffstat (limited to 'source3/nsswitch/winbindd_pam.c')
-rw-r--r-- | source3/nsswitch/winbindd_pam.c | 418 |
1 files changed, 276 insertions, 142 deletions
diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index 9061391118..97dc35c0e7 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -145,24 +145,99 @@ static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, return NT_STATUS_LOGON_FAILURE; } +static struct winbindd_domain *find_auth_domain(const char *domain_name) +{ + struct winbindd_domain *domain; + + if (IS_DC) { + domain = find_domain_from_name_noinit(domain_name); + if (domain == NULL) { + DEBUG(3, ("Authentication for domain [%s] " + "as it is not a trusted domain\n", + domain_name)); + } + return domain; + } + + if (is_myname(domain_name)) { + DEBUG(3, ("Authentication for domain %s (local domain " + "to this server) not supported at this " + "stage\n", domain_name)); + return NULL; + } + + return find_our_domain(); +} + +static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result) +{ + resp->data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result)); + + /* we might have given a more useful error above */ + if (*resp->data.auth.error_string == '\0') + fstrcpy(resp->data.auth.error_string, + get_friendly_nt_error_msg(result)); + resp->data.auth.pam_error = nt_status_to_pam(result); +} + /********************************************************************** Authenticate a user with a clear text password **********************************************************************/ -enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) +enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring name_domain, name_user; + + /* Ensure null termination */ + state->request.data.auth.user + [sizeof(state->request.data.auth.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth.pass + [sizeof(state->request.data.auth.pass)-1]='\0'; + + DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid, + state->request.data.auth.user)); + + /* Parse domain and username */ + + parse_domain_user(state->request.data.auth.user, + name_domain, name_user); + + domain = find_auth_domain(name_domain); + + if (domain == NULL) { + set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); + DEBUG(5, ("Plain text authentication for %s returned %s " + "(PAM: %d)\n", + state->request.data.auth.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + return WINBINDD_ERROR; + } + + async_domain_request(state->mem_ctx, domain, + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; +} + +enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain, + struct winbindd_cli_state *state) { NTSTATUS result; fstring name_domain, name_user; - unsigned char trust_passwd[16]; - time_t last_change_time; - uint32 sec_channel_type; + const char *srv_name_slash; NET_USER_INFO_3 info3; - struct cli_state *cli = NULL; + unsigned char *session_key; + struct rpc_pipe_client *pipe_cli; uchar chal[8]; - TALLOC_CTX *mem_ctx = NULL; DATA_BLOB lm_resp; DATA_BLOB nt_resp; DOM_CRED ret_creds; + DOM_CRED *credentials; int attempts = 0; unsigned char local_lm_response[24]; unsigned char local_nt_response[24]; @@ -178,12 +253,6 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid, state->request.data.auth.user)); - if (!(mem_ctx = talloc_init("winbind pam auth for %s", state->request.data.auth.user))) { - DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - /* Parse domain and username */ parse_domain_user(state->request.data.auth.user, name_domain, name_user); @@ -197,7 +266,7 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) DATA_BLOB names_blob; DATA_BLOB nt_response; DATA_BLOB lm_response; - server_chal = data_blob_talloc(mem_ctx, chal, 8); + server_chal = data_blob_talloc(state->mem_ctx, chal, 8); /* note that the 'workgroup' here is a best guess - we don't know the server's domain at this point. The 'server name' is also @@ -218,8 +287,10 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) } data_blob_free(&names_blob); data_blob_free(&server_chal); - lm_resp = data_blob_talloc(mem_ctx, lm_response.data, lm_response.length); - nt_resp = data_blob_talloc(mem_ctx, nt_response.data, nt_response.length); + lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data, + lm_response.length); + nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data, + nt_response.length); data_blob_free(&lm_response); data_blob_free(&nt_response); @@ -228,7 +299,7 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) && SMBencrypt(state->request.data.auth.pass, chal, local_lm_response)) { - lm_resp = data_blob_talloc(mem_ctx, + lm_resp = data_blob_talloc(state->mem_ctx, local_lm_response, sizeof(local_lm_response)); } else { @@ -238,7 +309,7 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) chal, local_nt_response); - nt_resp = data_blob_talloc(mem_ctx, + nt_resp = data_blob_talloc(state->mem_ctx, local_nt_response, sizeof(local_nt_response)); } @@ -261,80 +332,95 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) goto done; } - if (!(contact_domain = find_our_domain())) { - DEBUG(1, ("Authentication for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", - state->request.data.auth.user, name_domain, name_user)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } + contact_domain = find_our_domain(); } - if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) { - result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - goto done; + srv_name_slash = talloc_asprintf(state->mem_ctx, "\\\\%s", + contact_domain->dcname); + if (srv_name_slash == NULL) { + DEBUG(0, ("talloc_asprintf failed\n")); + return WINBINDD_ERROR; } - + /* check authentication loop */ do { + DOM_CRED clnt_creds; + ZERO_STRUCT(info3); ZERO_STRUCT(ret_creds); retry = False; - - /* Don't shut this down - it belongs to the connection cache code */ - result = cm_get_netlogon_cli(contact_domain, trust_passwd, - sec_channel_type, False, &cli); + + result = cm_connect_netlogon(contact_domain, state->mem_ctx, + &pipe_cli, &session_key, + &credentials); if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("could not open handle to NETLOGON pipe\n")); goto done; } - result = cli_netlogon_sam_network_logon(cli, mem_ctx, - &ret_creds, - name_user, name_domain, - global_myname(), chal, - lm_resp, nt_resp, - &info3); + credentials->timestamp.time = time(NULL); + memcpy(&clnt_creds, credentials, sizeof(clnt_creds)); + + /* Calculate the new credentials. */ + cred_create(session_key, &credentials->challenge, + clnt_creds.timestamp, &(clnt_creds.challenge)); + + result = rpccli_netlogon_sam_network_logon(pipe_cli, + state->mem_ctx, + srv_name_slash, + &clnt_creds, + &ret_creds, + name_user, + name_domain, + global_myname(), + chal, lm_resp, + nt_resp, &info3, + session_key); attempts += 1; - - /* We have to try a second time as cm_get_netlogon_cli + + /* We have to try a second time as cm_connect_netlogon might not yet have noticed that the DC has killed our connection. */ - if ( cli->fd == -1 ) { + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { retry = True; continue; - } + } - /* if we get access denied, a possible cuase was that we had and open - connection to the DC, but someone changed our machine account password - out from underneath us using 'net rpc changetrustpw' */ + /* if we get access denied, a possible cause was that we had + and open connection to the DC, but someone changed our + machine account password out from underneath us using 'net + rpc changetrustpw' */ - if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) { - DEBUG(3,("winbindd_pam_auth: sam_logon returned ACCESS_DENIED. Maybe the trust account " - "password was changed and we didn't know it. Killing connections to domain %s\n", + if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { + DEBUG(3,("winbindd_pam_auth: sam_logon returned " + "ACCESS_DENIED. Maybe the trust account " + "password was changed and we didn't know it. " + "Killing connections to domain %s\n", name_domain)); - winbindd_cm_flush(); + invalidate_cm_connection(&contact_domain->conn); retry = True; - cli = NULL; } } while ( (attempts < 2) && retry ); - if (cli != NULL) { - /* We might have come out of the loop above with cli == NULL, - so don't dereference that. */ - clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds); + if (NT_STATUS_IS_OK(result) && + (!clnt_deal_with_creds(session_key, credentials, + &ret_creds))) { + DEBUG(3, ("DC %s sent wrong credentials\n", + pipe_cli->cli->srv_name_slash)); + result = NT_STATUS_ACCESS_DENIED; } - + if (NT_STATUS_IS_OK(result)) { - netsamlogon_cache_store( cli->mem_ctx, name_user, &info3 ); + netsamlogon_cache_store(state->mem_ctx, name_user, &info3); wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); /* Check if the user is in the right group */ - if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth.require_membership_of_sid))) { + if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3, state->request.data.auth.require_membership_of_sid))) { DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", state->request.data.auth.user, state->request.data.auth.require_membership_of_sid)); @@ -407,9 +493,6 @@ done: SAFE_FREE(afsname); } - if (mem_ctx) - talloc_destroy(mem_ctx); - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } @@ -417,15 +500,79 @@ done: Challenge Response Authentication Protocol **********************************************************************/ -enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) +enum winbindd_result winbindd_crap_auth(struct winbindd_cli_state *state) { + struct winbindd_domain *domain = NULL; + const char *domain_name = NULL; NTSTATUS result; - unsigned char trust_passwd[16]; - time_t last_change_time; - uint32 sec_channel_type; + + if (!state->privileged) { + char *error_string = NULL; + DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access " + "denied. !\n")); + DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions " + "on %s are set correctly.\n", + get_winbind_priv_pipe_dir())); + /* send a better message than ACCESS_DENIED */ + error_string = talloc_asprintf(state->mem_ctx, + "winbind client not authorized " + "to use winbindd_pam_auth_crap." + " Ensure permissions on %s " + "are set correctly.", + get_winbind_priv_pipe_dir()); + fstrcpy(state->response.data.auth.error_string, error_string); + result = NT_STATUS_ACCESS_DENIED; + goto done; + } + + /* Ensure null termination */ + state->request.data.auth_crap.user + [sizeof(state->request.data.auth_crap.user)-1]=0; + state->request.data.auth_crap.domain + [sizeof(state->request.data.auth_crap.domain)-1]=0; + + DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n", + (unsigned long)state->pid, + state->request.data.auth_crap.domain, + state->request.data.auth_crap.user)); + + if (*state->request.data.auth_crap.domain != '\0') { + domain_name = state->request.data.auth_crap.domain; + } else if (lp_winbind_use_default_domain()) { + domain_name = lp_workgroup(); + } + + if (domain_name != NULL) + domain = find_auth_domain(domain_name); + + if (domain != NULL) { + async_domain_request(state->mem_ctx, domain, + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; + } + + result = NT_STATUS_NO_SUCH_USER; + + done: + set_auth_errors(&state->response, result); + DEBUG(5, ("CRAP authentication for %s returned %s (PAM: %d)\n", + state->request.data.auth.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + return WINBINDD_ERROR; +} + + +enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result; + const char *srv_name_slash; NET_USER_INFO_3 info3; - struct cli_state *cli = NULL; - TALLOC_CTX *mem_ctx = NULL; + unsigned char *session_key; + struct rpc_pipe_client *pipe_cli; + DOM_CRED *credentials; const char *name_user = NULL; const char *name_domain = NULL; const char *workstation; @@ -436,30 +583,13 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) DATA_BLOB lm_resp, nt_resp; - if (!state->privileged) { - char *error_string = NULL; - DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied. !\n")); - DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions on %s are set correctly.\n", - get_winbind_priv_pipe_dir())); - /* send a better message than ACCESS_DENIED */ - asprintf(&error_string, "winbind client not authorized to use winbindd_pam_auth_crap. Ensure permissions on %s are set correctly.", - get_winbind_priv_pipe_dir()); - fstrcpy(state->response.data.auth.error_string, error_string); - SAFE_FREE(error_string); - result = NT_STATUS_ACCESS_DENIED; - goto done; - } + /* This is child-only, so no check for privileged access is needed + anymore */ /* Ensure null termination */ state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0; state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0; - if (!(mem_ctx = talloc_init("winbind pam auth crap for %s", state->request.data.auth_crap.user))) { - DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - name_user = state->request.data.auth_crap.user; if (*state->request.data.auth_crap.domain) { @@ -491,8 +621,8 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) goto done; } - lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len); - nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len); + lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len); + nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len); /* what domain should we contact? */ @@ -512,26 +642,25 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) goto done; } - if (!(contact_domain = find_our_domain())) { - DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", - state->request.data.auth_crap.user, name_domain, name_user)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - } - - if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) { - result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - goto done; + contact_domain = find_our_domain(); } + srv_name_slash = talloc_asprintf(state->mem_ctx, "\\\\%s", + contact_domain->dcname); + if (srv_name_slash == NULL) { + DEBUG(0, ("talloc_asprintf failed\n")); + return WINBINDD_ERROR; + } + do { + DOM_CRED clnt_creds; ZERO_STRUCT(info3); ZERO_STRUCT(ret_creds); retry = False; - /* Don't shut this down - it belongs to the connection cache code */ - result = cm_get_netlogon_cli(contact_domain, trust_passwd, sec_channel_type, False, &cli); + result = cm_connect_netlogon(contact_domain, state->mem_ctx, + &pipe_cli, &session_key, + &credentials); if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", @@ -539,51 +668,66 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) goto done; } - result = cli_netlogon_sam_network_logon(cli, mem_ctx, - &ret_creds, - name_user, name_domain, - workstation, - state->request.data.auth_crap.chal, - lm_resp, nt_resp, - &info3); + credentials->timestamp.time = time(NULL); + memcpy(&clnt_creds, credentials, sizeof(clnt_creds)); + + /* Calculate the new credentials. */ + cred_create(session_key, &credentials->challenge, + clnt_creds.timestamp, &(clnt_creds.challenge)); + + result = rpccli_netlogon_sam_network_logon(pipe_cli, + state->mem_ctx, + srv_name_slash, + &clnt_creds, + &ret_creds, + name_user, + name_domain, + global_myname(), + state->request.data.auth_crap.chal, + lm_resp, + nt_resp, &info3, + session_key); attempts += 1; - /* We have to try a second time as cm_get_netlogon_cli + /* We have to try a second time as cm_connect_netlogon might not yet have noticed that the DC has killed our connection. */ - if ( cli->fd == -1 ) { + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { retry = True; continue; - } + } /* if we get access denied, a possible cause was that we had and open connection to the DC, but someone changed our machine account password out from underneath us using 'net rpc changetrustpw' */ - if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) { - DEBUG(3,("winbindd_pam_auth_crap: sam_logon returned ACCESS_DENIED. Maybe the trust account " - "password was changed and we didn't know it. Killing connections to domain %s\n", - contact_domain->name)); - winbindd_cm_flush(); + if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { + DEBUG(3,("winbindd_pam_auth: sam_logon returned " + "ACCESS_DENIED. Maybe the trust account " + "password was changed and we didn't know it. " + "Killing connections to domain %s\n", + name_domain)); + invalidate_cm_connection(&contact_domain->conn); retry = True; - cli = NULL; } - + } while ( (attempts < 2) && retry ); - if (cli != NULL) { - /* We might have come out of the loop above with cli == NULL, - so don't dereference that. */ - clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds); + if (NT_STATUS_IS_OK(result) && + (!clnt_deal_with_creds(session_key, credentials, + &ret_creds))) { + DEBUG(3, ("DC %s sent wrong credentials\n", + pipe_cli->cli->srv_name_slash)); + result = NT_STATUS_ACCESS_DENIED; } if (NT_STATUS_IS_OK(result)) { - netsamlogon_cache_store( cli->mem_ctx, name_user, &info3 ); + netsamlogon_cache_store( state->mem_ctx, name_user, &info3 ); wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); - if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth_crap.require_membership_of_sid))) { + if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3, state->request.data.auth_crap.require_membership_of_sid))) { DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", state->request.data.auth_crap.user, state->request.data.auth_crap.require_membership_of_sid)); @@ -591,19 +735,19 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) } if (state->request.flags & WBFLAG_PAM_INFO3_NDR) { - result = append_info3_as_ndr(mem_ctx, state, &info3); + result = append_info3_as_ndr(state->mem_ctx, state, &info3); } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) { /* ntlm_auth should return the unix username, per 'winbind use default domain' settings and the like */ fstring username_out; const char *nt_username, *nt_domain; - if (!(nt_username = unistr2_tdup(mem_ctx, &(info3.uni_user_name)))) { + if (!(nt_username = unistr2_tdup(state->mem_ctx, &(info3.uni_user_name)))) { /* If the server didn't give us one, just use the one we sent them */ nt_username = name_user; } - if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3.uni_logon_dom)))) { + if (!(nt_domain = unistr2_tdup(state->mem_ctx, &(info3.uni_logon_dom)))) { /* If the server didn't give us one, just use the one we sent them */ nt_domain = name_domain; } @@ -653,9 +797,6 @@ done: state->response.data.auth.nt_status_string, state->response.data.auth.pam_error)); - if (mem_ctx) - talloc_destroy(mem_ctx); - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } @@ -666,20 +807,13 @@ enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state) NTSTATUS result; char *oldpass, *newpass; fstring domain, user; - CLI_POLICY_HND *hnd; - TALLOC_CTX *mem_ctx; + POLICY_HND dom_pol; struct winbindd_domain *contact_domain; + struct rpc_pipe_client *cli; DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid, state->request.data.chauthtok.user)); - if (!(mem_ctx = talloc_init("winbind password change for %s", - state->request.data.chauthtok.user))) { - DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - /* Setup crap */ if (state == NULL) @@ -701,12 +835,15 @@ enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state) /* Get sam handle */ - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(contact_domain, &hnd)) ) { + result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, + &dom_pol); + if (!NT_STATUS_IS_OK(result)) { DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); goto done; } - result = cli_samr_chgpasswd_user(hnd->cli, mem_ctx, user, newpass, oldpass); + result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, + oldpass); done: state->response.data.auth.nt_status = NT_STATUS_V(result); @@ -721,8 +858,5 @@ done: state->response.data.auth.nt_status_string, state->response.data.auth.pam_error)); - if (mem_ctx) - talloc_destroy(mem_ctx); - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } |