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
);
497 *user_ccache_file
= gen_cc
;
499 if (gen_cc
== NULL
) {
500 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
502 if (gen_cc
== NULL
) {
503 DEBUG(0,("out of memory\n"));
507 DEBUG(10, ("using ccache: %s%s\n", gen_cc
,
508 (*user_ccache_file
== NULL
) ? " (internal)":""));
515 uid_t
get_uid_from_request(struct winbindd_request
*request
)
519 uid
= request
->data
.auth
.uid
;
522 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
528 /**********************************************************************
529 Authenticate a user with a clear text password using Kerberos and fill up
531 **********************************************************************/
533 static NTSTATUS
winbindd_raw_kerberos_login(TALLOC_CTX
*mem_ctx
,
534 struct winbindd_domain
*domain
,
537 const char *krb5_cc_type
,
539 struct netr_SamInfo3
**info3
,
543 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
544 krb5_error_code krb5_ret
;
545 const char *cc
= NULL
;
546 const char *principal_s
= NULL
;
547 const char *service
= NULL
;
549 fstring name_domain
, name_user
;
550 time_t ticket_lifetime
= 0;
551 time_t renewal_until
= 0;
553 time_t time_offset
= 0;
554 const char *user_ccache_file
;
555 struct PAC_LOGON_INFO
*logon_info
= NULL
;
560 * prepare a krb5_cc_cache string for the user */
563 DEBUG(0,("no valid uid\n"));
566 cc
= generate_krb5_ccache(mem_ctx
,
571 return NT_STATUS_NO_MEMORY
;
576 * get kerberos properties */
578 if (domain
->private_data
) {
579 ads
= (ADS_STRUCT
*)domain
->private_data
;
580 time_offset
= ads
->auth
.time_offset
;
585 * do kerberos auth and setup ccache as the user */
587 parse_domain_user(user
, name_domain
, name_user
);
589 realm
= domain
->alt_name
;
590 if (!strupper_m(realm
)) {
591 return NT_STATUS_INVALID_PARAMETER
;
594 principal_s
= talloc_asprintf(mem_ctx
, "%s@%s", name_user
, realm
);
595 if (principal_s
== NULL
) {
596 return NT_STATUS_NO_MEMORY
;
599 service
= talloc_asprintf(mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
600 if (service
== NULL
) {
601 return NT_STATUS_NO_MEMORY
;
604 /* if this is a user ccache, we need to act as the user to let the krb5
605 * library handle the chown, etc. */
607 /************************ ENTERING NON-ROOT **********************/
609 if (user_ccache_file
!= NULL
) {
610 set_effective_uid(uid
);
611 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
614 result
= kerberos_return_pac(mem_ctx
,
623 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
626 if (user_ccache_file
!= NULL
) {
627 gain_root_privilege();
630 /************************ RETURNED TO ROOT **********************/
632 if (!NT_STATUS_IS_OK(result
)) {
636 *info3
= &logon_info
->info3
;
638 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
641 /* if we had a user's ccache then return that string for the pam
644 if (user_ccache_file
!= NULL
) {
646 fstrcpy(krb5ccname
, user_ccache_file
);
648 result
= add_ccache_to_list(principal_s
,
660 if (!NT_STATUS_IS_OK(result
)) {
661 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
666 /* need to delete the memory cred cache, it is not used anymore */
668 krb5_ret
= ads_kdestroy(cc
);
670 DEBUG(3,("winbindd_raw_kerberos_login: "
671 "could not destroy krb5 credential cache: "
672 "%s\n", error_message(krb5_ret
)));
681 * Do not delete an existing valid credential cache, if the user
682 * e.g. enters a wrong password
684 if ((strequal(krb5_cc_type
, "FILE") || strequal(krb5_cc_type
, "WRFILE"))
685 && user_ccache_file
!= NULL
) {
689 /* we could have created a new credential cache with a valid tgt in it
690 * but we werent able to get or verify the service ticket for this
691 * local host and therefor didn't get the PAC, we need to remove that
692 * cache entirely now */
694 krb5_ret
= ads_kdestroy(cc
);
696 DEBUG(3,("winbindd_raw_kerberos_login: "
697 "could not destroy krb5 credential cache: "
698 "%s\n", error_message(krb5_ret
)));
701 if (!NT_STATUS_IS_OK(remove_ccache(user
))) {
702 DEBUG(3,("winbindd_raw_kerberos_login: "
703 "could not remove ccache for user %s\n",
709 return NT_STATUS_NOT_SUPPORTED
;
710 #endif /* HAVE_KRB5 */
713 /****************************************************************
714 ****************************************************************/
716 bool check_request_flags(uint32_t flags
)
718 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
719 WBFLAG_PAM_INFO3_TEXT
|
720 WBFLAG_PAM_INFO3_NDR
;
722 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
723 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
724 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
725 !(flags
& flags_edata
) ) {
729 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
735 /****************************************************************
736 ****************************************************************/
738 NTSTATUS
append_auth_data(TALLOC_CTX
*mem_ctx
,
739 struct winbindd_response
*resp
,
740 uint32_t request_flags
,
741 struct netr_SamInfo3
*info3
,
742 const char *name_domain
,
743 const char *name_user
)
747 if (request_flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
748 memcpy(resp
->data
.auth
.user_session_key
,
750 sizeof(resp
->data
.auth
.user_session_key
)
754 if (request_flags
& WBFLAG_PAM_LMKEY
) {
755 memcpy(resp
->data
.auth
.first_8_lm_hash
,
756 info3
->base
.LMSessKey
.key
,
757 sizeof(resp
->data
.auth
.first_8_lm_hash
)
761 if (request_flags
& WBFLAG_PAM_UNIX_NAME
) {
762 result
= append_unix_username(mem_ctx
, resp
,
763 info3
, name_domain
, name_user
);
764 if (!NT_STATUS_IS_OK(result
)) {
765 DEBUG(10,("Failed to append Unix Username: %s\n",
771 /* currently, anything from here on potentially overwrites extra_data. */
773 if (request_flags
& WBFLAG_PAM_INFO3_NDR
) {
774 result
= append_info3_as_ndr(mem_ctx
, resp
, info3
);
775 if (!NT_STATUS_IS_OK(result
)) {
776 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
782 if (request_flags
& WBFLAG_PAM_INFO3_TEXT
) {
783 result
= append_info3_as_txt(mem_ctx
, resp
, info3
);
784 if (!NT_STATUS_IS_OK(result
)) {
785 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
791 if (request_flags
& WBFLAG_PAM_AFS_TOKEN
) {
792 result
= append_afs_token(mem_ctx
, resp
,
793 info3
, name_domain
, name_user
);
794 if (!NT_STATUS_IS_OK(result
)) {
795 DEBUG(10,("Failed to append AFS token: %s\n",
804 static NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
805 struct winbindd_cli_state
*state
,
806 struct netr_SamInfo3
**info3
)
808 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
809 uint16 max_allowed_bad_attempts
;
810 fstring name_domain
, name_user
;
812 enum lsa_SidType type
;
813 uchar new_nt_pass
[NT_HASH_LEN
];
814 const uint8
*cached_nt_pass
;
815 const uint8
*cached_salt
;
816 struct netr_SamInfo3
*my_info3
;
817 time_t kickoff_time
, must_change_time
;
818 bool password_good
= false;
820 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
827 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
829 /* Parse domain and username */
831 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
834 if (!lookup_cached_name(name_domain
,
838 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
839 return NT_STATUS_NO_SUCH_USER
;
842 if (type
!= SID_NAME_USER
) {
843 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
844 return NT_STATUS_LOGON_FAILURE
;
847 result
= winbindd_get_creds(domain
,
853 if (!NT_STATUS_IS_OK(result
)) {
854 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
860 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
862 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
863 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
865 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
869 /* In this case we didn't store the nt_hash itself,
870 but the MD5 combination of salt + nt_hash. */
871 uchar salted_hash
[NT_HASH_LEN
];
872 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
874 password_good
= (memcmp(cached_nt_pass
, salted_hash
,
877 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
878 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
884 /* User *DOES* know the password, update logon_time and reset
887 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
889 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
890 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
893 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
894 return NT_STATUS_ACCOUNT_DISABLED
;
897 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
898 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
901 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
902 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
905 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
906 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
909 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
910 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
911 my_info3
->base
.acct_flags
));
912 return NT_STATUS_LOGON_FAILURE
;
915 kickoff_time
= nt_time_to_unix(my_info3
->base
.kickoff_time
);
916 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
917 return NT_STATUS_ACCOUNT_EXPIRED
;
920 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
921 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
922 /* we allow grace logons when the password has expired */
923 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
924 /* return NT_STATUS_PASSWORD_EXPIRED; */
929 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
930 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
931 ((tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
) ||
932 /* used to cope with the case winbindd starting without network. */
933 !strequal(tdc_domain
->domain_name
, tdc_domain
->dns_name
))) {
936 const char *cc
= NULL
;
938 const char *principal_s
= NULL
;
939 const char *service
= NULL
;
940 const char *user_ccache_file
;
942 uid
= get_uid_from_request(state
->request
);
944 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
945 return NT_STATUS_INVALID_PARAMETER
;
948 cc
= generate_krb5_ccache(state
->mem_ctx
,
949 state
->request
->data
.auth
.krb5_cc_type
,
950 state
->request
->data
.auth
.uid
,
953 return NT_STATUS_NO_MEMORY
;
956 realm
= domain
->alt_name
;
957 if (!strupper_m(realm
)) {
958 return NT_STATUS_INVALID_PARAMETER
;
961 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
962 if (principal_s
== NULL
) {
963 return NT_STATUS_NO_MEMORY
;
966 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
967 if (service
== NULL
) {
968 return NT_STATUS_NO_MEMORY
;
971 if (user_ccache_file
!= NULL
) {
973 fstrcpy(state
->response
->data
.auth
.krb5ccname
,
976 result
= add_ccache_to_list(principal_s
,
979 state
->request
->data
.auth
.user
,
980 state
->request
->data
.auth
.pass
,
984 time(NULL
) + lp_winbind_cache_time(),
985 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
988 if (!NT_STATUS_IS_OK(result
)) {
989 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
990 "to add ccache to list: %s\n",
995 #endif /* HAVE_KRB5 */
997 /* FIXME: we possibly should handle logon hours as well (does xp when
998 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1000 unix_to_nt_time(&my_info3
->base
.logon_time
, time(NULL
));
1001 my_info3
->base
.bad_password_count
= 0;
1003 result
= winbindd_update_creds_by_info3(domain
,
1004 state
->request
->data
.auth
.user
,
1005 state
->request
->data
.auth
.pass
,
1007 if (!NT_STATUS_IS_OK(result
)) {
1008 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1009 nt_errstr(result
)));
1013 return NT_STATUS_OK
;
1017 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1018 if (domain
->online
== false) {
1022 /* failure of this is not critical */
1023 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1024 if (!NT_STATUS_IS_OK(result
)) {
1025 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1026 "Won't be able to honour account lockout policies\n"));
1029 /* increase counter */
1030 my_info3
->base
.bad_password_count
++;
1032 if (max_allowed_bad_attempts
== 0) {
1037 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1039 uint32 password_properties
;
1041 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1042 if (!NT_STATUS_IS_OK(result
)) {
1043 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1046 if ((my_info3
->base
.rid
!= DOMAIN_RID_ADMINISTRATOR
) ||
1047 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1048 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1053 result
= winbindd_update_creds_by_info3(domain
,
1054 state
->request
->data
.auth
.user
,
1058 if (!NT_STATUS_IS_OK(result
)) {
1059 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1060 nt_errstr(result
)));
1063 return NT_STATUS_LOGON_FAILURE
;
1066 static NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1067 struct winbindd_cli_state
*state
,
1068 struct netr_SamInfo3
**info3
)
1070 struct winbindd_domain
*contact_domain
;
1071 fstring name_domain
, name_user
;
1074 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1076 /* Parse domain and username */
1078 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1080 /* what domain should we contact? */
1083 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1084 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1085 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1086 result
= NT_STATUS_NO_SUCH_USER
;
1091 if (is_myname(name_domain
)) {
1092 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1093 result
= NT_STATUS_NO_SUCH_USER
;
1097 contact_domain
= find_domain_from_name(name_domain
);
1098 if (contact_domain
== NULL
) {
1099 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1100 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1102 result
= NT_STATUS_NO_SUCH_USER
;
1107 if (contact_domain
->initialized
&&
1108 contact_domain
->active_directory
) {
1112 if (!contact_domain
->initialized
) {
1113 init_dc_connection(contact_domain
);
1116 if (!contact_domain
->active_directory
) {
1117 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1118 return NT_STATUS_INVALID_LOGON_TYPE
;
1121 result
= winbindd_raw_kerberos_login(
1122 state
->mem_ctx
, contact_domain
,
1123 state
->request
->data
.auth
.user
,
1124 state
->request
->data
.auth
.pass
,
1125 state
->request
->data
.auth
.krb5_cc_type
,
1126 get_uid_from_request(state
->request
),
1127 info3
, state
->response
->data
.auth
.krb5ccname
);
1132 static NTSTATUS
winbindd_dual_auth_passdb(TALLOC_CTX
*mem_ctx
,
1133 uint32_t logon_parameters
,
1134 const char *domain
, const char *user
,
1135 const DATA_BLOB
*challenge
,
1136 const DATA_BLOB
*lm_resp
,
1137 const DATA_BLOB
*nt_resp
,
1138 struct netr_SamInfo3
**pinfo3
)
1140 struct auth_usersupplied_info
*user_info
= NULL
;
1141 struct tsocket_address
*local
;
1145 rc
= tsocket_address_inet_from_strings(mem_ctx
,
1151 return NT_STATUS_NO_MEMORY
;
1153 status
= make_user_info(&user_info
, user
, user
, domain
, domain
,
1154 lp_netbios_name(), local
, lm_resp
, nt_resp
, NULL
, NULL
,
1155 NULL
, AUTH_PASSWORD_RESPONSE
);
1156 if (!NT_STATUS_IS_OK(status
)) {
1157 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status
)));
1160 user_info
->logon_parameters
= logon_parameters
;
1162 /* We don't want any more mapping of the username */
1163 user_info
->mapped_state
= True
;
1165 status
= check_sam_security_info3(challenge
, talloc_tos(), user_info
,
1167 free_user_info(&user_info
);
1168 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain
,
1169 user
, nt_errstr(status
)));
1173 static NTSTATUS
winbind_samlogon_retry_loop(struct winbindd_domain
*domain
,
1174 TALLOC_CTX
*mem_ctx
,
1175 uint32_t logon_parameters
,
1177 const char *username
,
1178 const char *domainname
,
1179 const char *workstation
,
1180 const uint8_t chal
[8],
1181 DATA_BLOB lm_response
,
1182 DATA_BLOB nt_response
,
1183 struct netr_SamInfo3
**info3
)
1186 int netr_attempts
= 0;
1191 struct rpc_pipe_client
*netlogon_pipe
;
1192 const struct pipe_auth_data
*auth
;
1193 uint32_t neg_flags
= 0;
1195 ZERO_STRUCTP(info3
);
1198 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1200 if (!NT_STATUS_IS_OK(result
)) {
1201 DEBUG(3,("Could not open handle to NETLOGON pipe "
1202 "(error: %s, attempts: %d)\n",
1203 nt_errstr(result
), netr_attempts
));
1205 /* After the first retry always close the connection */
1206 if (netr_attempts
> 0) {
1207 DEBUG(3, ("This is again a problem for this "
1208 "particular call, forcing the close "
1209 "of this connection\n"));
1210 invalidate_cm_connection(&domain
->conn
);
1213 /* After the second retry failover to the next DC */
1214 if (netr_attempts
> 1) {
1216 * If the netlogon server is not reachable then
1217 * it is possible that the DC is rebuilding
1218 * sysvol and shutdown netlogon for that time.
1219 * We should failover to the next dc.
1221 DEBUG(3, ("This is the third problem for this "
1222 "particular call, adding DC to the "
1223 "negative cache list\n"));
1224 add_failed_connection_entry(domain
->name
,
1227 saf_delete(domain
->name
);
1230 /* Only allow 3 retries */
1231 if (netr_attempts
< 3) {
1232 DEBUG(3, ("The connection to netlogon "
1233 "failed, retrying\n"));
1242 auth
= netlogon_pipe
->auth
;
1243 if (netlogon_pipe
->dc
) {
1244 neg_flags
= netlogon_pipe
->dc
->negotiate_flags
;
1247 /* It is really important to try SamLogonEx here,
1248 * because in a clustered environment, we want to use
1249 * one machine account from multiple physical
1252 * With a normal SamLogon call, we must keep the
1253 * credentials chain updated and intact between all
1254 * users of the machine account (which would imply
1255 * cross-node communication for every NTLM logon).
1257 * (The credentials chain is not per NETLOGON pipe
1258 * connection, but globally on the server/client pair
1261 * When using SamLogonEx, the credentials are not
1262 * supplied, but the session key is implied by the
1263 * wrapping SamLogon context.
1265 * -- abartlet 21 April 2008
1267 * It's also important to use NetlogonValidationSamInfo4 (6),
1268 * because it relies on the rpc transport encryption
1269 * and avoids using the global netlogon schannel
1270 * session key to en/decrypt secret information
1271 * like the user_session_key for network logons.
1273 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1274 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1275 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1276 * are the indication that the server supports
1277 * NetlogonValidationSamInfo4 (6). And it must only
1278 * be used if "SealSecureChannel" is used.
1280 * -- metze 4 February 2011
1284 domain
->can_do_validation6
= false;
1285 } else if (auth
->auth_type
!= DCERPC_AUTH_TYPE_SCHANNEL
) {
1286 domain
->can_do_validation6
= false;
1287 } else if (auth
->auth_level
!= DCERPC_AUTH_LEVEL_PRIVACY
) {
1288 domain
->can_do_validation6
= false;
1289 } else if (!(neg_flags
& NETLOGON_NEG_CROSS_FOREST_TRUSTS
)) {
1290 domain
->can_do_validation6
= false;
1291 } else if (!(neg_flags
& NETLOGON_NEG_AUTHENTICATED_RPC
)) {
1292 domain
->can_do_validation6
= false;
1295 if (domain
->can_do_samlogon_ex
&& domain
->can_do_validation6
) {
1296 result
= rpccli_netlogon_sam_network_logon_ex(
1300 server
, /* server name */
1301 username
, /* user name */
1302 domainname
, /* target domain */
1303 workstation
, /* workstation */
1310 result
= rpccli_netlogon_sam_network_logon(
1314 server
, /* server name */
1315 username
, /* user name */
1316 domainname
, /* target domain */
1317 workstation
, /* workstation */
1319 domain
->can_do_validation6
? 6 : 3,
1325 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
)) {
1328 * It's likely that the server also does not support
1329 * validation level 6
1331 domain
->can_do_validation6
= false;
1333 if (domain
->can_do_samlogon_ex
) {
1334 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1335 "retrying with NetSamLogon\n"));
1336 domain
->can_do_samlogon_ex
= false;
1342 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1343 * (no Ex). This happens against old Samba
1344 * DCs. Drop the connection.
1346 invalidate_cm_connection(&domain
->conn
);
1347 result
= NT_STATUS_LOGON_FAILURE
;
1351 if (domain
->can_do_validation6
&&
1352 (NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_INFO_CLASS
) ||
1353 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_PARAMETER
) ||
1354 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
))) {
1355 DEBUG(3,("Got a DC that can not do validation level 6, "
1356 "retrying with level 3\n"));
1357 domain
->can_do_validation6
= false;
1363 * we increment this after the "feature negotiation"
1364 * for can_do_samlogon_ex and can_do_validation6
1368 /* We have to try a second time as cm_connect_netlogon
1369 might not yet have noticed that the DC has killed
1372 if (!rpccli_is_connected(netlogon_pipe
)) {
1377 /* if we get access denied, a possible cause was that we had
1378 and open connection to the DC, but someone changed our
1379 machine account password out from underneath us using 'net
1380 rpc changetrustpw' */
1382 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1383 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1384 "ACCESS_DENIED. Maybe the trust account "
1385 "password was changed and we didn't know it. "
1386 "Killing connections to domain %s\n",
1388 invalidate_cm_connection(&domain
->conn
);
1392 } while ( (attempts
< 2) && retry
);
1394 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
)) {
1395 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1396 "returned NT_STATUS_IO_TIMEOUT after the retry."
1397 "Killing connections to domain %s\n",
1399 invalidate_cm_connection(&domain
->conn
);
1404 static NTSTATUS
winbindd_dual_pam_auth_samlogon(TALLOC_CTX
*mem_ctx
,
1405 struct winbindd_domain
*domain
,
1408 uint32_t request_flags
,
1409 struct netr_SamInfo3
**info3
)
1415 unsigned char local_nt_response
[24];
1416 fstring name_domain
, name_user
;
1418 struct netr_SamInfo3
*my_info3
= NULL
;
1422 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1424 /* Parse domain and username */
1426 parse_domain_user(user
, name_domain
, name_user
);
1428 /* do password magic */
1430 generate_random_buffer(chal
, sizeof(chal
));
1432 if (lp_client_ntlmv2_auth()) {
1433 DATA_BLOB server_chal
;
1434 DATA_BLOB names_blob
;
1435 server_chal
= data_blob_const(chal
, 8);
1437 /* note that the 'workgroup' here is for the local
1438 machine. The 'server name' must match the
1439 'workstation' passed to the actual SamLogon call.
1441 names_blob
= NTLMv2_generate_names_blob(
1442 mem_ctx
, lp_netbios_name(), lp_workgroup());
1444 if (!SMBNTLMv2encrypt(mem_ctx
, name_user
, name_domain
,
1448 &lm_resp
, &nt_resp
, NULL
, NULL
)) {
1449 data_blob_free(&names_blob
);
1450 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1451 result
= NT_STATUS_NO_MEMORY
;
1454 data_blob_free(&names_blob
);
1456 lm_resp
= data_blob_null
;
1457 SMBNTencrypt(pass
, chal
, local_nt_response
);
1459 nt_resp
= data_blob_talloc(mem_ctx
, local_nt_response
,
1460 sizeof(local_nt_response
));
1463 if (strequal(name_domain
, get_global_sam_name())) {
1464 DATA_BLOB chal_blob
= data_blob_const(chal
, sizeof(chal
));
1466 result
= winbindd_dual_auth_passdb(
1467 mem_ctx
, 0, name_domain
, name_user
,
1468 &chal_blob
, &lm_resp
, &nt_resp
, info3
);
1472 /* check authentication loop */
1474 result
= winbind_samlogon_retry_loop(domain
,
1485 if (!NT_STATUS_IS_OK(result
)) {
1489 /* handle the case where a NT4 DC does not fill in the acct_flags in
1490 * the samlogon reply info3. When accurate info3 is required by the
1491 * caller, we look up the account flags ourselve - gd */
1493 if ((request_flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1494 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1496 struct rpc_pipe_client
*samr_pipe
;
1497 struct policy_handle samr_domain_handle
, user_pol
;
1498 union samr_UserInfo
*info
= NULL
;
1499 NTSTATUS status_tmp
, result_tmp
;
1501 struct dcerpc_binding_handle
*b
;
1503 status_tmp
= cm_connect_sam(domain
, mem_ctx
,
1504 &samr_pipe
, &samr_domain_handle
);
1506 if (!NT_STATUS_IS_OK(status_tmp
)) {
1507 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1508 nt_errstr(status_tmp
)));
1512 b
= samr_pipe
->binding_handle
;
1514 status_tmp
= dcerpc_samr_OpenUser(b
, mem_ctx
,
1515 &samr_domain_handle
,
1516 MAXIMUM_ALLOWED_ACCESS
,
1521 if (!NT_STATUS_IS_OK(status_tmp
)) {
1522 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1523 nt_errstr(status_tmp
)));
1526 if (!NT_STATUS_IS_OK(result_tmp
)) {
1527 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1528 nt_errstr(result_tmp
)));
1532 status_tmp
= dcerpc_samr_QueryUserInfo(b
, mem_ctx
,
1538 if (!NT_STATUS_IS_OK(status_tmp
)) {
1539 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1540 nt_errstr(status_tmp
)));
1541 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1544 if (!NT_STATUS_IS_OK(result_tmp
)) {
1545 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1546 nt_errstr(result_tmp
)));
1547 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1551 acct_flags
= info
->info16
.acct_flags
;
1553 if (acct_flags
== 0) {
1554 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1558 my_info3
->base
.acct_flags
= acct_flags
;
1560 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1562 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1570 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1571 struct winbindd_cli_state
*state
)
1573 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1574 NTSTATUS krb5_result
= NT_STATUS_OK
;
1575 fstring name_domain
, name_user
;
1577 fstring domain_user
;
1578 struct netr_SamInfo3
*info3
= NULL
;
1579 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1581 /* Ensure null termination */
1582 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1584 /* Ensure null termination */
1585 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1587 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1588 state
->request
->data
.auth
.user
));
1590 /* Parse domain and username */
1592 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1593 state
->request
->data
.auth
.user
,
1596 /* If the name normalization didnt' actually do anything,
1597 just use the original name */
1599 if (!NT_STATUS_IS_OK(name_map_status
) &&
1600 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1602 mapped_user
= state
->request
->data
.auth
.user
;
1605 parse_domain_user(mapped_user
, name_domain
, name_user
);
1607 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1608 fstr_sprintf( domain_user
, "%s%c%s", name_domain
,
1609 *lp_winbind_separator(),
1611 strlcpy( state
->request
->data
.auth
.user
, domain_user
,
1612 sizeof(state
->request
->data
.auth
.user
));
1615 if (!domain
->online
) {
1616 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1617 if (domain
->startup
) {
1618 /* Logons are very important to users. If we're offline and
1619 we get a request within the first 30 seconds of startup,
1620 try very hard to find a DC and go online. */
1622 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1623 "request in startup mode.\n", domain
->name
));
1625 winbindd_flush_negative_conn_cache(domain
);
1626 result
= init_dc_connection(domain
);
1630 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1632 /* Check for Kerberos authentication */
1633 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1635 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1636 /* save for later */
1637 krb5_result
= result
;
1640 if (NT_STATUS_IS_OK(result
)) {
1641 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1642 goto process_result
;
1644 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1647 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1648 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1649 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1650 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1651 set_domain_offline( domain
);
1655 /* there are quite some NT_STATUS errors where there is no
1656 * point in retrying with a samlogon, we explictly have to take
1657 * care not to increase the bad logon counter on the DC */
1659 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1660 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1661 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1662 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1663 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1664 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1665 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1666 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1667 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1668 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1672 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1673 DEBUG(3,("falling back to samlogon\n"));
1681 /* Check for Samlogon authentication */
1682 if (domain
->online
) {
1683 result
= winbindd_dual_pam_auth_samlogon(
1684 state
->mem_ctx
, domain
,
1685 state
->request
->data
.auth
.user
,
1686 state
->request
->data
.auth
.pass
,
1687 state
->request
->flags
,
1690 if (NT_STATUS_IS_OK(result
)) {
1691 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1692 /* add the Krb5 err if we have one */
1693 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1694 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1696 goto process_result
;
1699 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1700 nt_errstr(result
)));
1702 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1703 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1704 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1706 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1707 set_domain_offline( domain
);
1711 if (domain
->online
) {
1712 /* We're still online - fail. */
1718 /* Check for Cached logons */
1719 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1720 lp_winbind_offline_logon()) {
1722 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1724 if (NT_STATUS_IS_OK(result
)) {
1725 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1726 goto process_result
;
1728 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1735 if (NT_STATUS_IS_OK(result
)) {
1737 struct dom_sid user_sid
;
1739 /* In all codepaths where result == NT_STATUS_OK info3 must have
1740 been initialized. */
1742 result
= NT_STATUS_INTERNAL_ERROR
;
1746 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1749 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1751 netsamlogon_cache_store(name_user
, info3
);
1753 /* save name_to_sid info as early as possible (only if
1754 this is our primary domain so we don't invalidate
1755 the cache entry by storing the seq_num for the wrong
1757 if ( domain
->primary
) {
1758 cache_name2sid(domain
, name_domain
, name_user
,
1759 SID_NAME_USER
, &user_sid
);
1762 /* Check if the user is in the right group */
1764 result
= check_info3_in_group(
1766 state
->request
->data
.auth
.require_membership_of_sid
);
1767 if (!NT_STATUS_IS_OK(result
)) {
1768 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1769 state
->request
->data
.auth
.user
,
1770 state
->request
->data
.auth
.require_membership_of_sid
));
1774 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1775 state
->request
->flags
, info3
,
1776 name_domain
, name_user
);
1777 if (!NT_STATUS_IS_OK(result
)) {
1781 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
1782 && lp_winbind_offline_logon()) {
1784 result
= winbindd_store_creds(domain
,
1785 state
->request
->data
.auth
.user
,
1786 state
->request
->data
.auth
.pass
,
1790 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1791 struct winbindd_domain
*our_domain
= find_our_domain();
1793 /* This is not entirely correct I believe, but it is
1794 consistent. Only apply the password policy settings
1795 too warn users for our own domain. Cannot obtain these
1796 from trusted DCs all the time so don't do it at all.
1799 result
= NT_STATUS_NOT_SUPPORTED
;
1800 if (our_domain
== domain
) {
1801 result
= fillup_password_policy(
1802 our_domain
, state
->response
);
1805 if (!NT_STATUS_IS_OK(result
)
1806 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1808 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1809 domain
->name
, nt_errstr(result
)));
1814 result
= NT_STATUS_OK
;
1818 /* give us a more useful (more correct?) error code */
1819 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1820 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1821 result
= NT_STATUS_NO_LOGON_SERVERS
;
1824 set_auth_errors(state
->response
, result
);
1826 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1827 state
->request
->data
.auth
.user
,
1828 state
->response
->data
.auth
.nt_status_string
,
1829 state
->response
->data
.auth
.pam_error
));
1831 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1834 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1835 struct winbindd_cli_state
*state
)
1838 struct netr_SamInfo3
*info3
= NULL
;
1839 const char *name_user
= NULL
;
1840 const char *name_domain
= NULL
;
1841 const char *workstation
;
1843 DATA_BLOB lm_resp
, nt_resp
;
1845 /* This is child-only, so no check for privileged access is needed
1848 /* Ensure null termination */
1849 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1850 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1852 name_user
= state
->request
->data
.auth_crap
.user
;
1853 name_domain
= state
->request
->data
.auth_crap
.domain
;
1854 workstation
= state
->request
->data
.auth_crap
.workstation
;
1856 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1857 name_domain
, name_user
));
1859 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1860 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1861 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1862 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1863 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1864 state
->request
->data
.auth_crap
.lm_resp_len
,
1865 state
->request
->data
.auth_crap
.nt_resp_len
));
1866 result
= NT_STATUS_INVALID_PARAMETER
;
1871 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1872 state
->request
->data
.auth_crap
.lm_resp_len
);
1874 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1875 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1876 state
->request
->extra_data
.data
,
1877 state
->request
->data
.auth_crap
.nt_resp_len
);
1879 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1880 state
->request
->data
.auth_crap
.nt_resp
,
1881 state
->request
->data
.auth_crap
.nt_resp_len
);
1884 if (strequal(name_domain
, get_global_sam_name())) {
1885 DATA_BLOB chal_blob
= data_blob_const(
1886 state
->request
->data
.auth_crap
.chal
,
1887 sizeof(state
->request
->data
.auth_crap
.chal
));
1889 result
= winbindd_dual_auth_passdb(
1891 state
->request
->data
.auth_crap
.logon_parameters
,
1892 name_domain
, name_user
,
1893 &chal_blob
, &lm_resp
, &nt_resp
, &info3
);
1894 goto process_result
;
1897 result
= winbind_samlogon_retry_loop(domain
,
1899 state
->request
->data
.auth_crap
.logon_parameters
,
1903 /* Bug #3248 - found by Stefan Burkei. */
1904 workstation
, /* We carefully set this above so use it... */
1905 state
->request
->data
.auth_crap
.chal
,
1909 if (!NT_STATUS_IS_OK(result
)) {
1915 if (NT_STATUS_IS_OK(result
)) {
1916 struct dom_sid user_sid
;
1918 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1920 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1922 netsamlogon_cache_store(name_user
, info3
);
1924 /* Check if the user is in the right group */
1926 result
= check_info3_in_group(
1928 state
->request
->data
.auth_crap
.require_membership_of_sid
);
1929 if (!NT_STATUS_IS_OK(result
)) {
1930 DEBUG(3, ("User %s is not in the required group (%s), so "
1931 "crap authentication is rejected\n",
1932 state
->request
->data
.auth_crap
.user
,
1933 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1937 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1938 state
->request
->flags
, info3
,
1939 name_domain
, name_user
);
1940 if (!NT_STATUS_IS_OK(result
)) {
1947 /* give us a more useful (more correct?) error code */
1948 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1949 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1950 result
= NT_STATUS_NO_LOGON_SERVERS
;
1953 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1954 result
= nt_status_squash(result
);
1957 set_auth_errors(state
->response
, result
);
1959 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1960 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1963 state
->response
->data
.auth
.nt_status_string
,
1964 state
->response
->data
.auth
.pam_error
));
1966 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1969 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
1970 struct winbindd_cli_state
*state
)
1973 char *newpass
= NULL
;
1974 struct policy_handle dom_pol
;
1975 struct rpc_pipe_client
*cli
= NULL
;
1976 bool got_info
= false;
1977 struct samr_DomInfo1
*info
= NULL
;
1978 struct userPwdChangeFailureInformation
*reject
= NULL
;
1979 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1980 fstring domain
, user
;
1981 struct dcerpc_binding_handle
*b
= NULL
;
1983 ZERO_STRUCT(dom_pol
);
1985 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
1986 state
->request
->data
.auth
.user
));
1988 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
1992 /* Change password */
1994 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
1995 newpass
= state
->request
->data
.chauthtok
.newpass
;
1997 /* Initialize reject reason */
1998 state
->response
->data
.auth
.reject_reason
= Undefined
;
2000 /* Get sam handle */
2002 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
2004 if (!NT_STATUS_IS_OK(result
)) {
2005 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2009 b
= cli
->binding_handle
;
2011 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2018 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2020 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2022 fill_in_password_policy(state
->response
, info
);
2024 state
->response
->data
.auth
.reject_reason
=
2025 reject
->extendedFailureReason
;
2030 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2031 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2032 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2033 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2035 /* only fallback when the chgpasswd_user3 call is not supported */
2036 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
) ||
2037 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) ||
2038 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
) ||
2039 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
2041 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2042 nt_errstr(result
)));
2044 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2046 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2047 Map to the same status code as Windows 2003. */
2049 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2050 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2056 if (NT_STATUS_IS_OK(result
)
2057 && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
2058 && lp_winbind_offline_logon()) {
2059 result
= winbindd_update_creds_by_name(contact_domain
, user
,
2061 /* Again, this happens when we login from gdm or xdm
2062 * and the password expires, *BUT* cached crendentials
2063 * doesn't exist. winbindd_update_creds_by_name()
2064 * returns NT_STATUS_NO_SUCH_USER.
2065 * This is not a failure.
2068 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2069 result
= NT_STATUS_OK
;
2072 if (!NT_STATUS_IS_OK(result
)) {
2073 DEBUG(10, ("Failed to store creds: %s\n",
2074 nt_errstr(result
)));
2075 goto process_result
;
2079 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2081 NTSTATUS policy_ret
;
2083 policy_ret
= fillup_password_policy(
2084 contact_domain
, state
->response
);
2086 /* failure of this is non critical, it will just provide no
2087 * additional information to the client why the change has
2088 * failed - Guenther */
2090 if (!NT_STATUS_IS_OK(policy_ret
)) {
2091 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2092 goto process_result
;
2098 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2099 /* FIXME: internal rpc pipe does not cache handles yet */
2101 if (is_valid_policy_hnd(&dom_pol
)) {
2103 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2109 set_auth_errors(state
->response
, result
);
2111 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2112 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2115 state
->response
->data
.auth
.nt_status_string
,
2116 state
->response
->data
.auth
.pam_error
));
2118 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2121 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2122 struct winbindd_cli_state
*state
)
2124 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2126 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2127 state
->request
->data
.logoff
.user
));
2129 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2130 result
= NT_STATUS_OK
;
2131 goto process_result
;
2134 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2135 result
= NT_STATUS_OK
;
2136 goto process_result
;
2141 if (state
->request
->data
.logoff
.uid
< 0) {
2142 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2143 goto process_result
;
2146 /* what we need here is to find the corresponding krb5 ccache name *we*
2147 * created for a given username and destroy it */
2149 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2150 result
= NT_STATUS_OK
;
2151 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2152 goto process_result
;
2155 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2156 state
->request
->data
.logoff
.uid
,
2157 state
->request
->data
.logoff
.krb5ccname
)) {
2158 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2159 goto process_result
;
2162 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2163 if (!NT_STATUS_IS_OK(result
)) {
2164 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2165 nt_errstr(result
)));
2166 goto process_result
;
2170 * Remove any mlock'ed memory creds in the child
2171 * we might be using for krb5 ticket renewal.
2174 winbindd_delete_memory_creds(state
->request
->data
.logoff
.user
);
2177 result
= NT_STATUS_NOT_SUPPORTED
;
2183 set_auth_errors(state
->response
, result
);
2185 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2188 /* Change user password with auth crap*/
2190 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2193 DATA_BLOB new_nt_password
;
2194 DATA_BLOB old_nt_hash_enc
;
2195 DATA_BLOB new_lm_password
;
2196 DATA_BLOB old_lm_hash_enc
;
2197 fstring domain
,user
;
2198 struct policy_handle dom_pol
;
2199 struct winbindd_domain
*contact_domain
= domainSt
;
2200 struct rpc_pipe_client
*cli
= NULL
;
2201 struct dcerpc_binding_handle
*b
= NULL
;
2203 ZERO_STRUCT(dom_pol
);
2205 /* Ensure null termination */
2206 state
->request
->data
.chng_pswd_auth_crap
.user
[
2207 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2208 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2209 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2213 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2214 (unsigned long)state
->pid
,
2215 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2216 state
->request
->data
.chng_pswd_auth_crap
.user
));
2218 if (lp_winbind_offline_logon()) {
2219 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2220 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2221 result
= NT_STATUS_ACCESS_DENIED
;
2225 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2226 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2228 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2232 DEBUG(3,("no domain specified with username (%s) - "
2234 state
->request
->data
.chng_pswd_auth_crap
.user
));
2235 result
= NT_STATUS_NO_SUCH_USER
;
2240 if (!*domain
&& lp_winbind_use_default_domain()) {
2241 fstrcpy(domain
,lp_workgroup());
2245 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2248 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2249 (unsigned long)state
->pid
, domain
, user
));
2251 /* Change password */
2252 new_nt_password
= data_blob_const(
2253 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2254 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2256 old_nt_hash_enc
= data_blob_const(
2257 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2258 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2260 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2261 new_lm_password
= data_blob_const(
2262 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2263 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2265 old_lm_hash_enc
= data_blob_const(
2266 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2267 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2269 new_lm_password
= data_blob_null
;
2270 old_lm_hash_enc
= data_blob_null
;
2273 /* Get sam handle */
2275 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2276 if (!NT_STATUS_IS_OK(result
)) {
2277 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2281 b
= cli
->binding_handle
;
2283 result
= rpccli_samr_chng_pswd_auth_crap(
2284 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2285 new_lm_password
, old_lm_hash_enc
);
2289 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2290 /* FIXME: internal rpc pipe does not cache handles yet */
2292 if (is_valid_policy_hnd(&dom_pol
)) {
2294 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2300 set_auth_errors(state
->response
, result
);
2302 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2303 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2305 state
->response
->data
.auth
.nt_status_string
,
2306 state
->response
->data
.auth
.pam_error
));
2308 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2312 static NTSTATUS
extract_pac_vrfy_sigs(TALLOC_CTX
*mem_ctx
, DATA_BLOB pac_blob
,
2313 struct PAC_LOGON_INFO
**logon_info
)
2315 krb5_context krbctx
= NULL
;
2316 krb5_error_code k5ret
;
2318 krb5_kt_cursor cursor
;
2319 krb5_keytab_entry entry
;
2320 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
2323 ZERO_STRUCT(cursor
);
2325 k5ret
= krb5_init_context(&krbctx
);
2327 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2328 error_message(k5ret
)));
2329 status
= krb5_to_nt_status(k5ret
);
2333 k5ret
= gse_krb5_get_server_keytab(krbctx
, &keytab
);
2335 DEBUG(1, ("Failed to get keytab: %s\n",
2336 error_message(k5ret
)));
2337 status
= krb5_to_nt_status(k5ret
);
2341 k5ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &cursor
);
2343 DEBUG(1, ("Failed to start seq: %s\n",
2344 error_message(k5ret
)));
2345 status
= krb5_to_nt_status(k5ret
);
2349 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2350 while (k5ret
== 0) {
2351 status
= kerberos_pac_logon_info(mem_ctx
, pac_blob
,
2353 KRB5_KT_KEY(&entry
), NULL
, 0,
2355 if (NT_STATUS_IS_OK(status
)) {
2358 k5ret
= smb_krb5_kt_free_entry(krbctx
, &entry
);
2359 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2362 k5ret
= krb5_kt_end_seq_get(krbctx
, keytab
, &cursor
);
2364 DEBUG(1, ("Failed to end seq: %s\n",
2365 error_message(k5ret
)));
2368 k5ret
= krb5_kt_close(krbctx
, keytab
);
2370 DEBUG(1, ("Failed to close keytab: %s\n",
2371 error_message(k5ret
)));
2374 krb5_free_context(krbctx
);
2379 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2380 struct netr_SamInfo3
**info3
)
2382 struct winbindd_request
*req
= state
->request
;
2384 struct PAC_LOGON_INFO
*logon_info
= NULL
;
2387 pac_blob
= data_blob_const(req
->extra_data
.data
, req
->extra_len
);
2388 result
= extract_pac_vrfy_sigs(state
->mem_ctx
, pac_blob
, &logon_info
);
2389 if (!NT_STATUS_IS_OK(result
) &&
2390 !NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
)) {
2391 DEBUG(1, ("Error during PAC signature verification: %s\n",
2392 nt_errstr(result
)));
2397 /* Signature verification succeeded, trust the PAC */
2398 netsamlogon_cache_store(NULL
, &logon_info
->info3
);
2401 /* Try without signature verification */
2402 result
= kerberos_pac_logon_info(state
->mem_ctx
, pac_blob
, NULL
,
2403 NULL
, NULL
, NULL
, 0,
2405 if (!NT_STATUS_IS_OK(result
)) {
2406 DEBUG(10, ("Could not extract PAC: %s\n",
2407 nt_errstr(result
)));
2412 *info3
= &logon_info
->info3
;
2414 return NT_STATUS_OK
;
2416 #else /* HAVE_KRB5 */
2417 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2418 struct netr_SamInfo3
**info3
)
2420 return NT_STATUS_NO_SUCH_USER
;
2422 #endif /* HAVE_KRB5 */