fstring str_sid;
state->response.data.auth.info3.logon_time =
- nt_time_to_unix(&(info3->logon_time));
+ nt_time_to_unix(info3->logon_time);
state->response.data.auth.info3.logoff_time =
- nt_time_to_unix(&(info3->logoff_time));
+ nt_time_to_unix(info3->logoff_time);
state->response.data.auth.info3.kickoff_time =
- nt_time_to_unix(&(info3->kickoff_time));
+ nt_time_to_unix(info3->kickoff_time);
state->response.data.auth.info3.pass_last_set_time =
- nt_time_to_unix(&(info3->pass_last_set_time));
+ nt_time_to_unix(info3->pass_last_set_time);
state->response.data.auth.info3.pass_can_change_time =
- nt_time_to_unix(&(info3->pass_can_change_time));
+ nt_time_to_unix(info3->pass_can_change_time);
state->response.data.auth.info3.pass_must_change_time =
- nt_time_to_unix(&(info3->pass_must_change_time));
+ nt_time_to_unix(info3->pass_must_change_time);
state->response.data.auth.info3.logon_count = info3->logon_count;
state->response.data.auth.info3.bad_pw_count = info3->bad_pw_count;
}
size = prs_data_size(&ps);
- state->response.extra_data = SMB_MALLOC(size);
- if (!state->response.extra_data) {
+ SAFE_FREE(state->response.extra_data.data);
+ state->response.extra_data.data = SMB_MALLOC(size);
+ if (!state->response.extra_data.data) {
prs_mem_free(&ps);
return NT_STATUS_NO_MEMORY;
}
- memset( state->response.extra_data, '\0', size );
- prs_copy_all_data_out(state->response.extra_data, &ps);
+ memset( state->response.extra_data.data, '\0', size );
+ prs_copy_all_data_out((char *)state->response.extra_data.data, &ps);
state->response.length += size;
prs_mem_free(&ps);
return NT_STATUS_OK;
static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
NET_USER_INFO_3 *info3,
const char *group_sid)
+/**
+ * Check whether a user belongs to a group or list of groups.
+ *
+ * @param mem_ctx talloc memory context.
+ * @param info3 user information, including group membership info.
+ * @param group_sid One or more groups , separated by commas.
+ *
+ * @return NT_STATUS_OK on success,
+ * NT_STATUS_LOGON_FAILURE if the user does not belong,
+ * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
+ */
{
- DOM_SID require_membership_of_sid;
- DOM_SID *all_sids;
- size_t num_all_sids = (2 + info3->num_groups2 + info3->num_other_sids);
- size_t i, j = 0;
+ DOM_SID *require_membership_of_sid;
+ size_t num_require_membership_of_sid;
+ fstring req_sid;
+ const char *p;
+ DOM_SID sid;
+ size_t i;
+ struct nt_user_token *token;
+ NTSTATUS status;
/* Parse the 'required group' SID */
/* NO sid supplied, all users may access */
return NT_STATUS_OK;
}
-
- if (!string_to_sid(&require_membership_of_sid, group_sid)) {
- DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!",
- group_sid));
- return NT_STATUS_INVALID_PARAMETER;
+ if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
+ DEBUG(0, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
}
- all_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, num_all_sids);
- if (!all_sids)
- return NT_STATUS_NO_MEMORY;
+ num_require_membership_of_sid = 0;
+ require_membership_of_sid = NULL;
- /* and create (by appending rids) the 'domain' sids */
-
- sid_copy(&all_sids[0], &(info3->dom_sid.sid));
-
- if (!sid_append_rid(&all_sids[0], info3->user_rid)) {
- DEBUG(3,("could not append user's primary RID 0x%x\n",
+ p = group_sid;
+
+ while (next_token(&p, req_sid, ",", sizeof(req_sid))) {
+ if (!string_to_sid(&sid, req_sid)) {
+ DEBUG(0, ("check_info3_in_group: could not parse %s "
+ "as a SID!", req_sid));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!add_sid_to_array(mem_ctx, &sid,
+ &require_membership_of_sid,
+ &num_require_membership_of_sid)) {
+ DEBUG(0, ("add_sid_to_array failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!sid_compose(&sid, &(info3->dom_sid.sid),
+ info3->user_rid)
+ || !add_sid_to_array(mem_ctx, &sid,
+ &token->user_sids, &token->num_sids)) {
+ DEBUG(3,("could not add user SID from rid 0x%x\n",
info3->user_rid));
-
return NT_STATUS_INVALID_PARAMETER;
}
- j++;
- sid_copy(&all_sids[1], &(info3->dom_sid.sid));
-
- if (!sid_append_rid(&all_sids[1], info3->group_rid)) {
+ if (!sid_compose(&sid, &(info3->dom_sid.sid),
+ info3->group_rid)
+ || !add_sid_to_array(mem_ctx, &sid,
+ &token->user_sids, &token->num_sids)) {
DEBUG(3,("could not append additional group rid 0x%x\n",
info3->group_rid));
return NT_STATUS_INVALID_PARAMETER;
}
- j++;
for (i = 0; i < info3->num_groups2; i++) {
-
- sid_copy(&all_sids[j], &(info3->dom_sid.sid));
-
- if (!sid_append_rid(&all_sids[j], info3->gids[i].g_rid)) {
+ if (!sid_compose(&sid, &(info3->dom_sid.sid),
+ info3->gids[i].g_rid)
+ || !add_sid_to_array(mem_ctx, &sid,
+ &token->user_sids, &token->num_sids)) {
DEBUG(3,("could not append additional group rid 0x%x\n",
- info3->gids[i].g_rid));
-
+ info3->gids[i].g_rid));
return NT_STATUS_INVALID_PARAMETER;
}
- j++;
}
/* Copy 'other' sids. We need to do sid filtering here to
*/
for (i = 0; i < info3->num_other_sids; i++) {
- sid_copy(&all_sids[info3->num_groups2 + i + 2],
- &info3->other_sids[i].sid);
- j++;
- }
-
- for (i = 0; i < j; i++) {
- fstring sid1, sid2;
- DEBUG(10, ("User has SID: %s\n",
- sid_to_string(sid1, &all_sids[i])));
- if (sid_equal(&require_membership_of_sid, &all_sids[i])) {
- DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n",
- sid_to_string(sid1, &require_membership_of_sid), sid_to_string(sid2, &all_sids[i])));
+ if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid,
+ &token->user_sids, &token->num_sids)) {
+ DEBUG(3, ("could not add SID to array: %s\n",
+ sid_string_static(&info3->other_sids[i].sid)));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
+ token))
+ || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
+ token))) {
+ DEBUG(3, ("could not add aliases: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ debug_nt_user_token(DBGC_CLASS, 10, token);
+
+ for (i=0; i<num_require_membership_of_sid; i++) {
+ DEBUG(10, ("Checking SID %s\n", sid_string_static(
+ &require_membership_of_sid[i])));
+ if (nt_token_check_sid(&require_membership_of_sid[i],
+ token)) {
+ DEBUG(10, ("Access ok\n"));
return NT_STATUS_OK;
}
}
return NT_STATUS_LOGON_FAILURE;
}
-static struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
- const char *domain_name)
+struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
+ const char *domain_name)
{
struct winbindd_domain *domain;
return NULL;
}
+ /* we can auth against trusted domains */
+ if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ DEBUG(3, ("Authentication for domain [%s] skipped "
+ "as it is not a trusted domain\n",
+ domain_name));
+ } else {
+ return domain;
+ }
+ }
+
return find_our_domain();
}
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
SAM_UNK_INFO_1 password_policy;
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(5,("fillup_password_policy: No inbound trust to "
+ "contact domain %s\n", domain->name));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
methods = domain->methods;
status = methods->password_policy(domain, state->mem_ctx, &password_policy);
return NT_STATUS_OK;
}
+#ifdef HAVE_KRB5
+
static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
const char *type,
uid_t uid,
goto done;
memory_ccache:
- gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbind_cache");
+ gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
done:
if (gen_cc == NULL) {
return gen_cc;
}
-static uid_t get_uid_from_state(struct winbindd_cli_state *state)
-{
- uid_t uid = -1;
-
- uid = state->request.data.auth.uid;
-
- if (uid < 0) {
- DEBUG(1,("invalid uid: '%d'\n", uid));
- return -1;
- }
- return uid;
-}
-
static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
{
const char *type = state->request.data.auth.krb5_cc_type;
fstrcpy(state->response.data.auth.krb5ccname, cc);
}
+#endif
+
+static uid_t get_uid_from_state(struct winbindd_cli_state *state)
+{
+ uid_t uid = -1;
+
+ uid = state->request.data.auth.uid;
+
+ if (uid < 0) {
+ DEBUG(1,("invalid uid: '%d'\n", uid));
+ return -1;
+ }
+ return uid;
+}
+
/**********************************************************************
Authenticate a user with a clear text password using Kerberos and fill up
ccache if required
**********************************************************************/
+
static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
struct winbindd_cli_state *state,
NET_USER_INFO_3 **info3)
if (!internal_ccache) {
- seteuid(uid);
+ set_effective_uid(uid);
DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
}
- krb5_ret = kerberos_kinit_password(principal_s,
- state->request.data.auth.pass,
- time_offset,
- &ticket_lifetime,
- &renewal_until,
- cc,
- True,
- WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
+ krb5_ret = kerberos_kinit_password_ext(principal_s,
+ state->request.data.auth.pass,
+ time_offset,
+ &ticket_lifetime,
+ &renewal_until,
+ cc,
+ True,
+ True,
+ WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
+ &result);
if (krb5_ret) {
DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n",
principal_s, error_message(krb5_ret), krb5_ret));
- result = krb5_to_nt_status(krb5_ret);
- goto done;
+ goto failed;
}
/* does http_timestring use heimdals libroken strftime?? - Guenther */
http_timestring(ticket_lifetime), (int)ticket_lifetime,
http_timestring(renewal_until), (int)renewal_until));
+ /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
+ * in that case fallback to NTLM - gd */
+
+ if ((ticket_lifetime == 0) && (renewal_until == 0)) {
+ result = NT_STATUS_INVALID_LOGON_TYPE;
+ goto failed;
+ }
+
client_princ = talloc_strdup(state->mem_ctx, global_myname());
if (client_princ == NULL) {
result = NT_STATUS_NO_MEMORY;
- goto done;
+ goto failed;
}
strlower_m(client_princ);
- local_service = talloc_asprintf(state->mem_ctx, "HOST/%s@%s", client_princ, lp_realm());
+ local_service = talloc_asprintf(state->mem_ctx, "%s$@%s", client_princ, lp_realm());
if (local_service == NULL) {
DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
result = NT_STATUS_NO_MEMORY;
- goto done;
+ goto failed;
}
krb5_ret = cli_krb5_get_ticket(local_service,
&tkt,
&session_key_krb5,
0,
- cc);
+ cc,
+ NULL);
if (krb5_ret) {
- DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for: %s\n",
- local_service));
+ DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for %s: %s\n",
+ local_service, error_message(krb5_ret)));
result = krb5_to_nt_status(krb5_ret);
- goto done;
+ goto failed;
}
if (!internal_ccache) {
- seteuid(0);
+ gain_root_privilege();
}
/************************ NON-ROOT **********************/
result = ads_verify_ticket(state->mem_ctx,
lp_realm(),
+ time_offset,
&tkt,
&client_princ_out,
&pac_data,
if (!NT_STATUS_IS_OK(result)) {
DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n",
nt_errstr(result)));
- goto done;
+ goto failed;
}
- DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
- local_service));
-
if (!pac_data) {
DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
result = NT_STATUS_INVALID_PARAMETER;
- goto done;
+ goto failed;
}
logon_info = get_logon_info_from_pac(pac_data);
if (logon_info == NULL) {
DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
result = NT_STATUS_INVALID_PARAMETER;
- goto done;
+ goto failed;
}
+ DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
+ local_service));
+
/* last step:
* put results together */
cc,
service,
state->request.data.auth.user,
- NULL,
- state->request.data.auth.pass,
+ realm,
uid,
time(NULL),
ticket_lifetime,
renewal_until,
- lp_winbind_refresh_tickets());
+ False);
if (!NT_STATUS_IS_OK(result)) {
DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
nt_errstr(result)));
}
+ } else {
+
+ /* need to delete the memory cred cache, it is not used anymore */
+
+ krb5_ret = ads_kdestroy(cc);
+ if (krb5_ret) {
+ DEBUG(3,("winbindd_raw_kerberos_login: "
+ "could not destroy krb5 credential cache: "
+ "%s\n", error_message(krb5_ret)));
+ }
+
}
result = NT_STATUS_OK;
+ goto done;
+
+failed:
+
+ /* we could have created a new credential cache with a valid tgt in it
+ * but we werent able to get or verify the service ticket for this
+ * local host and therefor didn't get the PAC, we need to remove that
+ * cache entirely now */
+
+ krb5_ret = ads_kdestroy(cc);
+ if (krb5_ret) {
+ DEBUG(3,("winbindd_raw_kerberos_login: "
+ "could not destroy krb5 credential cache: "
+ "%s\n", error_message(krb5_ret)));
+ }
+
+ if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
+ DEBUG(3,("winbindd_raw_kerberos_login: "
+ "could not remove ccache for user %s\n",
+ state->request.data.auth.user));
+ }
+
done:
data_blob_free(&session_key);
data_blob_free(&session_key_krb5);
SAFE_FREE(client_princ_out);
if (!internal_ccache) {
- seteuid(0);
+ gain_root_privilege();
}
return result;
/* Parse domain and username */
- if (!parse_domain_user(state->request.data.auth.user,
+ ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
+
+ if (!canonicalize_username(state->request.data.auth.user,
name_domain, name_user)) {
set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
DEBUG(5, ("Plain text authentication for %s returned %s "
uint16 max_allowed_bad_attempts;
fstring name_domain, name_user;
DOM_SID sid;
- enum SID_NAME_USE type;
+ enum lsa_SidType type;
uchar new_nt_pass[NT_HASH_LEN];
const uint8 *cached_nt_pass;
+ const uint8 *cached_salt;
NET_USER_INFO_3 *my_info3;
time_t kickoff_time, must_change_time;
+ BOOL password_good = False;
+#ifdef HAVE_KRB5
+ struct winbindd_tdc_domain *tdc_domain = NULL;
+#endif
*info3 = NULL;
state->mem_ctx,
&sid,
&my_info3,
- &cached_nt_pass);
+ &cached_nt_pass,
+ &cached_salt);
if (!NT_STATUS_IS_OK(result)) {
DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
return result;
E_md4hash(state->request.data.auth.pass, new_nt_pass);
- dump_data(100, (const char *)new_nt_pass, NT_HASH_LEN);
- dump_data(100, (const char *)cached_nt_pass, NT_HASH_LEN);
+#if DEBUG_PASSWORD
+ dump_data(100, new_nt_pass, NT_HASH_LEN);
+ dump_data(100, cached_nt_pass, NT_HASH_LEN);
+ if (cached_salt) {
+ dump_data(100, cached_salt, NT_HASH_LEN);
+ }
+#endif
+
+ if (cached_salt) {
+ /* In this case we didn't store the nt_hash itself,
+ but the MD5 combination of salt + nt_hash. */
+ uchar salted_hash[NT_HASH_LEN];
+ E_md5hash(cached_salt, new_nt_pass, salted_hash);
- if (!memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN)) {
+ password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
+ True : False;
+ } else {
+ /* Old cached cred - direct store of nt_hash (bad bad bad !). */
+ password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
+ True : False;
+ }
+
+ if (password_good) {
/* User *DOES* know the password, update logon_time and reset
* bad_pw_count */
return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
}
- /* The info3 acct_flags in NT4's samlogon reply don't have
- * ACB_NORMAL set. */
-#if 0
if (!(my_info3->acct_flags & ACB_NORMAL)) {
- DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
+ DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
my_info3->acct_flags));
return NT_STATUS_LOGON_FAILURE;
}
-#endif
- kickoff_time = nt_time_to_unix(&my_info3->kickoff_time);
+
+ kickoff_time = nt_time_to_unix(my_info3->kickoff_time);
if (kickoff_time != 0 && time(NULL) > kickoff_time) {
return NT_STATUS_ACCOUNT_EXPIRED;
}
- must_change_time = nt_time_to_unix(&my_info3->pass_must_change_time);
+ must_change_time = nt_time_to_unix(my_info3->pass_must_change_time);
if (must_change_time != 0 && must_change_time < time(NULL)) {
- return NT_STATUS_PASSWORD_EXPIRED;
+ /* we allow grace logons when the password has expired */
+ my_info3->user_flgs |= LOGON_GRACE_LOGON;
+ /* return NT_STATUS_PASSWORD_EXPIRED; */
+ goto success;
}
+#ifdef HAVE_KRB5
+ if ((state->request.flags & WBFLAG_PAM_KRB5) &&
+ ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
+ (tdc_domain->trust_type & DS_DOMAIN_TRUST_TYPE_UPLEVEL)) {
+
+ uid_t uid = -1;
+ const char *cc = NULL;
+ char *realm = NULL;
+ const char *principal_s = NULL;
+ const char *service = NULL;
+ BOOL internal_ccache = False;
+
+ uid = get_uid_from_state(state);
+ if (uid == -1) {
+ DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ cc = generate_krb5_ccache(state->mem_ctx,
+ state->request.data.auth.krb5_cc_type,
+ state->request.data.auth.uid,
+ &internal_ccache);
+ if (cc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ realm = domain->alt_name;
+ strupper_m(realm);
+
+ principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
+ if (principal_s == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
+ if (service == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!internal_ccache) {
+
+ setup_return_cc_name(state, cc);
+
+ result = add_ccache_to_list(principal_s,
+ cc,
+ service,
+ state->request.data.auth.user,
+ domain->alt_name,
+ uid,
+ time(NULL),
+ time(NULL) + lp_winbind_cache_time(),
+ time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
+ True);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
+ "to add ccache to list: %s\n",
+ nt_errstr(result)));
+ }
+ }
+ }
+#endif /* HAVE_KRB5 */
+ success:
/* FIXME: we possibly should handle logon hours as well (does xp when
* offline?) see auth/auth_sam.c:sam_account_ok for details */
state->request.data.auth.pass,
my_info3);
if (!NT_STATUS_IS_OK(result)) {
- DEBUG(1,("failed to update creds: %s\n", nt_errstr(result)));
+ DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
+ nt_errstr(result)));
return result;
}
}
}
- set_dc_type_and_flags(contact_domain);
+ if (contact_domain->initialized &&
+ contact_domain->active_directory) {
+ goto try_login;
+ }
+
+ if (!contact_domain->initialized) {
+ init_dc_connection(contact_domain);
+ }
if (!contact_domain->active_directory) {
DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
return NT_STATUS_INVALID_LOGON_TYPE;
}
-
+try_login:
result = winbindd_raw_kerberos_login(contact_domain, state, info3);
done:
return result;
local_lm_response,
sizeof(local_lm_response));
} else {
- lm_resp = data_blob(NULL, 0);
+ lm_resp = data_blob_null;
}
SMBNTencrypt(state->request.data.auth.pass,
chal,
} while ( (attempts < 2) && retry );
+ /* handle the case where a NT4 DC does not fill in the acct_flags in
+ * the samlogon reply info3. When accurate info3 is required by the
+ * caller, we look up the account flags ourselve - gd */
+
+ if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
+ (my_info3->acct_flags == 0) && NT_STATUS_IS_OK(result)) {
+
+ struct rpc_pipe_client *samr_pipe;
+ POLICY_HND samr_domain_handle, user_pol;
+ SAM_USERINFO_CTR *user_ctr;
+ NTSTATUS status_tmp;
+ uint32 acct_flags;
+
+ ZERO_STRUCT(user_ctr);
+
+ status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
+ &samr_pipe, &samr_domain_handle);
+
+ if (!NT_STATUS_IS_OK(status_tmp)) {
+ DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
+ nt_errstr(status_tmp)));
+ goto done;
+ }
+
+ status_tmp = rpccli_samr_open_user(samr_pipe, state->mem_ctx,
+ &samr_domain_handle,
+ MAXIMUM_ALLOWED_ACCESS,
+ my_info3->user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(status_tmp)) {
+ DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
+ nt_errstr(status_tmp)));
+ goto done;
+ }
+
+ status_tmp = rpccli_samr_query_userinfo(samr_pipe, state->mem_ctx,
+ &user_pol, 16, &user_ctr);
+
+ if (!NT_STATUS_IS_OK(status_tmp)) {
+ DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
+ nt_errstr(status_tmp)));
+ rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
+ goto done;
+ }
+
+ acct_flags = user_ctr->info.id16->acb_info;
+
+ if (acct_flags == 0) {
+ rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
+ goto done;
+ }
+
+ my_info3->acct_flags = acct_flags;
+
+ DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
+
+ rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
+ }
+
*info3 = my_info3;
done:
return result;
struct winbindd_cli_state *state)
{
NTSTATUS result = NT_STATUS_LOGON_FAILURE;
+ NTSTATUS krb5_result = NT_STATUS_OK;
fstring name_domain, name_user;
NET_USER_INFO_3 *info3 = NULL;
/* Parse domain and username */
+ ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
+
parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+ if (domain->online == False) {
+ result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ if (domain->startup) {
+ /* Logons are very important to users. If we're offline and
+ we get a request within the first 30 seconds of startup,
+ try very hard to find a DC and go online. */
+
+ DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
+ "request in startup mode.\n", domain->name ));
+
+ winbindd_flush_negative_conn_cache(domain);
+ result = init_dc_connection(domain);
+ }
+ }
+
DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
/* Check for Kerberos authentication */
if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
+ /* save for later */
+ krb5_result = result;
+
if (NT_STATUS_IS_OK(result)) {
DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
}
- if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)) {
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
- domain->online = False;
+ set_domain_offline( domain );
+ goto cached_logon;
}
/* there are quite some NT_STATUS errors where there is no
if (NT_STATUS_IS_OK(result)) {
DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
+ /* add the Krb5 err if we have one */
+ if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
+ info3->user_flgs |= LOGON_KRB5_FAIL_CLOCK_SKEW;
+ }
goto process_result;
- } else {
- DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result)));
+ }
+
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
+ nt_errstr(result)));
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
+ {
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
+ set_domain_offline( domain );
+ goto cached_logon;
+ }
+
if (domain->online) {
/* We're still online - fail. */
goto done;
}
- /* Else drop through and see if we can check offline.... */
- }
}
cached_logon:
DOM_SID user_sid;
- /* In all codepaths were result == NT_STATUS_OK info3 must have
+ /* In all codepaths where result == NT_STATUS_OK info3 must have
been initialized. */
if (!info3) {
result = NT_STATUS_INTERNAL_ERROR;
netsamlogon_cache_store(name_user, info3);
wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
- /* save name_to_sid info as early as possible */
- sid_compose(&user_sid, &info3->dom_sid.sid, info3->user_rid);
- cache_name2sid(domain, name_domain, name_user, SID_NAME_USER, &user_sid);
+ /* save name_to_sid info as early as possible (only if
+ this is our primary domain so we don't invalidate
+ the cache entry by storing the seq_num for the wrong
+ domain). */
+ if ( domain->primary ) {
+ sid_compose(&user_sid, &info3->dom_sid.sid,
+ info3->user_rid);
+ cache_name2sid(domain, name_domain, name_user,
+ SID_NAME_USER, &user_sid);
+ }
/* Check if the user is in the right group */
}
- if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
- lp_winbind_offline_logon()) {
+ if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
- result = winbindd_store_creds(domain,
+ /* Store in-memory creds for single-signon using ntlm_auth. */
+ result = winbindd_add_memory_creds(state->request.data.auth.user,
+ get_uid_from_state(state),
+ state->request.data.auth.pass);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
+ goto done;
+ }
+
+ if (lp_winbind_offline_logon()) {
+ result = winbindd_store_creds(domain,
state->mem_ctx,
state->request.data.auth.user,
state->request.data.auth.pass,
info3, NULL);
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
- goto done;
+ if (!NT_STATUS_IS_OK(result)) {
+
+ /* Release refcount. */
+ winbindd_delete_memory_creds(state->request.data.auth.user);
+
+ DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
+ goto done;
+ }
}
+ }
+
+ result = fillup_password_policy(domain, state);
+ if (!NT_STATUS_IS_OK(result)
+ && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
+ {
+ DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
+ goto done;
}
- /* this is required to provide password expiry warning */
- if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
- result = fillup_password_policy(domain, state);
+ result = NT_STATUS_OK;
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
+ if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
+ /* We've been asked to 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(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(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;
+ }
+
+ fill_domain_username(username_out, nt_domain, nt_username, True);
+
+ DEBUG(5, ("Setting unix username to [%s]\n", username_out));
+
+ SAFE_FREE(state->response.extra_data.data);
+ state->response.extra_data.data = SMB_STRDUP(username_out);
+ if (!state->response.extra_data.data) {
+ result = NT_STATUS_NO_MEMORY;
goto done;
}
+ state->response.length +=
+ strlen((const char *)state->response.extra_data.data)+1;
}
-
- }
+ }
+
done:
/* give us a more useful (more correct?) error code */
state->response.data.auth.nt_status_string,
state->response.data.auth.pam_error));
- if ( NT_STATUS_IS_OK(result) &&
+ if ( NT_STATUS_IS_OK(result) && info3 &&
(state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
char *afsname = talloc_strdup(state->mem_ctx,
cell += 1;
/* Append an AFS token string */
- state->response.extra_data =
+ SAFE_FREE(state->response.extra_data.data);
+ state->response.extra_data.data =
afs_createtoken_str(afsname, cell);
- if (state->response.extra_data != NULL)
+ if (state->response.extra_data.data != NULL) {
state->response.length +=
- strlen(state->response.extra_data)+1;
+ strlen((const char *)state->response.extra_data.data)+1;
+ }
no_token:
TALLOC_FREE(afsname);
}
-
+
return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
}
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",
+ DEBUG(3, ("User %s is not in the required group (%s), so "
+ "crap authentication is rejected\n",
state->request.data.auth_crap.user,
state->request.data.auth_crap.require_membership_of_sid));
goto done;
DEBUG(5, ("Setting unix username to [%s]\n", username_out));
- state->response.extra_data = SMB_STRDUP(username_out);
- if (!state->response.extra_data) {
+ SAFE_FREE(state->response.extra_data.data);
+ state->response.extra_data.data = SMB_STRDUP(username_out);
+ if (!state->response.extra_data.data) {
result = NT_STATUS_NO_MEMORY;
goto done;
}
- state->response.length += strlen(state->response.extra_data)+1;
+ state->response.length +=
+ strlen((const char *)state->response.extra_data.data)+1;
}
if (state->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
{
- NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
- char *oldpass;
- char *newpass = NULL;
fstring domain, user;
- POLICY_HND dom_pol;
struct winbindd_domain *contact_domain;
- struct rpc_pipe_client *cli;
- BOOL got_info = False;
- SAM_UNK_INFO_1 info;
- SAMR_CHANGE_REJECT reject;
DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
state->request.data.chauthtok.user));
/* Setup crap */
- parse_domain_user(state->request.data.chauthtok.user, domain, user);
+ ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
+
+ if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
+ set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
+ DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
+ "(PAM: %d)\n",
+ state->request.data.auth.user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+ request_error(state);
+ return;
+ }
contact_domain = find_domain_from_name(domain);
if (!contact_domain) {
+ set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
state->request.data.chauthtok.user, domain, user, domain));
- result = NT_STATUS_NO_SUCH_USER;
+ request_error(state);
+ return;
+ }
+
+ sendto_domain(state, contact_domain);
+}
+
+enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
+ struct winbindd_cli_state *state)
+{
+ char *oldpass;
+ char *newpass = NULL;
+ POLICY_HND dom_pol;
+ struct rpc_pipe_client *cli;
+ BOOL got_info = False;
+ SAM_UNK_INFO_1 info;
+ SAMR_CHANGE_REJECT reject;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring domain, user;
+
+ DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
+ state->request.data.auth.user));
+
+ if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
goto done;
}
oldpass = state->request.data.chauthtok.oldpass;
newpass = state->request.data.chauthtok.newpass;
+ /* Initialize reject reason */
+ state->response.data.auth.reject_reason = Undefined;
+
/* Get sam handle */
result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject);
- /* FIXME: need to check for other error codes ? */
- if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
state->response.data.auth.policy.min_length_password =
info.min_length_password;
state->response.data.auth.policy.password_history =
reject.reject_reason;
got_info = True;
-
- } else if (!NT_STATUS_IS_OK(result)) {
+ }
+
+ /* only fallback when the chgpasswd3 call is not supported */
+ if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
+ (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
+ (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
nt_errstr(result)));
- state->response.data.auth.reject_reason = 0;
-
result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
+
+ /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
+ Map to the same status code as Windows 2003. */
+
+ if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
+ result = NT_STATUS_PASSWORD_RESTRICTION;
+ }
}
done:
- if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
- lp_winbind_offline_logon()) {
- NTSTATUS cred_ret;
+ if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
- cred_ret = winbindd_update_creds_by_name(contact_domain,
+ /* Update the single sign-on memory creds. */
+ result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
+ newpass);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
+ goto process_result;
+ }
+
+ if (lp_winbind_offline_logon()) {
+ result = winbindd_update_creds_by_name(contact_domain,
state->mem_ctx, user,
newpass);
- if (!NT_STATUS_IS_OK(cred_ret)) {
- DEBUG(10,("Failed to store creds: %s\n", nt_errstr(cred_ret)));
- goto process_result; /* FIXME: hm, risking inconsistant cache ? */
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
+ goto process_result;
+ }
}
}
state->response.data.auth.nt_status_string,
state->response.data.auth.pam_error));
- if (NT_STATUS_IS_OK(result)) {
- request_ok(state);
- } else {
- request_error(state);
- }
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
}
void winbindd_pam_logoff(struct winbindd_cli_state *state)
{
struct winbindd_domain *domain;
fstring name_domain, user;
-
+ uid_t caller_uid = (uid_t)-1;
+ uid_t request_uid = state->request.data.logoff.uid;
+
DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
state->request.data.logoff.user));
state->request.data.logoff.krb5ccname
[sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
- parse_domain_user(state->request.data.logoff.user, name_domain, user);
+ if (request_uid == (gid_t)-1) {
+ goto failed;
+ }
- domain = find_auth_domain(state, name_domain);
+ if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
+ goto failed;
+ }
- if (domain == NULL) {
- set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
- DEBUG(5, ("Pam Logoff 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));
- request_error(state);
- return;
+ if ((domain = find_auth_domain(state, name_domain)) == NULL) {
+ goto failed;
+ }
+
+ if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
+ DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
+ strerror(errno)));
+ goto failed;
+ }
+
+ switch (caller_uid) {
+ case -1:
+ goto failed;
+ case 0:
+ /* root must be able to logoff any user - gd */
+ state->request.data.logoff.uid = request_uid;
+ break;
+ default:
+ if (caller_uid != request_uid) {
+ DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
+ goto failed;
+ }
+ state->request.data.logoff.uid = caller_uid;
+ break;
}
sendto_domain(state, domain);
+ return;
+
+ failed:
+ set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
+ DEBUG(5, ("Pam Logoff for %s returned %s "
+ "(PAM: %d)\n",
+ state->request.data.logoff.user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+ request_error(state);
+ return;
}
enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
struct winbindd_cli_state *state)
{
NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
- struct WINBINDD_CCACHE_ENTRY *entry;
- int ret;
DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
state->request.data.logoff.user));
goto process_result;
}
-#ifdef HAVE_KRB5
-
- /* what we need here is to find the corresponding krb5 ccache name *we*
- * created for a given username and destroy it (as the user who created it) */
-
- entry = get_ccache_by_username(state->request.data.logoff.user);
- if (entry == NULL) {
- DEBUG(10,("winbindd_pam_logoff: could not get ccname for user %s\n",
- state->request.data.logoff.user));
+ if (state->request.data.logoff.krb5ccname[0] == '\0') {
+ result = NT_STATUS_OK;
goto process_result;
}
- DEBUG(10,("winbindd_pam_logoff: found ccache [%s]\n", entry->ccname));
-
- if (entry->uid < 0 || state->request.data.logoff.uid < 0) {
+#ifdef HAVE_KRB5
+
+ if (state->request.data.logoff.uid < 0) {
DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
goto process_result;
}
- if (entry->uid != state->request.data.logoff.uid) {
- DEBUG(0,("winbindd_pam_logoff: uid's differ: %d != %d\n",
- entry->uid, state->request.data.logoff.uid));
+ /* what we need here is to find the corresponding krb5 ccache name *we*
+ * created for a given username and destroy it */
+
+ if (!ccache_entry_exists(state->request.data.logoff.user)) {
+ result = NT_STATUS_OK;
+ DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
goto process_result;
}
- if (!strcsequal(entry->ccname, state->request.data.logoff.krb5ccname)) {
- DEBUG(0,("winbindd_pam_logoff: krb5ccnames differ: (daemon) %s != (client) %s\n",
- entry->ccname, state->request.data.logoff.krb5ccname));
+ if (!ccache_entry_identical(state->request.data.logoff.user,
+ state->request.data.logoff.uid,
+ state->request.data.logoff.krb5ccname)) {
+ DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
goto process_result;
}
- seteuid(entry->uid);
-
- ret = ads_kdestroy(entry->ccname);
-
- seteuid(0);
-
- if (ret) {
- DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n",
- entry->ccname, error_message(ret)));
- } else {
- DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n",
- entry->ccname, state->request.data.logoff.user));
- remove_ccache_by_ccname(entry->ccname);
+ result = remove_ccache(state->request.data.logoff.user);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
+ nt_errstr(result)));
+ goto process_result;
}
- result = krb5_to_nt_status(ret);
#else
result = NT_STATUS_NOT_SUPPORTED;
#endif
process_result:
+
+ winbindd_delete_memory_creds(state->request.data.logoff.user);
+
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));
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 (lp_winbind_offline_logon()) {
+ DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
+ DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ 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;
+}