diff options
-rw-r--r-- | source3/nsswitch/winbindd.c | 1 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_dual.c | 1 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_nss.h | 13 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_pam.c | 148 | ||||
-rw-r--r-- | source3/rpc_client/cli_samr.c | 44 | ||||
-rw-r--r-- | source3/utils/ntlm_auth.c | 296 |
6 files changed, 502 insertions, 1 deletions
diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index e5582915b2..a4cd724e00 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -214,6 +214,7 @@ static struct winbindd_dispatch_table { { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" }, { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" }, { WINBINDD_PAM_LOGOFF, winbindd_pam_logoff, "PAM_LOGOFF" }, + { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, winbindd_pam_chng_pswd_auth_crap, "CHNG_PSWD_AUTH_CRAP" }, /* Enumeration functions */ diff --git a/source3/nsswitch/winbindd_dual.c b/source3/nsswitch/winbindd_dual.c index 5908c78d9a..b4d4a794ae 100644 --- a/source3/nsswitch/winbindd_dual.c +++ b/source3/nsswitch/winbindd_dual.c @@ -353,6 +353,7 @@ static struct winbindd_child_dispatch_table child_dispatch_table[] = { { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" }, { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" }, { WINBINDD_PAM_LOGOFF, winbindd_dual_pam_logoff, "PAM_LOGOFF" }, + { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,winbindd_dual_pam_chng_pswd_auth_crap,"CHNG_PSWD_AUTH_CRAP" }, { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, "CHECK_MACHACC" }, { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" }, { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" }, diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index 6167a10c46..be0bb11948 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -65,6 +65,7 @@ enum winbindd_cmd { WINBINDD_PAM_AUTH_CRAP, WINBINDD_PAM_CHAUTHTOK, WINBINDD_PAM_LOGOFF, + WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, /* List various things */ @@ -227,6 +228,18 @@ struct winbindd_request { } chauthtok; /* pam_winbind passwd module */ struct { fstring user; + fstring domain; + unsigned char new_nt_pswd[516]; + uint16 new_nt_pswd_len; + unsigned char old_nt_hash_enc[16]; + uint16 old_nt_hash_enc_len; + unsigned char new_lm_pswd[516]; + uint16 new_lm_pswd_len; + unsigned char old_lm_hash_enc[16]; + uint16 old_lm_hash_enc_len; + } chng_pswd_auth_crap;/* pam_winbind passwd module */ + struct { + fstring user; fstring krb5ccname; uid_t uid; } logoff; /* pam_winbind session module */ diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index 34d23ebf8f..6af8e31bc0 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -1901,3 +1901,151 @@ process_result: return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } +/* Change user password with auth crap*/ + +void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain = NULL; + const char *domain_name = NULL; + + /* Ensure null termination */ + state->request.data.chng_pswd_auth_crap.user[ + sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0; + state->request.data.chng_pswd_auth_crap.domain[ + sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0; + + DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n", + (unsigned long)state->pid, + state->request.data.chng_pswd_auth_crap.domain, + state->request.data.chng_pswd_auth_crap.user)); + + if (*state->request.data.chng_pswd_auth_crap.domain != '\0') { + domain_name = state->request.data.chng_pswd_auth_crap.domain; + } else if (lp_winbind_use_default_domain()) { + domain_name = lp_workgroup(); + } + + if (domain_name != NULL) + domain = find_domain_from_name(domain_name); + + if (domain != NULL) { + DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: " + "%s\n", (unsigned long)state->pid,domain->name)); + sendto_domain(state, domain); + return; + } + + set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); + DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n", + state->request.data.chng_pswd_auth_crap.domain, + state->request.data.chng_pswd_auth_crap.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + request_error(state); + return; +} + +enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state) +{ + NTSTATUS result; + DATA_BLOB new_nt_password; + DATA_BLOB old_nt_hash_enc; + DATA_BLOB new_lm_password; + DATA_BLOB old_lm_hash_enc; + fstring domain,user; + POLICY_HND dom_pol; + struct winbindd_domain *contact_domain = domainSt; + struct rpc_pipe_client *cli; + + /* Ensure null termination */ + state->request.data.chng_pswd_auth_crap.user[ + sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0; + state->request.data.chng_pswd_auth_crap.domain[ + sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0; + *domain = 0; + *user = 0; + + DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n", + (unsigned long)state->pid, + state->request.data.chng_pswd_auth_crap.domain, + state->request.data.chng_pswd_auth_crap.user)); + + if (*state->request.data.chng_pswd_auth_crap.domain) { + fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain); + } else { + parse_domain_user(state->request.data.chng_pswd_auth_crap.user, + domain, user); + + if(!*domain) { + DEBUG(3,("no domain specified with username (%s) - " + "failing auth\n", + state->request.data.chng_pswd_auth_crap.user)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + } + + if (!*domain && lp_winbind_use_default_domain()) { + fstrcpy(domain,(char *)lp_workgroup()); + } + + if(!*user) { + fstrcpy(user, state->request.data.chng_pswd_auth_crap.user); + } + + DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", + (unsigned long)state->pid, domain, user)); + + /* Change password */ + new_nt_password = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.new_nt_pswd, + state->request.data.chng_pswd_auth_crap.new_nt_pswd_len); + + old_nt_hash_enc = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.old_nt_hash_enc, + state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len); + + if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) { + new_lm_password = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.new_lm_pswd, + state->request.data.chng_pswd_auth_crap.new_lm_pswd_len); + + old_lm_hash_enc = data_blob_talloc( + state->mem_ctx, + state->request.data.chng_pswd_auth_crap.old_lm_hash_enc, + state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len); + } else { + new_lm_password.length = 0; + old_lm_hash_enc.length = 0; + } + + /* Get sam handle */ + + 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 = rpccli_samr_chng_pswd_auth_crap( + cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc, + new_lm_password, old_lm_hash_enc); + + done: + state->response.data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); + fstrcpy(state->response.data.auth.error_string, + get_friendly_nt_error_msg(result)); + state->response.data.auth.pam_error = nt_status_to_pam(result); + + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, + ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", + domain, user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} diff --git a/source3/rpc_client/cli_samr.c b/source3/rpc_client/cli_samr.c index ea8db63642..4c6a868e7f 100644 --- a/source3/rpc_client/cli_samr.c +++ b/source3/rpc_client/cli_samr.c @@ -1288,6 +1288,50 @@ NTSTATUS rpccli_samr_chgpasswd_user(struct rpc_pipe_client *cli, return result; } +/* User change passwd with auth crap */ + +NTSTATUS rpccli_samr_chng_pswd_auth_crap(struct rpc_pipe_client *cli, + TALLOC_CTX *mem_ctx, + const char *username, + DATA_BLOB new_nt_password, + DATA_BLOB old_nt_hash_enc, + DATA_BLOB new_lm_password, + DATA_BLOB old_lm_hash_enc) +{ + prs_struct qbuf, rbuf; + SAMR_Q_CHGPASSWD_USER q; + SAMR_R_CHGPASSWD_USER r; + char *srv_name_slash; + + if (!(srv_name_slash = talloc_asprintf(mem_ctx, "\\\\%s", + cli->cli->desthost))) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(5,("rpccli_samr_chng_pswd_auth_crap on server: %s\n", + srv_name_slash)); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Marshall data and send request */ + + init_samr_q_chgpasswd_user(&q, srv_name_slash, username, + new_nt_password.data, + old_nt_hash_enc.data, + new_lm_password.data, + old_lm_hash_enc.data); + + CLI_DO_RPC(cli, mem_ctx, PI_SAMR, SAMR_CHGPASSWD_USER, + q, r, + qbuf, rbuf, + samr_io_q_chgpasswd_user, + samr_io_r_chgpasswd_user, + NT_STATUS_UNSUCCESSFUL); + + return r.status; +} + /* change password 3 */ NTSTATUS rpccli_samr_chgpasswd3(struct rpc_pipe_client *cli, diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c index fc9e3e9546..9e178ec945 100644 --- a/source3/utils/ntlm_auth.c +++ b/source3/utils/ntlm_auth.c @@ -38,6 +38,7 @@ enum stdio_helper_mode { GSS_SPNEGO, GSS_SPNEGO_CLIENT, NTLM_SERVER_1, + NTLM_CHANGE_PASSWORD_1, NUM_HELPER_MODES }; @@ -62,6 +63,8 @@ static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helpe static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, char *buf, int length); +static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length); + static const struct { enum stdio_helper_mode mode; const char *name; @@ -74,6 +77,7 @@ static const struct { { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request}, { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request}, { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request}, + { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request}, { NUM_HELPER_MODES, NULL, NULL} }; @@ -390,7 +394,87 @@ NTSTATUS contact_winbind_auth_crap(const char *username, free_response(&response); return nt_status; } - + +/* contact server to change user password using auth crap */ +static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username, + const char *domain, + const DATA_BLOB new_nt_pswd, + const DATA_BLOB old_nt_hash_enc, + const DATA_BLOB new_lm_pswd, + const DATA_BLOB old_lm_hash_enc, + char **error_string) +{ + NTSTATUS nt_status; + NSS_STATUS result; + struct winbindd_request request; + struct winbindd_response response; + + if (!get_require_membership_sid()) + { + if(error_string) + *error_string = smb_xstrdup("Can't get membership sid."); + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if(username != NULL) + fstrcpy(request.data.chng_pswd_auth_crap.user, username); + if(domain != NULL) + fstrcpy(request.data.chng_pswd_auth_crap.domain,domain); + + if(new_nt_pswd.length) + { + memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd)); + request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length; + } + + if(old_nt_hash_enc.length) + { + memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc, old_nt_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_nt_hash_enc)); + request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length; + } + + if(new_lm_pswd.length) + { + memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd)); + request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length; + } + + if(old_lm_hash_enc.length) + { + memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc, old_lm_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_lm_hash_enc)); + request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length; + } + + result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response); + + /* Display response */ + + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) + { + nt_status = NT_STATUS_UNSUCCESSFUL; + if (error_string) + *error_string = smb_xstrdup("Reading winbind reply failed!"); + free_response(&response); + return nt_status; + } + + nt_status = (NT_STATUS(response.data.auth.nt_status)); + if (!NT_STATUS_IS_OK(nt_status)) + { + if (error_string) + *error_string = smb_xstrdup(response.data.auth.error_string); + free_response(&response); + return nt_status; + } + + free_response(&response); + + return nt_status; +} + static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) { static const char zeros[16]; @@ -1580,6 +1664,216 @@ static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mod } } +static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length) +{ + char *request, *parameter; + static DATA_BLOB new_nt_pswd; + static DATA_BLOB old_nt_hash_enc; + static DATA_BLOB new_lm_pswd; + static DATA_BLOB old_lm_hash_enc; + static char *full_username = NULL; + static char *username = NULL; + static char *domain = NULL; + static char *newpswd = NULL; + static char *oldpswd = NULL; + + if (strequal(buf, ".")) { + if(newpswd && oldpswd) { + uchar old_nt_hash[16]; + uchar old_lm_hash[16]; + uchar new_nt_hash[16]; + uchar new_lm_hash[16]; + + new_nt_pswd = data_blob(NULL, 516); + old_nt_hash_enc = data_blob(NULL, 16); + + /* Calculate the MD4 hash (NT compatible) of the + * password */ + E_md4hash(oldpswd, old_nt_hash); + E_md4hash(newpswd, new_nt_hash); + + /* E_deshash returns false for 'long' + passwords (> 14 DOS chars). + + Therefore, don't send a buffer + encrypted with the truncated hash + (it could allow an even easier + attack on the password) + + Likewise, obey the admin's restriction + */ + + if (lp_client_lanman_auth() && + E_deshash(newpswd, new_lm_hash) && + E_deshash(oldpswd, old_lm_hash)) { + new_lm_pswd = data_blob(NULL, 516); + old_lm_hash_enc = data_blob(NULL, 16); + encode_pw_buffer(new_lm_pswd.data, newpswd, + STR_UNICODE); + + SamOEMhash(new_lm_pswd.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_lm_hash, + old_lm_hash_enc.data); + } else { + new_lm_pswd.data = NULL; + new_lm_pswd.length = 0; + old_lm_hash_enc.data = NULL; + old_lm_hash_enc.length = 0; + } + + encode_pw_buffer(new_nt_pswd.data, newpswd, + STR_UNICODE); + + SamOEMhash(new_nt_pswd.data, old_nt_hash, 516); + E_old_pw_hash(new_nt_hash, old_nt_hash, + old_nt_hash_enc.data); + } + + if (!full_username && !username) { + x_fprintf(x_stdout, "Error: No username supplied!\n"); + } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) && + (!new_lm_pswd.data || old_lm_hash_enc.data) ) { + x_fprintf(x_stdout, "Error: No NT or LM password " + "blobs supplied!\n"); + } else { + char *error_string = NULL; + + if (full_username && !username) { + fstring fstr_user; + fstring fstr_domain; + + if (!parse_ntlm_auth_domain_user(full_username, + fstr_user, + fstr_domain)) { + /* username might be 'tainted', don't + * print into our new-line + * deleimianted stream */ + x_fprintf(x_stdout, "Error: Could not " + "parse into domain and " + "username\n"); + SAFE_FREE(username); + username = smb_xstrdup(full_username); + } else { + SAFE_FREE(username); + SAFE_FREE(domain); + username = smb_xstrdup(fstr_user); + domain = smb_xstrdup(fstr_domain); + } + + } + + if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap( + username, domain, + new_nt_pswd, + old_nt_hash_enc, + new_lm_pswd, + old_lm_hash_enc, + &error_string))) { + x_fprintf(x_stdout, "Password-Change: No\n"); + x_fprintf(x_stdout, "Password-Change-Error: " + "%s\n.\n", error_string); + } else { + x_fprintf(x_stdout, "Password-Change: Yes\n"); + } + + SAFE_FREE(error_string); + } + /* clear out the state */ + new_nt_pswd = data_blob(NULL, 0); + old_nt_hash_enc = data_blob(NULL, 0); + new_lm_pswd = data_blob(NULL, 0); + old_nt_hash_enc = data_blob(NULL, 0); + SAFE_FREE(full_username); + SAFE_FREE(username); + SAFE_FREE(domain); + SAFE_FREE(newpswd); + SAFE_FREE(oldpswd); + x_fprintf(x_stdout, ".\n"); + + return; + } + + request = buf; + + /* Indicates a base64 encoded structure */ + parameter = strstr_m(request, ":: "); + if (!parameter) { + parameter = strstr_m(request, ": "); + + if (!parameter) { + DEBUG(0, ("Parameter not found!\n")); + x_fprintf(x_stdout, "Error: Parameter not found!\n.\n"); + return; + } + + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + } else { + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + + base64_decode_inplace(parameter); + } + + if (strequal(request, "new-nt-password-blob")) { + new_nt_pswd = strhex_to_data_blob(NULL, parameter); + if (new_nt_pswd.length != 516) { + x_fprintf(x_stdout, "Error: hex decode of %s failed! " + "(got %d bytes, expected 516)\n.\n", + parameter, + (int)new_nt_pswd.length); + new_nt_pswd = data_blob(NULL, 0); + } + } else if (strequal(request, "old-nt-hash-blob")) { + old_nt_hash_enc = strhex_to_data_blob(NULL, parameter); + if (old_nt_hash_enc.length != 16) { + x_fprintf(x_stdout, "Error: hex decode of %s failed! " + "(got %d bytes, expected 16)\n.\n", + parameter, + (int)old_nt_hash_enc.length); + old_nt_hash_enc = data_blob(NULL, 0); + } + } else if (strequal(request, "new-lm-password-blob")) { + new_lm_pswd = strhex_to_data_blob(NULL, parameter); + if (new_lm_pswd.length != 516) { + x_fprintf(x_stdout, "Error: hex decode of %s failed! " + "(got %d bytes, expected 516)\n.\n", + parameter, + (int)new_lm_pswd.length); + new_lm_pswd = data_blob(NULL, 0); + } + } + else if (strequal(request, "old-lm-hash-blob")) { + old_lm_hash_enc = strhex_to_data_blob(NULL, parameter); + if (old_lm_hash_enc.length != 16) + { + x_fprintf(x_stdout, "Error: hex decode of %s failed! " + "(got %d bytes, expected 16)\n.\n", + parameter, + (int)old_lm_hash_enc.length); + old_lm_hash_enc = data_blob(NULL, 0); + } + } else if (strequal(request, "nt-domain")) { + domain = smb_xstrdup(parameter); + } else if(strequal(request, "username")) { + username = smb_xstrdup(parameter); + } else if(strequal(request, "full-username")) { + username = smb_xstrdup(parameter); + } else if(strequal(request, "new-password")) { + newpswd = smb_xstrdup(parameter); + } else if (strequal(request, "old-password")) { + oldpswd = smb_xstrdup(parameter); + } else { + x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request); + } +} + static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) { char buf[SQUID_BUFFER_SIZE+1]; |