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"
28 #include "../librpc/gen_ndr/ndr_samr_c.h"
29 #include "rpc_client/cli_pipe.h"
30 #include "rpc_client/cli_samr.h"
31 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "rpc_client/cli_netlogon.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
43 #define DBGC_CLASS DBGC_WINBIND
45 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
47 static NTSTATUS
append_info3_as_txt(TALLOC_CTX
*mem_ctx
,
48 struct winbindd_response
*resp
,
49 struct netr_SamInfo3
*info3
)
54 resp
->data
.auth
.info3
.logon_time
=
55 nt_time_to_unix(info3
->base
.last_logon
);
56 resp
->data
.auth
.info3
.logoff_time
=
57 nt_time_to_unix(info3
->base
.last_logoff
);
58 resp
->data
.auth
.info3
.kickoff_time
=
59 nt_time_to_unix(info3
->base
.acct_expiry
);
60 resp
->data
.auth
.info3
.pass_last_set_time
=
61 nt_time_to_unix(info3
->base
.last_password_change
);
62 resp
->data
.auth
.info3
.pass_can_change_time
=
63 nt_time_to_unix(info3
->base
.allow_password_change
);
64 resp
->data
.auth
.info3
.pass_must_change_time
=
65 nt_time_to_unix(info3
->base
.force_password_change
);
67 resp
->data
.auth
.info3
.logon_count
= info3
->base
.logon_count
;
68 resp
->data
.auth
.info3
.bad_pw_count
= info3
->base
.bad_password_count
;
70 resp
->data
.auth
.info3
.user_rid
= info3
->base
.rid
;
71 resp
->data
.auth
.info3
.group_rid
= info3
->base
.primary_gid
;
72 sid_to_fstring(resp
->data
.auth
.info3
.dom_sid
, info3
->base
.domain_sid
);
74 resp
->data
.auth
.info3
.num_groups
= info3
->base
.groups
.count
;
75 resp
->data
.auth
.info3
.user_flgs
= info3
->base
.user_flags
;
77 resp
->data
.auth
.info3
.acct_flags
= info3
->base
.acct_flags
;
78 resp
->data
.auth
.info3
.num_other_sids
= info3
->sidcount
;
80 fstrcpy(resp
->data
.auth
.info3
.user_name
,
81 info3
->base
.account_name
.string
);
82 fstrcpy(resp
->data
.auth
.info3
.full_name
,
83 info3
->base
.full_name
.string
);
84 fstrcpy(resp
->data
.auth
.info3
.logon_script
,
85 info3
->base
.logon_script
.string
);
86 fstrcpy(resp
->data
.auth
.info3
.profile_path
,
87 info3
->base
.profile_path
.string
);
88 fstrcpy(resp
->data
.auth
.info3
.home_dir
,
89 info3
->base
.home_directory
.string
);
90 fstrcpy(resp
->data
.auth
.info3
.dir_drive
,
91 info3
->base
.home_drive
.string
);
93 fstrcpy(resp
->data
.auth
.info3
.logon_srv
,
94 info3
->base
.logon_server
.string
);
95 fstrcpy(resp
->data
.auth
.info3
.logon_dom
,
96 info3
->base
.domain
.string
);
98 ex
= talloc_strdup(mem_ctx
, "");
99 NT_STATUS_HAVE_NO_MEMORY(ex
);
101 for (i
=0; i
< info3
->base
.groups
.count
; i
++) {
102 ex
= talloc_asprintf_append_buffer(ex
, "0x%08X:0x%08X\n",
103 info3
->base
.groups
.rids
[i
].rid
,
104 info3
->base
.groups
.rids
[i
].attributes
);
105 NT_STATUS_HAVE_NO_MEMORY(ex
);
108 for (i
=0; i
< info3
->sidcount
; i
++) {
111 sid
= dom_sid_string(mem_ctx
, info3
->sids
[i
].sid
);
112 NT_STATUS_HAVE_NO_MEMORY(sid
);
114 ex
= talloc_asprintf_append_buffer(ex
, "%s:0x%08X\n",
116 info3
->sids
[i
].attributes
);
117 NT_STATUS_HAVE_NO_MEMORY(ex
);
122 resp
->extra_data
.data
= ex
;
123 resp
->length
+= talloc_get_size(ex
);
128 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
129 struct winbindd_response
*resp
,
130 struct netr_SamInfo3
*info3
)
133 enum ndr_err_code ndr_err
;
135 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, info3
,
136 (ndr_push_flags_fn_t
)ndr_push_netr_SamInfo3
);
137 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
138 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
139 return ndr_map_error2ntstatus(ndr_err
);
142 resp
->extra_data
.data
= blob
.data
;
143 resp
->length
+= blob
.length
;
148 static NTSTATUS
append_unix_username(TALLOC_CTX
*mem_ctx
,
149 struct winbindd_response
*resp
,
150 const struct netr_SamInfo3
*info3
,
151 const char *name_domain
,
152 const char *name_user
)
154 /* We've been asked to return the unix username, per
155 'winbind use default domain' settings and the like */
157 const char *nt_username
, *nt_domain
;
159 nt_domain
= talloc_strdup(mem_ctx
, info3
->base
.domain
.string
);
161 /* If the server didn't give us one, just use the one
163 nt_domain
= name_domain
;
166 nt_username
= talloc_strdup(mem_ctx
, info3
->base
.account_name
.string
);
168 /* If the server didn't give us one, just use the one
170 nt_username
= name_user
;
173 fill_domain_username(resp
->data
.auth
.unix_username
,
174 nt_domain
, nt_username
, true);
176 DEBUG(5, ("Setting unix username to [%s]\n",
177 resp
->data
.auth
.unix_username
));
182 static NTSTATUS
append_afs_token(TALLOC_CTX
*mem_ctx
,
183 struct winbindd_response
*resp
,
184 const struct netr_SamInfo3
*info3
,
185 const char *name_domain
,
186 const char *name_user
)
188 char *afsname
= NULL
;
192 afsname
= talloc_strdup(mem_ctx
, lp_afs_username_map());
193 if (afsname
== NULL
) {
194 return NT_STATUS_NO_MEMORY
;
197 afsname
= talloc_string_sub(mem_ctx
,
198 lp_afs_username_map(),
200 afsname
= talloc_string_sub(mem_ctx
, afsname
,
202 afsname
= talloc_string_sub(mem_ctx
, afsname
,
206 struct dom_sid user_sid
;
209 sid_compose(&user_sid
, info3
->base
.domain_sid
,
211 sid_to_fstring(sidstr
, &user_sid
);
212 afsname
= talloc_string_sub(mem_ctx
, afsname
,
216 if (afsname
== NULL
) {
217 return NT_STATUS_NO_MEMORY
;
222 DEBUG(10, ("Generating token for user %s\n", afsname
));
224 cell
= strchr(afsname
, '@');
227 return NT_STATUS_NO_MEMORY
;
233 token
= afs_createtoken_str(afsname
, cell
);
237 resp
->extra_data
.data
= talloc_strdup(mem_ctx
, token
);
238 if (resp
->extra_data
.data
== NULL
) {
239 return NT_STATUS_NO_MEMORY
;
241 resp
->length
+= strlen((const char *)resp
->extra_data
.data
)+1;
246 static NTSTATUS
check_info3_in_group(struct netr_SamInfo3
*info3
,
247 const char *group_sid
)
249 * Check whether a user belongs to a group or list of groups.
251 * @param mem_ctx talloc memory context.
252 * @param info3 user information, including group membership info.
253 * @param group_sid One or more groups , separated by commas.
255 * @return NT_STATUS_OK on success,
256 * NT_STATUS_LOGON_FAILURE if the user does not belong,
257 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
260 struct dom_sid
*require_membership_of_sid
;
261 uint32_t num_require_membership_of_sid
;
266 struct security_token
*token
;
267 TALLOC_CTX
*frame
= talloc_stackframe();
270 /* Parse the 'required group' SID */
272 if (!group_sid
|| !group_sid
[0]) {
273 /* NO sid supplied, all users may access */
277 token
= talloc_zero(talloc_tos(), struct security_token
);
279 DEBUG(0, ("talloc failed\n"));
281 return NT_STATUS_NO_MEMORY
;
284 num_require_membership_of_sid
= 0;
285 require_membership_of_sid
= NULL
;
289 while (next_token_talloc(talloc_tos(), &p
, &req_sid
, ",")) {
290 if (!string_to_sid(&sid
, req_sid
)) {
291 DEBUG(0, ("check_info3_in_group: could not parse %s "
292 "as a SID!", req_sid
));
294 return NT_STATUS_INVALID_PARAMETER
;
297 status
= add_sid_to_array(talloc_tos(), &sid
,
298 &require_membership_of_sid
,
299 &num_require_membership_of_sid
);
300 if (!NT_STATUS_IS_OK(status
)) {
301 DEBUG(0, ("add_sid_to_array failed\n"));
307 status
= sid_array_from_info3(talloc_tos(), info3
,
311 if (!NT_STATUS_IS_OK(status
)) {
316 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
318 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
320 DEBUG(3, ("could not add aliases: %s\n",
326 security_token_debug(DBGC_CLASS
, 10, token
);
328 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
329 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
330 &require_membership_of_sid
[i
])));
331 if (nt_token_check_sid(&require_membership_of_sid
[i
],
333 DEBUG(10, ("Access ok\n"));
339 /* Do not distinguish this error from a wrong username/pw */
342 return NT_STATUS_LOGON_FAILURE
;
345 struct winbindd_domain
*find_auth_domain(uint8_t flags
,
346 const char *domain_name
)
348 struct winbindd_domain
*domain
;
351 domain
= find_domain_from_name_noinit(domain_name
);
352 if (domain
== NULL
) {
353 DEBUG(3, ("Authentication for domain [%s] refused "
354 "as it is not a trusted domain\n",
360 if (strequal(domain_name
, get_global_sam_name())) {
361 return find_domain_from_name_noinit(domain_name
);
364 /* we can auth against trusted domains */
365 if (flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
366 domain
= find_domain_from_name_noinit(domain_name
);
367 if (domain
== NULL
) {
368 DEBUG(3, ("Authentication for domain [%s] skipped "
369 "as it is not a trusted domain\n",
376 return find_our_domain();
379 static void fill_in_password_policy(struct winbindd_response
*r
,
380 const struct samr_DomInfo1
*p
)
382 r
->data
.auth
.policy
.min_length_password
=
383 p
->min_password_length
;
384 r
->data
.auth
.policy
.password_history
=
385 p
->password_history_length
;
386 r
->data
.auth
.policy
.password_properties
=
387 p
->password_properties
;
388 r
->data
.auth
.policy
.expire
=
389 nt_time_to_unix_abs((NTTIME
*)&(p
->max_password_age
));
390 r
->data
.auth
.policy
.min_passwordage
=
391 nt_time_to_unix_abs((NTTIME
*)&(p
->min_password_age
));
394 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
395 struct winbindd_response
*response
)
397 TALLOC_CTX
*frame
= talloc_stackframe();
398 struct winbindd_methods
*methods
;
400 struct samr_DomInfo1 password_policy
;
402 if ( !winbindd_can_contact_domain( domain
) ) {
403 DEBUG(5,("fillup_password_policy: No inbound trust to "
404 "contact domain %s\n", domain
->name
));
405 status
= NT_STATUS_NOT_SUPPORTED
;
409 methods
= domain
->methods
;
411 status
= methods
->password_policy(domain
, talloc_tos(), &password_policy
);
412 if (NT_STATUS_IS_ERR(status
)) {
416 fill_in_password_policy(response
, &password_policy
);
423 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
425 uint16
*lockout_threshold
)
427 struct winbindd_methods
*methods
;
428 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
429 struct samr_DomInfo12 lockout_policy
;
431 *lockout_threshold
= 0;
433 methods
= domain
->methods
;
435 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
436 if (NT_STATUS_IS_ERR(status
)) {
440 *lockout_threshold
= lockout_policy
.lockout_threshold
;
445 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
447 uint32
*password_properties
)
449 struct winbindd_methods
*methods
;
450 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
451 struct samr_DomInfo1 password_policy
;
453 *password_properties
= 0;
455 methods
= domain
->methods
;
457 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
458 if (NT_STATUS_IS_ERR(status
)) {
462 *password_properties
= password_policy
.password_properties
;
469 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
472 const char **user_ccache_file
)
474 /* accept FILE and WRFILE as krb5_cc_type from the client and then
475 * build the full ccname string based on the user's uid here -
478 const char *gen_cc
= NULL
;
481 if (strequal(type
, "FILE")) {
482 gen_cc
= talloc_asprintf(
483 mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
485 if (strequal(type
, "WRFILE")) {
486 gen_cc
= talloc_asprintf(
487 mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
491 *user_ccache_file
= gen_cc
;
493 if (gen_cc
== NULL
) {
494 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
496 if (gen_cc
== NULL
) {
497 DEBUG(0,("out of memory\n"));
501 DEBUG(10, ("using ccache: %s%s\n", gen_cc
,
502 (*user_ccache_file
== NULL
) ? " (internal)":""));
509 uid_t
get_uid_from_request(struct winbindd_request
*request
)
513 uid
= request
->data
.auth
.uid
;
516 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
522 /**********************************************************************
523 Authenticate a user with a clear text password using Kerberos and fill up
525 **********************************************************************/
527 static NTSTATUS
winbindd_raw_kerberos_login(TALLOC_CTX
*mem_ctx
,
528 struct winbindd_domain
*domain
,
531 const char *krb5_cc_type
,
533 struct netr_SamInfo3
**info3
,
537 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
538 krb5_error_code krb5_ret
;
539 const char *cc
= NULL
;
540 const char *principal_s
= NULL
;
541 const char *service
= NULL
;
543 fstring name_domain
, name_user
;
544 time_t ticket_lifetime
= 0;
545 time_t renewal_until
= 0;
547 time_t time_offset
= 0;
548 const char *user_ccache_file
;
549 struct PAC_LOGON_INFO
*logon_info
= NULL
;
554 * prepare a krb5_cc_cache string for the user */
557 DEBUG(0,("no valid uid\n"));
560 cc
= generate_krb5_ccache(mem_ctx
,
565 return NT_STATUS_NO_MEMORY
;
570 * get kerberos properties */
572 if (domain
->private_data
) {
573 ads
= (ADS_STRUCT
*)domain
->private_data
;
574 time_offset
= ads
->auth
.time_offset
;
579 * do kerberos auth and setup ccache as the user */
581 parse_domain_user(user
, name_domain
, name_user
);
583 realm
= domain
->alt_name
;
586 principal_s
= talloc_asprintf(mem_ctx
, "%s@%s", name_user
, realm
);
587 if (principal_s
== NULL
) {
588 return NT_STATUS_NO_MEMORY
;
591 service
= talloc_asprintf(mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
592 if (service
== NULL
) {
593 return NT_STATUS_NO_MEMORY
;
596 /* if this is a user ccache, we need to act as the user to let the krb5
597 * library handle the chown, etc. */
599 /************************ ENTERING NON-ROOT **********************/
601 if (user_ccache_file
!= NULL
) {
602 set_effective_uid(uid
);
603 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
606 result
= kerberos_return_pac(mem_ctx
,
615 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
618 if (user_ccache_file
!= NULL
) {
619 gain_root_privilege();
622 /************************ RETURNED TO ROOT **********************/
624 if (!NT_STATUS_IS_OK(result
)) {
628 *info3
= &logon_info
->info3
;
630 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
633 /* if we had a user's ccache then return that string for the pam
636 if (user_ccache_file
!= NULL
) {
638 fstrcpy(krb5ccname
, user_ccache_file
);
640 result
= add_ccache_to_list(principal_s
,
651 if (!NT_STATUS_IS_OK(result
)) {
652 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
657 /* need to delete the memory cred cache, it is not used anymore */
659 krb5_ret
= ads_kdestroy(cc
);
661 DEBUG(3,("winbindd_raw_kerberos_login: "
662 "could not destroy krb5 credential cache: "
663 "%s\n", error_message(krb5_ret
)));
672 /* we could have created a new credential cache with a valid tgt in it
673 * but we werent able to get or verify the service ticket for this
674 * local host and therefor didn't get the PAC, we need to remove that
675 * cache entirely now */
677 krb5_ret
= ads_kdestroy(cc
);
679 DEBUG(3,("winbindd_raw_kerberos_login: "
680 "could not destroy krb5 credential cache: "
681 "%s\n", error_message(krb5_ret
)));
684 if (!NT_STATUS_IS_OK(remove_ccache(user
))) {
685 DEBUG(3,("winbindd_raw_kerberos_login: "
686 "could not remove ccache for user %s\n",
692 return NT_STATUS_NOT_SUPPORTED
;
693 #endif /* HAVE_KRB5 */
696 /****************************************************************
697 ****************************************************************/
699 bool check_request_flags(uint32_t flags
)
701 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
702 WBFLAG_PAM_INFO3_TEXT
|
703 WBFLAG_PAM_INFO3_NDR
;
705 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
706 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
707 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
708 !(flags
& flags_edata
) ) {
712 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
718 /****************************************************************
719 ****************************************************************/
721 static NTSTATUS
append_auth_data(TALLOC_CTX
*mem_ctx
,
722 struct winbindd_response
*resp
,
723 uint32_t request_flags
,
724 struct netr_SamInfo3
*info3
,
725 const char *name_domain
,
726 const char *name_user
)
730 if (request_flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
731 memcpy(resp
->data
.auth
.user_session_key
,
733 sizeof(resp
->data
.auth
.user_session_key
)
737 if (request_flags
& WBFLAG_PAM_LMKEY
) {
738 memcpy(resp
->data
.auth
.first_8_lm_hash
,
739 info3
->base
.LMSessKey
.key
,
740 sizeof(resp
->data
.auth
.first_8_lm_hash
)
744 if (request_flags
& WBFLAG_PAM_UNIX_NAME
) {
745 result
= append_unix_username(mem_ctx
, resp
,
746 info3
, name_domain
, name_user
);
747 if (!NT_STATUS_IS_OK(result
)) {
748 DEBUG(10,("Failed to append Unix Username: %s\n",
754 /* currently, anything from here on potentially overwrites extra_data. */
756 if (request_flags
& WBFLAG_PAM_INFO3_NDR
) {
757 result
= append_info3_as_ndr(mem_ctx
, resp
, info3
);
758 if (!NT_STATUS_IS_OK(result
)) {
759 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
765 if (request_flags
& WBFLAG_PAM_INFO3_TEXT
) {
766 result
= append_info3_as_txt(mem_ctx
, resp
, info3
);
767 if (!NT_STATUS_IS_OK(result
)) {
768 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
774 if (request_flags
& WBFLAG_PAM_AFS_TOKEN
) {
775 result
= append_afs_token(mem_ctx
, resp
,
776 info3
, name_domain
, name_user
);
777 if (!NT_STATUS_IS_OK(result
)) {
778 DEBUG(10,("Failed to append AFS token: %s\n",
787 static NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
788 struct winbindd_cli_state
*state
,
789 struct netr_SamInfo3
**info3
)
791 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
792 uint16 max_allowed_bad_attempts
;
793 fstring name_domain
, name_user
;
795 enum lsa_SidType type
;
796 uchar new_nt_pass
[NT_HASH_LEN
];
797 const uint8
*cached_nt_pass
;
798 const uint8
*cached_salt
;
799 struct netr_SamInfo3
*my_info3
;
800 time_t kickoff_time
, must_change_time
;
801 bool password_good
= false;
803 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
810 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
812 /* Parse domain and username */
814 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
817 if (!lookup_cached_name(name_domain
,
821 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
822 return NT_STATUS_NO_SUCH_USER
;
825 if (type
!= SID_NAME_USER
) {
826 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
827 return NT_STATUS_LOGON_FAILURE
;
830 result
= winbindd_get_creds(domain
,
836 if (!NT_STATUS_IS_OK(result
)) {
837 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
843 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
845 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
846 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
848 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
852 /* In this case we didn't store the nt_hash itself,
853 but the MD5 combination of salt + nt_hash. */
854 uchar salted_hash
[NT_HASH_LEN
];
855 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
857 password_good
= (memcmp(cached_nt_pass
, salted_hash
,
860 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
861 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
867 /* User *DOES* know the password, update logon_time and reset
870 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
872 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
873 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
876 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
877 return NT_STATUS_ACCOUNT_DISABLED
;
880 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
881 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
884 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
885 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
888 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
889 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
892 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
893 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
894 my_info3
->base
.acct_flags
));
895 return NT_STATUS_LOGON_FAILURE
;
898 kickoff_time
= nt_time_to_unix(my_info3
->base
.acct_expiry
);
899 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
900 return NT_STATUS_ACCOUNT_EXPIRED
;
903 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
904 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
905 /* we allow grace logons when the password has expired */
906 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
907 /* return NT_STATUS_PASSWORD_EXPIRED; */
912 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
913 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
914 ((tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
) ||
915 /* used to cope with the case winbindd starting without network. */
916 !strequal(tdc_domain
->domain_name
, tdc_domain
->dns_name
))) {
919 const char *cc
= NULL
;
921 const char *principal_s
= NULL
;
922 const char *service
= NULL
;
923 const char *user_ccache_file
;
925 uid
= get_uid_from_request(state
->request
);
927 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
928 return NT_STATUS_INVALID_PARAMETER
;
931 cc
= generate_krb5_ccache(state
->mem_ctx
,
932 state
->request
->data
.auth
.krb5_cc_type
,
933 state
->request
->data
.auth
.uid
,
936 return NT_STATUS_NO_MEMORY
;
939 realm
= domain
->alt_name
;
942 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
943 if (principal_s
== NULL
) {
944 return NT_STATUS_NO_MEMORY
;
947 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
948 if (service
== NULL
) {
949 return NT_STATUS_NO_MEMORY
;
952 if (user_ccache_file
!= NULL
) {
954 fstrcpy(state
->response
->data
.auth
.krb5ccname
,
957 result
= add_ccache_to_list(principal_s
,
960 state
->request
->data
.auth
.user
,
964 time(NULL
) + lp_winbind_cache_time(),
965 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
968 if (!NT_STATUS_IS_OK(result
)) {
969 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
970 "to add ccache to list: %s\n",
975 #endif /* HAVE_KRB5 */
977 /* FIXME: we possibly should handle logon hours as well (does xp when
978 * offline?) see auth/auth_sam.c:sam_account_ok for details */
980 unix_to_nt_time(&my_info3
->base
.last_logon
, time(NULL
));
981 my_info3
->base
.bad_password_count
= 0;
983 result
= winbindd_update_creds_by_info3(domain
,
984 state
->request
->data
.auth
.user
,
985 state
->request
->data
.auth
.pass
,
987 if (!NT_STATUS_IS_OK(result
)) {
988 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
997 /* User does *NOT* know the correct password, modify info3 accordingly */
999 /* failure of this is not critical */
1000 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1001 if (!NT_STATUS_IS_OK(result
)) {
1002 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1003 "Won't be able to honour account lockout policies\n"));
1006 /* increase counter */
1007 my_info3
->base
.bad_password_count
++;
1009 if (max_allowed_bad_attempts
== 0) {
1014 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1016 uint32 password_properties
;
1018 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1019 if (!NT_STATUS_IS_OK(result
)) {
1020 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1023 if ((my_info3
->base
.rid
!= DOMAIN_RID_ADMINISTRATOR
) ||
1024 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1025 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1030 result
= winbindd_update_creds_by_info3(domain
,
1031 state
->request
->data
.auth
.user
,
1035 if (!NT_STATUS_IS_OK(result
)) {
1036 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1037 nt_errstr(result
)));
1040 return NT_STATUS_LOGON_FAILURE
;
1043 static NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1044 struct winbindd_cli_state
*state
,
1045 struct netr_SamInfo3
**info3
)
1047 struct winbindd_domain
*contact_domain
;
1048 fstring name_domain
, name_user
;
1051 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1053 /* Parse domain and username */
1055 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1057 /* what domain should we contact? */
1060 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1061 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1062 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1063 result
= NT_STATUS_NO_SUCH_USER
;
1068 if (is_myname(name_domain
)) {
1069 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1070 result
= NT_STATUS_NO_SUCH_USER
;
1074 contact_domain
= find_domain_from_name(name_domain
);
1075 if (contact_domain
== NULL
) {
1076 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1077 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1079 contact_domain
= find_our_domain();
1083 if (contact_domain
->initialized
&&
1084 contact_domain
->active_directory
) {
1088 if (!contact_domain
->initialized
) {
1089 init_dc_connection(contact_domain
);
1092 if (!contact_domain
->active_directory
) {
1093 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1094 return NT_STATUS_INVALID_LOGON_TYPE
;
1097 result
= winbindd_raw_kerberos_login(
1098 state
->mem_ctx
, contact_domain
,
1099 state
->request
->data
.auth
.user
,
1100 state
->request
->data
.auth
.pass
,
1101 state
->request
->data
.auth
.krb5_cc_type
,
1102 get_uid_from_request(state
->request
),
1103 info3
, state
->response
->data
.auth
.krb5ccname
);
1108 static NTSTATUS
winbindd_dual_auth_passdb(TALLOC_CTX
*mem_ctx
,
1109 const char *domain
, const char *user
,
1110 const DATA_BLOB
*challenge
,
1111 const DATA_BLOB
*lm_resp
,
1112 const DATA_BLOB
*nt_resp
,
1113 struct netr_SamInfo3
**pinfo3
)
1115 struct auth_usersupplied_info
*user_info
= NULL
;
1118 status
= make_user_info(&user_info
, user
, user
, domain
, domain
,
1119 global_myname(), lm_resp
, nt_resp
, NULL
, NULL
,
1120 NULL
, AUTH_PASSWORD_RESPONSE
);
1121 if (!NT_STATUS_IS_OK(status
)) {
1122 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status
)));
1126 /* We don't want any more mapping of the username */
1127 user_info
->mapped_state
= True
;
1129 status
= check_sam_security_info3(challenge
, talloc_tos(), user_info
,
1131 free_user_info(&user_info
);
1132 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain
,
1133 user
, nt_errstr(status
)));
1137 static NTSTATUS
winbind_samlogon_retry_loop(struct winbindd_domain
*domain
,
1138 TALLOC_CTX
*mem_ctx
,
1139 uint32_t logon_parameters
,
1141 const char *username
,
1142 const char *domainname
,
1143 const char *workstation
,
1144 const uint8_t chal
[8],
1145 DATA_BLOB lm_response
,
1146 DATA_BLOB nt_response
,
1147 struct netr_SamInfo3
**info3
)
1154 struct rpc_pipe_client
*netlogon_pipe
;
1155 const struct pipe_auth_data
*auth
;
1156 uint32_t neg_flags
= 0;
1158 ZERO_STRUCTP(info3
);
1161 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1163 if (!NT_STATUS_IS_OK(result
)) {
1164 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1165 nt_errstr(result
)));
1168 auth
= netlogon_pipe
->auth
;
1169 if (netlogon_pipe
->dc
) {
1170 neg_flags
= netlogon_pipe
->dc
->negotiate_flags
;
1173 /* It is really important to try SamLogonEx here,
1174 * because in a clustered environment, we want to use
1175 * one machine account from multiple physical
1178 * With a normal SamLogon call, we must keep the
1179 * credentials chain updated and intact between all
1180 * users of the machine account (which would imply
1181 * cross-node communication for every NTLM logon).
1183 * (The credentials chain is not per NETLOGON pipe
1184 * connection, but globally on the server/client pair
1187 * When using SamLogonEx, the credentials are not
1188 * supplied, but the session key is implied by the
1189 * wrapping SamLogon context.
1191 * -- abartlet 21 April 2008
1193 * It's also important to use NetlogonValidationSamInfo4 (6),
1194 * because it relies on the rpc transport encryption
1195 * and avoids using the global netlogon schannel
1196 * session key to en/decrypt secret information
1197 * like the user_session_key for network logons.
1199 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1200 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1201 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1202 * are the indication that the server supports
1203 * NetlogonValidationSamInfo4 (6). And it must only
1204 * be used if "SealSecureChannel" is used.
1206 * -- metze 4 February 2011
1210 domain
->can_do_validation6
= false;
1211 } else if (auth
->auth_type
!= DCERPC_AUTH_TYPE_SCHANNEL
) {
1212 domain
->can_do_validation6
= false;
1213 } else if (auth
->auth_level
!= DCERPC_AUTH_LEVEL_PRIVACY
) {
1214 domain
->can_do_validation6
= false;
1215 } else if (!(neg_flags
& NETLOGON_NEG_CROSS_FOREST_TRUSTS
)) {
1216 domain
->can_do_validation6
= false;
1217 } else if (!(neg_flags
& NETLOGON_NEG_AUTHENTICATED_RPC
)) {
1218 domain
->can_do_validation6
= false;
1221 if (domain
->can_do_samlogon_ex
) {
1222 result
= rpccli_netlogon_sam_network_logon_ex(
1226 server
, /* server name */
1227 username
, /* user name */
1228 domainname
, /* target domain */
1229 workstation
, /* workstation */
1231 domain
->can_do_validation6
? 6 : 3,
1236 result
= rpccli_netlogon_sam_network_logon(
1240 server
, /* server name */
1241 username
, /* user name */
1242 domainname
, /* target domain */
1243 workstation
, /* workstation */
1245 domain
->can_do_validation6
? 6 : 3,
1251 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
)
1252 && domain
->can_do_samlogon_ex
) {
1253 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1254 "retrying with NetSamLogon\n"));
1255 domain
->can_do_samlogon_ex
= false;
1257 * It's likely that the server also does not support
1258 * validation level 6
1260 domain
->can_do_validation6
= false;
1265 if (domain
->can_do_validation6
&&
1266 (NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_INFO_CLASS
) ||
1267 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_PARAMETER
) ||
1268 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
))) {
1269 DEBUG(3,("Got a DC that can not do validation level 6, "
1270 "retrying with level 3\n"));
1271 domain
->can_do_validation6
= false;
1277 * we increment this after the "feature negotiation"
1278 * for can_do_samlogon_ex and can_do_validation6
1282 /* We have to try a second time as cm_connect_netlogon
1283 might not yet have noticed that the DC has killed
1286 if (!rpccli_is_connected(netlogon_pipe
)) {
1291 /* if we get access denied, a possible cause was that we had
1292 and open connection to the DC, but someone changed our
1293 machine account password out from underneath us using 'net
1294 rpc changetrustpw' */
1296 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1297 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1298 "ACCESS_DENIED. Maybe the trust account "
1299 "password was changed and we didn't know it. "
1300 "Killing connections to domain %s\n",
1302 invalidate_cm_connection(&domain
->conn
);
1306 } while ( (attempts
< 2) && retry
);
1311 static NTSTATUS
winbindd_dual_pam_auth_samlogon(TALLOC_CTX
*mem_ctx
,
1312 struct winbindd_domain
*domain
,
1315 uint32_t request_flags
,
1316 struct netr_SamInfo3
**info3
)
1322 unsigned char local_nt_response
[24];
1323 fstring name_domain
, name_user
;
1325 struct netr_SamInfo3
*my_info3
= NULL
;
1329 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1331 /* Parse domain and username */
1333 parse_domain_user(user
, name_domain
, name_user
);
1335 /* do password magic */
1337 generate_random_buffer(chal
, sizeof(chal
));
1339 if (lp_client_ntlmv2_auth()) {
1340 DATA_BLOB server_chal
;
1341 DATA_BLOB names_blob
;
1342 server_chal
= data_blob_const(chal
, 8);
1344 /* note that the 'workgroup' here is for the local
1345 machine. The 'server name' must match the
1346 'workstation' passed to the actual SamLogon call.
1348 names_blob
= NTLMv2_generate_names_blob(
1349 mem_ctx
, global_myname(), lp_workgroup());
1351 if (!SMBNTLMv2encrypt(mem_ctx
, name_user
, name_domain
,
1355 &lm_resp
, &nt_resp
, NULL
, NULL
)) {
1356 data_blob_free(&names_blob
);
1357 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1358 result
= NT_STATUS_NO_MEMORY
;
1361 data_blob_free(&names_blob
);
1363 lm_resp
= data_blob_null
;
1364 SMBNTencrypt(pass
, chal
, local_nt_response
);
1366 nt_resp
= data_blob_talloc(mem_ctx
, local_nt_response
,
1367 sizeof(local_nt_response
));
1370 if (strequal(name_domain
, get_global_sam_name())) {
1371 DATA_BLOB chal_blob
= data_blob_const(chal
, sizeof(chal
));
1373 result
= winbindd_dual_auth_passdb(
1374 mem_ctx
, name_domain
, name_user
,
1375 &chal_blob
, &lm_resp
, &nt_resp
, info3
);
1379 /* check authentication loop */
1381 result
= winbind_samlogon_retry_loop(domain
,
1392 if (!NT_STATUS_IS_OK(result
)) {
1396 /* handle the case where a NT4 DC does not fill in the acct_flags in
1397 * the samlogon reply info3. When accurate info3 is required by the
1398 * caller, we look up the account flags ourselve - gd */
1400 if ((request_flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1401 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1403 struct rpc_pipe_client
*samr_pipe
;
1404 struct policy_handle samr_domain_handle
, user_pol
;
1405 union samr_UserInfo
*info
= NULL
;
1406 NTSTATUS status_tmp
, result_tmp
;
1408 struct dcerpc_binding_handle
*b
;
1410 status_tmp
= cm_connect_sam(domain
, mem_ctx
,
1411 &samr_pipe
, &samr_domain_handle
);
1413 if (!NT_STATUS_IS_OK(status_tmp
)) {
1414 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1415 nt_errstr(status_tmp
)));
1419 b
= samr_pipe
->binding_handle
;
1421 status_tmp
= dcerpc_samr_OpenUser(b
, mem_ctx
,
1422 &samr_domain_handle
,
1423 MAXIMUM_ALLOWED_ACCESS
,
1428 if (!NT_STATUS_IS_OK(status_tmp
)) {
1429 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1430 nt_errstr(status_tmp
)));
1433 if (!NT_STATUS_IS_OK(result_tmp
)) {
1434 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1435 nt_errstr(result_tmp
)));
1439 status_tmp
= dcerpc_samr_QueryUserInfo(b
, mem_ctx
,
1445 if (!NT_STATUS_IS_OK(status_tmp
)) {
1446 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1447 nt_errstr(status_tmp
)));
1448 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1451 if (!NT_STATUS_IS_OK(result_tmp
)) {
1452 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1453 nt_errstr(result_tmp
)));
1454 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1458 acct_flags
= info
->info16
.acct_flags
;
1460 if (acct_flags
== 0) {
1461 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1465 my_info3
->base
.acct_flags
= acct_flags
;
1467 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1469 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1477 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1478 struct winbindd_cli_state
*state
)
1480 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1481 NTSTATUS krb5_result
= NT_STATUS_OK
;
1482 fstring name_domain
, name_user
;
1484 fstring domain_user
;
1485 struct netr_SamInfo3
*info3
= NULL
;
1486 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1488 /* Ensure null termination */
1489 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1491 /* Ensure null termination */
1492 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1494 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1495 state
->request
->data
.auth
.user
));
1497 /* Parse domain and username */
1499 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1500 state
->request
->data
.auth
.user
,
1503 /* If the name normalization didnt' actually do anything,
1504 just use the original name */
1506 if (!NT_STATUS_IS_OK(name_map_status
) &&
1507 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1509 mapped_user
= state
->request
->data
.auth
.user
;
1512 parse_domain_user(mapped_user
, name_domain
, name_user
);
1514 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1515 fstr_sprintf( domain_user
, "%s%c%s", name_domain
,
1516 *lp_winbind_separator(),
1518 safe_strcpy( state
->request
->data
.auth
.user
, domain_user
,
1519 sizeof(state
->request
->data
.auth
.user
)-1 );
1522 if (!domain
->online
) {
1523 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1524 if (domain
->startup
) {
1525 /* Logons are very important to users. If we're offline and
1526 we get a request within the first 30 seconds of startup,
1527 try very hard to find a DC and go online. */
1529 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1530 "request in startup mode.\n", domain
->name
));
1532 winbindd_flush_negative_conn_cache(domain
);
1533 result
= init_dc_connection(domain
);
1537 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1539 /* Check for Kerberos authentication */
1540 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1542 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1543 /* save for later */
1544 krb5_result
= result
;
1547 if (NT_STATUS_IS_OK(result
)) {
1548 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1549 goto process_result
;
1551 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1554 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1555 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1556 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1557 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1558 set_domain_offline( domain
);
1562 /* there are quite some NT_STATUS errors where there is no
1563 * point in retrying with a samlogon, we explictly have to take
1564 * care not to increase the bad logon counter on the DC */
1566 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1567 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1568 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1569 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1570 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1571 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1572 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1573 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1574 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1575 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1579 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1580 DEBUG(3,("falling back to samlogon\n"));
1588 /* Check for Samlogon authentication */
1589 if (domain
->online
) {
1590 result
= winbindd_dual_pam_auth_samlogon(
1591 state
->mem_ctx
, domain
,
1592 state
->request
->data
.auth
.user
,
1593 state
->request
->data
.auth
.pass
,
1594 state
->request
->flags
,
1597 if (NT_STATUS_IS_OK(result
)) {
1598 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1599 /* add the Krb5 err if we have one */
1600 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1601 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1603 goto process_result
;
1606 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1607 nt_errstr(result
)));
1609 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1610 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1611 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1613 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1614 set_domain_offline( domain
);
1618 if (domain
->online
) {
1619 /* We're still online - fail. */
1625 /* Check for Cached logons */
1626 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1627 lp_winbind_offline_logon()) {
1629 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1631 if (NT_STATUS_IS_OK(result
)) {
1632 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1633 goto process_result
;
1635 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1642 if (NT_STATUS_IS_OK(result
)) {
1644 struct dom_sid user_sid
;
1646 /* In all codepaths where result == NT_STATUS_OK info3 must have
1647 been initialized. */
1649 result
= NT_STATUS_INTERNAL_ERROR
;
1653 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1656 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1658 netsamlogon_cache_store(name_user
, info3
);
1660 /* save name_to_sid info as early as possible (only if
1661 this is our primary domain so we don't invalidate
1662 the cache entry by storing the seq_num for the wrong
1664 if ( domain
->primary
) {
1665 cache_name2sid(domain
, name_domain
, name_user
,
1666 SID_NAME_USER
, &user_sid
);
1669 /* Check if the user is in the right group */
1671 result
= check_info3_in_group(
1673 state
->request
->data
.auth
.require_membership_of_sid
);
1674 if (!NT_STATUS_IS_OK(result
)) {
1675 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1676 state
->request
->data
.auth
.user
,
1677 state
->request
->data
.auth
.require_membership_of_sid
));
1681 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1682 state
->request
->flags
, info3
,
1683 name_domain
, name_user
);
1684 if (!NT_STATUS_IS_OK(result
)) {
1688 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
1689 && lp_winbind_offline_logon()) {
1691 result
= winbindd_store_creds(domain
,
1692 state
->request
->data
.auth
.user
,
1693 state
->request
->data
.auth
.pass
,
1697 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1698 struct winbindd_domain
*our_domain
= find_our_domain();
1700 /* This is not entirely correct I believe, but it is
1701 consistent. Only apply the password policy settings
1702 too warn users for our own domain. Cannot obtain these
1703 from trusted DCs all the time so don't do it at all.
1706 result
= NT_STATUS_NOT_SUPPORTED
;
1707 if (our_domain
== domain
) {
1708 result
= fillup_password_policy(
1709 our_domain
, state
->response
);
1712 if (!NT_STATUS_IS_OK(result
)
1713 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1715 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1716 domain
->name
, nt_errstr(result
)));
1721 result
= NT_STATUS_OK
;
1725 /* give us a more useful (more correct?) error code */
1726 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1727 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1728 result
= NT_STATUS_NO_LOGON_SERVERS
;
1731 set_auth_errors(state
->response
, result
);
1733 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1734 state
->request
->data
.auth
.user
,
1735 state
->response
->data
.auth
.nt_status_string
,
1736 state
->response
->data
.auth
.pam_error
));
1738 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1741 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1742 struct winbindd_cli_state
*state
)
1745 struct netr_SamInfo3
*info3
= NULL
;
1746 const char *name_user
= NULL
;
1747 const char *name_domain
= NULL
;
1748 const char *workstation
;
1750 DATA_BLOB lm_resp
, nt_resp
;
1752 /* This is child-only, so no check for privileged access is needed
1755 /* Ensure null termination */
1756 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1757 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1759 name_user
= state
->request
->data
.auth_crap
.user
;
1760 name_domain
= state
->request
->data
.auth_crap
.domain
;
1761 workstation
= state
->request
->data
.auth_crap
.workstation
;
1763 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1764 name_domain
, name_user
));
1766 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1767 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1768 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1769 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1770 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1771 state
->request
->data
.auth_crap
.lm_resp_len
,
1772 state
->request
->data
.auth_crap
.nt_resp_len
));
1773 result
= NT_STATUS_INVALID_PARAMETER
;
1778 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1779 state
->request
->data
.auth_crap
.lm_resp_len
);
1781 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1782 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1783 state
->request
->extra_data
.data
,
1784 state
->request
->data
.auth_crap
.nt_resp_len
);
1786 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1787 state
->request
->data
.auth_crap
.nt_resp
,
1788 state
->request
->data
.auth_crap
.nt_resp_len
);
1791 if (strequal(name_domain
, get_global_sam_name())) {
1792 DATA_BLOB chal_blob
= data_blob_const(
1793 state
->request
->data
.auth_crap
.chal
,
1794 sizeof(state
->request
->data
.auth_crap
.chal
));
1796 result
= winbindd_dual_auth_passdb(
1797 state
->mem_ctx
, name_domain
, name_user
,
1798 &chal_blob
, &lm_resp
, &nt_resp
, &info3
);
1799 goto process_result
;
1802 result
= winbind_samlogon_retry_loop(domain
,
1804 state
->request
->data
.auth_crap
.logon_parameters
,
1808 /* Bug #3248 - found by Stefan Burkei. */
1809 workstation
, /* We carefully set this above so use it... */
1810 state
->request
->data
.auth_crap
.chal
,
1814 if (!NT_STATUS_IS_OK(result
)) {
1820 if (NT_STATUS_IS_OK(result
)) {
1821 struct dom_sid user_sid
;
1823 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1825 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1827 netsamlogon_cache_store(name_user
, info3
);
1829 /* Check if the user is in the right group */
1831 result
= check_info3_in_group(
1833 state
->request
->data
.auth_crap
.require_membership_of_sid
);
1834 if (!NT_STATUS_IS_OK(result
)) {
1835 DEBUG(3, ("User %s is not in the required group (%s), so "
1836 "crap authentication is rejected\n",
1837 state
->request
->data
.auth_crap
.user
,
1838 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1842 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1843 state
->request
->flags
, info3
,
1844 name_domain
, name_user
);
1845 if (!NT_STATUS_IS_OK(result
)) {
1852 /* give us a more useful (more correct?) error code */
1853 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1854 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1855 result
= NT_STATUS_NO_LOGON_SERVERS
;
1858 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1859 result
= nt_status_squash(result
);
1862 set_auth_errors(state
->response
, result
);
1864 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1865 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1868 state
->response
->data
.auth
.nt_status_string
,
1869 state
->response
->data
.auth
.pam_error
));
1871 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1874 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
1875 struct winbindd_cli_state
*state
)
1878 char *newpass
= NULL
;
1879 struct policy_handle dom_pol
;
1880 struct rpc_pipe_client
*cli
= NULL
;
1881 bool got_info
= false;
1882 struct samr_DomInfo1
*info
= NULL
;
1883 struct userPwdChangeFailureInformation
*reject
= NULL
;
1884 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1885 fstring domain
, user
;
1886 struct dcerpc_binding_handle
*b
= NULL
;
1888 ZERO_STRUCT(dom_pol
);
1890 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
1891 state
->request
->data
.auth
.user
));
1893 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
1897 /* Change password */
1899 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
1900 newpass
= state
->request
->data
.chauthtok
.newpass
;
1902 /* Initialize reject reason */
1903 state
->response
->data
.auth
.reject_reason
= Undefined
;
1905 /* Get sam handle */
1907 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1909 if (!NT_STATUS_IS_OK(result
)) {
1910 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
1914 b
= cli
->binding_handle
;
1916 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
1923 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1925 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
1927 fill_in_password_policy(state
->response
, info
);
1929 state
->response
->data
.auth
.reject_reason
=
1930 reject
->extendedFailureReason
;
1935 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1936 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1937 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1938 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1940 /* only fallback when the chgpasswd_user3 call is not supported */
1941 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
) ||
1942 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) ||
1943 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
) ||
1944 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
1946 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1947 nt_errstr(result
)));
1949 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
1951 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1952 Map to the same status code as Windows 2003. */
1954 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
1955 result
= NT_STATUS_PASSWORD_RESTRICTION
;
1961 if (NT_STATUS_IS_OK(result
)
1962 && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
1963 && lp_winbind_offline_logon()) {
1964 result
= winbindd_update_creds_by_name(contact_domain
, user
,
1966 /* Again, this happens when we login from gdm or xdm
1967 * and the password expires, *BUT* cached crendentials
1968 * doesn't exist. winbindd_update_creds_by_name()
1969 * returns NT_STATUS_NO_SUCH_USER.
1970 * This is not a failure.
1973 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
1974 result
= NT_STATUS_OK
;
1977 if (!NT_STATUS_IS_OK(result
)) {
1978 DEBUG(10, ("Failed to store creds: %s\n",
1979 nt_errstr(result
)));
1980 goto process_result
;
1984 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
1986 NTSTATUS policy_ret
;
1988 policy_ret
= fillup_password_policy(
1989 contact_domain
, state
->response
);
1991 /* failure of this is non critical, it will just provide no
1992 * additional information to the client why the change has
1993 * failed - Guenther */
1995 if (!NT_STATUS_IS_OK(policy_ret
)) {
1996 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
1997 goto process_result
;
2003 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2004 /* FIXME: internal rpc pipe does not cache handles yet */
2006 if (is_valid_policy_hnd(&dom_pol
)) {
2008 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2014 set_auth_errors(state
->response
, result
);
2016 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2017 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2020 state
->response
->data
.auth
.nt_status_string
,
2021 state
->response
->data
.auth
.pam_error
));
2023 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2026 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2027 struct winbindd_cli_state
*state
)
2029 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2031 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2032 state
->request
->data
.logoff
.user
));
2034 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2035 result
= NT_STATUS_OK
;
2036 goto process_result
;
2039 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2040 result
= NT_STATUS_OK
;
2041 goto process_result
;
2046 if (state
->request
->data
.logoff
.uid
< 0) {
2047 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2048 goto process_result
;
2051 /* what we need here is to find the corresponding krb5 ccache name *we*
2052 * created for a given username and destroy it */
2054 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2055 result
= NT_STATUS_OK
;
2056 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2057 goto process_result
;
2060 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2061 state
->request
->data
.logoff
.uid
,
2062 state
->request
->data
.logoff
.krb5ccname
)) {
2063 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2064 goto process_result
;
2067 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2068 if (!NT_STATUS_IS_OK(result
)) {
2069 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2070 nt_errstr(result
)));
2071 goto process_result
;
2075 result
= NT_STATUS_NOT_SUPPORTED
;
2081 set_auth_errors(state
->response
, result
);
2083 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2086 /* Change user password with auth crap*/
2088 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2091 DATA_BLOB new_nt_password
;
2092 DATA_BLOB old_nt_hash_enc
;
2093 DATA_BLOB new_lm_password
;
2094 DATA_BLOB old_lm_hash_enc
;
2095 fstring domain
,user
;
2096 struct policy_handle dom_pol
;
2097 struct winbindd_domain
*contact_domain
= domainSt
;
2098 struct rpc_pipe_client
*cli
= NULL
;
2099 struct dcerpc_binding_handle
*b
= NULL
;
2101 ZERO_STRUCT(dom_pol
);
2103 /* Ensure null termination */
2104 state
->request
->data
.chng_pswd_auth_crap
.user
[
2105 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2106 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2107 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2111 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2112 (unsigned long)state
->pid
,
2113 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2114 state
->request
->data
.chng_pswd_auth_crap
.user
));
2116 if (lp_winbind_offline_logon()) {
2117 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2118 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2119 result
= NT_STATUS_ACCESS_DENIED
;
2123 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2124 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2126 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2130 DEBUG(3,("no domain specified with username (%s) - "
2132 state
->request
->data
.chng_pswd_auth_crap
.user
));
2133 result
= NT_STATUS_NO_SUCH_USER
;
2138 if (!*domain
&& lp_winbind_use_default_domain()) {
2139 fstrcpy(domain
,(char *)lp_workgroup());
2143 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2146 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2147 (unsigned long)state
->pid
, domain
, user
));
2149 /* Change password */
2150 new_nt_password
= data_blob_const(
2151 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2152 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2154 old_nt_hash_enc
= data_blob_const(
2155 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2156 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2158 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2159 new_lm_password
= data_blob_const(
2160 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2161 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2163 old_lm_hash_enc
= data_blob_const(
2164 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2165 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2167 new_lm_password
.length
= 0;
2168 old_lm_hash_enc
.length
= 0;
2171 /* Get sam handle */
2173 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2174 if (!NT_STATUS_IS_OK(result
)) {
2175 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2179 b
= cli
->binding_handle
;
2181 result
= rpccli_samr_chng_pswd_auth_crap(
2182 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2183 new_lm_password
, old_lm_hash_enc
);
2187 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2188 /* FIXME: internal rpc pipe does not cache handles yet */
2190 if (is_valid_policy_hnd(&dom_pol
)) {
2192 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2198 set_auth_errors(state
->response
, result
);
2200 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2201 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2203 state
->response
->data
.auth
.nt_status_string
,
2204 state
->response
->data
.auth
.pam_error
));
2206 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;