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"
44 #include "lib/afs/afs_funcs.h"
45 #include "libsmb/samlogon_cache.h"
48 #define DBGC_CLASS DBGC_WINBIND
50 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
52 static NTSTATUS
append_info3_as_txt(TALLOC_CTX
*mem_ctx
,
53 struct winbindd_response
*resp
,
54 struct netr_SamInfo3
*info3
)
59 resp
->data
.auth
.info3
.logon_time
=
60 nt_time_to_unix(info3
->base
.logon_time
);
61 resp
->data
.auth
.info3
.logoff_time
=
62 nt_time_to_unix(info3
->base
.logoff_time
);
63 resp
->data
.auth
.info3
.kickoff_time
=
64 nt_time_to_unix(info3
->base
.kickoff_time
);
65 resp
->data
.auth
.info3
.pass_last_set_time
=
66 nt_time_to_unix(info3
->base
.last_password_change
);
67 resp
->data
.auth
.info3
.pass_can_change_time
=
68 nt_time_to_unix(info3
->base
.allow_password_change
);
69 resp
->data
.auth
.info3
.pass_must_change_time
=
70 nt_time_to_unix(info3
->base
.force_password_change
);
72 resp
->data
.auth
.info3
.logon_count
= info3
->base
.logon_count
;
73 resp
->data
.auth
.info3
.bad_pw_count
= info3
->base
.bad_password_count
;
75 resp
->data
.auth
.info3
.user_rid
= info3
->base
.rid
;
76 resp
->data
.auth
.info3
.group_rid
= info3
->base
.primary_gid
;
77 sid_to_fstring(resp
->data
.auth
.info3
.dom_sid
, info3
->base
.domain_sid
);
79 resp
->data
.auth
.info3
.num_groups
= info3
->base
.groups
.count
;
80 resp
->data
.auth
.info3
.user_flgs
= info3
->base
.user_flags
;
82 resp
->data
.auth
.info3
.acct_flags
= info3
->base
.acct_flags
;
83 resp
->data
.auth
.info3
.num_other_sids
= info3
->sidcount
;
85 fstrcpy(resp
->data
.auth
.info3
.user_name
,
86 info3
->base
.account_name
.string
);
87 fstrcpy(resp
->data
.auth
.info3
.full_name
,
88 info3
->base
.full_name
.string
);
89 fstrcpy(resp
->data
.auth
.info3
.logon_script
,
90 info3
->base
.logon_script
.string
);
91 fstrcpy(resp
->data
.auth
.info3
.profile_path
,
92 info3
->base
.profile_path
.string
);
93 fstrcpy(resp
->data
.auth
.info3
.home_dir
,
94 info3
->base
.home_directory
.string
);
95 fstrcpy(resp
->data
.auth
.info3
.dir_drive
,
96 info3
->base
.home_drive
.string
);
98 fstrcpy(resp
->data
.auth
.info3
.logon_srv
,
99 info3
->base
.logon_server
.string
);
100 fstrcpy(resp
->data
.auth
.info3
.logon_dom
,
101 info3
->base
.logon_domain
.string
);
103 ex
= talloc_strdup(mem_ctx
, "");
104 NT_STATUS_HAVE_NO_MEMORY(ex
);
106 for (i
=0; i
< info3
->base
.groups
.count
; i
++) {
107 ex
= talloc_asprintf_append_buffer(ex
, "0x%08X:0x%08X\n",
108 info3
->base
.groups
.rids
[i
].rid
,
109 info3
->base
.groups
.rids
[i
].attributes
);
110 NT_STATUS_HAVE_NO_MEMORY(ex
);
113 for (i
=0; i
< info3
->sidcount
; i
++) {
116 sid
= dom_sid_string(mem_ctx
, info3
->sids
[i
].sid
);
117 NT_STATUS_HAVE_NO_MEMORY(sid
);
119 ex
= talloc_asprintf_append_buffer(ex
, "%s:0x%08X\n",
121 info3
->sids
[i
].attributes
);
122 NT_STATUS_HAVE_NO_MEMORY(ex
);
127 resp
->extra_data
.data
= ex
;
128 resp
->length
+= talloc_get_size(ex
);
133 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
134 struct winbindd_response
*resp
,
135 struct netr_SamInfo3
*info3
)
138 enum ndr_err_code ndr_err
;
140 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, info3
,
141 (ndr_push_flags_fn_t
)ndr_push_netr_SamInfo3
);
142 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
143 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
144 return ndr_map_error2ntstatus(ndr_err
);
147 resp
->extra_data
.data
= blob
.data
;
148 resp
->length
+= blob
.length
;
153 static NTSTATUS
append_unix_username(TALLOC_CTX
*mem_ctx
,
154 struct winbindd_response
*resp
,
155 const struct netr_SamInfo3
*info3
,
156 const char *name_domain
,
157 const char *name_user
)
159 /* We've been asked to return the unix username, per
160 'winbind use default domain' settings and the like */
162 const char *nt_username
, *nt_domain
;
164 nt_domain
= talloc_strdup(mem_ctx
, info3
->base
.logon_domain
.string
);
166 /* If the server didn't give us one, just use the one
168 nt_domain
= name_domain
;
171 nt_username
= talloc_strdup(mem_ctx
, info3
->base
.account_name
.string
);
173 /* If the server didn't give us one, just use the one
175 nt_username
= name_user
;
178 fill_domain_username(resp
->data
.auth
.unix_username
,
179 nt_domain
, nt_username
, true);
181 DEBUG(5, ("Setting unix username to [%s]\n",
182 resp
->data
.auth
.unix_username
));
187 static NTSTATUS
append_afs_token(TALLOC_CTX
*mem_ctx
,
188 struct winbindd_response
*resp
,
189 const struct netr_SamInfo3
*info3
,
190 const char *name_domain
,
191 const char *name_user
)
193 char *afsname
= NULL
;
197 afsname
= talloc_strdup(mem_ctx
, lp_afs_username_map());
198 if (afsname
== NULL
) {
199 return NT_STATUS_NO_MEMORY
;
202 afsname
= talloc_string_sub(mem_ctx
,
203 lp_afs_username_map(),
205 afsname
= talloc_string_sub(mem_ctx
, afsname
,
207 afsname
= talloc_string_sub(mem_ctx
, afsname
,
211 struct dom_sid user_sid
;
214 sid_compose(&user_sid
, info3
->base
.domain_sid
,
216 sid_to_fstring(sidstr
, &user_sid
);
217 afsname
= talloc_string_sub(mem_ctx
, afsname
,
221 if (afsname
== NULL
) {
222 return NT_STATUS_NO_MEMORY
;
225 if (!strlower_m(afsname
)) {
226 return NT_STATUS_INVALID_PARAMETER
;
229 DEBUG(10, ("Generating token for user %s\n", afsname
));
231 cell
= strchr(afsname
, '@');
234 return NT_STATUS_NO_MEMORY
;
240 token
= afs_createtoken_str(afsname
, cell
);
244 resp
->extra_data
.data
= talloc_strdup(mem_ctx
, token
);
245 if (resp
->extra_data
.data
== NULL
) {
246 return NT_STATUS_NO_MEMORY
;
248 resp
->length
+= strlen((const char *)resp
->extra_data
.data
)+1;
253 static NTSTATUS
check_info3_in_group(struct netr_SamInfo3
*info3
,
254 const char *group_sid
)
256 * Check whether a user belongs to a group or list of groups.
258 * @param mem_ctx talloc memory context.
259 * @param info3 user information, including group membership info.
260 * @param group_sid One or more groups , separated by commas.
262 * @return NT_STATUS_OK on success,
263 * NT_STATUS_LOGON_FAILURE if the user does not belong,
264 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
267 struct dom_sid
*require_membership_of_sid
;
268 uint32_t num_require_membership_of_sid
;
273 struct security_token
*token
;
274 TALLOC_CTX
*frame
= talloc_stackframe();
277 /* Parse the 'required group' SID */
279 if (!group_sid
|| !group_sid
[0]) {
280 /* NO sid supplied, all users may access */
285 token
= talloc_zero(talloc_tos(), struct security_token
);
287 DEBUG(0, ("talloc failed\n"));
289 return NT_STATUS_NO_MEMORY
;
292 num_require_membership_of_sid
= 0;
293 require_membership_of_sid
= NULL
;
297 while (next_token_talloc(talloc_tos(), &p
, &req_sid
, ",")) {
298 if (!string_to_sid(&sid
, req_sid
)) {
299 DEBUG(0, ("check_info3_in_group: could not parse %s "
300 "as a SID!", req_sid
));
302 return NT_STATUS_INVALID_PARAMETER
;
305 status
= add_sid_to_array(talloc_tos(), &sid
,
306 &require_membership_of_sid
,
307 &num_require_membership_of_sid
);
308 if (!NT_STATUS_IS_OK(status
)) {
309 DEBUG(0, ("add_sid_to_array failed\n"));
315 status
= sid_array_from_info3(talloc_tos(), info3
,
319 if (!NT_STATUS_IS_OK(status
)) {
324 if (!NT_STATUS_IS_OK(status
= add_aliases(get_global_sam_sid(),
326 || !NT_STATUS_IS_OK(status
= add_aliases(&global_sid_Builtin
,
328 DEBUG(3, ("could not add aliases: %s\n",
334 security_token_debug(DBGC_CLASS
, 10, token
);
336 for (i
=0; i
<num_require_membership_of_sid
; i
++) {
337 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
338 &require_membership_of_sid
[i
])));
339 if (nt_token_check_sid(&require_membership_of_sid
[i
],
341 DEBUG(10, ("Access ok\n"));
347 /* Do not distinguish this error from a wrong username/pw */
350 return NT_STATUS_LOGON_FAILURE
;
353 struct winbindd_domain
*find_auth_domain(uint8_t flags
,
354 const char *domain_name
)
356 struct winbindd_domain
*domain
;
359 domain
= find_domain_from_name_noinit(domain_name
);
360 if (domain
== NULL
) {
361 DEBUG(3, ("Authentication for domain [%s] refused "
362 "as it is not a trusted domain\n",
368 if (strequal(domain_name
, get_global_sam_name())) {
369 return find_domain_from_name_noinit(domain_name
);
372 /* we can auth against trusted domains */
373 if (flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
374 domain
= find_domain_from_name_noinit(domain_name
);
375 if (domain
== NULL
) {
376 DEBUG(3, ("Authentication for domain [%s] skipped "
377 "as it is not a trusted domain\n",
384 return find_our_domain();
387 static void fill_in_password_policy(struct winbindd_response
*r
,
388 const struct samr_DomInfo1
*p
)
390 r
->data
.auth
.policy
.min_length_password
=
391 p
->min_password_length
;
392 r
->data
.auth
.policy
.password_history
=
393 p
->password_history_length
;
394 r
->data
.auth
.policy
.password_properties
=
395 p
->password_properties
;
396 r
->data
.auth
.policy
.expire
=
397 nt_time_to_unix_abs((const NTTIME
*)&(p
->max_password_age
));
398 r
->data
.auth
.policy
.min_passwordage
=
399 nt_time_to_unix_abs((const NTTIME
*)&(p
->min_password_age
));
402 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
403 struct winbindd_response
*response
)
405 TALLOC_CTX
*frame
= talloc_stackframe();
407 struct samr_DomInfo1 password_policy
;
409 if ( !winbindd_can_contact_domain( domain
) ) {
410 DEBUG(5,("fillup_password_policy: No inbound trust to "
411 "contact domain %s\n", domain
->name
));
412 status
= NT_STATUS_NOT_SUPPORTED
;
416 status
= wb_cache_password_policy(domain
, talloc_tos(),
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_t *lockout_threshold
)
433 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
434 struct samr_DomInfo12 lockout_policy
;
436 *lockout_threshold
= 0;
438 status
= wb_cache_lockout_policy(domain
, mem_ctx
, &lockout_policy
);
439 if (NT_STATUS_IS_ERR(status
)) {
443 *lockout_threshold
= lockout_policy
.lockout_threshold
;
448 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
450 uint32_t *password_properties
)
452 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
453 struct samr_DomInfo1 password_policy
;
455 *password_properties
= 0;
457 status
= wb_cache_password_policy(domain
, mem_ctx
, &password_policy
);
458 if (NT_STATUS_IS_ERR(status
)) {
462 *password_properties
= password_policy
.password_properties
;
469 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
472 const char **user_ccache_file
)
474 /* accept FILE and WRFILE as krb5_cc_type from the client and then
475 * build the full ccname string based on the user's uid here -
478 const char *gen_cc
= NULL
;
481 if (strequal(type
, "FILE")) {
482 gen_cc
= talloc_asprintf(
483 mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
485 if (strequal(type
, "WRFILE")) {
486 gen_cc
= talloc_asprintf(
487 mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
489 if (strequal(type
, "KEYRING")) {
490 gen_cc
= talloc_asprintf(
491 mem_ctx
, "KEYRING:persistent:%d", uid
);
494 if (strnequal(type
, "FILE:/", 6) ||
495 strnequal(type
, "WRFILE:/", 8) ||
496 strnequal(type
, "DIR:/", 5)) {
498 /* we allow only one "%u" substitution */
502 p
= strchr(type
, '%');
507 if (p
!= NULL
&& *p
== 'u' && strchr(p
, '%') == NULL
) {
508 char uid_str
[sizeof("18446744073709551615")];
510 snprintf(uid_str
, sizeof(uid_str
), "%u", uid
);
512 gen_cc
= talloc_string_sub2(mem_ctx
,
516 /* remove_unsafe_characters */
520 /* allow_trailing_dollar */
527 *user_ccache_file
= gen_cc
;
529 if (gen_cc
== NULL
) {
530 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
532 if (gen_cc
== NULL
) {
533 DEBUG(0,("out of memory\n"));
537 DEBUG(10, ("using ccache: %s%s\n", gen_cc
,
538 (*user_ccache_file
== NULL
) ? " (internal)":""));
545 uid_t
get_uid_from_request(struct winbindd_request
*request
)
549 uid
= request
->data
.auth
.uid
;
551 if (uid
== (uid_t
)-1) {
552 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid
));
558 /**********************************************************************
559 Authenticate a user with a clear text password using Kerberos and fill up
561 **********************************************************************/
563 static NTSTATUS
winbindd_raw_kerberos_login(TALLOC_CTX
*mem_ctx
,
564 struct winbindd_domain
*domain
,
567 const char *krb5_cc_type
,
569 struct netr_SamInfo3
**info3
,
573 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
574 krb5_error_code krb5_ret
;
575 const char *cc
= NULL
;
576 const char *principal_s
= NULL
;
577 const char *service
= NULL
;
579 fstring name_domain
, name_user
;
580 time_t ticket_lifetime
= 0;
581 time_t renewal_until
= 0;
583 time_t time_offset
= 0;
584 const char *user_ccache_file
;
585 struct PAC_LOGON_INFO
*logon_info
= NULL
;
586 struct PAC_DATA
*pac_data
= NULL
;
587 struct PAC_DATA_CTR
*pac_data_ctr
= NULL
;
588 const char *local_service
;
590 struct netr_SamInfo3
*info3_copy
= NULL
;
594 if (domain
->alt_name
== NULL
) {
595 return NT_STATUS_INVALID_PARAMETER
;
599 * prepare a krb5_cc_cache string for the user */
602 DEBUG(0,("no valid uid\n"));
605 cc
= generate_krb5_ccache(mem_ctx
,
610 return NT_STATUS_NO_MEMORY
;
615 * get kerberos properties */
617 if (domain
->private_data
) {
618 ads
= (ADS_STRUCT
*)domain
->private_data
;
619 time_offset
= ads
->auth
.time_offset
;
624 * do kerberos auth and setup ccache as the user */
626 parse_domain_user(user
, name_domain
, name_user
);
628 realm
= talloc_strdup(mem_ctx
, domain
->alt_name
);
630 return NT_STATUS_NO_MEMORY
;
633 if (!strupper_m(realm
)) {
634 return NT_STATUS_INVALID_PARAMETER
;
637 principal_s
= talloc_asprintf(mem_ctx
, "%s@%s", name_user
, realm
);
638 if (principal_s
== NULL
) {
639 return NT_STATUS_NO_MEMORY
;
642 service
= talloc_asprintf(mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
643 if (service
== NULL
) {
644 return NT_STATUS_NO_MEMORY
;
647 local_service
= talloc_asprintf(mem_ctx
, "%s$@%s",
648 lp_netbios_name(), lp_realm());
649 if (local_service
== NULL
) {
650 return NT_STATUS_NO_MEMORY
;
654 /* if this is a user ccache, we need to act as the user to let the krb5
655 * library handle the chown, etc. */
657 /************************ ENTERING NON-ROOT **********************/
659 if (user_ccache_file
!= NULL
) {
660 set_effective_uid(uid
);
661 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
664 result
= kerberos_return_pac(mem_ctx
,
673 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
677 if (user_ccache_file
!= NULL
) {
678 gain_root_privilege();
681 /************************ RETURNED TO ROOT **********************/
683 if (!NT_STATUS_IS_OK(result
)) {
687 if (pac_data_ctr
== NULL
) {
691 pac_data
= pac_data_ctr
->pac_data
;
692 if (pac_data
== NULL
) {
696 for (i
=0; i
< pac_data
->num_buffers
; i
++) {
698 if (pac_data
->buffers
[i
].type
!= PAC_TYPE_LOGON_INFO
) {
702 logon_info
= pac_data
->buffers
[i
].info
->logon_info
.info
;
704 return NT_STATUS_INVALID_PARAMETER
;
710 if (logon_info
== NULL
) {
711 DEBUG(10,("Missing logon_info in ticket of %s\n",
713 return NT_STATUS_INVALID_PARAMETER
;
716 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
719 result
= create_info3_from_pac_logon_info(mem_ctx
, logon_info
, &info3_copy
);
720 if (!NT_STATUS_IS_OK(result
)) {
724 /* if we had a user's ccache then return that string for the pam
727 if (user_ccache_file
!= NULL
) {
729 fstrcpy(krb5ccname
, user_ccache_file
);
731 result
= add_ccache_to_list(principal_s
,
743 if (!NT_STATUS_IS_OK(result
)) {
744 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
749 /* need to delete the memory cred cache, it is not used anymore */
751 krb5_ret
= ads_kdestroy(cc
);
753 DEBUG(3,("winbindd_raw_kerberos_login: "
754 "could not destroy krb5 credential cache: "
755 "%s\n", error_message(krb5_ret
)));
764 * Do not delete an existing valid credential cache, if the user
765 * e.g. enters a wrong password
767 if ((strequal(krb5_cc_type
, "FILE") || strequal(krb5_cc_type
, "WRFILE"))
768 && user_ccache_file
!= NULL
) {
772 /* we could have created a new credential cache with a valid tgt in it
773 * but we werent able to get or verify the service ticket for this
774 * local host and therefor didn't get the PAC, we need to remove that
775 * cache entirely now */
777 krb5_ret
= ads_kdestroy(cc
);
779 DEBUG(3,("winbindd_raw_kerberos_login: "
780 "could not destroy krb5 credential cache: "
781 "%s\n", error_message(krb5_ret
)));
784 if (!NT_STATUS_IS_OK(remove_ccache(user
))) {
785 DEBUG(3,("winbindd_raw_kerberos_login: "
786 "could not remove ccache for user %s\n",
792 return NT_STATUS_NOT_SUPPORTED
;
793 #endif /* HAVE_KRB5 */
796 /****************************************************************
797 ****************************************************************/
799 bool check_request_flags(uint32_t flags
)
801 uint32_t flags_edata
= WBFLAG_PAM_AFS_TOKEN
|
802 WBFLAG_PAM_INFO3_TEXT
|
803 WBFLAG_PAM_INFO3_NDR
;
805 if ( ( (flags
& flags_edata
) == WBFLAG_PAM_AFS_TOKEN
) ||
806 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_NDR
) ||
807 ( (flags
& flags_edata
) == WBFLAG_PAM_INFO3_TEXT
)||
808 !(flags
& flags_edata
) ) {
812 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
818 /****************************************************************
819 ****************************************************************/
821 NTSTATUS
append_auth_data(TALLOC_CTX
*mem_ctx
,
822 struct winbindd_response
*resp
,
823 uint32_t request_flags
,
824 struct netr_SamInfo3
*info3
,
825 const char *name_domain
,
826 const char *name_user
)
830 if (request_flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
831 memcpy(resp
->data
.auth
.user_session_key
,
833 sizeof(resp
->data
.auth
.user_session_key
)
837 if (request_flags
& WBFLAG_PAM_LMKEY
) {
838 memcpy(resp
->data
.auth
.first_8_lm_hash
,
839 info3
->base
.LMSessKey
.key
,
840 sizeof(resp
->data
.auth
.first_8_lm_hash
)
844 if (request_flags
& WBFLAG_PAM_UNIX_NAME
) {
845 result
= append_unix_username(mem_ctx
, resp
,
846 info3
, name_domain
, name_user
);
847 if (!NT_STATUS_IS_OK(result
)) {
848 DEBUG(10,("Failed to append Unix Username: %s\n",
854 /* currently, anything from here on potentially overwrites extra_data. */
856 if (request_flags
& WBFLAG_PAM_INFO3_NDR
) {
857 result
= append_info3_as_ndr(mem_ctx
, resp
, info3
);
858 if (!NT_STATUS_IS_OK(result
)) {
859 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
865 if (request_flags
& WBFLAG_PAM_INFO3_TEXT
) {
866 result
= append_info3_as_txt(mem_ctx
, resp
, info3
);
867 if (!NT_STATUS_IS_OK(result
)) {
868 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
874 if (request_flags
& WBFLAG_PAM_AFS_TOKEN
) {
875 result
= append_afs_token(mem_ctx
, resp
,
876 info3
, name_domain
, name_user
);
877 if (!NT_STATUS_IS_OK(result
)) {
878 DEBUG(10,("Failed to append AFS token: %s\n",
887 static NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
888 struct winbindd_cli_state
*state
,
889 struct netr_SamInfo3
**info3
)
891 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
892 uint16_t max_allowed_bad_attempts
;
893 fstring name_domain
, name_user
;
895 enum lsa_SidType type
;
896 uchar new_nt_pass
[NT_HASH_LEN
];
897 const uint8_t *cached_nt_pass
;
898 const uint8_t *cached_salt
;
899 struct netr_SamInfo3
*my_info3
;
900 time_t kickoff_time
, must_change_time
;
901 bool password_good
= false;
903 struct winbindd_tdc_domain
*tdc_domain
= NULL
;
910 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
912 /* Parse domain and username */
914 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
917 if (!lookup_cached_name(name_domain
,
921 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
922 return NT_STATUS_NO_SUCH_USER
;
925 if (type
!= SID_NAME_USER
) {
926 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
927 return NT_STATUS_LOGON_FAILURE
;
930 result
= winbindd_get_creds(domain
,
936 if (!NT_STATUS_IS_OK(result
)) {
937 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
943 E_md4hash(state
->request
->data
.auth
.pass
, new_nt_pass
);
945 dump_data_pw("new_nt_pass", new_nt_pass
, NT_HASH_LEN
);
946 dump_data_pw("cached_nt_pass", cached_nt_pass
, NT_HASH_LEN
);
948 dump_data_pw("cached_salt", cached_salt
, NT_HASH_LEN
);
952 /* In this case we didn't store the nt_hash itself,
953 but the MD5 combination of salt + nt_hash. */
954 uchar salted_hash
[NT_HASH_LEN
];
955 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
957 password_good
= (memcmp(cached_nt_pass
, salted_hash
,
960 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
961 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
,
967 /* User *DOES* know the password, update logon_time and reset
970 my_info3
->base
.user_flags
|= NETLOGON_CACHED_ACCOUNT
;
972 if (my_info3
->base
.acct_flags
& ACB_AUTOLOCK
) {
973 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
976 if (my_info3
->base
.acct_flags
& ACB_DISABLED
) {
977 return NT_STATUS_ACCOUNT_DISABLED
;
980 if (my_info3
->base
.acct_flags
& ACB_WSTRUST
) {
981 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
984 if (my_info3
->base
.acct_flags
& ACB_SVRTRUST
) {
985 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
988 if (my_info3
->base
.acct_flags
& ACB_DOMTRUST
) {
989 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
992 if (!(my_info3
->base
.acct_flags
& ACB_NORMAL
)) {
993 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
994 my_info3
->base
.acct_flags
));
995 return NT_STATUS_LOGON_FAILURE
;
998 kickoff_time
= nt_time_to_unix(my_info3
->base
.kickoff_time
);
999 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
1000 return NT_STATUS_ACCOUNT_EXPIRED
;
1003 must_change_time
= nt_time_to_unix(my_info3
->base
.force_password_change
);
1004 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
1005 /* we allow grace logons when the password has expired */
1006 my_info3
->base
.user_flags
|= NETLOGON_GRACE_LOGON
;
1007 /* return NT_STATUS_PASSWORD_EXPIRED; */
1012 if ((state
->request
->flags
& WBFLAG_PAM_KRB5
) &&
1013 ((tdc_domain
= wcache_tdc_fetch_domain(state
->mem_ctx
, name_domain
)) != NULL
) &&
1014 ((tdc_domain
->trust_type
& LSA_TRUST_TYPE_UPLEVEL
) ||
1015 /* used to cope with the case winbindd starting without network. */
1016 !strequal(tdc_domain
->domain_name
, tdc_domain
->dns_name
))) {
1019 const char *cc
= NULL
;
1021 const char *principal_s
= NULL
;
1022 const char *service
= NULL
;
1023 const char *user_ccache_file
;
1025 if (domain
->alt_name
== NULL
) {
1026 return NT_STATUS_INVALID_PARAMETER
;
1029 uid
= get_uid_from_request(state
->request
);
1031 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1032 return NT_STATUS_INVALID_PARAMETER
;
1035 cc
= generate_krb5_ccache(state
->mem_ctx
,
1036 state
->request
->data
.auth
.krb5_cc_type
,
1037 state
->request
->data
.auth
.uid
,
1040 return NT_STATUS_NO_MEMORY
;
1043 realm
= talloc_strdup(state
->mem_ctx
, domain
->alt_name
);
1044 if (realm
== NULL
) {
1045 return NT_STATUS_NO_MEMORY
;
1048 if (!strupper_m(realm
)) {
1049 return NT_STATUS_INVALID_PARAMETER
;
1052 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
1053 if (principal_s
== NULL
) {
1054 return NT_STATUS_NO_MEMORY
;
1057 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
1058 if (service
== NULL
) {
1059 return NT_STATUS_NO_MEMORY
;
1062 if (user_ccache_file
!= NULL
) {
1064 fstrcpy(state
->response
->data
.auth
.krb5ccname
,
1067 result
= add_ccache_to_list(principal_s
,
1070 state
->request
->data
.auth
.user
,
1071 state
->request
->data
.auth
.pass
,
1075 time(NULL
) + lp_winbind_cache_time(),
1076 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
1079 if (!NT_STATUS_IS_OK(result
)) {
1080 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1081 "to add ccache to list: %s\n",
1082 nt_errstr(result
)));
1086 #endif /* HAVE_KRB5 */
1088 /* FIXME: we possibly should handle logon hours as well (does xp when
1089 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1091 unix_to_nt_time(&my_info3
->base
.logon_time
, time(NULL
));
1092 my_info3
->base
.bad_password_count
= 0;
1094 result
= winbindd_update_creds_by_info3(domain
,
1095 state
->request
->data
.auth
.user
,
1096 state
->request
->data
.auth
.pass
,
1098 if (!NT_STATUS_IS_OK(result
)) {
1099 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1100 nt_errstr(result
)));
1104 return NT_STATUS_OK
;
1108 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1109 if (domain
->online
== false) {
1113 /* failure of this is not critical */
1114 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
1115 if (!NT_STATUS_IS_OK(result
)) {
1116 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1117 "Won't be able to honour account lockout policies\n"));
1120 /* increase counter */
1121 my_info3
->base
.bad_password_count
++;
1123 if (max_allowed_bad_attempts
== 0) {
1128 if (my_info3
->base
.bad_password_count
>= max_allowed_bad_attempts
) {
1130 uint32_t password_properties
;
1132 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
1133 if (!NT_STATUS_IS_OK(result
)) {
1134 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1137 if ((my_info3
->base
.rid
!= DOMAIN_RID_ADMINISTRATOR
) ||
1138 (password_properties
& DOMAIN_PASSWORD_LOCKOUT_ADMINS
)) {
1139 my_info3
->base
.acct_flags
|= ACB_AUTOLOCK
;
1144 result
= winbindd_update_creds_by_info3(domain
,
1145 state
->request
->data
.auth
.user
,
1149 if (!NT_STATUS_IS_OK(result
)) {
1150 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1151 nt_errstr(result
)));
1154 return NT_STATUS_LOGON_FAILURE
;
1157 static NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
1158 struct winbindd_cli_state
*state
,
1159 struct netr_SamInfo3
**info3
)
1161 struct winbindd_domain
*contact_domain
;
1162 fstring name_domain
, name_user
;
1165 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1167 /* Parse domain and username */
1169 parse_domain_user(state
->request
->data
.auth
.user
, name_domain
, name_user
);
1171 /* what domain should we contact? */
1174 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1175 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1176 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1177 result
= NT_STATUS_NO_SUCH_USER
;
1182 if (is_myname(name_domain
)) {
1183 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1184 result
= NT_STATUS_NO_SUCH_USER
;
1188 contact_domain
= find_domain_from_name(name_domain
);
1189 if (contact_domain
== NULL
) {
1190 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1191 state
->request
->data
.auth
.user
, name_domain
, name_user
, name_domain
));
1193 result
= NT_STATUS_NO_SUCH_USER
;
1198 if (contact_domain
->initialized
&&
1199 contact_domain
->active_directory
) {
1203 if (!contact_domain
->initialized
) {
1204 init_dc_connection(contact_domain
, false);
1207 if (!contact_domain
->active_directory
) {
1208 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1209 return NT_STATUS_INVALID_LOGON_TYPE
;
1212 result
= winbindd_raw_kerberos_login(
1213 state
->mem_ctx
, contact_domain
,
1214 state
->request
->data
.auth
.user
,
1215 state
->request
->data
.auth
.pass
,
1216 state
->request
->data
.auth
.krb5_cc_type
,
1217 get_uid_from_request(state
->request
),
1218 info3
, state
->response
->data
.auth
.krb5ccname
);
1223 static NTSTATUS
winbindd_dual_auth_passdb(TALLOC_CTX
*mem_ctx
,
1224 uint32_t logon_parameters
,
1225 const char *domain
, const char *user
,
1226 const DATA_BLOB
*challenge
,
1227 const DATA_BLOB
*lm_resp
,
1228 const DATA_BLOB
*nt_resp
,
1230 struct netr_SamInfo3
**pinfo3
)
1232 struct auth_context
*auth_context
;
1233 struct auth_serversupplied_info
*server_info
;
1234 struct auth_usersupplied_info
*user_info
= NULL
;
1235 struct tsocket_address
*local
;
1236 struct netr_SamInfo3
*info3
;
1239 TALLOC_CTX
*frame
= talloc_stackframe();
1241 rc
= tsocket_address_inet_from_strings(frame
,
1248 return NT_STATUS_NO_MEMORY
;
1250 status
= make_user_info(frame
, &user_info
, user
, user
, domain
, domain
,
1251 lp_netbios_name(), local
, lm_resp
, nt_resp
, NULL
, NULL
,
1252 NULL
, AUTH_PASSWORD_RESPONSE
);
1253 if (!NT_STATUS_IS_OK(status
)) {
1254 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status
)));
1259 user_info
->logon_parameters
= logon_parameters
;
1261 /* We don't want any more mapping of the username */
1262 user_info
->mapped_state
= True
;
1264 /* We don't want to come back to winbindd or to do PAM account checks */
1265 user_info
->flags
|= USER_INFO_LOCAL_SAM_ONLY
| USER_INFO_INFO3_AND_NO_AUTHZ
;
1268 user_info
->flags
|= USER_INFO_INTERACTIVE_LOGON
;
1271 status
= make_auth_context_fixed(frame
, &auth_context
, challenge
->data
);
1273 if (!NT_STATUS_IS_OK(status
)) {
1274 DBG_ERR("make_auth_context_fixed failed: %s\n",
1280 status
= auth_check_ntlm_password(mem_ctx
,
1285 if (!NT_STATUS_IS_OK(status
)) {
1290 info3
= talloc_zero(mem_ctx
, struct netr_SamInfo3
);
1291 if (info3
== NULL
) {
1293 return NT_STATUS_NO_MEMORY
;
1296 status
= serverinfo_to_SamInfo3(server_info
, info3
);
1297 if (!NT_STATUS_IS_OK(status
)) {
1300 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1301 nt_errstr(status
)));
1306 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain
,
1307 user
, nt_errstr(status
)));
1312 static NTSTATUS
winbind_samlogon_retry_loop(struct winbindd_domain
*domain
,
1313 TALLOC_CTX
*mem_ctx
,
1314 uint32_t logon_parameters
,
1315 const char *username
,
1316 const char *password
,
1317 const char *domainname
,
1318 const char *workstation
,
1319 const uint8_t chal
[8],
1320 DATA_BLOB lm_response
,
1321 DATA_BLOB nt_response
,
1323 uint8_t *authoritative
,
1325 struct netr_SamInfo3
**info3
)
1328 int netr_attempts
= 0;
1333 struct rpc_pipe_client
*netlogon_pipe
;
1335 ZERO_STRUCTP(info3
);
1338 result
= cm_connect_netlogon(domain
, &netlogon_pipe
);
1340 if (!NT_STATUS_IS_OK(result
)) {
1341 DEBUG(3,("Could not open handle to NETLOGON pipe "
1342 "(error: %s, attempts: %d)\n",
1343 nt_errstr(result
), netr_attempts
));
1345 /* After the first retry always close the connection */
1346 if (netr_attempts
> 0) {
1347 DEBUG(3, ("This is again a problem for this "
1348 "particular call, forcing the close "
1349 "of this connection\n"));
1350 invalidate_cm_connection(domain
);
1353 /* After the second retry failover to the next DC */
1354 if (netr_attempts
> 1) {
1356 * If the netlogon server is not reachable then
1357 * it is possible that the DC is rebuilding
1358 * sysvol and shutdown netlogon for that time.
1359 * We should failover to the next dc.
1361 DEBUG(3, ("This is the third problem for this "
1362 "particular call, adding DC to the "
1363 "negative cache list\n"));
1364 add_failed_connection_entry(domain
->name
,
1367 saf_delete(domain
->name
);
1370 /* Only allow 3 retries */
1371 if (netr_attempts
< 3) {
1372 DEBUG(3, ("The connection to netlogon "
1373 "failed, retrying\n"));
1381 if (domain
->conn
.netlogon_creds
== NULL
) {
1382 DBG_NOTICE("No security credentials available for "
1383 "domain [%s]\n", domainname
);
1384 result
= NT_STATUS_CANT_ACCESS_DOMAIN_INFO
;
1385 } else if (interactive
&& username
!= NULL
&& password
!= NULL
) {
1386 result
= rpccli_netlogon_password_logon(domain
->conn
.netlogon_creds
,
1387 netlogon_pipe
->binding_handle
,
1394 NetlogonInteractiveInformation
,
1399 result
= rpccli_netlogon_network_logon(domain
->conn
.netlogon_creds
,
1400 netlogon_pipe
->binding_handle
,
1415 * we increment this after the "feature negotiation"
1416 * for can_do_samlogon_ex and can_do_validation6
1420 /* We have to try a second time as cm_connect_netlogon
1421 might not yet have noticed that the DC has killed
1424 if (!rpccli_is_connected(netlogon_pipe
)) {
1429 /* if we get access denied, a possible cause was that we had
1430 and open connection to the DC, but someone changed our
1431 machine account password out from underneath us using 'net
1432 rpc changetrustpw' */
1434 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1435 DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1436 "ACCESS_DENIED. Maybe the DC has Restrict "
1437 "NTLM set or the trust account "
1438 "password was changed and we didn't know it. "
1439 "Killing connections to domain %s\n",
1441 invalidate_cm_connection(domain
);
1445 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
)) {
1447 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1448 * (no Ex). This happens against old Samba
1449 * DCs, if LogonSamLogonEx() fails with an error
1450 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1452 * The server will log something like this:
1453 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1455 * This sets the whole connection into a fault_state mode
1456 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1458 * This also happens to our retry with LogonSamLogonWithFlags()
1459 * and LogonSamLogon().
1461 * In order to recover from this situation, we need to
1462 * drop the connection.
1464 invalidate_cm_connection(domain
);
1465 result
= NT_STATUS_LOGON_FAILURE
;
1469 } while ( (attempts
< 2) && retry
);
1471 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
)) {
1472 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1473 "returned NT_STATUS_IO_TIMEOUT after the retry."
1474 "Killing connections to domain %s\n",
1476 invalidate_cm_connection(domain
);
1481 static NTSTATUS
winbindd_dual_pam_auth_samlogon(TALLOC_CTX
*mem_ctx
,
1482 struct winbindd_domain
*domain
,
1485 uint32_t request_flags
,
1486 struct netr_SamInfo3
**info3
)
1492 unsigned char local_nt_response
[24];
1493 fstring name_domain
, name_user
;
1495 struct netr_SamInfo3
*my_info3
= NULL
;
1496 uint8_t authoritative
= 0;
1501 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1503 /* Parse domain and username */
1505 parse_domain_user(user
, name_domain
, name_user
);
1507 /* do password magic */
1509 generate_random_buffer(chal
, sizeof(chal
));
1511 if (lp_client_ntlmv2_auth()) {
1512 DATA_BLOB server_chal
;
1513 DATA_BLOB names_blob
;
1514 server_chal
= data_blob_const(chal
, 8);
1516 /* note that the 'workgroup' here is for the local
1517 machine. The 'server name' must match the
1518 'workstation' passed to the actual SamLogon call.
1520 names_blob
= NTLMv2_generate_names_blob(
1521 mem_ctx
, lp_netbios_name(), lp_workgroup());
1523 if (!SMBNTLMv2encrypt(mem_ctx
, name_user
, name_domain
,
1527 &lm_resp
, &nt_resp
, NULL
, NULL
)) {
1528 data_blob_free(&names_blob
);
1529 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1530 result
= NT_STATUS_NO_MEMORY
;
1533 data_blob_free(&names_blob
);
1535 lm_resp
= data_blob_null
;
1536 SMBNTencrypt(pass
, chal
, local_nt_response
);
1538 nt_resp
= data_blob_talloc(mem_ctx
, local_nt_response
,
1539 sizeof(local_nt_response
));
1542 if (strequal(name_domain
, get_global_sam_name())) {
1543 DATA_BLOB chal_blob
= data_blob_const(chal
, sizeof(chal
));
1545 result
= winbindd_dual_auth_passdb(
1546 mem_ctx
, 0, name_domain
, name_user
,
1547 &chal_blob
, &lm_resp
, &nt_resp
,
1548 true, /* interactive */
1552 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1554 if (!NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
1559 /* check authentication loop */
1561 result
= winbind_samlogon_retry_loop(domain
,
1571 true, /* interactive */
1575 if (!NT_STATUS_IS_OK(result
)) {
1579 /* handle the case where a NT4 DC does not fill in the acct_flags in
1580 * the samlogon reply info3. When accurate info3 is required by the
1581 * caller, we look up the account flags ourselve - gd */
1583 if ((request_flags
& WBFLAG_PAM_INFO3_TEXT
) &&
1584 NT_STATUS_IS_OK(result
) && (my_info3
->base
.acct_flags
== 0)) {
1586 struct rpc_pipe_client
*samr_pipe
;
1587 struct policy_handle samr_domain_handle
, user_pol
;
1588 union samr_UserInfo
*info
= NULL
;
1589 NTSTATUS status_tmp
, result_tmp
;
1590 uint32_t acct_flags
;
1591 struct dcerpc_binding_handle
*b
;
1593 status_tmp
= cm_connect_sam(domain
, mem_ctx
, false,
1594 &samr_pipe
, &samr_domain_handle
);
1596 if (!NT_STATUS_IS_OK(status_tmp
)) {
1597 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1598 nt_errstr(status_tmp
)));
1602 b
= samr_pipe
->binding_handle
;
1604 status_tmp
= dcerpc_samr_OpenUser(b
, mem_ctx
,
1605 &samr_domain_handle
,
1606 MAXIMUM_ALLOWED_ACCESS
,
1611 if (!NT_STATUS_IS_OK(status_tmp
)) {
1612 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1613 nt_errstr(status_tmp
)));
1616 if (!NT_STATUS_IS_OK(result_tmp
)) {
1617 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1618 nt_errstr(result_tmp
)));
1622 status_tmp
= dcerpc_samr_QueryUserInfo(b
, mem_ctx
,
1628 if (!NT_STATUS_IS_OK(status_tmp
)) {
1629 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1630 nt_errstr(status_tmp
)));
1631 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1634 if (!NT_STATUS_IS_OK(result_tmp
)) {
1635 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1636 nt_errstr(result_tmp
)));
1637 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1641 acct_flags
= info
->info16
.acct_flags
;
1643 if (acct_flags
== 0) {
1644 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1648 my_info3
->base
.acct_flags
= acct_flags
;
1650 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags
));
1652 dcerpc_samr_Close(b
, mem_ctx
, &user_pol
, &result_tmp
);
1660 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1661 struct winbindd_cli_state
*state
)
1663 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1664 NTSTATUS krb5_result
= NT_STATUS_OK
;
1665 fstring name_domain
, name_user
;
1667 fstring domain_user
;
1668 struct netr_SamInfo3
*info3
= NULL
;
1669 NTSTATUS name_map_status
= NT_STATUS_UNSUCCESSFUL
;
1671 /* Ensure null termination */
1672 state
->request
->data
.auth
.user
[sizeof(state
->request
->data
.auth
.user
)-1]='\0';
1674 /* Ensure null termination */
1675 state
->request
->data
.auth
.pass
[sizeof(state
->request
->data
.auth
.pass
)-1]='\0';
1677 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1678 state
->request
->data
.auth
.user
));
1680 /* Parse domain and username */
1682 name_map_status
= normalize_name_unmap(state
->mem_ctx
,
1683 state
->request
->data
.auth
.user
,
1686 /* If the name normalization didnt' actually do anything,
1687 just use the original name */
1689 if (!NT_STATUS_IS_OK(name_map_status
) &&
1690 !NT_STATUS_EQUAL(name_map_status
, NT_STATUS_FILE_RENAMED
))
1692 mapped_user
= state
->request
->data
.auth
.user
;
1695 parse_domain_user(mapped_user
, name_domain
, name_user
);
1697 if ( mapped_user
!= state
->request
->data
.auth
.user
) {
1698 fstr_sprintf( domain_user
, "%s%c%s", name_domain
,
1699 *lp_winbind_separator(),
1701 strlcpy( state
->request
->data
.auth
.user
, domain_user
,
1702 sizeof(state
->request
->data
.auth
.user
));
1705 if (!domain
->online
) {
1706 result
= NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
;
1707 if (domain
->startup
) {
1708 /* Logons are very important to users. If we're offline and
1709 we get a request within the first 30 seconds of startup,
1710 try very hard to find a DC and go online. */
1712 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1713 "request in startup mode.\n", domain
->name
));
1715 winbindd_flush_negative_conn_cache(domain
);
1716 result
= init_dc_connection(domain
, false);
1720 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1722 /* Check for Kerberos authentication */
1723 if (domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
1725 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1726 /* save for later */
1727 krb5_result
= result
;
1730 if (NT_STATUS_IS_OK(result
)) {
1731 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1732 goto process_result
;
1734 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1737 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1738 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1739 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1740 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1741 set_domain_offline( domain
);
1745 /* there are quite some NT_STATUS errors where there is no
1746 * point in retrying with a samlogon, we explictly have to take
1747 * care not to increase the bad logon counter on the DC */
1749 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1750 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1751 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1752 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1753 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1754 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1755 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1756 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1757 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1758 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1762 if (state
->request
->flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1763 DEBUG(3,("falling back to samlogon\n"));
1771 /* Check for Samlogon authentication */
1772 if (domain
->online
) {
1773 result
= winbindd_dual_pam_auth_samlogon(
1774 state
->mem_ctx
, domain
,
1775 state
->request
->data
.auth
.user
,
1776 state
->request
->data
.auth
.pass
,
1777 state
->request
->flags
,
1780 if (NT_STATUS_IS_OK(result
)) {
1781 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1782 /* add the Krb5 err if we have one */
1783 if ( NT_STATUS_EQUAL(krb5_result
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ) {
1784 info3
->base
.user_flags
|= LOGON_KRB5_FAIL_CLOCK_SKEW
;
1786 goto process_result
;
1789 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1790 nt_errstr(result
)));
1792 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1793 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1794 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1796 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1797 set_domain_offline( domain
);
1801 if (domain
->online
) {
1802 /* We're still online - fail. */
1808 /* Check for Cached logons */
1809 if (!domain
->online
&& (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1810 lp_winbind_offline_logon()) {
1812 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1814 if (NT_STATUS_IS_OK(result
)) {
1815 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1816 goto process_result
;
1818 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1825 if (NT_STATUS_IS_OK(result
)) {
1827 struct dom_sid user_sid
;
1829 /* In all codepaths where result == NT_STATUS_OK info3 must have
1830 been initialized. */
1832 result
= NT_STATUS_INTERNAL_ERROR
;
1836 sid_compose(&user_sid
, info3
->base
.domain_sid
,
1839 if (info3
->base
.full_name
.string
== NULL
) {
1840 struct netr_SamInfo3
*cached_info3
;
1842 cached_info3
= netsamlogon_cache_get(state
->mem_ctx
,
1844 if (cached_info3
!= NULL
&&
1845 cached_info3
->base
.full_name
.string
!= NULL
) {
1846 info3
->base
.full_name
.string
=
1847 talloc_strdup(info3
,
1848 cached_info3
->base
.full_name
.string
);
1851 /* this might fail so we don't check the return code */
1852 wcache_query_user_fullname(domain
,
1855 &info3
->base
.full_name
.string
);
1859 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
1861 netsamlogon_cache_store(name_user
, info3
);
1863 /* save name_to_sid info as early as possible (only if
1864 this is our primary domain so we don't invalidate
1865 the cache entry by storing the seq_num for the wrong
1867 if ( domain
->primary
) {
1868 cache_name2sid(domain
, name_domain
, name_user
,
1869 SID_NAME_USER
, &user_sid
);
1872 /* Check if the user is in the right group */
1874 result
= check_info3_in_group(
1876 state
->request
->data
.auth
.require_membership_of_sid
);
1877 if (!NT_STATUS_IS_OK(result
)) {
1878 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1879 state
->request
->data
.auth
.user
,
1880 state
->request
->data
.auth
.require_membership_of_sid
));
1884 result
= append_auth_data(state
->mem_ctx
, state
->response
,
1885 state
->request
->flags
, info3
,
1886 name_domain
, name_user
);
1887 if (!NT_STATUS_IS_OK(result
)) {
1891 if ((state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
1892 && lp_winbind_offline_logon()) {
1894 result
= winbindd_store_creds(domain
,
1895 state
->request
->data
.auth
.user
,
1896 state
->request
->data
.auth
.pass
,
1900 if (state
->request
->flags
& WBFLAG_PAM_GET_PWD_POLICY
) {
1901 struct winbindd_domain
*our_domain
= find_our_domain();
1903 /* This is not entirely correct I believe, but it is
1904 consistent. Only apply the password policy settings
1905 too warn users for our own domain. Cannot obtain these
1906 from trusted DCs all the time so don't do it at all.
1909 result
= NT_STATUS_NOT_SUPPORTED
;
1910 if (our_domain
== domain
) {
1911 result
= fillup_password_policy(
1912 our_domain
, state
->response
);
1915 if (!NT_STATUS_IS_OK(result
)
1916 && !NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) )
1918 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1919 domain
->name
, nt_errstr(result
)));
1924 result
= NT_STATUS_OK
;
1928 /* give us a more useful (more correct?) error code */
1929 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1930 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1931 result
= NT_STATUS_NO_LOGON_SERVERS
;
1934 set_auth_errors(state
->response
, result
);
1936 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1937 state
->request
->data
.auth
.user
,
1938 state
->response
->data
.auth
.nt_status_string
,
1939 state
->response
->data
.auth
.pam_error
));
1941 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1944 NTSTATUS
winbind_dual_SamLogon(struct winbindd_domain
*domain
,
1945 TALLOC_CTX
*mem_ctx
,
1946 uint32_t logon_parameters
,
1947 const char *name_user
,
1948 const char *name_domain
,
1949 const char *workstation
,
1950 const uint8_t chal
[8],
1951 DATA_BLOB lm_response
,
1952 DATA_BLOB nt_response
,
1953 uint8_t *authoritative
,
1955 struct netr_SamInfo3
**info3
)
1959 if (strequal(name_domain
, get_global_sam_name())) {
1960 DATA_BLOB chal_blob
= data_blob_const(
1963 result
= winbindd_dual_auth_passdb(
1966 name_domain
, name_user
,
1967 &chal_blob
, &lm_response
, &nt_response
,
1968 false, /* interactive */
1972 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1974 if (!NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
1977 goto process_result
;
1981 result
= winbind_samlogon_retry_loop(domain
,
1985 NULL
, /* password */
1987 /* Bug #3248 - found by Stefan Burkei. */
1988 workstation
, /* We carefully set this above so use it... */
1992 false, /* interactive */
1996 if (!NT_STATUS_IS_OK(result
)) {
2002 if (NT_STATUS_IS_OK(result
)) {
2003 struct dom_sid user_sid
;
2005 sid_compose(&user_sid
, (*info3
)->base
.domain_sid
,
2006 (*info3
)->base
.rid
);
2008 if ((*info3
)->base
.full_name
.string
== NULL
) {
2009 struct netr_SamInfo3
*cached_info3
;
2011 cached_info3
= netsamlogon_cache_get(mem_ctx
,
2013 if (cached_info3
!= NULL
&&
2014 cached_info3
->base
.full_name
.string
!= NULL
) {
2015 (*info3
)->base
.full_name
.string
=
2016 talloc_strdup(*info3
,
2017 cached_info3
->base
.full_name
.string
);
2020 /* this might fail so we don't check the return code */
2021 wcache_query_user_fullname(domain
,
2024 &(*info3
)->base
.full_name
.string
);
2028 wcache_invalidate_samlogon(find_domain_from_name(name_domain
),
2030 netsamlogon_cache_store(name_user
, *info3
);
2035 /* give us a more useful (more correct?) error code */
2036 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
2037 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
2038 result
= NT_STATUS_NO_LOGON_SERVERS
;
2041 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2042 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2045 nt_errstr(result
)));
2050 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
2051 struct winbindd_cli_state
*state
)
2054 struct netr_SamInfo3
*info3
= NULL
;
2055 const char *name_user
= NULL
;
2056 const char *name_domain
= NULL
;
2057 const char *workstation
;
2058 uint8_t authoritative
;
2061 DATA_BLOB lm_resp
, nt_resp
;
2063 /* This is child-only, so no check for privileged access is needed
2066 /* Ensure null termination */
2067 state
->request
->data
.auth_crap
.user
[sizeof(state
->request
->data
.auth_crap
.user
)-1]=0;
2068 state
->request
->data
.auth_crap
.domain
[sizeof(state
->request
->data
.auth_crap
.domain
)-1]=0;
2070 name_user
= state
->request
->data
.auth_crap
.user
;
2071 name_domain
= state
->request
->data
.auth_crap
.domain
;
2072 workstation
= state
->request
->data
.auth_crap
.workstation
;
2074 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
2075 name_domain
, name_user
));
2077 if (state
->request
->data
.auth_crap
.lm_resp_len
> sizeof(state
->request
->data
.auth_crap
.lm_resp
)
2078 || state
->request
->data
.auth_crap
.nt_resp_len
> sizeof(state
->request
->data
.auth_crap
.nt_resp
)) {
2079 if (!(state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) ||
2080 state
->request
->extra_len
!= state
->request
->data
.auth_crap
.nt_resp_len
) {
2081 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2082 state
->request
->data
.auth_crap
.lm_resp_len
,
2083 state
->request
->data
.auth_crap
.nt_resp_len
));
2084 result
= NT_STATUS_INVALID_PARAMETER
;
2089 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
->data
.auth_crap
.lm_resp
,
2090 state
->request
->data
.auth_crap
.lm_resp_len
);
2092 if (state
->request
->flags
& WBFLAG_BIG_NTLMV2_BLOB
) {
2093 nt_resp
= data_blob_talloc(state
->mem_ctx
,
2094 state
->request
->extra_data
.data
,
2095 state
->request
->data
.auth_crap
.nt_resp_len
);
2097 nt_resp
= data_blob_talloc(state
->mem_ctx
,
2098 state
->request
->data
.auth_crap
.nt_resp
,
2099 state
->request
->data
.auth_crap
.nt_resp_len
);
2102 result
= winbind_dual_SamLogon(domain
,
2104 state
->request
->data
.auth_crap
.logon_parameters
,
2107 /* Bug #3248 - found by Stefan Burkei. */
2108 workstation
, /* We carefully set this above so use it... */
2109 state
->request
->data
.auth_crap
.chal
,
2115 if (!NT_STATUS_IS_OK(result
)) {
2119 if (NT_STATUS_IS_OK(result
)) {
2120 /* Check if the user is in the right group */
2122 result
= check_info3_in_group(
2124 state
->request
->data
.auth_crap
.require_membership_of_sid
);
2125 if (!NT_STATUS_IS_OK(result
)) {
2126 DEBUG(3, ("User %s is not in the required group (%s), so "
2127 "crap authentication is rejected\n",
2128 state
->request
->data
.auth_crap
.user
,
2129 state
->request
->data
.auth_crap
.require_membership_of_sid
));
2133 result
= append_auth_data(state
->mem_ctx
, state
->response
,
2134 state
->request
->flags
, info3
,
2135 name_domain
, name_user
);
2136 if (!NT_STATUS_IS_OK(result
)) {
2143 if (state
->request
->flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
2144 result
= nt_status_squash(result
);
2147 set_auth_errors(state
->response
, result
);
2149 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2152 enum winbindd_result
winbindd_dual_pam_chauthtok(struct winbindd_domain
*contact_domain
,
2153 struct winbindd_cli_state
*state
)
2156 char *newpass
= NULL
;
2157 struct policy_handle dom_pol
;
2158 struct rpc_pipe_client
*cli
= NULL
;
2159 bool got_info
= false;
2160 struct samr_DomInfo1
*info
= NULL
;
2161 struct userPwdChangeFailureInformation
*reject
= NULL
;
2162 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2163 fstring domain
, user
;
2164 struct dcerpc_binding_handle
*b
= NULL
;
2166 ZERO_STRUCT(dom_pol
);
2168 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state
->pid
,
2169 state
->request
->data
.auth
.user
));
2171 if (!parse_domain_user(state
->request
->data
.chauthtok
.user
, domain
, user
)) {
2175 /* Change password */
2177 oldpass
= state
->request
->data
.chauthtok
.oldpass
;
2178 newpass
= state
->request
->data
.chauthtok
.newpass
;
2180 /* Initialize reject reason */
2181 state
->response
->data
.auth
.reject_reason
= Undefined
;
2183 /* Get sam handle */
2185 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, true, &cli
,
2187 if (!NT_STATUS_IS_OK(result
)) {
2188 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2192 b
= cli
->binding_handle
;
2194 result
= rpccli_samr_chgpasswd_user3(cli
, state
->mem_ctx
,
2201 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2203 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
) ) {
2205 fill_in_password_policy(state
->response
, info
);
2207 state
->response
->data
.auth
.reject_reason
=
2208 reject
->extendedFailureReason
;
2213 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2214 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2215 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2216 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2218 /* only fallback when the chgpasswd_user3 call is not supported */
2219 if (NT_STATUS_EQUAL(result
, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
) ||
2220 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
) ||
2221 NT_STATUS_EQUAL(result
, NT_STATUS_BUFFER_TOO_SMALL
) ||
2222 NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
)) {
2224 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2225 nt_errstr(result
)));
2227 result
= rpccli_samr_chgpasswd_user2(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
2229 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2230 Map to the same status code as Windows 2003. */
2232 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION
, result
) ) {
2233 result
= NT_STATUS_PASSWORD_RESTRICTION
;
2239 if (NT_STATUS_IS_OK(result
)
2240 && (state
->request
->flags
& WBFLAG_PAM_CACHED_LOGIN
)
2241 && lp_winbind_offline_logon()) {
2242 result
= winbindd_update_creds_by_name(contact_domain
, user
,
2244 /* Again, this happens when we login from gdm or xdm
2245 * and the password expires, *BUT* cached crendentials
2246 * doesn't exist. winbindd_update_creds_by_name()
2247 * returns NT_STATUS_NO_SUCH_USER.
2248 * This is not a failure.
2251 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
)) {
2252 result
= NT_STATUS_OK
;
2255 if (!NT_STATUS_IS_OK(result
)) {
2256 DEBUG(10, ("Failed to store creds: %s\n",
2257 nt_errstr(result
)));
2258 goto process_result
;
2262 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
2264 NTSTATUS policy_ret
;
2266 policy_ret
= fillup_password_policy(
2267 contact_domain
, state
->response
);
2269 /* failure of this is non critical, it will just provide no
2270 * additional information to the client why the change has
2271 * failed - Guenther */
2273 if (!NT_STATUS_IS_OK(policy_ret
)) {
2274 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
2275 goto process_result
;
2281 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2282 /* FIXME: internal rpc pipe does not cache handles yet */
2284 if (is_valid_policy_hnd(&dom_pol
)) {
2286 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2292 set_auth_errors(state
->response
, result
);
2294 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2295 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2298 state
->response
->data
.auth
.nt_status_string
,
2299 state
->response
->data
.auth
.pam_error
));
2301 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2304 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
2305 struct winbindd_cli_state
*state
)
2307 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
2309 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
2310 state
->request
->data
.logoff
.user
));
2312 if (!(state
->request
->flags
& WBFLAG_PAM_KRB5
)) {
2313 result
= NT_STATUS_OK
;
2314 goto process_result
;
2317 if (state
->request
->data
.logoff
.krb5ccname
[0] == '\0') {
2318 result
= NT_STATUS_OK
;
2319 goto process_result
;
2324 if (state
->request
->data
.logoff
.uid
== (uid_t
)-1) {
2325 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2326 goto process_result
;
2329 /* what we need here is to find the corresponding krb5 ccache name *we*
2330 * created for a given username and destroy it */
2332 if (!ccache_entry_exists(state
->request
->data
.logoff
.user
)) {
2333 result
= NT_STATUS_OK
;
2334 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2335 goto process_result
;
2338 if (!ccache_entry_identical(state
->request
->data
.logoff
.user
,
2339 state
->request
->data
.logoff
.uid
,
2340 state
->request
->data
.logoff
.krb5ccname
)) {
2341 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2342 goto process_result
;
2345 result
= remove_ccache(state
->request
->data
.logoff
.user
);
2346 if (!NT_STATUS_IS_OK(result
)) {
2347 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2348 nt_errstr(result
)));
2349 goto process_result
;
2353 * Remove any mlock'ed memory creds in the child
2354 * we might be using for krb5 ticket renewal.
2357 winbindd_delete_memory_creds(state
->request
->data
.logoff
.user
);
2360 result
= NT_STATUS_NOT_SUPPORTED
;
2366 set_auth_errors(state
->response
, result
);
2368 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2371 /* Change user password with auth crap*/
2373 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2376 DATA_BLOB new_nt_password
;
2377 DATA_BLOB old_nt_hash_enc
;
2378 DATA_BLOB new_lm_password
;
2379 DATA_BLOB old_lm_hash_enc
;
2380 fstring domain
,user
;
2381 struct policy_handle dom_pol
;
2382 struct winbindd_domain
*contact_domain
= domainSt
;
2383 struct rpc_pipe_client
*cli
= NULL
;
2384 struct dcerpc_binding_handle
*b
= NULL
;
2386 ZERO_STRUCT(dom_pol
);
2388 /* Ensure null termination */
2389 state
->request
->data
.chng_pswd_auth_crap
.user
[
2390 sizeof(state
->request
->data
.chng_pswd_auth_crap
.user
)-1]=0;
2391 state
->request
->data
.chng_pswd_auth_crap
.domain
[
2392 sizeof(state
->request
->data
.chng_pswd_auth_crap
.domain
)-1]=0;
2396 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2397 (unsigned long)state
->pid
,
2398 state
->request
->data
.chng_pswd_auth_crap
.domain
,
2399 state
->request
->data
.chng_pswd_auth_crap
.user
));
2401 if (lp_winbind_offline_logon()) {
2402 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2403 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2404 result
= NT_STATUS_ACCESS_DENIED
;
2408 if (*state
->request
->data
.chng_pswd_auth_crap
.domain
) {
2409 fstrcpy(domain
,state
->request
->data
.chng_pswd_auth_crap
.domain
);
2411 parse_domain_user(state
->request
->data
.chng_pswd_auth_crap
.user
,
2415 DEBUG(3,("no domain specified with username (%s) - "
2417 state
->request
->data
.chng_pswd_auth_crap
.user
));
2418 result
= NT_STATUS_NO_SUCH_USER
;
2423 if (!*domain
&& lp_winbind_use_default_domain()) {
2424 fstrcpy(domain
,lp_workgroup());
2428 fstrcpy(user
, state
->request
->data
.chng_pswd_auth_crap
.user
);
2431 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2432 (unsigned long)state
->pid
, domain
, user
));
2434 /* Change password */
2435 new_nt_password
= data_blob_const(
2436 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd
,
2437 state
->request
->data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2439 old_nt_hash_enc
= data_blob_const(
2440 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2441 state
->request
->data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2443 if(state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2444 new_lm_password
= data_blob_const(
2445 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd
,
2446 state
->request
->data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2448 old_lm_hash_enc
= data_blob_const(
2449 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2450 state
->request
->data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2452 new_lm_password
= data_blob_null
;
2453 old_lm_hash_enc
= data_blob_null
;
2456 /* Get sam handle */
2458 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, true, &cli
, &dom_pol
);
2459 if (!NT_STATUS_IS_OK(result
)) {
2460 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2464 b
= cli
->binding_handle
;
2466 result
= rpccli_samr_chng_pswd_auth_crap(
2467 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2468 new_lm_password
, old_lm_hash_enc
);
2472 if (strequal(contact_domain
->name
, get_global_sam_name())) {
2473 /* FIXME: internal rpc pipe does not cache handles yet */
2475 if (is_valid_policy_hnd(&dom_pol
)) {
2477 dcerpc_samr_Close(b
, state
->mem_ctx
, &dom_pol
, &_result
);
2483 set_auth_errors(state
->response
, result
);
2485 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2486 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2488 state
->response
->data
.auth
.nt_status_string
,
2489 state
->response
->data
.auth
.pam_error
));
2491 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2495 static NTSTATUS
extract_pac_vrfy_sigs(TALLOC_CTX
*mem_ctx
, DATA_BLOB pac_blob
,
2496 struct PAC_LOGON_INFO
**logon_info
)
2498 krb5_context krbctx
= NULL
;
2499 krb5_error_code k5ret
;
2501 krb5_kt_cursor cursor
;
2502 krb5_keytab_entry entry
;
2503 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
2506 ZERO_STRUCT(cursor
);
2508 k5ret
= krb5_init_context(&krbctx
);
2510 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2511 error_message(k5ret
)));
2512 status
= krb5_to_nt_status(k5ret
);
2516 k5ret
= gse_krb5_get_server_keytab(krbctx
, &keytab
);
2518 DEBUG(1, ("Failed to get keytab: %s\n",
2519 error_message(k5ret
)));
2520 status
= krb5_to_nt_status(k5ret
);
2524 k5ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &cursor
);
2526 DEBUG(1, ("Failed to start seq: %s\n",
2527 error_message(k5ret
)));
2528 status
= krb5_to_nt_status(k5ret
);
2532 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2533 while (k5ret
== 0) {
2534 status
= kerberos_pac_logon_info(mem_ctx
, pac_blob
,
2536 KRB5_KT_KEY(&entry
), NULL
, 0,
2538 if (NT_STATUS_IS_OK(status
)) {
2541 k5ret
= smb_krb5_kt_free_entry(krbctx
, &entry
);
2542 k5ret
= krb5_kt_next_entry(krbctx
, keytab
, &entry
, &cursor
);
2545 k5ret
= krb5_kt_end_seq_get(krbctx
, keytab
, &cursor
);
2547 DEBUG(1, ("Failed to end seq: %s\n",
2548 error_message(k5ret
)));
2551 k5ret
= krb5_kt_close(krbctx
, keytab
);
2553 DEBUG(1, ("Failed to close keytab: %s\n",
2554 error_message(k5ret
)));
2557 krb5_free_context(krbctx
);
2562 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2563 struct netr_SamInfo3
**info3
)
2565 struct winbindd_request
*req
= state
->request
;
2567 struct PAC_LOGON_INFO
*logon_info
= NULL
;
2568 struct netr_SamInfo3
*info3_copy
= NULL
;
2571 pac_blob
= data_blob_const(req
->extra_data
.data
, req
->extra_len
);
2572 result
= extract_pac_vrfy_sigs(state
->mem_ctx
, pac_blob
, &logon_info
);
2573 if (!NT_STATUS_IS_OK(result
) &&
2574 !NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
)) {
2575 DEBUG(1, ("Error during PAC signature verification: %s\n",
2576 nt_errstr(result
)));
2582 * Signature verification succeeded, we can
2583 * trust the PAC and prime the netsamlogon
2584 * and name2sid caches. DO NOT DO THIS
2585 * in the signature verification failed
2588 struct winbindd_domain
*domain
= NULL
;
2590 result
= create_info3_from_pac_logon_info(state
->mem_ctx
,
2593 if (!NT_STATUS_IS_OK(result
)) {
2596 netsamlogon_cache_store(NULL
, info3_copy
);
2599 * We're in the parent here, so find the child
2600 * pointer from the PAC domain name.
2602 domain
= find_domain_from_name_noinit(
2603 info3_copy
->base
.logon_domain
.string
);
2604 if (domain
&& domain
->primary
) {
2605 struct dom_sid user_sid
;
2607 sid_compose(&user_sid
,
2608 info3_copy
->base
.domain_sid
,
2609 info3_copy
->base
.rid
);
2611 cache_name2sid_trusted(domain
,
2612 info3_copy
->base
.logon_domain
.string
,
2613 info3_copy
->base
.account_name
.string
,
2617 DBG_INFO("PAC for user %s\%s SID %s primed cache\n",
2618 info3_copy
->base
.logon_domain
.string
,
2619 info3_copy
->base
.account_name
.string
,
2620 sid_string_dbg(&user_sid
));
2624 /* Try without signature verification */
2625 result
= kerberos_pac_logon_info(state
->mem_ctx
, pac_blob
, NULL
,
2626 NULL
, NULL
, NULL
, 0,
2628 if (!NT_STATUS_IS_OK(result
)) {
2629 DEBUG(10, ("Could not extract PAC: %s\n",
2630 nt_errstr(result
)));
2635 * Don't strictly need to copy here,
2636 * but it makes it explicit we're
2637 * returning a copy talloc'ed off
2638 * the state->mem_ctx.
2640 info3_copy
= copy_netr_SamInfo3(state
->mem_ctx
,
2641 &logon_info
->info3
);
2642 if (info3_copy
== NULL
) {
2643 return NT_STATUS_NO_MEMORY
;
2648 *info3
= info3_copy
;
2650 return NT_STATUS_OK
;
2652 #else /* HAVE_KRB5 */
2653 NTSTATUS
winbindd_pam_auth_pac_send(struct winbindd_cli_state
*state
,
2654 struct netr_SamInfo3
**info3
)
2656 return NT_STATUS_NO_SUCH_USER
;
2658 #endif /* HAVE_KRB5 */