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"
40 #include "../lib/tsocket/tsocket.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
.logon_time
);
56 resp
->data
.auth
.info3
.logoff_time
=
57 nt_time_to_unix(info3
->base
.logoff_time
);
58 resp
->data
.auth
.info3
.kickoff_time
=
59 nt_time_to_unix(info3
->base
.kickoff_time
);
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
.logon_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
.logon_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
;
220 if (!strlower_m(afsname
)) {
221 return NT_STATUS_INVALID_PARAMETER
;
224 DEBUG(10, ("Generating token for user %s\n", afsname
));
226 cell
= strchr(afsname
, '@');
229 return NT_STATUS_NO_MEMORY
;
235 token
= afs_createtoken_str(afsname
, cell
);
239 resp
->extra_data
.data
= talloc_strdup(mem_ctx
, token
);
240 if (resp
->extra_data
.data
== NULL
) {
241 return NT_STATUS_NO_MEMORY
;
243 resp
->length
+= strlen((const char *)resp
->extra_data
.data
)+1;
248 static NTSTATUS
check_info3_in_group(struct netr_SamInfo3
*info3
,
249 const char *group_sid
)
251 * Check whether a user belongs to a group or list of groups.
253 * @param mem_ctx talloc memory context.
254 * @param info3 user information, including group membership info.
255 * @param group_sid One or more groups , separated by commas.
257 * @return NT_STATUS_OK on success,
258 * NT_STATUS_LOGON_FAILURE if the user does not belong,
259 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
262 struct dom_sid
*require_membership_of_sid
;
263 uint32_t num_require_membership_of_sid
;
268 struct security_token
*token
;
269 TALLOC_CTX
*frame
= talloc_stackframe();
272 /* Parse the 'required group' SID */
274 if (!group_sid
|| !group_sid
[0]) {
275 /* NO sid supplied, all users may access */
280 token
= talloc_zero(talloc_tos(), struct security_token
);
282 DEBUG(0, ("talloc failed\n"));
284 return NT_STATUS_NO_MEMORY
;
287 num_require_membership_of_sid
= 0;
288 require_membership_of_sid
= NULL
;
292 while (next_token_talloc(talloc_tos(), &p
, &req_sid
, ",")) {
293 if (!string_to_sid(&sid
, req_sid
)) {
294 DEBUG(0, ("check_info3_in_group: could not parse %s "
295 "as a SID!", req_sid
));
297 return NT_STATUS_INVALID_PARAMETER
;
300 status
= add_sid_to_array(talloc_tos(), &sid
,
301 &require_membership_of_sid
,
302 &num_require_membership_of_sid
);
303 if (!NT_STATUS_IS_OK(status
)) {
304 DEBUG(0, ("add_sid_to_array failed\n"));
310 status
= sid_array_from_info3(talloc_tos(), info3
,
314 if (!NT_STATUS_IS_OK(status
)) {
319 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
321 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
323 DEBUG(3, ("could not add aliases: %s\n",
329 security_token_debug(DBGC_CLASS
, 10, token
);
331 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
332 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
333 &require_membership_of_sid
[i
])));
334 if (nt_token_check_sid(&require_membership_of_sid
[i
],
336 DEBUG(10, ("Access ok\n"));
342 /* Do not distinguish this error from a wrong username/pw */
345 return NT_STATUS_LOGON_FAILURE
;
348 struct winbindd_domain
*find_auth_domain(uint8_t flags
,
349 const char *domain_name
)
351 struct winbindd_domain
*domain
;
354 domain
= find_domain_from_name_noinit(domain_name
);
355 if (domain
== NULL
) {
356 DEBUG(3, ("Authentication for domain [%s] refused "
357 "as it is not a trusted domain\n",
363 if (strequal(domain_name
, get_global_sam_name())) {
364 return find_domain_from_name_noinit(domain_name
);
367 /* we can auth against trusted domains */
368 if (flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
369 domain
= find_domain_from_name_noinit(domain_name
);
370 if (domain
== NULL
) {
371 DEBUG(3, ("Authentication for domain [%s] skipped "
372 "as it is not a trusted domain\n",
379 return find_our_domain();
382 static void fill_in_password_policy(struct winbindd_response
*r
,
383 const struct samr_DomInfo1
*p
)
385 r
->data
.auth
.policy
.min_length_password
=
386 p
->min_password_length
;
387 r
->data
.auth
.policy
.password_history
=
388 p
->password_history_length
;
389 r
->data
.auth
.policy
.password_properties
=
390 p
->password_properties
;
391 r
->data
.auth
.policy
.expire
=
392 nt_time_to_unix_abs((const NTTIME
*)&(p
->max_password_age
));
393 r
->data
.auth
.policy
.min_passwordage
=
394 nt_time_to_unix_abs((const NTTIME
*)&(p
->min_password_age
));
397 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
398 struct winbindd_response
*response
)
400 TALLOC_CTX
*frame
= talloc_stackframe();
401 struct winbindd_methods
*methods
;
403 struct samr_DomInfo1 password_policy
;
405 if ( !winbindd_can_contact_domain( domain
) ) {
406 DEBUG(5,("fillup_password_policy: No inbound trust to "
407 "contact domain %s\n", domain
->name
));
408 status
= NT_STATUS_NOT_SUPPORTED
;
412 methods
= domain
->methods
;
414 status
= methods
->password_policy(domain
, talloc_tos(), &password_policy
);
415 if (NT_STATUS_IS_ERR(status
)) {
419 fill_in_password_policy(response
, &password_policy
);
426 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
428 uint16
*lockout_threshold
)
430 struct winbindd_methods
*methods
;
431 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
432 struct samr_DomInfo12 lockout_policy
;
434 *lockout_threshold
= 0;
436 methods
= domain
->methods
;
438 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
439 if (NT_STATUS_IS_ERR(status
)) {
443 *lockout_threshold
= lockout_policy
.lockout_threshold
;
448 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
450 uint32
*password_properties
)
452 struct winbindd_methods
*methods
;
453 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
454 struct samr_DomInfo1 password_policy
;
456 *password_properties
= 0;
458 methods
= domain
->methods
;
460 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
461 if (NT_STATUS_IS_ERR(status
)) {
465 *password_properties
= password_policy
.password_properties
;
472 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
475 const char **user_ccache_file
)
477 /* accept FILE and WRFILE as krb5_cc_type from the client and then
478 * build the full ccname string based on the user's uid here -
481 const char *gen_cc
= NULL
;
484 if (strequal(type
, "FILE")) {
485 gen_cc
= talloc_asprintf(
486 mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
488 if (strequal(type
, "WRFILE")) {
489 gen_cc
= talloc_asprintf(
490 mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
494 *user_ccache_file
= gen_cc
;
496 if (gen_cc
== NULL
) {
497 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
499 if (gen_cc
== NULL
) {
500 DEBUG(0,("out of memory\n"));
504 DEBUG(10, ("using ccache: %s%s\n", gen_cc
,
505 (*user_ccache_file
== NULL
) ? " (internal)":""));
512 uid_t
get_uid_from_request(struct winbindd_request
*request
)
516 uid
= request
->data
.auth
.uid
;
519 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
525 /**********************************************************************
526 Authenticate a user with a clear text password using Kerberos and fill up
528 **********************************************************************/
530 static NTSTATUS
winbindd_raw_kerberos_login(TALLOC_CTX
*mem_ctx
,
531 struct winbindd_domain
*domain
,
534 const char *krb5_cc_type
,
536 struct netr_SamInfo3
**info3
,
540 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
541 krb5_error_code krb5_ret
;
542 const char *cc
= NULL
;
543 const char *principal_s
= NULL
;
544 const char *service
= NULL
;
546 fstring name_domain
, name_user
;
547 time_t ticket_lifetime
= 0;
548 time_t renewal_until
= 0;
550 time_t time_offset
= 0;
551 const char *user_ccache_file
;
552 struct PAC_LOGON_INFO
*logon_info
= NULL
;
557 * prepare a krb5_cc_cache string for the user */
560 DEBUG(0,("no valid uid\n"));
563 cc
= generate_krb5_ccache(mem_ctx
,
568 return NT_STATUS_NO_MEMORY
;
573 * get kerberos properties */
575 if (domain
->private_data
) {
576 ads
= (ADS_STRUCT
*)domain
->private_data
;
577 time_offset
= ads
->auth
.time_offset
;
582 * do kerberos auth and setup ccache as the user */
584 parse_domain_user(user
, name_domain
, name_user
);
586 realm
= domain
->alt_name
;
587 if (!strupper_m(realm
)) {
588 return NT_STATUS_INVALID_PARAMETER
;
591 principal_s
= talloc_asprintf(mem_ctx
, "%s@%s", name_user
, realm
);
592 if (principal_s
== NULL
) {
593 return NT_STATUS_NO_MEMORY
;
596 service
= talloc_asprintf(mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
597 if (service
== NULL
) {
598 return NT_STATUS_NO_MEMORY
;
601 /* if this is a user ccache, we need to act as the user to let the krb5
602 * library handle the chown, etc. */
604 /************************ ENTERING NON-ROOT **********************/
606 if (user_ccache_file
!= NULL
) {
607 set_effective_uid(uid
);
608 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
611 result
= kerberos_return_pac(mem_ctx
,
620 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
623 if (user_ccache_file
!= NULL
) {
624 gain_root_privilege();
627 /************************ RETURNED TO ROOT **********************/
629 if (!NT_STATUS_IS_OK(result
)) {
633 *info3
= &logon_info
->info3
;
635 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
638 /* if we had a user's ccache then return that string for the pam
641 if (user_ccache_file
!= NULL
) {
643 fstrcpy(krb5ccname
, user_ccache_file
);
645 result
= add_ccache_to_list(principal_s
,
657 if (!NT_STATUS_IS_OK(result
)) {
658 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
663 /* need to delete the memory cred cache, it is not used anymore */
665 krb5_ret
= ads_kdestroy(cc
);
667 DEBUG(3,("winbindd_raw_kerberos_login: "
668 "could not destroy krb5 credential cache: "
669 "%s\n", error_message(krb5_ret
)));
678 /* we could have created a new credential cache with a valid tgt in it
679 * but we werent able to get or verify the service ticket for this
680 * local host and therefor didn't get the PAC, we need to remove that
681 * cache entirely now */
683 krb5_ret
= ads_kdestroy(cc
);
685 DEBUG(3,("winbindd_raw_kerberos_login: "
686 "could not destroy krb5 credential cache: "
687 "%s\n", error_message(krb5_ret
)));
690 if (!NT_STATUS_IS_OK(remove_ccache(user
))) {
691 DEBUG(3,("winbindd_raw_kerberos_login: "
692 "could not remove ccache for user %s\n",
698 return NT_STATUS_NOT_SUPPORTED
;
699 #endif /* HAVE_KRB5 */
702 /****************************************************************
703 ****************************************************************/
705 bool check_request_flags(uint32_t flags
)
707 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
708 WBFLAG_PAM_INFO3_TEXT
|
709 WBFLAG_PAM_INFO3_NDR
;
711 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
712 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
713 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
714 !(flags
& flags_edata
) ) {
718 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
724 /****************************************************************
725 ****************************************************************/
727 static NTSTATUS
append_auth_data(TALLOC_CTX
*mem_ctx
,
728 struct winbindd_response
*resp
,
729 uint32_t request_flags
,
730 struct netr_SamInfo3
*info3
,
731 const char *name_domain
,
732 const char *name_user
)
736 if (request_flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
737 memcpy(resp
->data
.auth
.user_session_key
,
739 sizeof(resp
->data
.auth
.user_session_key
)
743 if (request_flags
& WBFLAG_PAM_LMKEY
) {
744 memcpy(resp
->data
.auth
.first_8_lm_hash
,
745 info3
->base
.LMSessKey
.key
,
746 sizeof(resp
->data
.auth
.first_8_lm_hash
)
750 if (request_flags
& WBFLAG_PAM_UNIX_NAME
) {
751 result
= append_unix_username(mem_ctx
, resp
,
752 info3
, name_domain
, name_user
);
753 if (!NT_STATUS_IS_OK(result
)) {
754 DEBUG(10,("Failed to append Unix Username: %s\n",
760 /* currently, anything from here on potentially overwrites extra_data. */
762 if (request_flags
& WBFLAG_PAM_INFO3_NDR
) {
763 result
= append_info3_as_ndr(mem_ctx
, resp
, info3
);
764 if (!NT_STATUS_IS_OK(result
)) {
765 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
771 if (request_flags
& WBFLAG_PAM_INFO3_TEXT
) {
772 result
= append_info3_as_txt(mem_ctx
, resp
, info3
);
773 if (!NT_STATUS_IS_OK(result
)) {
774 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
780 if (request_flags
& WBFLAG_PAM_AFS_TOKEN
) {
781 result
= append_afs_token(mem_ctx
, resp
,
782 info3
, name_domain
, name_user
);
783 if (!NT_STATUS_IS_OK(result
)) {
784 DEBUG(10,("Failed to append AFS token: %s\n",
793 static NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
794 struct winbindd_cli_state
*state
,
795 struct netr_SamInfo3
**info3
)
797 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
798 uint16 max_allowed_bad_attempts
;
799 fstring name_domain
, name_user
;
801 enum lsa_SidType type
;
802 uchar new_nt_pass
[NT_HASH_LEN
];
803 const uint8
*cached_nt_pass
;
804 const uint8
*cached_salt
;
805 struct netr_SamInfo3
*my_info3
;
806 time_t kickoff_time
, must_change_time
;
807 bool password_good
= false;
809 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
816 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
818 /* Parse domain and username */
820 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
823 if (!lookup_cached_name(name_domain
,
827 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
828 return NT_STATUS_NO_SUCH_USER
;
831 if (type
!= SID_NAME_USER
) {
832 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
833 return NT_STATUS_LOGON_FAILURE
;
836 result
= winbindd_get_creds(domain
,
842 if (!NT_STATUS_IS_OK(result
)) {
843 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
849 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
851 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
852 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
854 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
858 /* In this case we didn't store the nt_hash itself,
859 but the MD5 combination of salt + nt_hash. */
860 uchar salted_hash
[NT_HASH_LEN
];
861 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
863 password_good
= (memcmp(cached_nt_pass
, salted_hash
,
866 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
867 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
873 /* User *DOES* know the password, update logon_time and reset
876 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
878 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
879 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
882 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
883 return NT_STATUS_ACCOUNT_DISABLED
;
886 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
887 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
890 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
891 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
894 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
895 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
898 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
899 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
900 my_info3
->base
.acct_flags
));
901 return NT_STATUS_LOGON_FAILURE
;
904 kickoff_time
= nt_time_to_unix(my_info3
->base
.kickoff_time
);
905 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
906 return NT_STATUS_ACCOUNT_EXPIRED
;
909 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
910 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
911 /* we allow grace logons when the password has expired */
912 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
913 /* return NT_STATUS_PASSWORD_EXPIRED; */
918 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
919 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
920 ((tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
) ||
921 /* used to cope with the case winbindd starting without network. */
922 !strequal(tdc_domain
->domain_name
, tdc_domain
->dns_name
))) {
925 const char *cc
= NULL
;
927 const char *principal_s
= NULL
;
928 const char *service
= NULL
;
929 const char *user_ccache_file
;
931 uid
= get_uid_from_request(state
->request
);
933 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
934 return NT_STATUS_INVALID_PARAMETER
;
937 cc
= generate_krb5_ccache(state
->mem_ctx
,
938 state
->request
->data
.auth
.krb5_cc_type
,
939 state
->request
->data
.auth
.uid
,
942 return NT_STATUS_NO_MEMORY
;
945 realm
= domain
->alt_name
;
946 if (!strupper_m(realm
)) {
947 return NT_STATUS_INVALID_PARAMETER
;
950 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
951 if (principal_s
== NULL
) {
952 return NT_STATUS_NO_MEMORY
;
955 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
956 if (service
== NULL
) {
957 return NT_STATUS_NO_MEMORY
;
960 if (user_ccache_file
!= NULL
) {
962 fstrcpy(state
->response
->data
.auth
.krb5ccname
,
965 result
= add_ccache_to_list(principal_s
,
968 state
->request
->data
.auth
.user
,
969 state
->request
->data
.auth
.pass
,
973 time(NULL
) + lp_winbind_cache_time(),
974 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
977 if (!NT_STATUS_IS_OK(result
)) {
978 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
979 "to add ccache to list: %s\n",
984 #endif /* HAVE_KRB5 */
986 /* FIXME: we possibly should handle logon hours as well (does xp when
987 * offline?) see auth/auth_sam.c:sam_account_ok for details */
989 unix_to_nt_time(&my_info3
->base
.logon_time
, time(NULL
));
990 my_info3
->base
.bad_password_count
= 0;
992 result
= winbindd_update_creds_by_info3(domain
,
993 state
->request
->data
.auth
.user
,
994 state
->request
->data
.auth
.pass
,
996 if (!NT_STATUS_IS_OK(result
)) {
997 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1002 return NT_STATUS_OK
;
1006 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1007 if (domain
->online
== false) {
1011 /* failure of this is not critical */
1012 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1013 if (!NT_STATUS_IS_OK(result
)) {
1014 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1015 "Won't be able to honour account lockout policies\n"));
1018 /* increase counter */
1019 my_info3
->base
.bad_password_count
++;
1021 if (max_allowed_bad_attempts
== 0) {
1026 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1028 uint32 password_properties
;
1030 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1031 if (!NT_STATUS_IS_OK(result
)) {
1032 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1035 if ((my_info3
->base
.rid
!= DOMAIN_RID_ADMINISTRATOR
) ||
1036 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1037 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1042 result
= winbindd_update_creds_by_info3(domain
,
1043 state
->request
->data
.auth
.user
,
1047 if (!NT_STATUS_IS_OK(result
)) {
1048 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1049 nt_errstr(result
)));
1052 return NT_STATUS_LOGON_FAILURE
;
1055 static NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1056 struct winbindd_cli_state
*state
,
1057 struct netr_SamInfo3
**info3
)
1059 struct winbindd_domain
*contact_domain
;
1060 fstring name_domain
, name_user
;
1063 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1065 /* Parse domain and username */
1067 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1069 /* what domain should we contact? */
1072 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1073 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1074 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1075 result
= NT_STATUS_NO_SUCH_USER
;
1080 if (is_myname(name_domain
)) {
1081 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1082 result
= NT_STATUS_NO_SUCH_USER
;
1086 contact_domain
= find_domain_from_name(name_domain
);
1087 if (contact_domain
== NULL
) {
1088 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1089 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1091 result
= NT_STATUS_NO_SUCH_USER
;
1096 if (contact_domain
->initialized
&&
1097 contact_domain
->active_directory
) {
1101 if (!contact_domain
->initialized
) {
1102 init_dc_connection(contact_domain
);
1105 if (!contact_domain
->active_directory
) {
1106 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1107 return NT_STATUS_INVALID_LOGON_TYPE
;
1110 result
= winbindd_raw_kerberos_login(
1111 state
->mem_ctx
, contact_domain
,
1112 state
->request
->data
.auth
.user
,
1113 state
->request
->data
.auth
.pass
,
1114 state
->request
->data
.auth
.krb5_cc_type
,
1115 get_uid_from_request(state
->request
),
1116 info3
, state
->response
->data
.auth
.krb5ccname
);
1121 static NTSTATUS
winbindd_dual_auth_passdb(TALLOC_CTX
*mem_ctx
,
1122 uint32_t logon_parameters
,
1123 const char *domain
, const char *user
,
1124 const DATA_BLOB
*challenge
,
1125 const DATA_BLOB
*lm_resp
,
1126 const DATA_BLOB
*nt_resp
,
1127 struct netr_SamInfo3
**pinfo3
)
1129 struct auth_usersupplied_info
*user_info
= NULL
;
1130 struct tsocket_address
*local
;
1134 rc
= tsocket_address_inet_from_strings(mem_ctx
,
1140 return NT_STATUS_NO_MEMORY
;
1142 status
= make_user_info(&user_info
, user
, user
, domain
, domain
,
1143 lp_netbios_name(), local
, lm_resp
, nt_resp
, NULL
, NULL
,
1144 NULL
, AUTH_PASSWORD_RESPONSE
);
1145 if (!NT_STATUS_IS_OK(status
)) {
1146 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status
)));
1149 user_info
->logon_parameters
= logon_parameters
;
1151 /* We don't want any more mapping of the username */
1152 user_info
->mapped_state
= True
;
1154 status
= check_sam_security_info3(challenge
, talloc_tos(), user_info
,
1156 free_user_info(&user_info
);
1157 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain
,
1158 user
, nt_errstr(status
)));
1162 static NTSTATUS
winbind_samlogon_retry_loop(struct winbindd_domain
*domain
,
1163 TALLOC_CTX
*mem_ctx
,
1164 uint32_t logon_parameters
,
1166 const char *username
,
1167 const char *domainname
,
1168 const char *workstation
,
1169 const uint8_t chal
[8],
1170 DATA_BLOB lm_response
,
1171 DATA_BLOB nt_response
,
1172 struct netr_SamInfo3
**info3
)
1179 struct rpc_pipe_client
*netlogon_pipe
;
1180 const struct pipe_auth_data
*auth
;
1181 uint32_t neg_flags
= 0;
1183 ZERO_STRUCTP(info3
);
1186 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1188 if (!NT_STATUS_IS_OK(result
)) {
1189 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1190 nt_errstr(result
)));
1191 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
)) {
1193 DEBUG(3, ("This is the second problem for this "
1194 "particular call, forcing the close of "
1195 "this connection\n"));
1196 invalidate_cm_connection(&domain
->conn
);
1198 DEBUG(3, ("First call to cm_connect_netlogon "
1199 "has timed out, retrying\n"));
1205 auth
= netlogon_pipe
->auth
;
1206 if (netlogon_pipe
->dc
) {
1207 neg_flags
= netlogon_pipe
->dc
->negotiate_flags
;
1210 /* It is really important to try SamLogonEx here,
1211 * because in a clustered environment, we want to use
1212 * one machine account from multiple physical
1215 * With a normal SamLogon call, we must keep the
1216 * credentials chain updated and intact between all
1217 * users of the machine account (which would imply
1218 * cross-node communication for every NTLM logon).
1220 * (The credentials chain is not per NETLOGON pipe
1221 * connection, but globally on the server/client pair
1224 * When using SamLogonEx, the credentials are not
1225 * supplied, but the session key is implied by the
1226 * wrapping SamLogon context.
1228 * -- abartlet 21 April 2008
1230 * It's also important to use NetlogonValidationSamInfo4 (6),
1231 * because it relies on the rpc transport encryption
1232 * and avoids using the global netlogon schannel
1233 * session key to en/decrypt secret information
1234 * like the user_session_key for network logons.
1236 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1237 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1238 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1239 * are the indication that the server supports
1240 * NetlogonValidationSamInfo4 (6). And it must only
1241 * be used if "SealSecureChannel" is used.
1243 * -- metze 4 February 2011
1247 domain
->can_do_validation6
= false;
1248 } else if (auth
->auth_type
!= DCERPC_AUTH_TYPE_SCHANNEL
) {
1249 domain
->can_do_validation6
= false;
1250 } else if (auth
->auth_level
!= DCERPC_AUTH_LEVEL_PRIVACY
) {
1251 domain
->can_do_validation6
= false;
1252 } else if (!(neg_flags
& NETLOGON_NEG_CROSS_FOREST_TRUSTS
)) {
1253 domain
->can_do_validation6
= false;
1254 } else if (!(neg_flags
& NETLOGON_NEG_AUTHENTICATED_RPC
)) {
1255 domain
->can_do_validation6
= false;
1258 if (domain
->can_do_samlogon_ex
&& domain
->can_do_validation6
) {
1259 result
= rpccli_netlogon_sam_network_logon_ex(
1263 server
, /* server name */
1264 username
, /* user name */
1265 domainname
, /* target domain */
1266 workstation
, /* workstation */
1273 result
= rpccli_netlogon_sam_network_logon(
1277 server
, /* server name */
1278 username
, /* user name */
1279 domainname
, /* target domain */
1280 workstation
, /* workstation */
1282 domain
->can_do_validation6
? 6 : 3,
1288 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
)) {
1291 * It's likely that the server also does not support
1292 * validation level 6
1294 domain
->can_do_validation6
= false;
1296 if (domain
->can_do_samlogon_ex
) {
1297 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1298 "retrying with NetSamLogon\n"));
1299 domain
->can_do_samlogon_ex
= false;
1305 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1306 * (no Ex). This happens against old Samba
1307 * DCs. Drop the connection.
1309 invalidate_cm_connection(&domain
->conn
);
1310 result
= NT_STATUS_LOGON_FAILURE
;
1314 if (domain
->can_do_validation6
&&
1315 (NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_INFO_CLASS
) ||
1316 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_PARAMETER
) ||
1317 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
))) {
1318 DEBUG(3,("Got a DC that can not do validation level 6, "
1319 "retrying with level 3\n"));
1320 domain
->can_do_validation6
= false;
1326 * we increment this after the "feature negotiation"
1327 * for can_do_samlogon_ex and can_do_validation6
1331 /* We have to try a second time as cm_connect_netlogon
1332 might not yet have noticed that the DC has killed
1335 if (!rpccli_is_connected(netlogon_pipe
)) {
1340 /* if we get access denied, a possible cause was that we had
1341 and open connection to the DC, but someone changed our
1342 machine account password out from underneath us using 'net
1343 rpc changetrustpw' */
1345 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1346 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1347 "ACCESS_DENIED. Maybe the trust account "
1348 "password was changed and we didn't know it. "
1349 "Killing connections to domain %s\n",
1351 invalidate_cm_connection(&domain
->conn
);
1355 } while ( (attempts
< 2) && retry
);
1357 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
)) {
1358 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1359 "returned NT_STATUS_IO_TIMEOUT after the retry."
1360 "Killing connections to domain %s\n",
1362 invalidate_cm_connection(&domain
->conn
);
1367 static NTSTATUS
winbindd_dual_pam_auth_samlogon(TALLOC_CTX
*mem_ctx
,
1368 struct winbindd_domain
*domain
,
1371 uint32_t request_flags
,
1372 struct netr_SamInfo3
**info3
)
1378 unsigned char local_nt_response
[24];
1379 fstring name_domain
, name_user
;
1381 struct netr_SamInfo3
*my_info3
= NULL
;
1385 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1387 /* Parse domain and username */
1389 parse_domain_user(user
, name_domain
, name_user
);
1391 /* do password magic */
1393 generate_random_buffer(chal
, sizeof(chal
));
1395 if (lp_client_ntlmv2_auth()) {
1396 DATA_BLOB server_chal
;
1397 DATA_BLOB names_blob
;
1398 server_chal
= data_blob_const(chal
, 8);
1400 /* note that the 'workgroup' here is for the local
1401 machine. The 'server name' must match the
1402 'workstation' passed to the actual SamLogon call.
1404 names_blob
= NTLMv2_generate_names_blob(
1405 mem_ctx
, lp_netbios_name(), lp_workgroup());
1407 if (!SMBNTLMv2encrypt(mem_ctx
, name_user
, name_domain
,
1411 &lm_resp
, &nt_resp
, NULL
, NULL
)) {
1412 data_blob_free(&names_blob
);
1413 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1414 result
= NT_STATUS_NO_MEMORY
;
1417 data_blob_free(&names_blob
);
1419 lm_resp
= data_blob_null
;
1420 SMBNTencrypt(pass
, chal
, local_nt_response
);
1422 nt_resp
= data_blob_talloc(mem_ctx
, local_nt_response
,
1423 sizeof(local_nt_response
));
1426 if (strequal(name_domain
, get_global_sam_name())) {
1427 DATA_BLOB chal_blob
= data_blob_const(chal
, sizeof(chal
));
1429 result
= winbindd_dual_auth_passdb(
1430 mem_ctx
, 0, name_domain
, name_user
,
1431 &chal_blob
, &lm_resp
, &nt_resp
, info3
);
1435 /* check authentication loop */
1437 result
= winbind_samlogon_retry_loop(domain
,
1448 if (!NT_STATUS_IS_OK(result
)) {
1452 /* handle the case where a NT4 DC does not fill in the acct_flags in
1453 * the samlogon reply info3. When accurate info3 is required by the
1454 * caller, we look up the account flags ourselve - gd */
1456 if ((request_flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1457 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1459 struct rpc_pipe_client
*samr_pipe
;
1460 struct policy_handle samr_domain_handle
, user_pol
;
1461 union samr_UserInfo
*info
= NULL
;
1462 NTSTATUS status_tmp
, result_tmp
;
1464 struct dcerpc_binding_handle
*b
;
1466 status_tmp
= cm_connect_sam(domain
, mem_ctx
,
1467 &samr_pipe
, &samr_domain_handle
);
1469 if (!NT_STATUS_IS_OK(status_tmp
)) {
1470 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1471 nt_errstr(status_tmp
)));
1475 b
= samr_pipe
->binding_handle
;
1477 status_tmp
= dcerpc_samr_OpenUser(b
, mem_ctx
,
1478 &samr_domain_handle
,
1479 MAXIMUM_ALLOWED_ACCESS
,
1484 if (!NT_STATUS_IS_OK(status_tmp
)) {
1485 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1486 nt_errstr(status_tmp
)));
1489 if (!NT_STATUS_IS_OK(result_tmp
)) {
1490 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1491 nt_errstr(result_tmp
)));
1495 status_tmp
= dcerpc_samr_QueryUserInfo(b
, mem_ctx
,
1501 if (!NT_STATUS_IS_OK(status_tmp
)) {
1502 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1503 nt_errstr(status_tmp
)));
1504 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1507 if (!NT_STATUS_IS_OK(result_tmp
)) {
1508 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1509 nt_errstr(result_tmp
)));
1510 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1514 acct_flags
= info
->info16
.acct_flags
;
1516 if (acct_flags
== 0) {
1517 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1521 my_info3
->base
.acct_flags
= acct_flags
;
1523 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1525 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1533 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1534 struct winbindd_cli_state
*state
)
1536 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1537 NTSTATUS krb5_result
= NT_STATUS_OK
;
1538 fstring name_domain
, name_user
;
1540 fstring domain_user
;
1541 struct netr_SamInfo3
*info3
= NULL
;
1542 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1544 /* Ensure null termination */
1545 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1547 /* Ensure null termination */
1548 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1550 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1551 state
->request
->data
.auth
.user
));
1553 /* Parse domain and username */
1555 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1556 state
->request
->data
.auth
.user
,
1559 /* If the name normalization didnt' actually do anything,
1560 just use the original name */
1562 if (!NT_STATUS_IS_OK(name_map_status
) &&
1563 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1565 mapped_user
= state
->request
->data
.auth
.user
;
1568 parse_domain_user(mapped_user
, name_domain
, name_user
);
1570 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1571 fstr_sprintf( domain_user
, "%s%c%s", name_domain
,
1572 *lp_winbind_separator(),
1574 strlcpy( state
->request
->data
.auth
.user
, domain_user
,
1575 sizeof(state
->request
->data
.auth
.user
));
1578 if (!domain
->online
) {
1579 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1580 if (domain
->startup
) {
1581 /* Logons are very important to users. If we're offline and
1582 we get a request within the first 30 seconds of startup,
1583 try very hard to find a DC and go online. */
1585 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1586 "request in startup mode.\n", domain
->name
));
1588 winbindd_flush_negative_conn_cache(domain
);
1589 result
= init_dc_connection(domain
);
1593 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1595 /* Check for Kerberos authentication */
1596 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1598 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1599 /* save for later */
1600 krb5_result
= result
;
1603 if (NT_STATUS_IS_OK(result
)) {
1604 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1605 goto process_result
;
1607 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1610 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1611 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1612 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1613 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1614 set_domain_offline( domain
);
1618 /* there are quite some NT_STATUS errors where there is no
1619 * point in retrying with a samlogon, we explictly have to take
1620 * care not to increase the bad logon counter on the DC */
1622 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1623 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1624 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1625 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1626 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1627 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1628 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1629 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1630 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1631 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1635 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1636 DEBUG(3,("falling back to samlogon\n"));
1644 /* Check for Samlogon authentication */
1645 if (domain
->online
) {
1646 result
= winbindd_dual_pam_auth_samlogon(
1647 state
->mem_ctx
, domain
,
1648 state
->request
->data
.auth
.user
,
1649 state
->request
->data
.auth
.pass
,
1650 state
->request
->flags
,
1653 if (NT_STATUS_IS_OK(result
)) {
1654 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1655 /* add the Krb5 err if we have one */
1656 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1657 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1659 goto process_result
;
1662 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1663 nt_errstr(result
)));
1665 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1666 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1667 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1669 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1670 set_domain_offline( domain
);
1674 if (domain
->online
) {
1675 /* We're still online - fail. */
1681 /* Check for Cached logons */
1682 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1683 lp_winbind_offline_logon()) {
1685 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1687 if (NT_STATUS_IS_OK(result
)) {
1688 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1689 goto process_result
;
1691 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1698 if (NT_STATUS_IS_OK(result
)) {
1700 struct dom_sid user_sid
;
1702 /* In all codepaths where result == NT_STATUS_OK info3 must have
1703 been initialized. */
1705 result
= NT_STATUS_INTERNAL_ERROR
;
1709 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1712 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1714 netsamlogon_cache_store(name_user
, info3
);
1716 /* save name_to_sid info as early as possible (only if
1717 this is our primary domain so we don't invalidate
1718 the cache entry by storing the seq_num for the wrong
1720 if ( domain
->primary
) {
1721 cache_name2sid(domain
, name_domain
, name_user
,
1722 SID_NAME_USER
, &user_sid
);
1725 /* Check if the user is in the right group */
1727 result
= check_info3_in_group(
1729 state
->request
->data
.auth
.require_membership_of_sid
);
1730 if (!NT_STATUS_IS_OK(result
)) {
1731 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1732 state
->request
->data
.auth
.user
,
1733 state
->request
->data
.auth
.require_membership_of_sid
));
1737 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1738 state
->request
->flags
, info3
,
1739 name_domain
, name_user
);
1740 if (!NT_STATUS_IS_OK(result
)) {
1744 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
1745 && lp_winbind_offline_logon()) {
1747 result
= winbindd_store_creds(domain
,
1748 state
->request
->data
.auth
.user
,
1749 state
->request
->data
.auth
.pass
,
1753 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1754 struct winbindd_domain
*our_domain
= find_our_domain();
1756 /* This is not entirely correct I believe, but it is
1757 consistent. Only apply the password policy settings
1758 too warn users for our own domain. Cannot obtain these
1759 from trusted DCs all the time so don't do it at all.
1762 result
= NT_STATUS_NOT_SUPPORTED
;
1763 if (our_domain
== domain
) {
1764 result
= fillup_password_policy(
1765 our_domain
, state
->response
);
1768 if (!NT_STATUS_IS_OK(result
)
1769 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1771 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1772 domain
->name
, nt_errstr(result
)));
1777 result
= NT_STATUS_OK
;
1781 /* give us a more useful (more correct?) error code */
1782 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1783 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1784 result
= NT_STATUS_NO_LOGON_SERVERS
;
1787 set_auth_errors(state
->response
, result
);
1789 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1790 state
->request
->data
.auth
.user
,
1791 state
->response
->data
.auth
.nt_status_string
,
1792 state
->response
->data
.auth
.pam_error
));
1794 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1797 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1798 struct winbindd_cli_state
*state
)
1801 struct netr_SamInfo3
*info3
= NULL
;
1802 const char *name_user
= NULL
;
1803 const char *name_domain
= NULL
;
1804 const char *workstation
;
1806 DATA_BLOB lm_resp
, nt_resp
;
1808 /* This is child-only, so no check for privileged access is needed
1811 /* Ensure null termination */
1812 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1813 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1815 name_user
= state
->request
->data
.auth_crap
.user
;
1816 name_domain
= state
->request
->data
.auth_crap
.domain
;
1817 workstation
= state
->request
->data
.auth_crap
.workstation
;
1819 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1820 name_domain
, name_user
));
1822 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1823 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1824 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1825 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1826 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1827 state
->request
->data
.auth_crap
.lm_resp_len
,
1828 state
->request
->data
.auth_crap
.nt_resp_len
));
1829 result
= NT_STATUS_INVALID_PARAMETER
;
1834 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1835 state
->request
->data
.auth_crap
.lm_resp_len
);
1837 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1838 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1839 state
->request
->extra_data
.data
,
1840 state
->request
->data
.auth_crap
.nt_resp_len
);
1842 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1843 state
->request
->data
.auth_crap
.nt_resp
,
1844 state
->request
->data
.auth_crap
.nt_resp_len
);
1847 if (strequal(name_domain
, get_global_sam_name())) {
1848 DATA_BLOB chal_blob
= data_blob_const(
1849 state
->request
->data
.auth_crap
.chal
,
1850 sizeof(state
->request
->data
.auth_crap
.chal
));
1852 result
= winbindd_dual_auth_passdb(
1854 state
->request
->data
.auth_crap
.logon_parameters
,
1855 name_domain
, name_user
,
1856 &chal_blob
, &lm_resp
, &nt_resp
, &info3
);
1857 goto process_result
;
1860 result
= winbind_samlogon_retry_loop(domain
,
1862 state
->request
->data
.auth_crap
.logon_parameters
,
1866 /* Bug #3248 - found by Stefan Burkei. */
1867 workstation
, /* We carefully set this above so use it... */
1868 state
->request
->data
.auth_crap
.chal
,
1872 if (!NT_STATUS_IS_OK(result
)) {
1878 if (NT_STATUS_IS_OK(result
)) {
1879 struct dom_sid user_sid
;
1881 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1883 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1885 netsamlogon_cache_store(name_user
, info3
);
1887 /* Check if the user is in the right group */
1889 result
= check_info3_in_group(
1891 state
->request
->data
.auth_crap
.require_membership_of_sid
);
1892 if (!NT_STATUS_IS_OK(result
)) {
1893 DEBUG(3, ("User %s is not in the required group (%s), so "
1894 "crap authentication is rejected\n",
1895 state
->request
->data
.auth_crap
.user
,
1896 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1900 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1901 state
->request
->flags
, info3
,
1902 name_domain
, name_user
);
1903 if (!NT_STATUS_IS_OK(result
)) {
1910 /* give us a more useful (more correct?) error code */
1911 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1912 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1913 result
= NT_STATUS_NO_LOGON_SERVERS
;
1916 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1917 result
= nt_status_squash(result
);
1920 set_auth_errors(state
->response
, result
);
1922 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1923 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1926 state
->response
->data
.auth
.nt_status_string
,
1927 state
->response
->data
.auth
.pam_error
));
1929 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1932 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
1933 struct winbindd_cli_state
*state
)
1936 char *newpass
= NULL
;
1937 struct policy_handle dom_pol
;
1938 struct rpc_pipe_client
*cli
= NULL
;
1939 bool got_info
= false;
1940 struct samr_DomInfo1
*info
= NULL
;
1941 struct userPwdChangeFailureInformation
*reject
= NULL
;
1942 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1943 fstring domain
, user
;
1944 struct dcerpc_binding_handle
*b
= NULL
;
1946 ZERO_STRUCT(dom_pol
);
1948 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
1949 state
->request
->data
.auth
.user
));
1951 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
1955 /* Change password */
1957 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
1958 newpass
= state
->request
->data
.chauthtok
.newpass
;
1960 /* Initialize reject reason */
1961 state
->response
->data
.auth
.reject_reason
= Undefined
;
1963 /* Get sam handle */
1965 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1967 if (!NT_STATUS_IS_OK(result
)) {
1968 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
1972 b
= cli
->binding_handle
;
1974 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
1981 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1983 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
1985 fill_in_password_policy(state
->response
, info
);
1987 state
->response
->data
.auth
.reject_reason
=
1988 reject
->extendedFailureReason
;
1993 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1994 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1995 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1996 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1998 /* only fallback when the chgpasswd_user3 call is not supported */
1999 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
) ||
2000 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) ||
2001 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
) ||
2002 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
2004 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2005 nt_errstr(result
)));
2007 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2009 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2010 Map to the same status code as Windows 2003. */
2012 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2013 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2019 if (NT_STATUS_IS_OK(result
)
2020 && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
2021 && lp_winbind_offline_logon()) {
2022 result
= winbindd_update_creds_by_name(contact_domain
, user
,
2024 /* Again, this happens when we login from gdm or xdm
2025 * and the password expires, *BUT* cached crendentials
2026 * doesn't exist. winbindd_update_creds_by_name()
2027 * returns NT_STATUS_NO_SUCH_USER.
2028 * This is not a failure.
2031 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2032 result
= NT_STATUS_OK
;
2035 if (!NT_STATUS_IS_OK(result
)) {
2036 DEBUG(10, ("Failed to store creds: %s\n",
2037 nt_errstr(result
)));
2038 goto process_result
;
2042 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2044 NTSTATUS policy_ret
;
2046 policy_ret
= fillup_password_policy(
2047 contact_domain
, state
->response
);
2049 /* failure of this is non critical, it will just provide no
2050 * additional information to the client why the change has
2051 * failed - Guenther */
2053 if (!NT_STATUS_IS_OK(policy_ret
)) {
2054 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2055 goto process_result
;
2061 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2062 /* FIXME: internal rpc pipe does not cache handles yet */
2064 if (is_valid_policy_hnd(&dom_pol
)) {
2066 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2072 set_auth_errors(state
->response
, result
);
2074 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2075 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2078 state
->response
->data
.auth
.nt_status_string
,
2079 state
->response
->data
.auth
.pam_error
));
2081 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2084 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2085 struct winbindd_cli_state
*state
)
2087 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2089 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2090 state
->request
->data
.logoff
.user
));
2092 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2093 result
= NT_STATUS_OK
;
2094 goto process_result
;
2097 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2098 result
= NT_STATUS_OK
;
2099 goto process_result
;
2104 if (state
->request
->data
.logoff
.uid
< 0) {
2105 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2106 goto process_result
;
2109 /* what we need here is to find the corresponding krb5 ccache name *we*
2110 * created for a given username and destroy it */
2112 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2113 result
= NT_STATUS_OK
;
2114 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2115 goto process_result
;
2118 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2119 state
->request
->data
.logoff
.uid
,
2120 state
->request
->data
.logoff
.krb5ccname
)) {
2121 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2122 goto process_result
;
2125 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2126 if (!NT_STATUS_IS_OK(result
)) {
2127 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2128 nt_errstr(result
)));
2129 goto process_result
;
2133 * Remove any mlock'ed memory creds in the child
2134 * we might be using for krb5 ticket renewal.
2137 winbindd_delete_memory_creds(state
->request
->data
.logoff
.user
);
2140 result
= NT_STATUS_NOT_SUPPORTED
;
2146 set_auth_errors(state
->response
, result
);
2148 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2151 /* Change user password with auth crap*/
2153 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2156 DATA_BLOB new_nt_password
;
2157 DATA_BLOB old_nt_hash_enc
;
2158 DATA_BLOB new_lm_password
;
2159 DATA_BLOB old_lm_hash_enc
;
2160 fstring domain
,user
;
2161 struct policy_handle dom_pol
;
2162 struct winbindd_domain
*contact_domain
= domainSt
;
2163 struct rpc_pipe_client
*cli
= NULL
;
2164 struct dcerpc_binding_handle
*b
= NULL
;
2166 ZERO_STRUCT(dom_pol
);
2168 /* Ensure null termination */
2169 state
->request
->data
.chng_pswd_auth_crap
.user
[
2170 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2171 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2172 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2176 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2177 (unsigned long)state
->pid
,
2178 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2179 state
->request
->data
.chng_pswd_auth_crap
.user
));
2181 if (lp_winbind_offline_logon()) {
2182 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2183 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2184 result
= NT_STATUS_ACCESS_DENIED
;
2188 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2189 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2191 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2195 DEBUG(3,("no domain specified with username (%s) - "
2197 state
->request
->data
.chng_pswd_auth_crap
.user
));
2198 result
= NT_STATUS_NO_SUCH_USER
;
2203 if (!*domain
&& lp_winbind_use_default_domain()) {
2204 fstrcpy(domain
,lp_workgroup());
2208 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2211 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2212 (unsigned long)state
->pid
, domain
, user
));
2214 /* Change password */
2215 new_nt_password
= data_blob_const(
2216 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2217 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2219 old_nt_hash_enc
= data_blob_const(
2220 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2221 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2223 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2224 new_lm_password
= data_blob_const(
2225 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2226 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2228 old_lm_hash_enc
= data_blob_const(
2229 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2230 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2232 new_lm_password
= data_blob_null
;
2233 old_lm_hash_enc
= data_blob_null
;
2236 /* Get sam handle */
2238 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2239 if (!NT_STATUS_IS_OK(result
)) {
2240 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2244 b
= cli
->binding_handle
;
2246 result
= rpccli_samr_chng_pswd_auth_crap(
2247 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2248 new_lm_password
, old_lm_hash_enc
);
2252 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2253 /* FIXME: internal rpc pipe does not cache handles yet */
2255 if (is_valid_policy_hnd(&dom_pol
)) {
2257 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2263 set_auth_errors(state
->response
, result
);
2265 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2266 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2268 state
->response
->data
.auth
.nt_status_string
,
2269 state
->response
->data
.auth
.pam_error
));
2271 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;