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
;
579 struct PAC_DATA
*pac_data
= NULL
;
580 struct PAC_DATA_CTR
*pac_data_ctr
= NULL
;
581 const char *local_service
;
586 if (domain
->alt_name
== NULL
) {
587 return NT_STATUS_INVALID_PARAMETER
;
591 * prepare a krb5_cc_cache string for the user */
594 DEBUG(0,("no valid uid\n"));
597 cc
= generate_krb5_ccache(mem_ctx
,
602 return NT_STATUS_NO_MEMORY
;
607 * get kerberos properties */
609 if (domain
->private_data
) {
610 ads
= (ADS_STRUCT
*)domain
->private_data
;
611 time_offset
= ads
->auth
.time_offset
;
616 * do kerberos auth and setup ccache as the user */
618 parse_domain_user(user
, name_domain
, name_user
);
620 realm
= talloc_strdup(mem_ctx
, domain
->alt_name
);
622 return NT_STATUS_NO_MEMORY
;
625 if (!strupper_m(realm
)) {
626 return NT_STATUS_INVALID_PARAMETER
;
629 principal_s
= talloc_asprintf(mem_ctx
, "%s@%s", name_user
, realm
);
630 if (principal_s
== NULL
) {
631 return NT_STATUS_NO_MEMORY
;
634 service
= talloc_asprintf(mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
635 if (service
== NULL
) {
636 return NT_STATUS_NO_MEMORY
;
639 local_service
= talloc_asprintf(mem_ctx
, "%s$@%s",
640 lp_netbios_name(), lp_realm());
641 if (local_service
== NULL
) {
642 return NT_STATUS_NO_MEMORY
;
646 /* if this is a user ccache, we need to act as the user to let the krb5
647 * library handle the chown, etc. */
649 /************************ ENTERING NON-ROOT **********************/
651 if (user_ccache_file
!= NULL
) {
652 set_effective_uid(uid
);
653 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
656 result
= kerberos_return_pac(mem_ctx
,
665 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
669 if (user_ccache_file
!= NULL
) {
670 gain_root_privilege();
673 /************************ RETURNED TO ROOT **********************/
675 if (!NT_STATUS_IS_OK(result
)) {
679 if (pac_data_ctr
== NULL
) {
683 pac_data
= pac_data_ctr
->pac_data
;
684 if (pac_data
== NULL
) {
688 for (i
=0; i
< pac_data
->num_buffers
; i
++) {
690 if (pac_data
->buffers
[i
].type
!= PAC_TYPE_LOGON_INFO
) {
694 logon_info
= pac_data
->buffers
[i
].info
->logon_info
.info
;
696 return NT_STATUS_INVALID_PARAMETER
;
702 *info3
= &logon_info
->info3
;
704 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
707 /* if we had a user's ccache then return that string for the pam
710 if (user_ccache_file
!= NULL
) {
712 fstrcpy(krb5ccname
, user_ccache_file
);
714 result
= add_ccache_to_list(principal_s
,
726 if (!NT_STATUS_IS_OK(result
)) {
727 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
732 /* need to delete the memory cred cache, it is not used anymore */
734 krb5_ret
= ads_kdestroy(cc
);
736 DEBUG(3,("winbindd_raw_kerberos_login: "
737 "could not destroy krb5 credential cache: "
738 "%s\n", error_message(krb5_ret
)));
747 * Do not delete an existing valid credential cache, if the user
748 * e.g. enters a wrong password
750 if ((strequal(krb5_cc_type
, "FILE") || strequal(krb5_cc_type
, "WRFILE"))
751 && user_ccache_file
!= NULL
) {
755 /* we could have created a new credential cache with a valid tgt in it
756 * but we werent able to get or verify the service ticket for this
757 * local host and therefor didn't get the PAC, we need to remove that
758 * cache entirely now */
760 krb5_ret
= ads_kdestroy(cc
);
762 DEBUG(3,("winbindd_raw_kerberos_login: "
763 "could not destroy krb5 credential cache: "
764 "%s\n", error_message(krb5_ret
)));
767 if (!NT_STATUS_IS_OK(remove_ccache(user
))) {
768 DEBUG(3,("winbindd_raw_kerberos_login: "
769 "could not remove ccache for user %s\n",
775 return NT_STATUS_NOT_SUPPORTED
;
776 #endif /* HAVE_KRB5 */
779 /****************************************************************
780 ****************************************************************/
782 bool check_request_flags(uint32_t flags
)
784 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
785 WBFLAG_PAM_INFO3_TEXT
|
786 WBFLAG_PAM_INFO3_NDR
;
788 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
789 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
790 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
791 !(flags
& flags_edata
) ) {
795 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
801 /****************************************************************
802 ****************************************************************/
804 NTSTATUS
append_auth_data(TALLOC_CTX
*mem_ctx
,
805 struct winbindd_response
*resp
,
806 uint32_t request_flags
,
807 struct netr_SamInfo3
*info3
,
808 const char *name_domain
,
809 const char *name_user
)
813 if (request_flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
814 memcpy(resp
->data
.auth
.user_session_key
,
816 sizeof(resp
->data
.auth
.user_session_key
)
820 if (request_flags
& WBFLAG_PAM_LMKEY
) {
821 memcpy(resp
->data
.auth
.first_8_lm_hash
,
822 info3
->base
.LMSessKey
.key
,
823 sizeof(resp
->data
.auth
.first_8_lm_hash
)
827 if (request_flags
& WBFLAG_PAM_UNIX_NAME
) {
828 result
= append_unix_username(mem_ctx
, resp
,
829 info3
, name_domain
, name_user
);
830 if (!NT_STATUS_IS_OK(result
)) {
831 DEBUG(10,("Failed to append Unix Username: %s\n",
837 /* currently, anything from here on potentially overwrites extra_data. */
839 if (request_flags
& WBFLAG_PAM_INFO3_NDR
) {
840 result
= append_info3_as_ndr(mem_ctx
, resp
, info3
);
841 if (!NT_STATUS_IS_OK(result
)) {
842 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
848 if (request_flags
& WBFLAG_PAM_INFO3_TEXT
) {
849 result
= append_info3_as_txt(mem_ctx
, resp
, info3
);
850 if (!NT_STATUS_IS_OK(result
)) {
851 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
857 if (request_flags
& WBFLAG_PAM_AFS_TOKEN
) {
858 result
= append_afs_token(mem_ctx
, resp
,
859 info3
, name_domain
, name_user
);
860 if (!NT_STATUS_IS_OK(result
)) {
861 DEBUG(10,("Failed to append AFS token: %s\n",
870 static NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
871 struct winbindd_cli_state
*state
,
872 struct netr_SamInfo3
**info3
)
874 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
875 uint16 max_allowed_bad_attempts
;
876 fstring name_domain
, name_user
;
878 enum lsa_SidType type
;
879 uchar new_nt_pass
[NT_HASH_LEN
];
880 const uint8
*cached_nt_pass
;
881 const uint8
*cached_salt
;
882 struct netr_SamInfo3
*my_info3
;
883 time_t kickoff_time
, must_change_time
;
884 bool password_good
= false;
886 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
893 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
895 /* Parse domain and username */
897 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
900 if (!lookup_cached_name(name_domain
,
904 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
905 return NT_STATUS_NO_SUCH_USER
;
908 if (type
!= SID_NAME_USER
) {
909 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
910 return NT_STATUS_LOGON_FAILURE
;
913 result
= winbindd_get_creds(domain
,
919 if (!NT_STATUS_IS_OK(result
)) {
920 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
926 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
928 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
929 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
931 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
935 /* In this case we didn't store the nt_hash itself,
936 but the MD5 combination of salt + nt_hash. */
937 uchar salted_hash
[NT_HASH_LEN
];
938 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
940 password_good
= (memcmp(cached_nt_pass
, salted_hash
,
943 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
944 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
950 /* User *DOES* know the password, update logon_time and reset
953 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
955 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
956 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
959 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
960 return NT_STATUS_ACCOUNT_DISABLED
;
963 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
964 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
967 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
968 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
971 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
972 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
975 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
976 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
977 my_info3
->base
.acct_flags
));
978 return NT_STATUS_LOGON_FAILURE
;
981 kickoff_time
= nt_time_to_unix(my_info3
->base
.kickoff_time
);
982 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
983 return NT_STATUS_ACCOUNT_EXPIRED
;
986 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
987 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
988 /* we allow grace logons when the password has expired */
989 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
990 /* return NT_STATUS_PASSWORD_EXPIRED; */
995 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
996 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
997 ((tdc_domain
->trust_type
& NETR_TRUST_TYPE_UPLEVEL
) ||
998 /* used to cope with the case winbindd starting without network. */
999 !strequal(tdc_domain
->domain_name
, tdc_domain
->dns_name
))) {
1002 const char *cc
= NULL
;
1004 const char *principal_s
= NULL
;
1005 const char *service
= NULL
;
1006 const char *user_ccache_file
;
1008 if (domain
->alt_name
== NULL
) {
1009 return NT_STATUS_INVALID_PARAMETER
;
1012 uid
= get_uid_from_request(state
->request
);
1014 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1015 return NT_STATUS_INVALID_PARAMETER
;
1018 cc
= generate_krb5_ccache(state
->mem_ctx
,
1019 state
->request
->data
.auth
.krb5_cc_type
,
1020 state
->request
->data
.auth
.uid
,
1023 return NT_STATUS_NO_MEMORY
;
1026 realm
= talloc_strdup(state
->mem_ctx
, domain
->alt_name
);
1027 if (realm
== NULL
) {
1028 return NT_STATUS_NO_MEMORY
;
1031 if (!strupper_m(realm
)) {
1032 return NT_STATUS_INVALID_PARAMETER
;
1035 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
1036 if (principal_s
== NULL
) {
1037 return NT_STATUS_NO_MEMORY
;
1040 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
1041 if (service
== NULL
) {
1042 return NT_STATUS_NO_MEMORY
;
1045 if (user_ccache_file
!= NULL
) {
1047 fstrcpy(state
->response
->data
.auth
.krb5ccname
,
1050 result
= add_ccache_to_list(principal_s
,
1053 state
->request
->data
.auth
.user
,
1054 state
->request
->data
.auth
.pass
,
1058 time(NULL
) + lp_winbind_cache_time(),
1059 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
1062 if (!NT_STATUS_IS_OK(result
)) {
1063 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1064 "to add ccache to list: %s\n",
1065 nt_errstr(result
)));
1069 #endif /* HAVE_KRB5 */
1071 /* FIXME: we possibly should handle logon hours as well (does xp when
1072 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1074 unix_to_nt_time(&my_info3
->base
.logon_time
, time(NULL
));
1075 my_info3
->base
.bad_password_count
= 0;
1077 result
= winbindd_update_creds_by_info3(domain
,
1078 state
->request
->data
.auth
.user
,
1079 state
->request
->data
.auth
.pass
,
1081 if (!NT_STATUS_IS_OK(result
)) {
1082 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1083 nt_errstr(result
)));
1087 return NT_STATUS_OK
;
1091 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1092 if (domain
->online
== false) {
1096 /* failure of this is not critical */
1097 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1098 if (!NT_STATUS_IS_OK(result
)) {
1099 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1100 "Won't be able to honour account lockout policies\n"));
1103 /* increase counter */
1104 my_info3
->base
.bad_password_count
++;
1106 if (max_allowed_bad_attempts
== 0) {
1111 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1113 uint32 password_properties
;
1115 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1116 if (!NT_STATUS_IS_OK(result
)) {
1117 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1120 if ((my_info3
->base
.rid
!= DOMAIN_RID_ADMINISTRATOR
) ||
1121 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1122 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1127 result
= winbindd_update_creds_by_info3(domain
,
1128 state
->request
->data
.auth
.user
,
1132 if (!NT_STATUS_IS_OK(result
)) {
1133 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1134 nt_errstr(result
)));
1137 return NT_STATUS_LOGON_FAILURE
;
1140 static NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1141 struct winbindd_cli_state
*state
,
1142 struct netr_SamInfo3
**info3
)
1144 struct winbindd_domain
*contact_domain
;
1145 fstring name_domain
, name_user
;
1148 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1150 /* Parse domain and username */
1152 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1154 /* what domain should we contact? */
1157 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1158 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1159 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1160 result
= NT_STATUS_NO_SUCH_USER
;
1165 if (is_myname(name_domain
)) {
1166 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1167 result
= NT_STATUS_NO_SUCH_USER
;
1171 contact_domain
= find_domain_from_name(name_domain
);
1172 if (contact_domain
== NULL
) {
1173 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1174 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1176 result
= NT_STATUS_NO_SUCH_USER
;
1181 if (contact_domain
->initialized
&&
1182 contact_domain
->active_directory
) {
1186 if (!contact_domain
->initialized
) {
1187 init_dc_connection(contact_domain
);
1190 if (!contact_domain
->active_directory
) {
1191 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1192 return NT_STATUS_INVALID_LOGON_TYPE
;
1195 result
= winbindd_raw_kerberos_login(
1196 state
->mem_ctx
, contact_domain
,
1197 state
->request
->data
.auth
.user
,
1198 state
->request
->data
.auth
.pass
,
1199 state
->request
->data
.auth
.krb5_cc_type
,
1200 get_uid_from_request(state
->request
),
1201 info3
, state
->response
->data
.auth
.krb5ccname
);
1206 static NTSTATUS
winbindd_dual_auth_passdb(TALLOC_CTX
*mem_ctx
,
1207 uint32_t logon_parameters
,
1208 const char *domain
, const char *user
,
1209 const DATA_BLOB
*challenge
,
1210 const DATA_BLOB
*lm_resp
,
1211 const DATA_BLOB
*nt_resp
,
1212 struct netr_SamInfo3
**pinfo3
)
1214 struct auth_usersupplied_info
*user_info
= NULL
;
1215 struct tsocket_address
*local
;
1218 TALLOC_CTX
*frame
= talloc_stackframe();
1220 rc
= tsocket_address_inet_from_strings(mem_ctx
,
1227 return NT_STATUS_NO_MEMORY
;
1229 status
= make_user_info(frame
, &user_info
, user
, user
, domain
, domain
,
1230 lp_netbios_name(), local
, lm_resp
, nt_resp
, NULL
, NULL
,
1231 NULL
, AUTH_PASSWORD_RESPONSE
);
1232 if (!NT_STATUS_IS_OK(status
)) {
1233 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status
)));
1237 user_info
->logon_parameters
= logon_parameters
;
1239 /* We don't want any more mapping of the username */
1240 user_info
->mapped_state
= True
;
1242 status
= check_sam_security_info3(challenge
, mem_ctx
, user_info
,
1244 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain
,
1245 user
, nt_errstr(status
)));
1250 static NTSTATUS
winbind_samlogon_retry_loop(struct winbindd_domain
*domain
,
1251 TALLOC_CTX
*mem_ctx
,
1252 uint32_t logon_parameters
,
1254 const char *username
,
1255 const char *domainname
,
1256 const char *workstation
,
1257 const uint8_t chal
[8],
1258 DATA_BLOB lm_response
,
1259 DATA_BLOB nt_response
,
1260 struct netr_SamInfo3
**info3
)
1263 int netr_attempts
= 0;
1268 struct rpc_pipe_client
*netlogon_pipe
;
1269 uint8_t authoritative
= 0;
1272 ZERO_STRUCTP(info3
);
1275 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1277 if (!NT_STATUS_IS_OK(result
)) {
1278 DEBUG(3,("Could not open handle to NETLOGON pipe "
1279 "(error: %s, attempts: %d)\n",
1280 nt_errstr(result
), netr_attempts
));
1282 /* After the first retry always close the connection */
1283 if (netr_attempts
> 0) {
1284 DEBUG(3, ("This is again a problem for this "
1285 "particular call, forcing the close "
1286 "of this connection\n"));
1287 invalidate_cm_connection(&domain
->conn
);
1290 /* After the second retry failover to the next DC */
1291 if (netr_attempts
> 1) {
1293 * If the netlogon server is not reachable then
1294 * it is possible that the DC is rebuilding
1295 * sysvol and shutdown netlogon for that time.
1296 * We should failover to the next dc.
1298 DEBUG(3, ("This is the third problem for this "
1299 "particular call, adding DC to the "
1300 "negative cache list\n"));
1301 add_failed_connection_entry(domain
->name
,
1304 saf_delete(domain
->name
);
1307 /* Only allow 3 retries */
1308 if (netr_attempts
< 3) {
1309 DEBUG(3, ("The connection to netlogon "
1310 "failed, retrying\n"));
1319 result
= rpccli_netlogon_network_logon(domain
->conn
.netlogon_creds
,
1320 netlogon_pipe
->binding_handle
,
1334 * we increment this after the "feature negotiation"
1335 * for can_do_samlogon_ex and can_do_validation6
1339 /* We have to try a second time as cm_connect_netlogon
1340 might not yet have noticed that the DC has killed
1343 if (!rpccli_is_connected(netlogon_pipe
)) {
1348 /* if we get access denied, a possible cause was that we had
1349 and open connection to the DC, but someone changed our
1350 machine account password out from underneath us using 'net
1351 rpc changetrustpw' */
1353 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1354 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1355 "ACCESS_DENIED. Maybe the trust account "
1356 "password was changed and we didn't know it. "
1357 "Killing connections to domain %s\n",
1359 invalidate_cm_connection(&domain
->conn
);
1363 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
)) {
1365 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1366 * (no Ex). This happens against old Samba
1367 * DCs, if LogonSamLogonEx() fails with an error
1368 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1370 * The server will log something like this:
1371 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1373 * This sets the whole connection into a fault_state mode
1374 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1376 * This also happens to our retry with LogonSamLogonWithFlags()
1377 * and LogonSamLogon().
1379 * In order to recover from this situation, we need to
1380 * drop the connection.
1382 invalidate_cm_connection(&domain
->conn
);
1383 result
= NT_STATUS_LOGON_FAILURE
;
1387 } while ( (attempts
< 2) && retry
);
1389 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
)) {
1390 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1391 "returned NT_STATUS_IO_TIMEOUT after the retry."
1392 "Killing connections to domain %s\n",
1394 invalidate_cm_connection(&domain
->conn
);
1399 static NTSTATUS
winbindd_dual_pam_auth_samlogon(TALLOC_CTX
*mem_ctx
,
1400 struct winbindd_domain
*domain
,
1403 uint32_t request_flags
,
1404 struct netr_SamInfo3
**info3
)
1410 unsigned char local_nt_response
[24];
1411 fstring name_domain
, name_user
;
1413 struct netr_SamInfo3
*my_info3
= NULL
;
1417 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1419 /* Parse domain and username */
1421 parse_domain_user(user
, name_domain
, name_user
);
1423 /* do password magic */
1425 generate_random_buffer(chal
, sizeof(chal
));
1427 if (lp_client_ntlmv2_auth()) {
1428 DATA_BLOB server_chal
;
1429 DATA_BLOB names_blob
;
1430 server_chal
= data_blob_const(chal
, 8);
1432 /* note that the 'workgroup' here is for the local
1433 machine. The 'server name' must match the
1434 'workstation' passed to the actual SamLogon call.
1436 names_blob
= NTLMv2_generate_names_blob(
1437 mem_ctx
, lp_netbios_name(), lp_workgroup());
1439 if (!SMBNTLMv2encrypt(mem_ctx
, name_user
, name_domain
,
1443 &lm_resp
, &nt_resp
, NULL
, NULL
)) {
1444 data_blob_free(&names_blob
);
1445 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1446 result
= NT_STATUS_NO_MEMORY
;
1449 data_blob_free(&names_blob
);
1451 lm_resp
= data_blob_null
;
1452 SMBNTencrypt(pass
, chal
, local_nt_response
);
1454 nt_resp
= data_blob_talloc(mem_ctx
, local_nt_response
,
1455 sizeof(local_nt_response
));
1458 if (strequal(name_domain
, get_global_sam_name())) {
1459 DATA_BLOB chal_blob
= data_blob_const(chal
, sizeof(chal
));
1461 result
= winbindd_dual_auth_passdb(
1462 mem_ctx
, 0, name_domain
, name_user
,
1463 &chal_blob
, &lm_resp
, &nt_resp
, info3
);
1467 /* check authentication loop */
1469 result
= winbind_samlogon_retry_loop(domain
,
1480 if (!NT_STATUS_IS_OK(result
)) {
1484 /* handle the case where a NT4 DC does not fill in the acct_flags in
1485 * the samlogon reply info3. When accurate info3 is required by the
1486 * caller, we look up the account flags ourselve - gd */
1488 if ((request_flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1489 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1491 struct rpc_pipe_client
*samr_pipe
;
1492 struct policy_handle samr_domain_handle
, user_pol
;
1493 union samr_UserInfo
*info
= NULL
;
1494 NTSTATUS status_tmp
, result_tmp
;
1496 struct dcerpc_binding_handle
*b
;
1498 status_tmp
= cm_connect_sam(domain
, mem_ctx
,
1499 &samr_pipe
, &samr_domain_handle
);
1501 if (!NT_STATUS_IS_OK(status_tmp
)) {
1502 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1503 nt_errstr(status_tmp
)));
1507 b
= samr_pipe
->binding_handle
;
1509 status_tmp
= dcerpc_samr_OpenUser(b
, mem_ctx
,
1510 &samr_domain_handle
,
1511 MAXIMUM_ALLOWED_ACCESS
,
1516 if (!NT_STATUS_IS_OK(status_tmp
)) {
1517 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1518 nt_errstr(status_tmp
)));
1521 if (!NT_STATUS_IS_OK(result_tmp
)) {
1522 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1523 nt_errstr(result_tmp
)));
1527 status_tmp
= dcerpc_samr_QueryUserInfo(b
, mem_ctx
,
1533 if (!NT_STATUS_IS_OK(status_tmp
)) {
1534 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1535 nt_errstr(status_tmp
)));
1536 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1539 if (!NT_STATUS_IS_OK(result_tmp
)) {
1540 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1541 nt_errstr(result_tmp
)));
1542 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1546 acct_flags
= info
->info16
.acct_flags
;
1548 if (acct_flags
== 0) {
1549 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1553 my_info3
->base
.acct_flags
= acct_flags
;
1555 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1557 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1565 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1566 struct winbindd_cli_state
*state
)
1568 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1569 NTSTATUS krb5_result
= NT_STATUS_OK
;
1570 fstring name_domain
, name_user
;
1572 fstring domain_user
;
1573 struct netr_SamInfo3
*info3
= NULL
;
1574 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1576 /* Ensure null termination */
1577 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1579 /* Ensure null termination */
1580 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1582 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1583 state
->request
->data
.auth
.user
));
1585 /* Parse domain and username */
1587 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1588 state
->request
->data
.auth
.user
,
1591 /* If the name normalization didnt' actually do anything,
1592 just use the original name */
1594 if (!NT_STATUS_IS_OK(name_map_status
) &&
1595 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1597 mapped_user
= state
->request
->data
.auth
.user
;
1600 parse_domain_user(mapped_user
, name_domain
, name_user
);
1602 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1603 fstr_sprintf( domain_user
, "%s%c%s", name_domain
,
1604 *lp_winbind_separator(),
1606 strlcpy( state
->request
->data
.auth
.user
, domain_user
,
1607 sizeof(state
->request
->data
.auth
.user
));
1610 if (!domain
->online
) {
1611 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1612 if (domain
->startup
) {
1613 /* Logons are very important to users. If we're offline and
1614 we get a request within the first 30 seconds of startup,
1615 try very hard to find a DC and go online. */
1617 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1618 "request in startup mode.\n", domain
->name
));
1620 winbindd_flush_negative_conn_cache(domain
);
1621 result
= init_dc_connection(domain
);
1625 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1627 /* Check for Kerberos authentication */
1628 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1630 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1631 /* save for later */
1632 krb5_result
= result
;
1635 if (NT_STATUS_IS_OK(result
)) {
1636 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1637 goto process_result
;
1639 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1642 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1643 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1644 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1645 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1646 set_domain_offline( domain
);
1650 /* there are quite some NT_STATUS errors where there is no
1651 * point in retrying with a samlogon, we explictly have to take
1652 * care not to increase the bad logon counter on the DC */
1654 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1655 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1656 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1657 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1658 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1659 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1660 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1661 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1662 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1663 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1667 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1668 DEBUG(3,("falling back to samlogon\n"));
1676 /* Check for Samlogon authentication */
1677 if (domain
->online
) {
1678 result
= winbindd_dual_pam_auth_samlogon(
1679 state
->mem_ctx
, domain
,
1680 state
->request
->data
.auth
.user
,
1681 state
->request
->data
.auth
.pass
,
1682 state
->request
->flags
,
1685 if (NT_STATUS_IS_OK(result
)) {
1686 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1687 /* add the Krb5 err if we have one */
1688 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1689 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1691 goto process_result
;
1694 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1695 nt_errstr(result
)));
1697 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1698 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1699 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1701 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1702 set_domain_offline( domain
);
1706 if (domain
->online
) {
1707 /* We're still online - fail. */
1713 /* Check for Cached logons */
1714 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1715 lp_winbind_offline_logon()) {
1717 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1719 if (NT_STATUS_IS_OK(result
)) {
1720 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1721 goto process_result
;
1723 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1730 if (NT_STATUS_IS_OK(result
)) {
1732 struct dom_sid user_sid
;
1734 /* In all codepaths where result == NT_STATUS_OK info3 must have
1735 been initialized. */
1737 result
= NT_STATUS_INTERNAL_ERROR
;
1741 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1744 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1746 netsamlogon_cache_store(name_user
, info3
);
1748 /* save name_to_sid info as early as possible (only if
1749 this is our primary domain so we don't invalidate
1750 the cache entry by storing the seq_num for the wrong
1752 if ( domain
->primary
) {
1753 cache_name2sid(domain
, name_domain
, name_user
,
1754 SID_NAME_USER
, &user_sid
);
1757 /* Check if the user is in the right group */
1759 result
= check_info3_in_group(
1761 state
->request
->data
.auth
.require_membership_of_sid
);
1762 if (!NT_STATUS_IS_OK(result
)) {
1763 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1764 state
->request
->data
.auth
.user
,
1765 state
->request
->data
.auth
.require_membership_of_sid
));
1769 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1770 state
->request
->flags
, info3
,
1771 name_domain
, name_user
);
1772 if (!NT_STATUS_IS_OK(result
)) {
1776 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
1777 && lp_winbind_offline_logon()) {
1779 result
= winbindd_store_creds(domain
,
1780 state
->request
->data
.auth
.user
,
1781 state
->request
->data
.auth
.pass
,
1785 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1786 struct winbindd_domain
*our_domain
= find_our_domain();
1788 /* This is not entirely correct I believe, but it is
1789 consistent. Only apply the password policy settings
1790 too warn users for our own domain. Cannot obtain these
1791 from trusted DCs all the time so don't do it at all.
1794 result
= NT_STATUS_NOT_SUPPORTED
;
1795 if (our_domain
== domain
) {
1796 result
= fillup_password_policy(
1797 our_domain
, state
->response
);
1800 if (!NT_STATUS_IS_OK(result
)
1801 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1803 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1804 domain
->name
, nt_errstr(result
)));
1809 result
= NT_STATUS_OK
;
1813 /* give us a more useful (more correct?) error code */
1814 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1815 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1816 result
= NT_STATUS_NO_LOGON_SERVERS
;
1819 set_auth_errors(state
->response
, result
);
1821 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1822 state
->request
->data
.auth
.user
,
1823 state
->response
->data
.auth
.nt_status_string
,
1824 state
->response
->data
.auth
.pam_error
));
1826 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1829 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1830 struct winbindd_cli_state
*state
)
1833 struct netr_SamInfo3
*info3
= NULL
;
1834 const char *name_user
= NULL
;
1835 const char *name_domain
= NULL
;
1836 const char *workstation
;
1838 DATA_BLOB lm_resp
, nt_resp
;
1840 /* This is child-only, so no check for privileged access is needed
1843 /* Ensure null termination */
1844 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
1845 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
1847 name_user
= state
->request
->data
.auth_crap
.user
;
1848 name_domain
= state
->request
->data
.auth_crap
.domain
;
1849 workstation
= state
->request
->data
.auth_crap
.workstation
;
1851 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1852 name_domain
, name_user
));
1854 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
1855 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
1856 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
1857 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
1858 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1859 state
->request
->data
.auth_crap
.lm_resp_len
,
1860 state
->request
->data
.auth_crap
.nt_resp_len
));
1861 result
= NT_STATUS_INVALID_PARAMETER
;
1866 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
1867 state
->request
->data
.auth_crap
.lm_resp_len
);
1869 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
1870 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1871 state
->request
->extra_data
.data
,
1872 state
->request
->data
.auth_crap
.nt_resp_len
);
1874 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1875 state
->request
->data
.auth_crap
.nt_resp
,
1876 state
->request
->data
.auth_crap
.nt_resp_len
);
1879 if (strequal(name_domain
, get_global_sam_name())) {
1880 DATA_BLOB chal_blob
= data_blob_const(
1881 state
->request
->data
.auth_crap
.chal
,
1882 sizeof(state
->request
->data
.auth_crap
.chal
));
1884 result
= winbindd_dual_auth_passdb(
1886 state
->request
->data
.auth_crap
.logon_parameters
,
1887 name_domain
, name_user
,
1888 &chal_blob
, &lm_resp
, &nt_resp
, &info3
);
1889 goto process_result
;
1892 result
= winbind_samlogon_retry_loop(domain
,
1894 state
->request
->data
.auth_crap
.logon_parameters
,
1898 /* Bug #3248 - found by Stefan Burkei. */
1899 workstation
, /* We carefully set this above so use it... */
1900 state
->request
->data
.auth_crap
.chal
,
1904 if (!NT_STATUS_IS_OK(result
)) {
1910 if (NT_STATUS_IS_OK(result
)) {
1911 struct dom_sid user_sid
;
1913 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1915 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1917 netsamlogon_cache_store(name_user
, info3
);
1919 /* Check if the user is in the right group */
1921 result
= check_info3_in_group(
1923 state
->request
->data
.auth_crap
.require_membership_of_sid
);
1924 if (!NT_STATUS_IS_OK(result
)) {
1925 DEBUG(3, ("User %s is not in the required group (%s), so "
1926 "crap authentication is rejected\n",
1927 state
->request
->data
.auth_crap
.user
,
1928 state
->request
->data
.auth_crap
.require_membership_of_sid
));
1932 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1933 state
->request
->flags
, info3
,
1934 name_domain
, name_user
);
1935 if (!NT_STATUS_IS_OK(result
)) {
1942 /* give us a more useful (more correct?) error code */
1943 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1944 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1945 result
= NT_STATUS_NO_LOGON_SERVERS
;
1948 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1949 result
= nt_status_squash(result
);
1952 set_auth_errors(state
->response
, result
);
1954 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1955 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1958 state
->response
->data
.auth
.nt_status_string
,
1959 state
->response
->data
.auth
.pam_error
));
1961 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1964 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
1965 struct winbindd_cli_state
*state
)
1968 char *newpass
= NULL
;
1969 struct policy_handle dom_pol
;
1970 struct rpc_pipe_client
*cli
= NULL
;
1971 bool got_info
= false;
1972 struct samr_DomInfo1
*info
= NULL
;
1973 struct userPwdChangeFailureInformation
*reject
= NULL
;
1974 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1975 fstring domain
, user
;
1976 struct dcerpc_binding_handle
*b
= NULL
;
1978 ZERO_STRUCT(dom_pol
);
1980 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
1981 state
->request
->data
.auth
.user
));
1983 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
1987 /* Change password */
1989 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
1990 newpass
= state
->request
->data
.chauthtok
.newpass
;
1992 /* Initialize reject reason */
1993 state
->response
->data
.auth
.reject_reason
= Undefined
;
1995 /* Get sam handle */
1997 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1999 if (!NT_STATUS_IS_OK(result
)) {
2000 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2004 b
= cli
->binding_handle
;
2006 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2013 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2015 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2017 fill_in_password_policy(state
->response
, info
);
2019 state
->response
->data
.auth
.reject_reason
=
2020 reject
->extendedFailureReason
;
2025 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2026 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2027 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2028 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2030 /* only fallback when the chgpasswd_user3 call is not supported */
2031 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
) ||
2032 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) ||
2033 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
) ||
2034 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
2036 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2037 nt_errstr(result
)));
2039 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2041 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2042 Map to the same status code as Windows 2003. */
2044 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2045 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2051 if (NT_STATUS_IS_OK(result
)
2052 && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
2053 && lp_winbind_offline_logon()) {
2054 result
= winbindd_update_creds_by_name(contact_domain
, user
,
2056 /* Again, this happens when we login from gdm or xdm
2057 * and the password expires, *BUT* cached crendentials
2058 * doesn't exist. winbindd_update_creds_by_name()
2059 * returns NT_STATUS_NO_SUCH_USER.
2060 * This is not a failure.
2063 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2064 result
= NT_STATUS_OK
;
2067 if (!NT_STATUS_IS_OK(result
)) {
2068 DEBUG(10, ("Failed to store creds: %s\n",
2069 nt_errstr(result
)));
2070 goto process_result
;
2074 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2076 NTSTATUS policy_ret
;
2078 policy_ret
= fillup_password_policy(
2079 contact_domain
, state
->response
);
2081 /* failure of this is non critical, it will just provide no
2082 * additional information to the client why the change has
2083 * failed - Guenther */
2085 if (!NT_STATUS_IS_OK(policy_ret
)) {
2086 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2087 goto process_result
;
2093 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2094 /* FIXME: internal rpc pipe does not cache handles yet */
2096 if (is_valid_policy_hnd(&dom_pol
)) {
2098 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2104 set_auth_errors(state
->response
, result
);
2106 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2107 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2110 state
->response
->data
.auth
.nt_status_string
,
2111 state
->response
->data
.auth
.pam_error
));
2113 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2116 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2117 struct winbindd_cli_state
*state
)
2119 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2121 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2122 state
->request
->data
.logoff
.user
));
2124 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2125 result
= NT_STATUS_OK
;
2126 goto process_result
;
2129 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2130 result
= NT_STATUS_OK
;
2131 goto process_result
;
2136 if (state
->request
->data
.logoff
.uid
< 0) {
2137 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2138 goto process_result
;
2141 /* what we need here is to find the corresponding krb5 ccache name *we*
2142 * created for a given username and destroy it */
2144 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2145 result
= NT_STATUS_OK
;
2146 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2147 goto process_result
;
2150 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2151 state
->request
->data
.logoff
.uid
,
2152 state
->request
->data
.logoff
.krb5ccname
)) {
2153 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2154 goto process_result
;
2157 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2158 if (!NT_STATUS_IS_OK(result
)) {
2159 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2160 nt_errstr(result
)));
2161 goto process_result
;
2165 * Remove any mlock'ed memory creds in the child
2166 * we might be using for krb5 ticket renewal.
2169 winbindd_delete_memory_creds(state
->request
->data
.logoff
.user
);
2172 result
= NT_STATUS_NOT_SUPPORTED
;
2178 set_auth_errors(state
->response
, result
);
2180 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2183 /* Change user password with auth crap*/
2185 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2188 DATA_BLOB new_nt_password
;
2189 DATA_BLOB old_nt_hash_enc
;
2190 DATA_BLOB new_lm_password
;
2191 DATA_BLOB old_lm_hash_enc
;
2192 fstring domain
,user
;
2193 struct policy_handle dom_pol
;
2194 struct winbindd_domain
*contact_domain
= domainSt
;
2195 struct rpc_pipe_client
*cli
= NULL
;
2196 struct dcerpc_binding_handle
*b
= NULL
;
2198 ZERO_STRUCT(dom_pol
);
2200 /* Ensure null termination */
2201 state
->request
->data
.chng_pswd_auth_crap
.user
[
2202 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2203 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2204 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2208 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2209 (unsigned long)state
->pid
,
2210 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2211 state
->request
->data
.chng_pswd_auth_crap
.user
));
2213 if (lp_winbind_offline_logon()) {
2214 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2215 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2216 result
= NT_STATUS_ACCESS_DENIED
;
2220 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2221 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2223 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2227 DEBUG(3,("no domain specified with username (%s) - "
2229 state
->request
->data
.chng_pswd_auth_crap
.user
));
2230 result
= NT_STATUS_NO_SUCH_USER
;
2235 if (!*domain
&& lp_winbind_use_default_domain()) {
2236 fstrcpy(domain
,lp_workgroup());
2240 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2243 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2244 (unsigned long)state
->pid
, domain
, user
));
2246 /* Change password */
2247 new_nt_password
= data_blob_const(
2248 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2249 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2251 old_nt_hash_enc
= data_blob_const(
2252 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2253 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2255 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2256 new_lm_password
= data_blob_const(
2257 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2258 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2260 old_lm_hash_enc
= data_blob_const(
2261 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2262 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2264 new_lm_password
= data_blob_null
;
2265 old_lm_hash_enc
= data_blob_null
;
2268 /* Get sam handle */
2270 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2271 if (!NT_STATUS_IS_OK(result
)) {
2272 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2276 b
= cli
->binding_handle
;
2278 result
= rpccli_samr_chng_pswd_auth_crap(
2279 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2280 new_lm_password
, old_lm_hash_enc
);
2284 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2285 /* FIXME: internal rpc pipe does not cache handles yet */
2287 if (is_valid_policy_hnd(&dom_pol
)) {
2289 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2295 set_auth_errors(state
->response
, result
);
2297 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2298 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2300 state
->response
->data
.auth
.nt_status_string
,
2301 state
->response
->data
.auth
.pam_error
));
2303 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2307 static NTSTATUS
extract_pac_vrfy_sigs(TALLOC_CTX
*mem_ctx
, DATA_BLOB pac_blob
,
2308 struct PAC_LOGON_INFO
**logon_info
)
2310 krb5_context krbctx
= NULL
;
2311 krb5_error_code k5ret
;
2313 krb5_kt_cursor cursor
;
2314 krb5_keytab_entry entry
;
2315 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
2318 ZERO_STRUCT(cursor
);
2320 k5ret
= krb5_init_context(&krbctx
);
2322 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2323 error_message(k5ret
)));
2324 status
= krb5_to_nt_status(k5ret
);
2328 k5ret
= gse_krb5_get_server_keytab(krbctx
, &keytab
);
2330 DEBUG(1, ("Failed to get keytab: %s\n",
2331 error_message(k5ret
)));
2332 status
= krb5_to_nt_status(k5ret
);
2336 k5ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &cursor
);
2338 DEBUG(1, ("Failed to start seq: %s\n",
2339 error_message(k5ret
)));
2340 status
= krb5_to_nt_status(k5ret
);
2344 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2345 while (k5ret
== 0) {
2346 status
= kerberos_pac_logon_info(mem_ctx
, pac_blob
,
2348 KRB5_KT_KEY(&entry
), NULL
, 0,
2350 if (NT_STATUS_IS_OK(status
)) {
2353 k5ret
= smb_krb5_kt_free_entry(krbctx
, &entry
);
2354 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2357 k5ret
= krb5_kt_end_seq_get(krbctx
, keytab
, &cursor
);
2359 DEBUG(1, ("Failed to end seq: %s\n",
2360 error_message(k5ret
)));
2363 k5ret
= krb5_kt_close(krbctx
, keytab
);
2365 DEBUG(1, ("Failed to close keytab: %s\n",
2366 error_message(k5ret
)));
2369 krb5_free_context(krbctx
);
2374 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2375 struct netr_SamInfo3
**info3
)
2377 struct winbindd_request
*req
= state
->request
;
2379 struct PAC_LOGON_INFO
*logon_info
= NULL
;
2382 pac_blob
= data_blob_const(req
->extra_data
.data
, req
->extra_len
);
2383 result
= extract_pac_vrfy_sigs(state
->mem_ctx
, pac_blob
, &logon_info
);
2384 if (!NT_STATUS_IS_OK(result
) &&
2385 !NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
)) {
2386 DEBUG(1, ("Error during PAC signature verification: %s\n",
2387 nt_errstr(result
)));
2392 /* Signature verification succeeded, trust the PAC */
2393 netsamlogon_cache_store(NULL
, &logon_info
->info3
);
2396 /* Try without signature verification */
2397 result
= kerberos_pac_logon_info(state
->mem_ctx
, pac_blob
, NULL
,
2398 NULL
, NULL
, NULL
, 0,
2400 if (!NT_STATUS_IS_OK(result
)) {
2401 DEBUG(10, ("Could not extract PAC: %s\n",
2402 nt_errstr(result
)));
2407 *info3
= &logon_info
->info3
;
2409 return NT_STATUS_OK
;
2411 #else /* HAVE_KRB5 */
2412 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2413 struct netr_SamInfo3
**info3
)
2415 return NT_STATUS_NO_SUCH_USER
;
2417 #endif /* HAVE_KRB5 */