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"
41 #include "auth/kerberos/pac_utils.h"
42 #include "auth/gensec/gensec.h"
43 #include "librpc/crypto/gse_krb5.h"
46 #define DBGC_CLASS DBGC_WINBIND
48 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
50 static NTSTATUS
append_info3_as_txt(TALLOC_CTX
*mem_ctx
,
51 struct winbindd_response
*resp
,
52 struct netr_SamInfo3
*info3
)
57 resp
->data
.auth
.info3
.logon_time
=
58 nt_time_to_unix(info3
->base
.logon_time
);
59 resp
->data
.auth
.info3
.logoff_time
=
60 nt_time_to_unix(info3
->base
.logoff_time
);
61 resp
->data
.auth
.info3
.kickoff_time
=
62 nt_time_to_unix(info3
->base
.kickoff_time
);
63 resp
->data
.auth
.info3
.pass_last_set_time
=
64 nt_time_to_unix(info3
->base
.last_password_change
);
65 resp
->data
.auth
.info3
.pass_can_change_time
=
66 nt_time_to_unix(info3
->base
.allow_password_change
);
67 resp
->data
.auth
.info3
.pass_must_change_time
=
68 nt_time_to_unix(info3
->base
.force_password_change
);
70 resp
->data
.auth
.info3
.logon_count
= info3
->base
.logon_count
;
71 resp
->data
.auth
.info3
.bad_pw_count
= info3
->base
.bad_password_count
;
73 resp
->data
.auth
.info3
.user_rid
= info3
->base
.rid
;
74 resp
->data
.auth
.info3
.group_rid
= info3
->base
.primary_gid
;
75 sid_to_fstring(resp
->data
.auth
.info3
.dom_sid
, info3
->base
.domain_sid
);
77 resp
->data
.auth
.info3
.num_groups
= info3
->base
.groups
.count
;
78 resp
->data
.auth
.info3
.user_flgs
= info3
->base
.user_flags
;
80 resp
->data
.auth
.info3
.acct_flags
= info3
->base
.acct_flags
;
81 resp
->data
.auth
.info3
.num_other_sids
= info3
->sidcount
;
83 fstrcpy(resp
->data
.auth
.info3
.user_name
,
84 info3
->base
.account_name
.string
);
85 fstrcpy(resp
->data
.auth
.info3
.full_name
,
86 info3
->base
.full_name
.string
);
87 fstrcpy(resp
->data
.auth
.info3
.logon_script
,
88 info3
->base
.logon_script
.string
);
89 fstrcpy(resp
->data
.auth
.info3
.profile_path
,
90 info3
->base
.profile_path
.string
);
91 fstrcpy(resp
->data
.auth
.info3
.home_dir
,
92 info3
->base
.home_directory
.string
);
93 fstrcpy(resp
->data
.auth
.info3
.dir_drive
,
94 info3
->base
.home_drive
.string
);
96 fstrcpy(resp
->data
.auth
.info3
.logon_srv
,
97 info3
->base
.logon_server
.string
);
98 fstrcpy(resp
->data
.auth
.info3
.logon_dom
,
99 info3
->base
.logon_domain
.string
);
101 ex
= talloc_strdup(mem_ctx
, "");
102 NT_STATUS_HAVE_NO_MEMORY(ex
);
104 for (i
=0; i
< info3
->base
.groups
.count
; i
++) {
105 ex
= talloc_asprintf_append_buffer(ex
, "0x%08X:0x%08X\n",
106 info3
->base
.groups
.rids
[i
].rid
,
107 info3
->base
.groups
.rids
[i
].attributes
);
108 NT_STATUS_HAVE_NO_MEMORY(ex
);
111 for (i
=0; i
< info3
->sidcount
; i
++) {
114 sid
= dom_sid_string(mem_ctx
, info3
->sids
[i
].sid
);
115 NT_STATUS_HAVE_NO_MEMORY(sid
);
117 ex
= talloc_asprintf_append_buffer(ex
, "%s:0x%08X\n",
119 info3
->sids
[i
].attributes
);
120 NT_STATUS_HAVE_NO_MEMORY(ex
);
125 resp
->extra_data
.data
= ex
;
126 resp
->length
+= talloc_get_size(ex
);
131 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
132 struct winbindd_response
*resp
,
133 struct netr_SamInfo3
*info3
)
136 enum ndr_err_code ndr_err
;
138 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, info3
,
139 (ndr_push_flags_fn_t
)ndr_push_netr_SamInfo3
);
140 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
141 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
142 return ndr_map_error2ntstatus(ndr_err
);
145 resp
->extra_data
.data
= blob
.data
;
146 resp
->length
+= blob
.length
;
151 static NTSTATUS
append_unix_username(TALLOC_CTX
*mem_ctx
,
152 struct winbindd_response
*resp
,
153 const struct netr_SamInfo3
*info3
,
154 const char *name_domain
,
155 const char *name_user
)
157 /* We've been asked to return the unix username, per
158 'winbind use default domain' settings and the like */
160 const char *nt_username
, *nt_domain
;
162 nt_domain
= talloc_strdup(mem_ctx
, info3
->base
.logon_domain
.string
);
164 /* If the server didn't give us one, just use the one
166 nt_domain
= name_domain
;
169 nt_username
= talloc_strdup(mem_ctx
, info3
->base
.account_name
.string
);
171 /* If the server didn't give us one, just use the one
173 nt_username
= name_user
;
176 fill_domain_username(resp
->data
.auth
.unix_username
,
177 nt_domain
, nt_username
, true);
179 DEBUG(5, ("Setting unix username to [%s]\n",
180 resp
->data
.auth
.unix_username
));
185 static NTSTATUS
append_afs_token(TALLOC_CTX
*mem_ctx
,
186 struct winbindd_response
*resp
,
187 const struct netr_SamInfo3
*info3
,
188 const char *name_domain
,
189 const char *name_user
)
191 char *afsname
= NULL
;
195 afsname
= talloc_strdup(mem_ctx
, lp_afs_username_map());
196 if (afsname
== NULL
) {
197 return NT_STATUS_NO_MEMORY
;
200 afsname
= talloc_string_sub(mem_ctx
,
201 lp_afs_username_map(),
203 afsname
= talloc_string_sub(mem_ctx
, afsname
,
205 afsname
= talloc_string_sub(mem_ctx
, afsname
,
209 struct dom_sid user_sid
;
212 sid_compose(&user_sid
, info3
->base
.domain_sid
,
214 sid_to_fstring(sidstr
, &user_sid
);
215 afsname
= talloc_string_sub(mem_ctx
, afsname
,
219 if (afsname
== NULL
) {
220 return NT_STATUS_NO_MEMORY
;
223 if (!strlower_m(afsname
)) {
224 return NT_STATUS_INVALID_PARAMETER
;
227 DEBUG(10, ("Generating token for user %s\n", afsname
));
229 cell
= strchr(afsname
, '@');
232 return NT_STATUS_NO_MEMORY
;
238 token
= afs_createtoken_str(afsname
, cell
);
242 resp
->extra_data
.data
= talloc_strdup(mem_ctx
, token
);
243 if (resp
->extra_data
.data
== NULL
) {
244 return NT_STATUS_NO_MEMORY
;
246 resp
->length
+= strlen((const char *)resp
->extra_data
.data
)+1;
251 static NTSTATUS
check_info3_in_group(struct netr_SamInfo3
*info3
,
252 const char *group_sid
)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 struct dom_sid
*require_membership_of_sid
;
266 uint32_t num_require_membership_of_sid
;
271 struct security_token
*token
;
272 TALLOC_CTX
*frame
= talloc_stackframe();
275 /* Parse the 'required group' SID */
277 if (!group_sid
|| !group_sid
[0]) {
278 /* NO sid supplied, all users may access */
283 token
= talloc_zero(talloc_tos(), struct security_token
);
285 DEBUG(0, ("talloc failed\n"));
287 return NT_STATUS_NO_MEMORY
;
290 num_require_membership_of_sid
= 0;
291 require_membership_of_sid
= NULL
;
295 while (next_token_talloc(talloc_tos(), &p
, &req_sid
, ",")) {
296 if (!string_to_sid(&sid
, req_sid
)) {
297 DEBUG(0, ("check_info3_in_group: could not parse %s "
298 "as a SID!", req_sid
));
300 return NT_STATUS_INVALID_PARAMETER
;
303 status
= add_sid_to_array(talloc_tos(), &sid
,
304 &require_membership_of_sid
,
305 &num_require_membership_of_sid
);
306 if (!NT_STATUS_IS_OK(status
)) {
307 DEBUG(0, ("add_sid_to_array failed\n"));
313 status
= sid_array_from_info3(talloc_tos(), info3
,
317 if (!NT_STATUS_IS_OK(status
)) {
322 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
324 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
326 DEBUG(3, ("could not add aliases: %s\n",
332 security_token_debug(DBGC_CLASS
, 10, token
);
334 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
335 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
336 &require_membership_of_sid
[i
])));
337 if (nt_token_check_sid(&require_membership_of_sid
[i
],
339 DEBUG(10, ("Access ok\n"));
345 /* Do not distinguish this error from a wrong username/pw */
348 return NT_STATUS_LOGON_FAILURE
;
351 struct winbindd_domain
*find_auth_domain(uint8_t flags
,
352 const char *domain_name
)
354 struct winbindd_domain
*domain
;
357 domain
= find_domain_from_name_noinit(domain_name
);
358 if (domain
== NULL
) {
359 DEBUG(3, ("Authentication for domain [%s] refused "
360 "as it is not a trusted domain\n",
366 if (strequal(domain_name
, get_global_sam_name())) {
367 return find_domain_from_name_noinit(domain_name
);
370 /* we can auth against trusted domains */
371 if (flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
372 domain
= find_domain_from_name_noinit(domain_name
);
373 if (domain
== NULL
) {
374 DEBUG(3, ("Authentication for domain [%s] skipped "
375 "as it is not a trusted domain\n",
382 return find_our_domain();
385 static void fill_in_password_policy(struct winbindd_response
*r
,
386 const struct samr_DomInfo1
*p
)
388 r
->data
.auth
.policy
.min_length_password
=
389 p
->min_password_length
;
390 r
->data
.auth
.policy
.password_history
=
391 p
->password_history_length
;
392 r
->data
.auth
.policy
.password_properties
=
393 p
->password_properties
;
394 r
->data
.auth
.policy
.expire
=
395 nt_time_to_unix_abs((const NTTIME
*)&(p
->max_password_age
));
396 r
->data
.auth
.policy
.min_passwordage
=
397 nt_time_to_unix_abs((const NTTIME
*)&(p
->min_password_age
));
400 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
401 struct winbindd_response
*response
)
403 TALLOC_CTX
*frame
= talloc_stackframe();
404 struct winbindd_methods
*methods
;
406 struct samr_DomInfo1 password_policy
;
408 if ( !winbindd_can_contact_domain( domain
) ) {
409 DEBUG(5,("fillup_password_policy: No inbound trust to "
410 "contact domain %s\n", domain
->name
));
411 status
= NT_STATUS_NOT_SUPPORTED
;
415 methods
= domain
->methods
;
417 status
= methods
->password_policy(domain
, talloc_tos(), &password_policy
);
418 if (NT_STATUS_IS_ERR(status
)) {
422 fill_in_password_policy(response
, &password_policy
);
429 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
431 uint16
*lockout_threshold
)
433 struct winbindd_methods
*methods
;
434 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
435 struct samr_DomInfo12 lockout_policy
;
437 *lockout_threshold
= 0;
439 methods
= domain
->methods
;
441 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
442 if (NT_STATUS_IS_ERR(status
)) {
446 *lockout_threshold
= lockout_policy
.lockout_threshold
;
451 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
453 uint32
*password_properties
)
455 struct winbindd_methods
*methods
;
456 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
457 struct samr_DomInfo1 password_policy
;
459 *password_properties
= 0;
461 methods
= domain
->methods
;
463 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
464 if (NT_STATUS_IS_ERR(status
)) {
468 *password_properties
= password_policy
.password_properties
;
475 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
478 const char **user_ccache_file
)
480 /* accept FILE and WRFILE as krb5_cc_type from the client and then
481 * build the full ccname string based on the user's uid here -
484 const char *gen_cc
= NULL
;
487 if (strequal(type
, "FILE")) {
488 gen_cc
= talloc_asprintf(
489 mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
491 if (strequal(type
, "WRFILE")) {
492 gen_cc
= talloc_asprintf(
493 mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
495 if (strequal(type
, "KEYRING")) {
496 gen_cc
= talloc_asprintf(
497 mem_ctx
, "KEYRING:persistent:%d", uid
);
500 if (strnequal(type
, "FILE:/", 6) ||
501 strnequal(type
, "WRFILE:/", 8) ||
502 strnequal(type
, "DIR:/", 5)) {
504 /* we allow only one "%u" substitution */
508 p
= strchr(type
, '%');
513 if (p
!= NULL
&& *p
== 'u' && strchr(p
, '%') == NULL
) {
514 gen_cc
= talloc_asprintf(mem_ctx
, type
, uid
);
520 *user_ccache_file
= gen_cc
;
522 if (gen_cc
== NULL
) {
523 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
525 if (gen_cc
== NULL
) {
526 DEBUG(0,("out of memory\n"));
530 DEBUG(10, ("using ccache: %s%s\n", gen_cc
,
531 (*user_ccache_file
== NULL
) ? " (internal)":""));
538 uid_t
get_uid_from_request(struct winbindd_request
*request
)
542 uid
= request
->data
.auth
.uid
;
545 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
551 /**********************************************************************
552 Authenticate a user with a clear text password using Kerberos and fill up
554 **********************************************************************/
556 static NTSTATUS
winbindd_raw_kerberos_login(TALLOC_CTX
*mem_ctx
,
557 struct winbindd_domain
*domain
,
560 const char *krb5_cc_type
,
562 struct netr_SamInfo3
**info3
,
566 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
567 krb5_error_code krb5_ret
;
568 const char *cc
= NULL
;
569 const char *principal_s
= NULL
;
570 const char *service
= NULL
;
572 fstring name_domain
, name_user
;
573 time_t ticket_lifetime
= 0;
574 time_t renewal_until
= 0;
576 time_t time_offset
= 0;
577 const char *user_ccache_file
;
578 struct PAC_LOGON_INFO
*logon_info
= NULL
;
583 * prepare a krb5_cc_cache string for the user */
586 DEBUG(0,("no valid uid\n"));
589 cc
= generate_krb5_ccache(mem_ctx
,
594 return NT_STATUS_NO_MEMORY
;
599 * get kerberos properties */
601 if (domain
->private_data
) {
602 ads
= (ADS_STRUCT
*)domain
->private_data
;
603 time_offset
= ads
->auth
.time_offset
;
608 * do kerberos auth and setup ccache as the user */
610 parse_domain_user(user
, name_domain
, name_user
);
612 realm
= domain
->alt_name
;
613 if (!strupper_m(realm
)) {
614 return NT_STATUS_INVALID_PARAMETER
;
617 principal_s
= talloc_asprintf(mem_ctx
, "%s@%s", name_user
, realm
);
618 if (principal_s
== NULL
) {
619 return NT_STATUS_NO_MEMORY
;
622 service
= talloc_asprintf(mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
623 if (service
== NULL
) {
624 return NT_STATUS_NO_MEMORY
;
627 /* if this is a user ccache, we need to act as the user to let the krb5
628 * library handle the chown, etc. */
630 /************************ ENTERING NON-ROOT **********************/
632 if (user_ccache_file
!= NULL
) {
633 set_effective_uid(uid
);
634 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
637 result
= kerberos_return_pac(mem_ctx
,
646 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
649 if (user_ccache_file
!= NULL
) {
650 gain_root_privilege();
653 /************************ RETURNED TO ROOT **********************/
655 if (!NT_STATUS_IS_OK(result
)) {
659 *info3
= &logon_info
->info3
;
661 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
664 /* if we had a user's ccache then return that string for the pam
667 if (user_ccache_file
!= NULL
) {
669 fstrcpy(krb5ccname
, user_ccache_file
);
671 result
= add_ccache_to_list(principal_s
,
683 if (!NT_STATUS_IS_OK(result
)) {
684 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
689 /* need to delete the memory cred cache, it is not used anymore */
691 krb5_ret
= ads_kdestroy(cc
);
693 DEBUG(3,("winbindd_raw_kerberos_login: "
694 "could not destroy krb5 credential cache: "
695 "%s\n", error_message(krb5_ret
)));
704 * Do not delete an existing valid credential cache, if the user
705 * e.g. enters a wrong password
707 if ((strequal(krb5_cc_type
, "FILE") || strequal(krb5_cc_type
, "WRFILE"))
708 && user_ccache_file
!= NULL
) {
712 /* we could have created a new credential cache with a valid tgt in it
713 * but we werent able to get or verify the service ticket for this
714 * local host and therefor didn't get the PAC, we need to remove that
715 * cache entirely now */
717 krb5_ret
= ads_kdestroy(cc
);
719 DEBUG(3,("winbindd_raw_kerberos_login: "
720 "could not destroy krb5 credential cache: "
721 "%s\n", error_message(krb5_ret
)));
724 if (!NT_STATUS_IS_OK(remove_ccache(user
))) {
725 DEBUG(3,("winbindd_raw_kerberos_login: "
726 "could not remove ccache for user %s\n",
732 return NT_STATUS_NOT_SUPPORTED
;
733 #endif /* HAVE_KRB5 */
736 /****************************************************************
737 ****************************************************************/
739 bool check_request_flags(uint32_t flags
)
741 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
742 WBFLAG_PAM_INFO3_TEXT
|
743 WBFLAG_PAM_INFO3_NDR
;
745 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
746 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
747 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
748 !(flags
& flags_edata
) ) {
752 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
758 /****************************************************************
759 ****************************************************************/
761 NTSTATUS
append_auth_data(TALLOC_CTX
*mem_ctx
,
762 struct winbindd_response
*resp
,
763 uint32_t request_flags
,
764 struct netr_SamInfo3
*info3
,
765 const char *name_domain
,
766 const char *name_user
)
770 if (request_flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
771 memcpy(resp
->data
.auth
.user_session_key
,
773 sizeof(resp
->data
.auth
.user_session_key
)
777 if (request_flags
& WBFLAG_PAM_LMKEY
) {
778 memcpy(resp
->data
.auth
.first_8_lm_hash
,
779 info3
->base
.LMSessKey
.key
,
780 sizeof(resp
->data
.auth
.first_8_lm_hash
)
784 if (request_flags
& WBFLAG_PAM_UNIX_NAME
) {
785 result
= append_unix_username(mem_ctx
, resp
,
786 info3
, name_domain
, name_user
);
787 if (!NT_STATUS_IS_OK(result
)) {
788 DEBUG(10,("Failed to append Unix Username: %s\n",
794 /* currently, anything from here on potentially overwrites extra_data. */
796 if (request_flags
& WBFLAG_PAM_INFO3_NDR
) {
797 result
= append_info3_as_ndr(mem_ctx
, resp
, info3
);
798 if (!NT_STATUS_IS_OK(result
)) {
799 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
805 if (request_flags
& WBFLAG_PAM_INFO3_TEXT
) {
806 result
= append_info3_as_txt(mem_ctx
, resp
, info3
);
807 if (!NT_STATUS_IS_OK(result
)) {
808 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
814 if (request_flags
& WBFLAG_PAM_AFS_TOKEN
) {
815 result
= append_afs_token(mem_ctx
, resp
,
816 info3
, name_domain
, name_user
);
817 if (!NT_STATUS_IS_OK(result
)) {
818 DEBUG(10,("Failed to append AFS token: %s\n",
827 static NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
828 struct winbindd_cli_state
*state
,
829 struct netr_SamInfo3
**info3
)
831 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
832 uint16 max_allowed_bad_attempts
;
833 fstring name_domain
, name_user
;
835 enum lsa_SidType type
;
836 uchar new_nt_pass
[NT_HASH_LEN
];
837 const uint8
*cached_nt_pass
;
838 const uint8
*cached_salt
;
839 struct netr_SamInfo3
*my_info3
;
840 time_t kickoff_time
, must_change_time
;
841 bool password_good
= false;
843 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
850 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
852 /* Parse domain and username */
854 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
857 if (!lookup_cached_name(name_domain
,
861 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
862 return NT_STATUS_NO_SUCH_USER
;
865 if (type
!= SID_NAME_USER
) {
866 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
867 return NT_STATUS_LOGON_FAILURE
;
870 result
= winbindd_get_creds(domain
,
876 if (!NT_STATUS_IS_OK(result
)) {
877 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
883 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
885 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
886 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
888 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
892 /* In this case we didn't store the nt_hash itself,
893 but the MD5 combination of salt + nt_hash. */
894 uchar salted_hash
[NT_HASH_LEN
];
895 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
897 password_good
= (memcmp(cached_nt_pass
, salted_hash
,
900 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
901 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
907 /* User *DOES* know the password, update logon_time and reset
910 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
912 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
913 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
916 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
917 return NT_STATUS_ACCOUNT_DISABLED
;
920 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
921 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
924 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
925 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
928 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
929 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
932 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
933 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
934 my_info3
->base
.acct_flags
));
935 return NT_STATUS_LOGON_FAILURE
;
938 kickoff_time
= nt_time_to_unix(my_info3
->base
.kickoff_time
);
939 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
940 return NT_STATUS_ACCOUNT_EXPIRED
;
943 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
944 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
945 /* we allow grace logons when the password has expired */
946 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
947 /* return NT_STATUS_PASSWORD_EXPIRED; */
952 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
953 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
954 ((tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
) ||
955 /* used to cope with the case winbindd starting without network. */
956 !strequal(tdc_domain
->domain_name
, tdc_domain
->dns_name
))) {
959 const char *cc
= NULL
;
961 const char *principal_s
= NULL
;
962 const char *service
= NULL
;
963 const char *user_ccache_file
;
965 uid
= get_uid_from_request(state
->request
);
967 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
968 return NT_STATUS_INVALID_PARAMETER
;
971 cc
= generate_krb5_ccache(state
->mem_ctx
,
972 state
->request
->data
.auth
.krb5_cc_type
,
973 state
->request
->data
.auth
.uid
,
976 return NT_STATUS_NO_MEMORY
;
979 realm
= domain
->alt_name
;
980 if (!strupper_m(realm
)) {
981 return NT_STATUS_INVALID_PARAMETER
;
984 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
985 if (principal_s
== NULL
) {
986 return NT_STATUS_NO_MEMORY
;
989 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
990 if (service
== NULL
) {
991 return NT_STATUS_NO_MEMORY
;
994 if (user_ccache_file
!= NULL
) {
996 fstrcpy(state
->response
->data
.auth
.krb5ccname
,
999 result
= add_ccache_to_list(principal_s
,
1002 state
->request
->data
.auth
.user
,
1003 state
->request
->data
.auth
.pass
,
1007 time(NULL
) + lp_winbind_cache_time(),
1008 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
1011 if (!NT_STATUS_IS_OK(result
)) {
1012 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1013 "to add ccache to list: %s\n",
1014 nt_errstr(result
)));
1018 #endif /* HAVE_KRB5 */
1020 /* FIXME: we possibly should handle logon hours as well (does xp when
1021 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1023 unix_to_nt_time(&my_info3
->base
.logon_time
, time(NULL
));
1024 my_info3
->base
.bad_password_count
= 0;
1026 result
= winbindd_update_creds_by_info3(domain
,
1027 state
->request
->data
.auth
.user
,
1028 state
->request
->data
.auth
.pass
,
1030 if (!NT_STATUS_IS_OK(result
)) {
1031 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1032 nt_errstr(result
)));
1036 return NT_STATUS_OK
;
1040 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1041 if (domain
->online
== false) {
1045 /* failure of this is not critical */
1046 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1047 if (!NT_STATUS_IS_OK(result
)) {
1048 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1049 "Won't be able to honour account lockout policies\n"));
1052 /* increase counter */
1053 my_info3
->base
.bad_password_count
++;
1055 if (max_allowed_bad_attempts
== 0) {
1060 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1062 uint32 password_properties
;
1064 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1065 if (!NT_STATUS_IS_OK(result
)) {
1066 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1069 if ((my_info3
->base
.rid
!= DOMAIN_RID_ADMINISTRATOR
) ||
1070 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1071 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1076 result
= winbindd_update_creds_by_info3(domain
,
1077 state
->request
->data
.auth
.user
,
1081 if (!NT_STATUS_IS_OK(result
)) {
1082 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1083 nt_errstr(result
)));
1086 return NT_STATUS_LOGON_FAILURE
;
1089 static NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1090 struct winbindd_cli_state
*state
,
1091 struct netr_SamInfo3
**info3
)
1093 struct winbindd_domain
*contact_domain
;
1094 fstring name_domain
, name_user
;
1097 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1099 /* Parse domain and username */
1101 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1103 /* what domain should we contact? */
1106 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1107 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1108 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1109 result
= NT_STATUS_NO_SUCH_USER
;
1114 if (is_myname(name_domain
)) {
1115 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1116 result
= NT_STATUS_NO_SUCH_USER
;
1120 contact_domain
= find_domain_from_name(name_domain
);
1121 if (contact_domain
== NULL
) {
1122 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1123 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1125 result
= NT_STATUS_NO_SUCH_USER
;
1130 if (contact_domain
->initialized
&&
1131 contact_domain
->active_directory
) {
1135 if (!contact_domain
->initialized
) {
1136 init_dc_connection(contact_domain
);
1139 if (!contact_domain
->active_directory
) {
1140 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1141 return NT_STATUS_INVALID_LOGON_TYPE
;
1144 result
= winbindd_raw_kerberos_login(
1145 state
->mem_ctx
, contact_domain
,
1146 state
->request
->data
.auth
.user
,
1147 state
->request
->data
.auth
.pass
,
1148 state
->request
->data
.auth
.krb5_cc_type
,
1149 get_uid_from_request(state
->request
),
1150 info3
, state
->response
->data
.auth
.krb5ccname
);
1155 static NTSTATUS
winbindd_dual_auth_passdb(TALLOC_CTX
*mem_ctx
,
1156 uint32_t logon_parameters
,
1157 const char *domain
, const char *user
,
1158 const DATA_BLOB
*challenge
,
1159 const DATA_BLOB
*lm_resp
,
1160 const DATA_BLOB
*nt_resp
,
1161 struct netr_SamInfo3
**pinfo3
)
1163 struct auth_usersupplied_info
*user_info
= NULL
;
1164 struct tsocket_address
*local
;
1168 rc
= tsocket_address_inet_from_strings(mem_ctx
,
1174 return NT_STATUS_NO_MEMORY
;
1176 status
= make_user_info(&user_info
, user
, user
, domain
, domain
,
1177 lp_netbios_name(), local
, lm_resp
, nt_resp
, NULL
, NULL
,
1178 NULL
, AUTH_PASSWORD_RESPONSE
);
1179 if (!NT_STATUS_IS_OK(status
)) {
1180 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status
)));
1183 user_info
->logon_parameters
= logon_parameters
;
1185 /* We don't want any more mapping of the username */
1186 user_info
->mapped_state
= True
;
1188 status
= check_sam_security_info3(challenge
, talloc_tos(), user_info
,
1190 free_user_info(&user_info
);
1191 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain
,
1192 user
, nt_errstr(status
)));
1196 static NTSTATUS
winbind_samlogon_retry_loop(struct winbindd_domain
*domain
,
1197 TALLOC_CTX
*mem_ctx
,
1198 uint32_t logon_parameters
,
1200 const char *username
,
1201 const char *domainname
,
1202 const char *workstation
,
1203 const uint8_t chal
[8],
1204 DATA_BLOB lm_response
,
1205 DATA_BLOB nt_response
,
1206 struct netr_SamInfo3
**info3
)
1209 int netr_attempts
= 0;
1214 struct rpc_pipe_client
*netlogon_pipe
;
1215 const struct pipe_auth_data
*auth
;
1216 uint32_t neg_flags
= 0;
1218 ZERO_STRUCTP(info3
);
1221 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1223 if (!NT_STATUS_IS_OK(result
)) {
1224 DEBUG(3,("Could not open handle to NETLOGON pipe "
1225 "(error: %s, attempts: %d)\n",
1226 nt_errstr(result
), netr_attempts
));
1228 /* After the first retry always close the connection */
1229 if (netr_attempts
> 0) {
1230 DEBUG(3, ("This is again a problem for this "
1231 "particular call, forcing the close "
1232 "of this connection\n"));
1233 invalidate_cm_connection(&domain
->conn
);
1236 /* After the second retry failover to the next DC */
1237 if (netr_attempts
> 1) {
1239 * If the netlogon server is not reachable then
1240 * it is possible that the DC is rebuilding
1241 * sysvol and shutdown netlogon for that time.
1242 * We should failover to the next dc.
1244 DEBUG(3, ("This is the third problem for this "
1245 "particular call, adding DC to the "
1246 "negative cache list\n"));
1247 add_failed_connection_entry(domain
->name
,
1250 saf_delete(domain
->name
);
1253 /* Only allow 3 retries */
1254 if (netr_attempts
< 3) {
1255 DEBUG(3, ("The connection to netlogon "
1256 "failed, retrying\n"));
1265 auth
= netlogon_pipe
->auth
;
1266 if (netlogon_pipe
->dc
) {
1267 neg_flags
= netlogon_pipe
->dc
->negotiate_flags
;
1270 /* It is really important to try SamLogonEx here,
1271 * because in a clustered environment, we want to use
1272 * one machine account from multiple physical
1275 * With a normal SamLogon call, we must keep the
1276 * credentials chain updated and intact between all
1277 * users of the machine account (which would imply
1278 * cross-node communication for every NTLM logon).
1280 * (The credentials chain is not per NETLOGON pipe
1281 * connection, but globally on the server/client pair
1284 * When using SamLogonEx, the credentials are not
1285 * supplied, but the session key is implied by the
1286 * wrapping SamLogon context.
1288 * -- abartlet 21 April 2008
1290 * It's also important to use NetlogonValidationSamInfo4 (6),
1291 * because it relies on the rpc transport encryption
1292 * and avoids using the global netlogon schannel
1293 * session key to en/decrypt secret information
1294 * like the user_session_key for network logons.
1296 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1297 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1298 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1299 * are the indication that the server supports
1300 * NetlogonValidationSamInfo4 (6). And it must only
1301 * be used if "SealSecureChannel" is used.
1303 * -- metze 4 February 2011
1307 domain
->can_do_validation6
= false;
1308 } else if (auth
->auth_type
!= DCERPC_AUTH_TYPE_SCHANNEL
) {
1309 domain
->can_do_validation6
= false;
1310 } else if (auth
->auth_level
!= DCERPC_AUTH_LEVEL_PRIVACY
) {
1311 domain
->can_do_validation6
= false;
1312 } else if (!(neg_flags
& NETLOGON_NEG_CROSS_FOREST_TRUSTS
)) {
1313 domain
->can_do_validation6
= false;
1314 } else if (!(neg_flags
& NETLOGON_NEG_AUTHENTICATED_RPC
)) {
1315 domain
->can_do_validation6
= false;
1318 if (domain
->can_do_samlogon_ex
&& domain
->can_do_validation6
) {
1319 result
= rpccli_netlogon_sam_network_logon_ex(
1323 server
, /* server name */
1324 username
, /* user name */
1325 domainname
, /* target domain */
1326 workstation
, /* workstation */
1333 result
= rpccli_netlogon_sam_network_logon(
1337 server
, /* server name */
1338 username
, /* user name */
1339 domainname
, /* target domain */
1340 workstation
, /* workstation */
1342 domain
->can_do_validation6
? 6 : 3,
1348 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
)) {
1351 * It's likely that the server also does not support
1352 * validation level 6
1354 domain
->can_do_validation6
= false;
1356 if (domain
->can_do_samlogon_ex
) {
1357 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1358 "retrying with NetSamLogon\n"));
1359 domain
->can_do_samlogon_ex
= false;
1365 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1366 * (no Ex). This happens against old Samba
1367 * DCs. Drop the connection.
1369 invalidate_cm_connection(&domain
->conn
);
1370 result
= NT_STATUS_LOGON_FAILURE
;
1374 if (domain
->can_do_validation6
&&
1375 (NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_INFO_CLASS
) ||
1376 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_PARAMETER
) ||
1377 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
))) {
1378 DEBUG(3,("Got a DC that can not do validation level 6, "
1379 "retrying with level 3\n"));
1380 domain
->can_do_validation6
= false;
1386 * we increment this after the "feature negotiation"
1387 * for can_do_samlogon_ex and can_do_validation6
1391 /* We have to try a second time as cm_connect_netlogon
1392 might not yet have noticed that the DC has killed
1395 if (!rpccli_is_connected(netlogon_pipe
)) {
1400 /* if we get access denied, a possible cause was that we had
1401 and open connection to the DC, but someone changed our
1402 machine account password out from underneath us using 'net
1403 rpc changetrustpw' */
1405 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1406 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1407 "ACCESS_DENIED. Maybe the trust account "
1408 "password was changed and we didn't know it. "
1409 "Killing connections to domain %s\n",
1411 invalidate_cm_connection(&domain
->conn
);
1415 } while ( (attempts
< 2) && retry
);
1417 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
)) {
1418 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1419 "returned NT_STATUS_IO_TIMEOUT after the retry."
1420 "Killing connections to domain %s\n",
1422 invalidate_cm_connection(&domain
->conn
);
1427 static NTSTATUS
winbindd_dual_pam_auth_samlogon(TALLOC_CTX
*mem_ctx
,
1428 struct winbindd_domain
*domain
,
1431 uint32_t request_flags
,
1432 struct netr_SamInfo3
**info3
)
1438 unsigned char local_nt_response
[24];
1439 fstring name_domain
, name_user
;
1441 struct netr_SamInfo3
*my_info3
= NULL
;
1445 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1447 /* Parse domain and username */
1449 parse_domain_user(user
, name_domain
, name_user
);
1451 /* do password magic */
1453 generate_random_buffer(chal
, sizeof(chal
));
1455 if (lp_client_ntlmv2_auth()) {
1456 DATA_BLOB server_chal
;
1457 DATA_BLOB names_blob
;
1458 server_chal
= data_blob_const(chal
, 8);
1460 /* note that the 'workgroup' here is for the local
1461 machine. The 'server name' must match the
1462 'workstation' passed to the actual SamLogon call.
1464 names_blob
= NTLMv2_generate_names_blob(
1465 mem_ctx
, lp_netbios_name(), lp_workgroup());
1467 if (!SMBNTLMv2encrypt(mem_ctx
, name_user
, name_domain
,
1471 &lm_resp
, &nt_resp
, NULL
, NULL
)) {
1472 data_blob_free(&names_blob
);
1473 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1474 result
= NT_STATUS_NO_MEMORY
;
1477 data_blob_free(&names_blob
);
1479 lm_resp
= data_blob_null
;
1480 SMBNTencrypt(pass
, chal
, local_nt_response
);
1482 nt_resp
= data_blob_talloc(mem_ctx
, local_nt_response
,
1483 sizeof(local_nt_response
));
1486 if (strequal(name_domain
, get_global_sam_name())) {
1487 DATA_BLOB chal_blob
= data_blob_const(chal
, sizeof(chal
));
1489 result
= winbindd_dual_auth_passdb(
1490 mem_ctx
, 0, name_domain
, name_user
,
1491 &chal_blob
, &lm_resp
, &nt_resp
, info3
);
1495 /* check authentication loop */
1497 result
= winbind_samlogon_retry_loop(domain
,
1508 if (!NT_STATUS_IS_OK(result
)) {
1512 /* handle the case where a NT4 DC does not fill in the acct_flags in
1513 * the samlogon reply info3. When accurate info3 is required by the
1514 * caller, we look up the account flags ourselve - gd */
1516 if ((request_flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1517 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1519 struct rpc_pipe_client
*samr_pipe
;
1520 struct policy_handle samr_domain_handle
, user_pol
;
1521 union samr_UserInfo
*info
= NULL
;
1522 NTSTATUS status_tmp
, result_tmp
;
1524 struct dcerpc_binding_handle
*b
;
1526 status_tmp
= cm_connect_sam(domain
, mem_ctx
,
1527 &samr_pipe
, &samr_domain_handle
);
1529 if (!NT_STATUS_IS_OK(status_tmp
)) {
1530 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1531 nt_errstr(status_tmp
)));
1535 b
= samr_pipe
->binding_handle
;
1537 status_tmp
= dcerpc_samr_OpenUser(b
, mem_ctx
,
1538 &samr_domain_handle
,
1539 MAXIMUM_ALLOWED_ACCESS
,
1544 if (!NT_STATUS_IS_OK(status_tmp
)) {
1545 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1546 nt_errstr(status_tmp
)));
1549 if (!NT_STATUS_IS_OK(result_tmp
)) {
1550 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1551 nt_errstr(result_tmp
)));
1555 status_tmp
= dcerpc_samr_QueryUserInfo(b
, mem_ctx
,
1561 if (!NT_STATUS_IS_OK(status_tmp
)) {
1562 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1563 nt_errstr(status_tmp
)));
1564 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1567 if (!NT_STATUS_IS_OK(result_tmp
)) {
1568 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1569 nt_errstr(result_tmp
)));
1570 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1574 acct_flags
= info
->info16
.acct_flags
;
1576 if (acct_flags
== 0) {
1577 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1581 my_info3
->base
.acct_flags
= acct_flags
;
1583 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1585 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1593 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1594 struct winbindd_cli_state
*state
)
1596 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1597 NTSTATUS krb5_result
= NT_STATUS_OK
;
1598 fstring name_domain
, name_user
;
1600 fstring domain_user
;
1601 struct netr_SamInfo3
*info3
= NULL
;
1602 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1604 /* Ensure null termination */
1605 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1607 /* Ensure null termination */
1608 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1610 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1611 state
->request
->data
.auth
.user
));
1613 /* Parse domain and username */
1615 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1616 state
->request
->data
.auth
.user
,
1619 /* If the name normalization didnt' actually do anything,
1620 just use the original name */
1622 if (!NT_STATUS_IS_OK(name_map_status
) &&
1623 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1625 mapped_user
= state
->request
->data
.auth
.user
;
1628 parse_domain_user(mapped_user
, name_domain
, name_user
);
1630 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1631 fstr_sprintf( domain_user
, "%s%c%s", name_domain
,
1632 *lp_winbind_separator(),
1634 strlcpy( state
->request
->data
.auth
.user
, domain_user
,
1635 sizeof(state
->request
->data
.auth
.user
));
1638 if (!domain
->online
) {
1639 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1640 if (domain
->startup
) {
1641 /* Logons are very important to users. If we're offline and
1642 we get a request within the first 30 seconds of startup,
1643 try very hard to find a DC and go online. */
1645 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1646 "request in startup mode.\n", domain
->name
));
1648 winbindd_flush_negative_conn_cache(domain
);
1649 result
= init_dc_connection(domain
);
1653 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1655 /* Check for Kerberos authentication */
1656 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1658 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1659 /* save for later */
1660 krb5_result
= result
;
1663 if (NT_STATUS_IS_OK(result
)) {
1664 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1665 goto process_result
;
1667 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1670 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1671 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1672 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1673 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1674 set_domain_offline( domain
);
1678 /* there are quite some NT_STATUS errors where there is no
1679 * point in retrying with a samlogon, we explictly have to take
1680 * care not to increase the bad logon counter on the DC */
1682 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1683 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1684 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1685 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1686 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1687 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1688 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1689 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1690 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1691 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1695 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1696 DEBUG(3,("falling back to samlogon\n"));
1704 /* Check for Samlogon authentication */
1705 if (domain
->online
) {
1706 result
= winbindd_dual_pam_auth_samlogon(
1707 state
->mem_ctx
, domain
,
1708 state
->request
->data
.auth
.user
,
1709 state
->request
->data
.auth
.pass
,
1710 state
->request
->flags
,
1713 if (NT_STATUS_IS_OK(result
)) {
1714 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1715 /* add the Krb5 err if we have one */
1716 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1717 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1719 goto process_result
;
1722 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1723 nt_errstr(result
)));
1725 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1726 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1727 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1729 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1730 set_domain_offline( domain
);
1734 if (domain
->online
) {
1735 /* We're still online - fail. */
1741 /* Check for Cached logons */
1742 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1743 lp_winbind_offline_logon()) {
1745 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1747 if (NT_STATUS_IS_OK(result
)) {
1748 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1749 goto process_result
;
1751 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1758 if (NT_STATUS_IS_OK(result
)) {
1760 struct dom_sid user_sid
;
1762 /* In all codepaths where result == NT_STATUS_OK info3 must have
1763 been initialized. */
1765 result
= NT_STATUS_INTERNAL_ERROR
;
1769 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1772 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1774 netsamlogon_cache_store(name_user
, info3
);
1776 /* save name_to_sid info as early as possible (only if
1777 this is our primary domain so we don't invalidate
1778 the cache entry by storing the seq_num for the wrong
1780 if ( domain
->primary
) {
1781 cache_name2sid(domain
, name_domain
, name_user
,
1782 SID_NAME_USER
, &user_sid
);
1785 /* Check if the user is in the right group */
1787 result
= check_info3_in_group(
1789 state
->request
->data
.auth
.require_membership_of_sid
);
1790 if (!NT_STATUS_IS_OK(result
)) {
1791 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1792 state
->request
->data
.auth
.user
,
1793 state
->request
->data
.auth
.require_membership_of_sid
));
1797 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1798 state
->request
->flags
, info3
,
1799 name_domain
, name_user
);
1800 if (!NT_STATUS_IS_OK(result
)) {
1804 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
1805 && lp_winbind_offline_logon()) {
1807 result
= winbindd_store_creds(domain
,
1808 state
->request
->data
.auth
.user
,
1809 state
->request
->data
.auth
.pass
,
1813 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1814 struct winbindd_domain
*our_domain
= find_our_domain();
1816 /* This is not entirely correct I believe, but it is
1817 consistent. Only apply the password policy settings
1818 too warn users for our own domain. Cannot obtain these
1819 from trusted DCs all the time so don't do it at all.
1822 result
= NT_STATUS_NOT_SUPPORTED
;
1823 if (our_domain
== domain
) {
1824 result
= fillup_password_policy(
1825 our_domain
, state
->response
);
1828 if (!NT_STATUS_IS_OK(result
)
1829 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1831 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1832 domain
->name
, nt_errstr(result
)));
1837 result
= NT_STATUS_OK
;
1841 /* give us a more useful (more correct?) error code */
1842 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1843 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1844 result
= NT_STATUS_NO_LOGON_SERVERS
;
1847 set_auth_errors(state
->response
, result
);
1849 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1850 state
->request
->data
.auth
.user
,
1851 state
->response
->data
.auth
.nt_status_string
,
1852 state
->response
->data
.auth
.pam_error
));
1854 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1857 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1858 struct winbindd_cli_state
*state
)
1861 struct netr_SamInfo3
*info3
= NULL
;
1862 const char *name_user
= NULL
;
1863 const char *name_domain
= NULL
;
1864 const char *workstation
;
1866 DATA_BLOB lm_resp
, nt_resp
;
1868 /* This is child-only, so no check for privileged access is needed
1871 /* Ensure null termination */
1872 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1873 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1875 name_user
= state
->request
->data
.auth_crap
.user
;
1876 name_domain
= state
->request
->data
.auth_crap
.domain
;
1877 workstation
= state
->request
->data
.auth_crap
.workstation
;
1879 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1880 name_domain
, name_user
));
1882 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1883 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1884 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1885 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1886 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1887 state
->request
->data
.auth_crap
.lm_resp_len
,
1888 state
->request
->data
.auth_crap
.nt_resp_len
));
1889 result
= NT_STATUS_INVALID_PARAMETER
;
1894 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1895 state
->request
->data
.auth_crap
.lm_resp_len
);
1897 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1898 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1899 state
->request
->extra_data
.data
,
1900 state
->request
->data
.auth_crap
.nt_resp_len
);
1902 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1903 state
->request
->data
.auth_crap
.nt_resp
,
1904 state
->request
->data
.auth_crap
.nt_resp_len
);
1907 if (strequal(name_domain
, get_global_sam_name())) {
1908 DATA_BLOB chal_blob
= data_blob_const(
1909 state
->request
->data
.auth_crap
.chal
,
1910 sizeof(state
->request
->data
.auth_crap
.chal
));
1912 result
= winbindd_dual_auth_passdb(
1914 state
->request
->data
.auth_crap
.logon_parameters
,
1915 name_domain
, name_user
,
1916 &chal_blob
, &lm_resp
, &nt_resp
, &info3
);
1917 goto process_result
;
1920 result
= winbind_samlogon_retry_loop(domain
,
1922 state
->request
->data
.auth_crap
.logon_parameters
,
1926 /* Bug #3248 - found by Stefan Burkei. */
1927 workstation
, /* We carefully set this above so use it... */
1928 state
->request
->data
.auth_crap
.chal
,
1932 if (!NT_STATUS_IS_OK(result
)) {
1938 if (NT_STATUS_IS_OK(result
)) {
1939 struct dom_sid user_sid
;
1941 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1943 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1945 netsamlogon_cache_store(name_user
, info3
);
1947 /* Check if the user is in the right group */
1949 result
= check_info3_in_group(
1951 state
->request
->data
.auth_crap
.require_membership_of_sid
);
1952 if (!NT_STATUS_IS_OK(result
)) {
1953 DEBUG(3, ("User %s is not in the required group (%s), so "
1954 "crap authentication is rejected\n",
1955 state
->request
->data
.auth_crap
.user
,
1956 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1960 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1961 state
->request
->flags
, info3
,
1962 name_domain
, name_user
);
1963 if (!NT_STATUS_IS_OK(result
)) {
1970 /* give us a more useful (more correct?) error code */
1971 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1972 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1973 result
= NT_STATUS_NO_LOGON_SERVERS
;
1976 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1977 result
= nt_status_squash(result
);
1980 set_auth_errors(state
->response
, result
);
1982 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1983 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1986 state
->response
->data
.auth
.nt_status_string
,
1987 state
->response
->data
.auth
.pam_error
));
1989 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1992 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
1993 struct winbindd_cli_state
*state
)
1996 char *newpass
= NULL
;
1997 struct policy_handle dom_pol
;
1998 struct rpc_pipe_client
*cli
= NULL
;
1999 bool got_info
= false;
2000 struct samr_DomInfo1
*info
= NULL
;
2001 struct userPwdChangeFailureInformation
*reject
= NULL
;
2002 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2003 fstring domain
, user
;
2004 struct dcerpc_binding_handle
*b
= NULL
;
2006 ZERO_STRUCT(dom_pol
);
2008 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
2009 state
->request
->data
.auth
.user
));
2011 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
2015 /* Change password */
2017 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
2018 newpass
= state
->request
->data
.chauthtok
.newpass
;
2020 /* Initialize reject reason */
2021 state
->response
->data
.auth
.reject_reason
= Undefined
;
2023 /* Get sam handle */
2025 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
2027 if (!NT_STATUS_IS_OK(result
)) {
2028 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2032 b
= cli
->binding_handle
;
2034 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2041 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2043 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2045 fill_in_password_policy(state
->response
, info
);
2047 state
->response
->data
.auth
.reject_reason
=
2048 reject
->extendedFailureReason
;
2053 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2054 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2055 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2056 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2058 /* only fallback when the chgpasswd_user3 call is not supported */
2059 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
) ||
2060 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) ||
2061 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
) ||
2062 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
2064 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2065 nt_errstr(result
)));
2067 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2069 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2070 Map to the same status code as Windows 2003. */
2072 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2073 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2079 if (NT_STATUS_IS_OK(result
)
2080 && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
2081 && lp_winbind_offline_logon()) {
2082 result
= winbindd_update_creds_by_name(contact_domain
, user
,
2084 /* Again, this happens when we login from gdm or xdm
2085 * and the password expires, *BUT* cached crendentials
2086 * doesn't exist. winbindd_update_creds_by_name()
2087 * returns NT_STATUS_NO_SUCH_USER.
2088 * This is not a failure.
2091 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2092 result
= NT_STATUS_OK
;
2095 if (!NT_STATUS_IS_OK(result
)) {
2096 DEBUG(10, ("Failed to store creds: %s\n",
2097 nt_errstr(result
)));
2098 goto process_result
;
2102 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2104 NTSTATUS policy_ret
;
2106 policy_ret
= fillup_password_policy(
2107 contact_domain
, state
->response
);
2109 /* failure of this is non critical, it will just provide no
2110 * additional information to the client why the change has
2111 * failed - Guenther */
2113 if (!NT_STATUS_IS_OK(policy_ret
)) {
2114 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2115 goto process_result
;
2121 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2122 /* FIXME: internal rpc pipe does not cache handles yet */
2124 if (is_valid_policy_hnd(&dom_pol
)) {
2126 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2132 set_auth_errors(state
->response
, result
);
2134 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2135 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2138 state
->response
->data
.auth
.nt_status_string
,
2139 state
->response
->data
.auth
.pam_error
));
2141 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2144 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2145 struct winbindd_cli_state
*state
)
2147 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2149 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2150 state
->request
->data
.logoff
.user
));
2152 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2153 result
= NT_STATUS_OK
;
2154 goto process_result
;
2157 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2158 result
= NT_STATUS_OK
;
2159 goto process_result
;
2164 if (state
->request
->data
.logoff
.uid
< 0) {
2165 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2166 goto process_result
;
2169 /* what we need here is to find the corresponding krb5 ccache name *we*
2170 * created for a given username and destroy it */
2172 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2173 result
= NT_STATUS_OK
;
2174 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2175 goto process_result
;
2178 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2179 state
->request
->data
.logoff
.uid
,
2180 state
->request
->data
.logoff
.krb5ccname
)) {
2181 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2182 goto process_result
;
2185 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2186 if (!NT_STATUS_IS_OK(result
)) {
2187 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2188 nt_errstr(result
)));
2189 goto process_result
;
2193 * Remove any mlock'ed memory creds in the child
2194 * we might be using for krb5 ticket renewal.
2197 winbindd_delete_memory_creds(state
->request
->data
.logoff
.user
);
2200 result
= NT_STATUS_NOT_SUPPORTED
;
2206 set_auth_errors(state
->response
, result
);
2208 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2211 /* Change user password with auth crap*/
2213 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2216 DATA_BLOB new_nt_password
;
2217 DATA_BLOB old_nt_hash_enc
;
2218 DATA_BLOB new_lm_password
;
2219 DATA_BLOB old_lm_hash_enc
;
2220 fstring domain
,user
;
2221 struct policy_handle dom_pol
;
2222 struct winbindd_domain
*contact_domain
= domainSt
;
2223 struct rpc_pipe_client
*cli
= NULL
;
2224 struct dcerpc_binding_handle
*b
= NULL
;
2226 ZERO_STRUCT(dom_pol
);
2228 /* Ensure null termination */
2229 state
->request
->data
.chng_pswd_auth_crap
.user
[
2230 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2231 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2232 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2236 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2237 (unsigned long)state
->pid
,
2238 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2239 state
->request
->data
.chng_pswd_auth_crap
.user
));
2241 if (lp_winbind_offline_logon()) {
2242 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2243 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2244 result
= NT_STATUS_ACCESS_DENIED
;
2248 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2249 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2251 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2255 DEBUG(3,("no domain specified with username (%s) - "
2257 state
->request
->data
.chng_pswd_auth_crap
.user
));
2258 result
= NT_STATUS_NO_SUCH_USER
;
2263 if (!*domain
&& lp_winbind_use_default_domain()) {
2264 fstrcpy(domain
,lp_workgroup());
2268 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2271 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2272 (unsigned long)state
->pid
, domain
, user
));
2274 /* Change password */
2275 new_nt_password
= data_blob_const(
2276 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2277 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2279 old_nt_hash_enc
= data_blob_const(
2280 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2281 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2283 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2284 new_lm_password
= data_blob_const(
2285 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2286 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2288 old_lm_hash_enc
= data_blob_const(
2289 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2290 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2292 new_lm_password
= data_blob_null
;
2293 old_lm_hash_enc
= data_blob_null
;
2296 /* Get sam handle */
2298 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2299 if (!NT_STATUS_IS_OK(result
)) {
2300 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2304 b
= cli
->binding_handle
;
2306 result
= rpccli_samr_chng_pswd_auth_crap(
2307 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2308 new_lm_password
, old_lm_hash_enc
);
2312 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2313 /* FIXME: internal rpc pipe does not cache handles yet */
2315 if (is_valid_policy_hnd(&dom_pol
)) {
2317 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2323 set_auth_errors(state
->response
, result
);
2325 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2326 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2328 state
->response
->data
.auth
.nt_status_string
,
2329 state
->response
->data
.auth
.pam_error
));
2331 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2335 static NTSTATUS
extract_pac_vrfy_sigs(TALLOC_CTX
*mem_ctx
, DATA_BLOB pac_blob
,
2336 struct PAC_LOGON_INFO
**logon_info
)
2338 krb5_context krbctx
= NULL
;
2339 krb5_error_code k5ret
;
2341 krb5_kt_cursor cursor
;
2342 krb5_keytab_entry entry
;
2343 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
2346 ZERO_STRUCT(cursor
);
2348 k5ret
= krb5_init_context(&krbctx
);
2350 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2351 error_message(k5ret
)));
2352 status
= krb5_to_nt_status(k5ret
);
2356 k5ret
= gse_krb5_get_server_keytab(krbctx
, &keytab
);
2358 DEBUG(1, ("Failed to get keytab: %s\n",
2359 error_message(k5ret
)));
2360 status
= krb5_to_nt_status(k5ret
);
2364 k5ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &cursor
);
2366 DEBUG(1, ("Failed to start seq: %s\n",
2367 error_message(k5ret
)));
2368 status
= krb5_to_nt_status(k5ret
);
2372 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2373 while (k5ret
== 0) {
2374 status
= kerberos_pac_logon_info(mem_ctx
, pac_blob
,
2376 KRB5_KT_KEY(&entry
), NULL
, 0,
2378 if (NT_STATUS_IS_OK(status
)) {
2381 k5ret
= smb_krb5_kt_free_entry(krbctx
, &entry
);
2382 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2385 k5ret
= krb5_kt_end_seq_get(krbctx
, keytab
, &cursor
);
2387 DEBUG(1, ("Failed to end seq: %s\n",
2388 error_message(k5ret
)));
2391 k5ret
= krb5_kt_close(krbctx
, keytab
);
2393 DEBUG(1, ("Failed to close keytab: %s\n",
2394 error_message(k5ret
)));
2397 krb5_free_context(krbctx
);
2402 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2403 struct netr_SamInfo3
**info3
)
2405 struct winbindd_request
*req
= state
->request
;
2407 struct PAC_LOGON_INFO
*logon_info
= NULL
;
2410 pac_blob
= data_blob_const(req
->extra_data
.data
, req
->extra_len
);
2411 result
= extract_pac_vrfy_sigs(state
->mem_ctx
, pac_blob
, &logon_info
);
2412 if (!NT_STATUS_IS_OK(result
) &&
2413 !NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
)) {
2414 DEBUG(1, ("Error during PAC signature verification: %s\n",
2415 nt_errstr(result
)));
2420 /* Signature verification succeeded, trust the PAC */
2421 netsamlogon_cache_store(NULL
, &logon_info
->info3
);
2424 /* Try without signature verification */
2425 result
= kerberos_pac_logon_info(state
->mem_ctx
, pac_blob
, NULL
,
2426 NULL
, NULL
, NULL
, 0,
2428 if (!NT_STATUS_IS_OK(result
)) {
2429 DEBUG(10, ("Could not extract PAC: %s\n",
2430 nt_errstr(result
)));
2435 *info3
= &logon_info
->info3
;
2437 return NT_STATUS_OK
;
2439 #else /* HAVE_KRB5 */
2440 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2441 struct netr_SamInfo3
**info3
)
2443 return NT_STATUS_NO_SUCH_USER
;
2445 #endif /* HAVE_KRB5 */