2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "../libcli/auth/libcli_auth.h"
29 #define DBGC_CLASS DBGC_WINBIND
31 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
33 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
34 struct winbindd_cli_state *state,
35 struct netr_SamInfo3 *info3)
40 state->response->data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response->data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response->data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response->data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response->data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response->data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response->data.auth.info3.logon_count = info3->base.logon_count;
54 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response->data.auth.info3.user_rid = info3->base.rid;
57 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response->data.auth.info3.num_groups = info3->base.groups.count;
61 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response->data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response->data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response->data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response->data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response->data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response->data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response->data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response->data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response->data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(state->mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
108 state->response->extra_data.data = ex;
109 state->response->length += talloc_get_size(ex);
114 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
115 struct winbindd_cli_state *state,
116 struct netr_SamInfo3 *info3)
119 enum ndr_err_code ndr_err;
121 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
122 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
125 return ndr_map_error2ntstatus(ndr_err);
128 state->response->extra_data.data = blob.data;
129 state->response->length += blob.length;
134 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
135 struct winbindd_cli_state *state,
136 const struct netr_SamInfo3 *info3,
137 const char *name_domain,
138 const char *name_user)
140 /* We've been asked to return the unix username, per
141 'winbind use default domain' settings and the like */
143 const char *nt_username, *nt_domain;
145 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
147 /* If the server didn't give us one, just use the one
149 nt_domain = name_domain;
152 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
154 /* If the server didn't give us one, just use the one
156 nt_username = name_user;
159 fill_domain_username(state->response->data.auth.unix_username,
160 nt_domain, nt_username, true);
162 DEBUG(5,("Setting unix username to [%s]\n",
163 state->response->data.auth.unix_username));
168 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
169 struct winbindd_cli_state *state,
170 const struct netr_SamInfo3 *info3,
171 const char *name_domain,
172 const char *name_user)
174 char *afsname = NULL;
178 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
179 if (afsname == NULL) {
180 return NT_STATUS_NO_MEMORY;
183 afsname = talloc_string_sub(mem_ctx,
184 lp_afs_username_map(),
186 afsname = talloc_string_sub(mem_ctx, afsname,
188 afsname = talloc_string_sub(mem_ctx, afsname,
195 sid_copy(&user_sid, info3->base.domain_sid);
196 sid_append_rid(&user_sid, info3->base.rid);
197 sid_to_fstring(sidstr, &user_sid);
198 afsname = talloc_string_sub(mem_ctx, afsname,
202 if (afsname == NULL) {
203 return NT_STATUS_NO_MEMORY;
208 DEBUG(10, ("Generating token for user %s\n", afsname));
210 cell = strchr(afsname, '@');
213 return NT_STATUS_NO_MEMORY;
219 token = afs_createtoken_str(afsname, cell);
223 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
225 if (state->response->extra_data.data == NULL) {
226 return NT_STATUS_NO_MEMORY;
228 state->response->length +=
229 strlen((const char *)state->response->extra_data.data)+1;
234 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
235 struct netr_SamInfo3 *info3,
236 const char *group_sid)
238 * Check whether a user belongs to a group or list of groups.
240 * @param mem_ctx talloc memory context.
241 * @param info3 user information, including group membership info.
242 * @param group_sid One or more groups , separated by commas.
244 * @return NT_STATUS_OK on success,
245 * NT_STATUS_LOGON_FAILURE if the user does not belong,
246 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
249 DOM_SID *require_membership_of_sid;
250 size_t num_require_membership_of_sid;
255 struct nt_user_token *token;
256 TALLOC_CTX *frame = NULL;
259 /* Parse the 'required group' SID */
261 if (!group_sid || !group_sid[0]) {
262 /* NO sid supplied, all users may access */
266 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
267 DEBUG(0, ("talloc failed\n"));
268 return NT_STATUS_NO_MEMORY;
271 num_require_membership_of_sid = 0;
272 require_membership_of_sid = NULL;
276 frame = talloc_stackframe();
277 while (next_token_talloc(frame, &p, &req_sid, ",")) {
278 if (!string_to_sid(&sid, req_sid)) {
279 DEBUG(0, ("check_info3_in_group: could not parse %s "
280 "as a SID!", req_sid));
282 return NT_STATUS_INVALID_PARAMETER;
285 status = add_sid_to_array(mem_ctx, &sid,
286 &require_membership_of_sid,
287 &num_require_membership_of_sid);
288 if (!NT_STATUS_IS_OK(status)) {
289 DEBUG(0, ("add_sid_to_array failed\n"));
297 status = sid_array_from_info3(mem_ctx, info3,
301 if (!NT_STATUS_IS_OK(status)) {
305 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
307 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
309 DEBUG(3, ("could not add aliases: %s\n",
314 debug_nt_user_token(DBGC_CLASS, 10, token);
316 for (i=0; i<num_require_membership_of_sid; i++) {
317 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
318 &require_membership_of_sid[i])));
319 if (nt_token_check_sid(&require_membership_of_sid[i],
321 DEBUG(10, ("Access ok\n"));
326 /* Do not distinguish this error from a wrong username/pw */
328 return NT_STATUS_LOGON_FAILURE;
331 struct winbindd_domain *find_auth_domain(uint8_t flags,
332 const char *domain_name)
334 struct winbindd_domain *domain;
337 domain = find_domain_from_name_noinit(domain_name);
338 if (domain == NULL) {
339 DEBUG(3, ("Authentication for domain [%s] refused "
340 "as it is not a trusted domain\n",
346 if (is_myname(domain_name)) {
347 DEBUG(3, ("Authentication for domain %s (local domain "
348 "to this server) not supported at this "
349 "stage\n", domain_name));
353 /* we can auth against trusted domains */
354 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
355 domain = find_domain_from_name_noinit(domain_name);
356 if (domain == NULL) {
357 DEBUG(3, ("Authentication for domain [%s] skipped "
358 "as it is not a trusted domain\n",
365 return find_our_domain();
368 static void fill_in_password_policy(struct winbindd_response *r,
369 const struct samr_DomInfo1 *p)
371 r->data.auth.policy.min_length_password =
372 p->min_password_length;
373 r->data.auth.policy.password_history =
374 p->password_history_length;
375 r->data.auth.policy.password_properties =
376 p->password_properties;
377 r->data.auth.policy.expire =
378 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
379 r->data.auth.policy.min_passwordage =
380 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
383 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
384 struct winbindd_cli_state *state)
386 struct winbindd_methods *methods;
387 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
388 struct samr_DomInfo1 password_policy;
390 if ( !winbindd_can_contact_domain( domain ) ) {
391 DEBUG(5,("fillup_password_policy: No inbound trust to "
392 "contact domain %s\n", domain->name));
393 return NT_STATUS_NOT_SUPPORTED;
396 methods = domain->methods;
398 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
399 if (NT_STATUS_IS_ERR(status)) {
403 fill_in_password_policy(state->response, &password_policy);
408 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
410 uint16 *lockout_threshold)
412 struct winbindd_methods *methods;
413 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
414 struct samr_DomInfo12 lockout_policy;
416 *lockout_threshold = 0;
418 methods = domain->methods;
420 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
421 if (NT_STATUS_IS_ERR(status)) {
425 *lockout_threshold = lockout_policy.lockout_threshold;
430 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
432 uint32 *password_properties)
434 struct winbindd_methods *methods;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 struct samr_DomInfo1 password_policy;
438 *password_properties = 0;
440 methods = domain->methods;
442 status = methods->password_policy(domain, mem_ctx, &password_policy);
443 if (NT_STATUS_IS_ERR(status)) {
447 *password_properties = password_policy.password_properties;
454 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
457 bool *internal_ccache)
459 /* accept FILE and WRFILE as krb5_cc_type from the client and then
460 * build the full ccname string based on the user's uid here -
463 const char *gen_cc = NULL;
465 *internal_ccache = true;
471 if (!type || type[0] == '\0') {
475 if (strequal(type, "FILE")) {
476 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
477 } else if (strequal(type, "WRFILE")) {
478 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
480 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
484 *internal_ccache = false;
488 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
491 if (gen_cc == NULL) {
492 DEBUG(0,("out of memory\n"));
496 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
501 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
503 const char *type = state->request->data.auth.krb5_cc_type;
505 state->response->data.auth.krb5ccname[0] = '\0';
507 if (type[0] == '\0') {
511 if (!strequal(type, "FILE") &&
512 !strequal(type, "WRFILE")) {
513 DEBUG(10,("won't return krbccname for a %s type ccache\n",
518 fstrcpy(state->response->data.auth.krb5ccname, cc);
523 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
527 uid = state->request->data.auth.uid;
530 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
536 /**********************************************************************
537 Authenticate a user with a clear text password using Kerberos and fill up
539 **********************************************************************/
541 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
542 struct winbindd_cli_state *state,
543 struct netr_SamInfo3 **info3)
546 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
547 krb5_error_code krb5_ret;
548 const char *cc = NULL;
549 const char *principal_s = NULL;
550 const char *service = NULL;
552 fstring name_domain, name_user;
553 time_t ticket_lifetime = 0;
554 time_t renewal_until = 0;
557 time_t time_offset = 0;
558 bool internal_ccache = true;
565 * prepare a krb5_cc_cache string for the user */
567 uid = get_uid_from_state(state);
569 DEBUG(0,("no valid uid\n"));
572 cc = generate_krb5_ccache(state->mem_ctx,
573 state->request->data.auth.krb5_cc_type,
574 state->request->data.auth.uid,
577 return NT_STATUS_NO_MEMORY;
582 * get kerberos properties */
584 if (domain->private_data) {
585 ads = (ADS_STRUCT *)domain->private_data;
586 time_offset = ads->auth.time_offset;
591 * do kerberos auth and setup ccache as the user */
593 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
595 realm = domain->alt_name;
598 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
599 if (principal_s == NULL) {
600 return NT_STATUS_NO_MEMORY;
603 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
604 if (service == NULL) {
605 return NT_STATUS_NO_MEMORY;
608 /* if this is a user ccache, we need to act as the user to let the krb5
609 * library handle the chown, etc. */
611 /************************ ENTERING NON-ROOT **********************/
613 if (!internal_ccache) {
614 set_effective_uid(uid);
615 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
618 result = kerberos_return_info3_from_pac(state->mem_ctx,
620 state->request->data.auth.pass,
627 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
629 if (!internal_ccache) {
630 gain_root_privilege();
633 /************************ RETURNED TO ROOT **********************/
635 if (!NT_STATUS_IS_OK(result)) {
639 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
642 /* if we had a user's ccache then return that string for the pam
645 if (!internal_ccache) {
647 setup_return_cc_name(state, cc);
649 result = add_ccache_to_list(principal_s,
652 state->request->data.auth.user,
660 if (!NT_STATUS_IS_OK(result)) {
661 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
666 /* need to delete the memory cred cache, it is not used anymore */
668 krb5_ret = ads_kdestroy(cc);
670 DEBUG(3,("winbindd_raw_kerberos_login: "
671 "could not destroy krb5 credential cache: "
672 "%s\n", error_message(krb5_ret)));
681 /* we could have created a new credential cache with a valid tgt in it
682 * but we werent able to get or verify the service ticket for this
683 * local host and therefor didn't get the PAC, we need to remove that
684 * cache entirely now */
686 krb5_ret = ads_kdestroy(cc);
688 DEBUG(3,("winbindd_raw_kerberos_login: "
689 "could not destroy krb5 credential cache: "
690 "%s\n", error_message(krb5_ret)));
693 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
694 DEBUG(3,("winbindd_raw_kerberos_login: "
695 "could not remove ccache for user %s\n",
696 state->request->data.auth.user));
701 return NT_STATUS_NOT_SUPPORTED;
702 #endif /* HAVE_KRB5 */
705 /****************************************************************
706 ****************************************************************/
708 bool check_request_flags(uint32_t flags)
710 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
711 WBFLAG_PAM_INFO3_TEXT |
712 WBFLAG_PAM_INFO3_NDR;
714 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
715 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
716 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
717 !(flags & flags_edata) ) {
721 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
727 /****************************************************************
728 ****************************************************************/
730 NTSTATUS append_auth_data(struct winbindd_cli_state *state,
731 struct netr_SamInfo3 *info3,
732 const char *name_domain,
733 const char *name_user)
736 uint32_t flags = state->request->flags;
738 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
739 memcpy(state->response->data.auth.user_session_key,
741 sizeof(state->response->data.auth.user_session_key)
745 if (flags & WBFLAG_PAM_LMKEY) {
746 memcpy(state->response->data.auth.first_8_lm_hash,
747 info3->base.LMSessKey.key,
748 sizeof(state->response->data.auth.first_8_lm_hash)
752 if (flags & WBFLAG_PAM_INFO3_TEXT) {
753 result = append_info3_as_txt(state->mem_ctx, state, info3);
754 if (!NT_STATUS_IS_OK(result)) {
755 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
761 /* currently, anything from here on potentially overwrites extra_data. */
763 if (flags & WBFLAG_PAM_INFO3_NDR) {
764 result = append_info3_as_ndr(state->mem_ctx, state, info3);
765 if (!NT_STATUS_IS_OK(result)) {
766 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
772 if (flags & WBFLAG_PAM_UNIX_NAME) {
773 result = append_unix_username(state->mem_ctx, state, info3,
774 name_domain, name_user);
775 if (!NT_STATUS_IS_OK(result)) {
776 DEBUG(10,("Failed to append Unix Username: %s\n",
782 if (flags & WBFLAG_PAM_AFS_TOKEN) {
783 result = append_afs_token(state->mem_ctx, state, info3,
784 name_domain, name_user);
785 if (!NT_STATUS_IS_OK(result)) {
786 DEBUG(10,("Failed to append AFS token: %s\n",
795 void winbindd_pam_auth(struct winbindd_cli_state *state)
797 struct winbindd_domain *domain;
798 fstring name_domain, name_user;
799 char *mapped_user = NULL;
801 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
803 /* Ensure null termination */
804 state->request->data.auth.user
805 [sizeof(state->request->data.auth.user)-1]='\0';
807 /* Ensure null termination */
808 state->request->data.auth.pass
809 [sizeof(state->request->data.auth.pass)-1]='\0';
811 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
812 state->request->data.auth.user));
814 if (!check_request_flags(state->request->flags)) {
815 result = NT_STATUS_INVALID_PARAMETER_MIX;
819 /* Parse domain and username */
821 name_map_status = normalize_name_unmap(state->mem_ctx,
822 state->request->data.auth.user,
825 /* If the name normalization didnt' actually do anything,
826 just use the original name */
828 if (!NT_STATUS_IS_OK(name_map_status) &&
829 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
831 mapped_user = state->request->data.auth.user;
834 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
835 result = NT_STATUS_NO_SUCH_USER;
839 domain = find_auth_domain(state->request->flags, name_domain);
841 if (domain == NULL) {
842 result = NT_STATUS_NO_SUCH_USER;
846 sendto_domain(state, domain);
849 set_auth_errors(state->response, result);
850 DEBUG(5, ("Plain text authentication for %s returned %s "
852 state->request->data.auth.user,
853 state->response->data.auth.nt_status_string,
854 state->response->data.auth.pam_error));
855 request_error(state);
858 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
859 struct winbindd_cli_state *state,
860 struct netr_SamInfo3 **info3)
862 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
863 uint16 max_allowed_bad_attempts;
864 fstring name_domain, name_user;
866 enum lsa_SidType type;
867 uchar new_nt_pass[NT_HASH_LEN];
868 const uint8 *cached_nt_pass;
869 const uint8 *cached_salt;
870 struct netr_SamInfo3 *my_info3;
871 time_t kickoff_time, must_change_time;
872 bool password_good = false;
874 struct winbindd_tdc_domain *tdc_domain = NULL;
881 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
883 /* Parse domain and username */
885 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
888 if (!lookup_cached_name(state->mem_ctx,
893 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
894 return NT_STATUS_NO_SUCH_USER;
897 if (type != SID_NAME_USER) {
898 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
899 return NT_STATUS_LOGON_FAILURE;
902 result = winbindd_get_creds(domain,
908 if (!NT_STATUS_IS_OK(result)) {
909 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
915 E_md4hash(state->request->data.auth.pass, new_nt_pass);
917 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
918 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
920 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
924 /* In this case we didn't store the nt_hash itself,
925 but the MD5 combination of salt + nt_hash. */
926 uchar salted_hash[NT_HASH_LEN];
927 E_md5hash(cached_salt, new_nt_pass, salted_hash);
929 password_good = (memcmp(cached_nt_pass, salted_hash,
932 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
933 password_good = (memcmp(cached_nt_pass, new_nt_pass,
939 /* User *DOES* know the password, update logon_time and reset
942 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
944 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
945 return NT_STATUS_ACCOUNT_LOCKED_OUT;
948 if (my_info3->base.acct_flags & ACB_DISABLED) {
949 return NT_STATUS_ACCOUNT_DISABLED;
952 if (my_info3->base.acct_flags & ACB_WSTRUST) {
953 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
956 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
957 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
960 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
961 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
964 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
965 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
966 my_info3->base.acct_flags));
967 return NT_STATUS_LOGON_FAILURE;
970 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
971 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
972 return NT_STATUS_ACCOUNT_EXPIRED;
975 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
976 if (must_change_time != 0 && must_change_time < time(NULL)) {
977 /* we allow grace logons when the password has expired */
978 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
979 /* return NT_STATUS_PASSWORD_EXPIRED; */
984 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
985 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
986 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
989 const char *cc = NULL;
991 const char *principal_s = NULL;
992 const char *service = NULL;
993 bool internal_ccache = false;
995 uid = get_uid_from_state(state);
997 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
998 return NT_STATUS_INVALID_PARAMETER;
1001 cc = generate_krb5_ccache(state->mem_ctx,
1002 state->request->data.auth.krb5_cc_type,
1003 state->request->data.auth.uid,
1006 return NT_STATUS_NO_MEMORY;
1009 realm = domain->alt_name;
1012 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1013 if (principal_s == NULL) {
1014 return NT_STATUS_NO_MEMORY;
1017 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1018 if (service == NULL) {
1019 return NT_STATUS_NO_MEMORY;
1022 if (!internal_ccache) {
1024 setup_return_cc_name(state, cc);
1026 result = add_ccache_to_list(principal_s,
1029 state->request->data.auth.user,
1033 time(NULL) + lp_winbind_cache_time(),
1034 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1037 if (!NT_STATUS_IS_OK(result)) {
1038 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1039 "to add ccache to list: %s\n",
1040 nt_errstr(result)));
1044 #endif /* HAVE_KRB5 */
1046 /* FIXME: we possibly should handle logon hours as well (does xp when
1047 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1049 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1050 my_info3->base.bad_password_count = 0;
1052 result = winbindd_update_creds_by_info3(domain,
1054 state->request->data.auth.user,
1055 state->request->data.auth.pass,
1057 if (!NT_STATUS_IS_OK(result)) {
1058 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1059 nt_errstr(result)));
1063 return NT_STATUS_OK;
1067 /* User does *NOT* know the correct password, modify info3 accordingly */
1069 /* failure of this is not critical */
1070 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1071 if (!NT_STATUS_IS_OK(result)) {
1072 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1073 "Won't be able to honour account lockout policies\n"));
1076 /* increase counter */
1077 my_info3->base.bad_password_count++;
1079 if (max_allowed_bad_attempts == 0) {
1084 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1086 uint32 password_properties;
1088 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1089 if (!NT_STATUS_IS_OK(result)) {
1090 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1093 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1094 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1095 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1100 result = winbindd_update_creds_by_info3(domain,
1102 state->request->data.auth.user,
1106 if (!NT_STATUS_IS_OK(result)) {
1107 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1108 nt_errstr(result)));
1111 return NT_STATUS_LOGON_FAILURE;
1114 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1115 struct winbindd_cli_state *state,
1116 struct netr_SamInfo3 **info3)
1118 struct winbindd_domain *contact_domain;
1119 fstring name_domain, name_user;
1122 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1124 /* Parse domain and username */
1126 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1128 /* what domain should we contact? */
1131 if (!(contact_domain = find_domain_from_name(name_domain))) {
1132 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1133 state->request->data.auth.user, name_domain, name_user, name_domain));
1134 result = NT_STATUS_NO_SUCH_USER;
1139 if (is_myname(name_domain)) {
1140 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1141 result = NT_STATUS_NO_SUCH_USER;
1145 contact_domain = find_domain_from_name(name_domain);
1146 if (contact_domain == NULL) {
1147 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1148 state->request->data.auth.user, name_domain, name_user, name_domain));
1150 contact_domain = find_our_domain();
1154 if (contact_domain->initialized &&
1155 contact_domain->active_directory) {
1159 if (!contact_domain->initialized) {
1160 init_dc_connection(contact_domain);
1163 if (!contact_domain->active_directory) {
1164 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1165 return NT_STATUS_INVALID_LOGON_TYPE;
1168 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1173 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1174 TALLOC_CTX *mem_ctx,
1175 uint32 logon_parameters,
1177 const char *username,
1179 const char *workstation,
1180 const uint8 chal[8],
1181 DATA_BLOB lm_response,
1182 DATA_BLOB nt_response,
1183 struct netr_SamInfo3 **info3);
1185 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1186 struct winbindd_cli_state *state,
1187 struct netr_SamInfo3 **info3)
1190 struct rpc_pipe_client *netlogon_pipe;
1195 unsigned char local_lm_response[24];
1196 unsigned char local_nt_response[24];
1197 struct winbindd_domain *contact_domain;
1198 fstring name_domain, name_user;
1201 struct netr_SamInfo3 *my_info3 = NULL;
1205 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1207 /* Parse domain and username */
1209 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1211 /* do password magic */
1214 generate_random_buffer(chal, 8);
1215 if (lp_client_ntlmv2_auth()) {
1216 DATA_BLOB server_chal;
1217 DATA_BLOB names_blob;
1218 DATA_BLOB nt_response;
1219 DATA_BLOB lm_response;
1220 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1222 /* note that the 'workgroup' here is a best guess - we don't know
1223 the server's domain at this point. The 'server name' is also
1226 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1228 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1229 state->request->data.auth.pass,
1232 &lm_response, &nt_response, NULL, NULL)) {
1233 data_blob_free(&names_blob);
1234 data_blob_free(&server_chal);
1235 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1236 result = NT_STATUS_NO_MEMORY;
1239 data_blob_free(&names_blob);
1240 data_blob_free(&server_chal);
1241 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1242 lm_response.length);
1243 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1244 nt_response.length);
1245 data_blob_free(&lm_response);
1246 data_blob_free(&nt_response);
1249 if (lp_client_lanman_auth()
1250 && SMBencrypt(state->request->data.auth.pass,
1252 local_lm_response)) {
1253 lm_resp = data_blob_talloc(state->mem_ctx,
1255 sizeof(local_lm_response));
1257 lm_resp = data_blob_null;
1259 SMBNTencrypt(state->request->data.auth.pass,
1263 nt_resp = data_blob_talloc(state->mem_ctx,
1265 sizeof(local_nt_response));
1268 /* what domain should we contact? */
1271 if (!(contact_domain = find_domain_from_name(name_domain))) {
1272 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1273 state->request->data.auth.user, name_domain, name_user, name_domain));
1274 result = NT_STATUS_NO_SUCH_USER;
1279 if (is_myname(name_domain)) {
1280 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1281 result = NT_STATUS_NO_SUCH_USER;
1285 contact_domain = find_our_domain();
1288 /* check authentication loop */
1291 netlogon_fn_t logon_fn;
1293 ZERO_STRUCTP(my_info3);
1296 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1298 if (!NT_STATUS_IS_OK(result)) {
1299 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1303 /* It is really important to try SamLogonEx here,
1304 * because in a clustered environment, we want to use
1305 * one machine account from multiple physical
1308 * With a normal SamLogon call, we must keep the
1309 * credentials chain updated and intact between all
1310 * users of the machine account (which would imply
1311 * cross-node communication for every NTLM logon).
1313 * (The credentials chain is not per NETLOGON pipe
1314 * connection, but globally on the server/client pair
1317 * When using SamLogonEx, the credentials are not
1318 * supplied, but the session key is implied by the
1319 * wrapping SamLogon context.
1321 * -- abartlet 21 April 2008
1324 logon_fn = contact_domain->can_do_samlogon_ex
1325 ? rpccli_netlogon_sam_network_logon_ex
1326 : rpccli_netlogon_sam_network_logon;
1328 result = logon_fn(netlogon_pipe,
1331 contact_domain->dcname, /* server name */
1332 name_user, /* user name */
1333 name_domain, /* target domain */
1334 global_myname(), /* workstation */
1341 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1342 && contact_domain->can_do_samlogon_ex) {
1343 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1344 "retrying with NetSamLogon\n"));
1345 contact_domain->can_do_samlogon_ex = false;
1350 /* We have to try a second time as cm_connect_netlogon
1351 might not yet have noticed that the DC has killed
1354 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1359 /* if we get access denied, a possible cause was that we had
1360 and open connection to the DC, but someone changed our
1361 machine account password out from underneath us using 'net
1362 rpc changetrustpw' */
1364 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1365 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1366 "ACCESS_DENIED. Maybe the trust account "
1367 "password was changed and we didn't know it. "
1368 "Killing connections to domain %s\n",
1370 invalidate_cm_connection(&contact_domain->conn);
1374 } while ( (attempts < 2) && retry );
1376 /* handle the case where a NT4 DC does not fill in the acct_flags in
1377 * the samlogon reply info3. When accurate info3 is required by the
1378 * caller, we look up the account flags ourselve - gd */
1380 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1381 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1383 struct rpc_pipe_client *samr_pipe;
1384 struct policy_handle samr_domain_handle, user_pol;
1385 union samr_UserInfo *info = NULL;
1386 NTSTATUS status_tmp;
1389 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1390 &samr_pipe, &samr_domain_handle);
1392 if (!NT_STATUS_IS_OK(status_tmp)) {
1393 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1394 nt_errstr(status_tmp)));
1398 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1399 &samr_domain_handle,
1400 MAXIMUM_ALLOWED_ACCESS,
1404 if (!NT_STATUS_IS_OK(status_tmp)) {
1405 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1406 nt_errstr(status_tmp)));
1410 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1415 if (!NT_STATUS_IS_OK(status_tmp)) {
1416 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1417 nt_errstr(status_tmp)));
1418 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1422 acct_flags = info->info16.acct_flags;
1424 if (acct_flags == 0) {
1425 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1429 my_info3->base.acct_flags = acct_flags;
1431 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1433 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1441 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1442 struct winbindd_cli_state *state)
1444 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1445 NTSTATUS krb5_result = NT_STATUS_OK;
1446 fstring name_domain, name_user;
1448 fstring domain_user;
1449 struct netr_SamInfo3 *info3 = NULL;
1450 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1452 /* Ensure null termination */
1453 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1455 /* Ensure null termination */
1456 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1458 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1459 state->request->data.auth.user));
1461 if (!check_request_flags(state->request->flags)) {
1462 result = NT_STATUS_INVALID_PARAMETER_MIX;
1466 /* Parse domain and username */
1468 name_map_status = normalize_name_unmap(state->mem_ctx,
1469 state->request->data.auth.user,
1472 /* If the name normalization didnt' actually do anything,
1473 just use the original name */
1475 if (!NT_STATUS_IS_OK(name_map_status) &&
1476 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1478 mapped_user = state->request->data.auth.user;
1481 parse_domain_user(mapped_user, name_domain, name_user);
1483 if ( mapped_user != state->request->data.auth.user ) {
1484 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1485 safe_strcpy( state->request->data.auth.user, domain_user,
1486 sizeof(state->request->data.auth.user)-1 );
1489 if (domain->online == false) {
1490 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1491 if (domain->startup) {
1492 /* Logons are very important to users. If we're offline and
1493 we get a request within the first 30 seconds of startup,
1494 try very hard to find a DC and go online. */
1496 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1497 "request in startup mode.\n", domain->name ));
1499 winbindd_flush_negative_conn_cache(domain);
1500 result = init_dc_connection(domain);
1504 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1506 /* Check for Kerberos authentication */
1507 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1509 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1510 /* save for later */
1511 krb5_result = result;
1514 if (NT_STATUS_IS_OK(result)) {
1515 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1516 goto process_result;
1518 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1521 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1524 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1525 set_domain_offline( domain );
1529 /* there are quite some NT_STATUS errors where there is no
1530 * point in retrying with a samlogon, we explictly have to take
1531 * care not to increase the bad logon counter on the DC */
1533 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1534 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1535 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1536 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1542 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1543 goto process_result;
1546 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1547 DEBUG(3,("falling back to samlogon\n"));
1555 /* Check for Samlogon authentication */
1556 if (domain->online) {
1557 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1559 if (NT_STATUS_IS_OK(result)) {
1560 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1561 /* add the Krb5 err if we have one */
1562 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1563 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1565 goto process_result;
1568 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1569 nt_errstr(result)));
1571 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1572 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1573 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1575 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1576 set_domain_offline( domain );
1580 if (domain->online) {
1581 /* We're still online - fail. */
1587 /* Check for Cached logons */
1588 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1589 lp_winbind_offline_logon()) {
1591 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1593 if (NT_STATUS_IS_OK(result)) {
1594 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1595 goto process_result;
1597 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1604 if (NT_STATUS_IS_OK(result)) {
1608 /* In all codepaths where result == NT_STATUS_OK info3 must have
1609 been initialized. */
1611 result = NT_STATUS_INTERNAL_ERROR;
1615 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1616 netsamlogon_cache_store(name_user, info3);
1618 /* save name_to_sid info as early as possible (only if
1619 this is our primary domain so we don't invalidate
1620 the cache entry by storing the seq_num for the wrong
1622 if ( domain->primary ) {
1623 sid_compose(&user_sid, info3->base.domain_sid,
1625 cache_name2sid(domain, name_domain, name_user,
1626 SID_NAME_USER, &user_sid);
1629 /* Check if the user is in the right group */
1631 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1632 state->request->data.auth.require_membership_of_sid))) {
1633 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1634 state->request->data.auth.user,
1635 state->request->data.auth.require_membership_of_sid));
1639 result = append_auth_data(state, info3, name_domain,
1641 if (!NT_STATUS_IS_OK(result)) {
1645 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1647 /* Store in-memory creds for single-signon using ntlm_auth. */
1648 result = winbindd_add_memory_creds(state->request->data.auth.user,
1649 get_uid_from_state(state),
1650 state->request->data.auth.pass);
1652 if (!NT_STATUS_IS_OK(result)) {
1653 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1657 if (lp_winbind_offline_logon()) {
1658 result = winbindd_store_creds(domain,
1660 state->request->data.auth.user,
1661 state->request->data.auth.pass,
1663 if (!NT_STATUS_IS_OK(result)) {
1665 /* Release refcount. */
1666 winbindd_delete_memory_creds(state->request->data.auth.user);
1668 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1675 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1676 struct winbindd_domain *our_domain = find_our_domain();
1678 /* This is not entirely correct I believe, but it is
1679 consistent. Only apply the password policy settings
1680 too warn users for our own domain. Cannot obtain these
1681 from trusted DCs all the time so don't do it at all.
1684 result = NT_STATUS_NOT_SUPPORTED;
1685 if (our_domain == domain ) {
1686 result = fillup_password_policy(our_domain, state);
1689 if (!NT_STATUS_IS_OK(result)
1690 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1692 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1693 domain->name, nt_errstr(result)));
1698 result = NT_STATUS_OK;
1702 /* give us a more useful (more correct?) error code */
1703 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1704 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1705 result = NT_STATUS_NO_LOGON_SERVERS;
1708 set_auth_errors(state->response, result);
1710 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1711 state->request->data.auth.user,
1712 state->response->data.auth.nt_status_string,
1713 state->response->data.auth.pam_error));
1715 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1719 /**********************************************************************
1720 Challenge Response Authentication Protocol
1721 **********************************************************************/
1723 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1725 struct winbindd_domain *domain = NULL;
1726 const char *domain_name = NULL;
1729 if (!check_request_flags(state->request->flags)) {
1730 result = NT_STATUS_INVALID_PARAMETER_MIX;
1734 if (!state->privileged) {
1735 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1737 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1738 "on %s are set correctly.\n",
1739 get_winbind_priv_pipe_dir()));
1740 /* send a better message than ACCESS_DENIED */
1741 fstr_sprintf(state->response->data.auth.error_string,
1742 "winbind client not authorized to use "
1743 "winbindd_pam_auth_crap. Ensure permissions on "
1744 "%s are set correctly.",
1745 get_winbind_priv_pipe_dir());
1746 result = NT_STATUS_ACCESS_DENIED;
1750 /* Ensure null termination */
1751 state->request->data.auth_crap.user
1752 [sizeof(state->request->data.auth_crap.user)-1]=0;
1753 state->request->data.auth_crap.domain
1754 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1756 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1757 (unsigned long)state->pid,
1758 state->request->data.auth_crap.domain,
1759 state->request->data.auth_crap.user));
1761 if (*state->request->data.auth_crap.domain != '\0') {
1762 domain_name = state->request->data.auth_crap.domain;
1763 } else if (lp_winbind_use_default_domain()) {
1764 domain_name = lp_workgroup();
1767 if (domain_name != NULL)
1768 domain = find_auth_domain(state->request->flags, domain_name);
1770 if (domain != NULL) {
1771 sendto_domain(state, domain);
1775 result = NT_STATUS_NO_SUCH_USER;
1778 set_auth_errors(state->response, result);
1779 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1780 state->request->data.auth_crap.domain,
1781 state->request->data.auth_crap.user,
1782 state->response->data.auth.nt_status_string,
1783 state->response->data.auth.pam_error));
1784 request_error(state);
1789 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1790 struct winbindd_cli_state *state)
1793 struct netr_SamInfo3 *info3 = NULL;
1794 struct rpc_pipe_client *netlogon_pipe;
1795 const char *name_user = NULL;
1796 const char *name_domain = NULL;
1797 const char *workstation;
1798 struct winbindd_domain *contact_domain;
1802 DATA_BLOB lm_resp, nt_resp;
1804 /* This is child-only, so no check for privileged access is needed
1807 /* Ensure null termination */
1808 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1809 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1811 if (!check_request_flags(state->request->flags)) {
1812 result = NT_STATUS_INVALID_PARAMETER_MIX;
1816 name_user = state->request->data.auth_crap.user;
1818 if (*state->request->data.auth_crap.domain) {
1819 name_domain = state->request->data.auth_crap.domain;
1820 } else if (lp_winbind_use_default_domain()) {
1821 name_domain = lp_workgroup();
1823 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1825 result = NT_STATUS_NO_SUCH_USER;
1829 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1830 name_domain, name_user));
1832 if (*state->request->data.auth_crap.workstation) {
1833 workstation = state->request->data.auth_crap.workstation;
1835 workstation = global_myname();
1838 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1839 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1840 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1841 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1842 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1843 state->request->data.auth_crap.lm_resp_len,
1844 state->request->data.auth_crap.nt_resp_len));
1845 result = NT_STATUS_INVALID_PARAMETER;
1850 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1851 state->request->data.auth_crap.lm_resp_len);
1853 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1854 nt_resp = data_blob_talloc(state->mem_ctx,
1855 state->request->extra_data.data,
1856 state->request->data.auth_crap.nt_resp_len);
1858 nt_resp = data_blob_talloc(state->mem_ctx,
1859 state->request->data.auth_crap.nt_resp,
1860 state->request->data.auth_crap.nt_resp_len);
1863 /* what domain should we contact? */
1866 if (!(contact_domain = find_domain_from_name(name_domain))) {
1867 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1868 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1869 result = NT_STATUS_NO_SUCH_USER;
1873 if (is_myname(name_domain)) {
1874 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1875 result = NT_STATUS_NO_SUCH_USER;
1878 contact_domain = find_our_domain();
1882 netlogon_fn_t logon_fn;
1886 netlogon_pipe = NULL;
1887 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1889 if (!NT_STATUS_IS_OK(result)) {
1890 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1891 nt_errstr(result)));
1895 logon_fn = contact_domain->can_do_samlogon_ex
1896 ? rpccli_netlogon_sam_network_logon_ex
1897 : rpccli_netlogon_sam_network_logon;
1899 result = logon_fn(netlogon_pipe,
1901 state->request->data.auth_crap.logon_parameters,
1902 contact_domain->dcname,
1905 /* Bug #3248 - found by Stefan Burkei. */
1906 workstation, /* We carefully set this above so use it... */
1907 state->request->data.auth_crap.chal,
1912 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1913 && contact_domain->can_do_samlogon_ex) {
1914 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1915 "retrying with NetSamLogon\n"));
1916 contact_domain->can_do_samlogon_ex = false;
1923 /* We have to try a second time as cm_connect_netlogon
1924 might not yet have noticed that the DC has killed
1927 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1932 /* if we get access denied, a possible cause was that we had and open
1933 connection to the DC, but someone changed our machine account password
1934 out from underneath us using 'net rpc changetrustpw' */
1936 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1937 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1938 "ACCESS_DENIED. Maybe the trust account "
1939 "password was changed and we didn't know it. "
1940 "Killing connections to domain %s\n",
1942 invalidate_cm_connection(&contact_domain->conn);
1946 } while ( (attempts < 2) && retry );
1948 if (NT_STATUS_IS_OK(result)) {
1950 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1951 netsamlogon_cache_store(name_user, info3);
1953 /* Check if the user is in the right group */
1955 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1956 state->request->data.auth_crap.require_membership_of_sid))) {
1957 DEBUG(3, ("User %s is not in the required group (%s), so "
1958 "crap authentication is rejected\n",
1959 state->request->data.auth_crap.user,
1960 state->request->data.auth_crap.require_membership_of_sid));
1964 result = append_auth_data(state, info3, name_domain,
1966 if (!NT_STATUS_IS_OK(result)) {
1973 /* give us a more useful (more correct?) error code */
1974 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1975 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1976 result = NT_STATUS_NO_LOGON_SERVERS;
1979 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1980 result = nt_status_squash(result);
1983 set_auth_errors(state->response, result);
1985 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1986 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1989 state->response->data.auth.nt_status_string,
1990 state->response->data.auth.pam_error));
1992 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1995 /* Change a user password */
1997 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1999 fstring domain, user;
2001 struct winbindd_domain *contact_domain;
2002 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2004 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2005 state->request->data.chauthtok.user));
2009 nt_status = normalize_name_unmap(state->mem_ctx,
2010 state->request->data.chauthtok.user,
2013 /* Update the chauthtok name if we did any mapping */
2015 if (NT_STATUS_IS_OK(nt_status) ||
2016 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2018 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2021 /* Must pass in state->...chauthtok.user because
2022 canonicalize_username() assumes an fstring(). Since
2023 we have already copied it (if necessary), this is ok. */
2025 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2026 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2027 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2029 state->request->data.auth.user,
2030 state->response->data.auth.nt_status_string,
2031 state->response->data.auth.pam_error));
2032 request_error(state);
2036 contact_domain = find_domain_from_name(domain);
2037 if (!contact_domain) {
2038 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2039 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2040 state->request->data.chauthtok.user, domain, user, domain));
2041 request_error(state);
2045 sendto_domain(state, contact_domain);
2048 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2049 struct winbindd_cli_state *state)
2052 char *newpass = NULL;
2053 struct policy_handle dom_pol;
2054 struct rpc_pipe_client *cli;
2055 bool got_info = false;
2056 struct samr_DomInfo1 *info = NULL;
2057 struct samr_ChangeReject *reject = NULL;
2058 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2059 fstring domain, user;
2061 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2062 state->request->data.auth.user));
2064 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2068 /* Change password */
2070 oldpass = state->request->data.chauthtok.oldpass;
2071 newpass = state->request->data.chauthtok.newpass;
2073 /* Initialize reject reason */
2074 state->response->data.auth.reject_reason = Undefined;
2076 /* Get sam handle */
2078 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2080 if (!NT_STATUS_IS_OK(result)) {
2081 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2085 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2092 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2094 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2096 fill_in_password_policy(state->response, info);
2098 state->response->data.auth.reject_reason =
2104 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2105 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2106 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2107 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2109 /* only fallback when the chgpasswd_user3 call is not supported */
2110 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2111 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2112 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2113 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2115 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2116 nt_errstr(result)));
2118 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2120 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2121 Map to the same status code as Windows 2003. */
2123 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2124 result = NT_STATUS_PASSWORD_RESTRICTION;
2130 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2132 /* Update the single sign-on memory creds. */
2133 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2136 /* When we login from gdm or xdm and password expires,
2137 * we change password, but there are no memory crendentials
2138 * So, winbindd_replace_memory_creds() returns
2139 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2142 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2143 result = NT_STATUS_OK;
2146 if (!NT_STATUS_IS_OK(result)) {
2147 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2148 goto process_result;
2151 if (lp_winbind_offline_logon()) {
2152 result = winbindd_update_creds_by_name(contact_domain,
2153 state->mem_ctx, user,
2155 /* Again, this happens when we login from gdm or xdm
2156 * and the password expires, *BUT* cached crendentials
2157 * doesn't exist. winbindd_update_creds_by_name()
2158 * returns NT_STATUS_NO_SUCH_USER.
2159 * This is not a failure.
2162 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2163 result = NT_STATUS_OK;
2166 if (!NT_STATUS_IS_OK(result)) {
2167 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2168 goto process_result;
2173 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2175 NTSTATUS policy_ret;
2177 policy_ret = fillup_password_policy(contact_domain, state);
2179 /* failure of this is non critical, it will just provide no
2180 * additional information to the client why the change has
2181 * failed - Guenther */
2183 if (!NT_STATUS_IS_OK(policy_ret)) {
2184 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2185 goto process_result;
2191 set_auth_errors(state->response, result);
2193 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2194 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2197 state->response->data.auth.nt_status_string,
2198 state->response->data.auth.pam_error));
2200 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2203 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2205 struct winbindd_domain *domain;
2206 fstring name_domain, user;
2207 uid_t caller_uid = (uid_t)-1;
2208 uid_t request_uid = state->request->data.logoff.uid;
2210 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2211 state->request->data.logoff.user));
2213 /* Ensure null termination */
2214 state->request->data.logoff.user
2215 [sizeof(state->request->data.logoff.user)-1]='\0';
2217 state->request->data.logoff.krb5ccname
2218 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2220 if (request_uid == (gid_t)-1) {
2224 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2228 if ((domain = find_auth_domain(state->request->flags,
2229 name_domain)) == NULL) {
2233 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2234 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2239 switch (caller_uid) {
2243 /* root must be able to logoff any user - gd */
2244 state->request->data.logoff.uid = request_uid;
2247 if (caller_uid != request_uid) {
2248 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2251 state->request->data.logoff.uid = caller_uid;
2255 sendto_domain(state, domain);
2259 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2260 DEBUG(5, ("Pam Logoff for %s returned %s "
2262 state->request->data.logoff.user,
2263 state->response->data.auth.nt_status_string,
2264 state->response->data.auth.pam_error));
2265 request_error(state);
2269 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2270 struct winbindd_cli_state *state)
2272 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2274 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2275 state->request->data.logoff.user));
2277 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2278 result = NT_STATUS_OK;
2279 goto process_result;
2282 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2283 result = NT_STATUS_OK;
2284 goto process_result;
2289 if (state->request->data.logoff.uid < 0) {
2290 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2291 goto process_result;
2294 /* what we need here is to find the corresponding krb5 ccache name *we*
2295 * created for a given username and destroy it */
2297 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2298 result = NT_STATUS_OK;
2299 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2300 goto process_result;
2303 if (!ccache_entry_identical(state->request->data.logoff.user,
2304 state->request->data.logoff.uid,
2305 state->request->data.logoff.krb5ccname)) {
2306 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2307 goto process_result;
2310 result = remove_ccache(state->request->data.logoff.user);
2311 if (!NT_STATUS_IS_OK(result)) {
2312 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2313 nt_errstr(result)));
2314 goto process_result;
2318 result = NT_STATUS_NOT_SUPPORTED;
2323 winbindd_delete_memory_creds(state->request->data.logoff.user);
2325 set_auth_errors(state->response, result);
2327 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2330 /* Change user password with auth crap*/
2332 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2334 struct winbindd_domain *domain = NULL;
2335 const char *domain_name = NULL;
2337 /* Ensure null termination */
2338 state->request->data.chng_pswd_auth_crap.user[
2339 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2340 state->request->data.chng_pswd_auth_crap.domain[
2341 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2343 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2344 (unsigned long)state->pid,
2345 state->request->data.chng_pswd_auth_crap.domain,
2346 state->request->data.chng_pswd_auth_crap.user));
2348 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2349 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2350 } else if (lp_winbind_use_default_domain()) {
2351 domain_name = lp_workgroup();
2354 if (domain_name != NULL)
2355 domain = find_domain_from_name(domain_name);
2357 if (domain != NULL) {
2358 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2359 "%s\n", (unsigned long)state->pid,domain->name));
2360 sendto_domain(state, domain);
2364 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2365 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2366 state->request->data.chng_pswd_auth_crap.domain,
2367 state->request->data.chng_pswd_auth_crap.user,
2368 state->response->data.auth.nt_status_string,
2369 state->response->data.auth.pam_error));
2370 request_error(state);
2374 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2377 DATA_BLOB new_nt_password;
2378 DATA_BLOB old_nt_hash_enc;
2379 DATA_BLOB new_lm_password;
2380 DATA_BLOB old_lm_hash_enc;
2381 fstring domain,user;
2382 struct policy_handle dom_pol;
2383 struct winbindd_domain *contact_domain = domainSt;
2384 struct rpc_pipe_client *cli;
2386 /* Ensure null termination */
2387 state->request->data.chng_pswd_auth_crap.user[
2388 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2389 state->request->data.chng_pswd_auth_crap.domain[
2390 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2394 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2395 (unsigned long)state->pid,
2396 state->request->data.chng_pswd_auth_crap.domain,
2397 state->request->data.chng_pswd_auth_crap.user));
2399 if (lp_winbind_offline_logon()) {
2400 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2401 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2402 result = NT_STATUS_ACCESS_DENIED;
2406 if (*state->request->data.chng_pswd_auth_crap.domain) {
2407 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2409 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2413 DEBUG(3,("no domain specified with username (%s) - "
2415 state->request->data.chng_pswd_auth_crap.user));
2416 result = NT_STATUS_NO_SUCH_USER;
2421 if (!*domain && lp_winbind_use_default_domain()) {
2422 fstrcpy(domain,(char *)lp_workgroup());
2426 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2429 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2430 (unsigned long)state->pid, domain, user));
2432 /* Change password */
2433 new_nt_password = data_blob_talloc(
2435 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2436 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2438 old_nt_hash_enc = data_blob_talloc(
2440 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2441 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2443 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2444 new_lm_password = data_blob_talloc(
2446 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2447 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2449 old_lm_hash_enc = data_blob_talloc(
2451 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2452 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2454 new_lm_password.length = 0;
2455 old_lm_hash_enc.length = 0;
2458 /* Get sam handle */
2460 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2461 if (!NT_STATUS_IS_OK(result)) {
2462 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2466 result = rpccli_samr_chng_pswd_auth_crap(
2467 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2468 new_lm_password, old_lm_hash_enc);
2472 set_auth_errors(state->response, result);
2474 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2475 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2477 state->response->data.auth.nt_status_string,
2478 state->response->data.auth.pam_error));
2480 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;