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;
1219 rc = tsocket_address_inet_from_strings(mem_ctx,
1225 return NT_STATUS_NO_MEMORY;
1227 status = make_user_info(&user_info, user, user, domain, domain,
1228 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1229 NULL, AUTH_PASSWORD_RESPONSE);
1230 if (!NT_STATUS_IS_OK(status)) {
1231 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1234 user_info->logon_parameters = logon_parameters;
1236 /* We don't want any more mapping of the username */
1237 user_info->mapped_state = True;
1239 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1241 free_user_info(&user_info);
1242 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1243 user, nt_errstr(status)));
1247 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1248 TALLOC_CTX *mem_ctx,
1249 uint32_t logon_parameters,
1251 const char *username,
1252 const char *domainname,
1253 const char *workstation,
1254 const uint8_t chal[8],
1255 DATA_BLOB lm_response,
1256 DATA_BLOB nt_response,
1257 struct netr_SamInfo3 **info3)
1260 int netr_attempts = 0;
1265 struct rpc_pipe_client *netlogon_pipe;
1266 uint8_t authoritative = 0;
1269 ZERO_STRUCTP(info3);
1272 result = cm_connect_netlogon(domain, &netlogon_pipe);
1274 if (!NT_STATUS_IS_OK(result)) {
1275 DEBUG(3,("Could not open handle to NETLOGON pipe "
1276 "(error: %s, attempts: %d)\n",
1277 nt_errstr(result), netr_attempts));
1279 /* After the first retry always close the connection */
1280 if (netr_attempts > 0) {
1281 DEBUG(3, ("This is again a problem for this "
1282 "particular call, forcing the close "
1283 "of this connection\n"));
1284 invalidate_cm_connection(&domain->conn);
1287 /* After the second retry failover to the next DC */
1288 if (netr_attempts > 1) {
1290 * If the netlogon server is not reachable then
1291 * it is possible that the DC is rebuilding
1292 * sysvol and shutdown netlogon for that time.
1293 * We should failover to the next dc.
1295 DEBUG(3, ("This is the third problem for this "
1296 "particular call, adding DC to the "
1297 "negative cache list\n"));
1298 add_failed_connection_entry(domain->name,
1301 saf_delete(domain->name);
1304 /* Only allow 3 retries */
1305 if (netr_attempts < 3) {
1306 DEBUG(3, ("The connection to netlogon "
1307 "failed, retrying\n"));
1316 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1317 netlogon_pipe->binding_handle,
1331 * we increment this after the "feature negotiation"
1332 * for can_do_samlogon_ex and can_do_validation6
1336 /* We have to try a second time as cm_connect_netlogon
1337 might not yet have noticed that the DC has killed
1340 if (!rpccli_is_connected(netlogon_pipe)) {
1345 /* if we get access denied, a possible cause was that we had
1346 and open connection to the DC, but someone changed our
1347 machine account password out from underneath us using 'net
1348 rpc changetrustpw' */
1350 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1351 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1352 "ACCESS_DENIED. Maybe the trust account "
1353 "password was changed and we didn't know it. "
1354 "Killing connections to domain %s\n",
1356 invalidate_cm_connection(&domain->conn);
1360 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1362 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1363 * (no Ex). This happens against old Samba
1364 * DCs, if LogonSamLogonEx() fails with an error
1365 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1367 * The server will log something like this:
1368 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1370 * This sets the whole connection into a fault_state mode
1371 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1373 * This also happens to our retry with LogonSamLogonWithFlags()
1374 * and LogonSamLogon().
1376 * In order to recover from this situation, we need to
1377 * drop the connection.
1379 invalidate_cm_connection(&domain->conn);
1380 result = NT_STATUS_LOGON_FAILURE;
1384 } while ( (attempts < 2) && retry );
1386 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1387 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1388 "returned NT_STATUS_IO_TIMEOUT after the retry."
1389 "Killing connections to domain %s\n",
1391 invalidate_cm_connection(&domain->conn);
1396 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1397 struct winbindd_domain *domain,
1400 uint32_t request_flags,
1401 struct netr_SamInfo3 **info3)
1407 unsigned char local_nt_response[24];
1408 fstring name_domain, name_user;
1410 struct netr_SamInfo3 *my_info3 = NULL;
1414 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1416 /* Parse domain and username */
1418 parse_domain_user(user, name_domain, name_user);
1420 /* do password magic */
1422 generate_random_buffer(chal, sizeof(chal));
1424 if (lp_client_ntlmv2_auth()) {
1425 DATA_BLOB server_chal;
1426 DATA_BLOB names_blob;
1427 server_chal = data_blob_const(chal, 8);
1429 /* note that the 'workgroup' here is for the local
1430 machine. The 'server name' must match the
1431 'workstation' passed to the actual SamLogon call.
1433 names_blob = NTLMv2_generate_names_blob(
1434 mem_ctx, lp_netbios_name(), lp_workgroup());
1436 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1440 &lm_resp, &nt_resp, NULL, NULL)) {
1441 data_blob_free(&names_blob);
1442 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1443 result = NT_STATUS_NO_MEMORY;
1446 data_blob_free(&names_blob);
1448 lm_resp = data_blob_null;
1449 SMBNTencrypt(pass, chal, local_nt_response);
1451 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1452 sizeof(local_nt_response));
1455 if (strequal(name_domain, get_global_sam_name())) {
1456 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1458 result = winbindd_dual_auth_passdb(
1459 mem_ctx, 0, name_domain, name_user,
1460 &chal_blob, &lm_resp, &nt_resp, info3);
1464 /* check authentication loop */
1466 result = winbind_samlogon_retry_loop(domain,
1477 if (!NT_STATUS_IS_OK(result)) {
1481 /* handle the case where a NT4 DC does not fill in the acct_flags in
1482 * the samlogon reply info3. When accurate info3 is required by the
1483 * caller, we look up the account flags ourselve - gd */
1485 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1486 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1488 struct rpc_pipe_client *samr_pipe;
1489 struct policy_handle samr_domain_handle, user_pol;
1490 union samr_UserInfo *info = NULL;
1491 NTSTATUS status_tmp, result_tmp;
1493 struct dcerpc_binding_handle *b;
1495 status_tmp = cm_connect_sam(domain, mem_ctx,
1496 &samr_pipe, &samr_domain_handle);
1498 if (!NT_STATUS_IS_OK(status_tmp)) {
1499 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1500 nt_errstr(status_tmp)));
1504 b = samr_pipe->binding_handle;
1506 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1507 &samr_domain_handle,
1508 MAXIMUM_ALLOWED_ACCESS,
1513 if (!NT_STATUS_IS_OK(status_tmp)) {
1514 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1515 nt_errstr(status_tmp)));
1518 if (!NT_STATUS_IS_OK(result_tmp)) {
1519 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1520 nt_errstr(result_tmp)));
1524 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1530 if (!NT_STATUS_IS_OK(status_tmp)) {
1531 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1532 nt_errstr(status_tmp)));
1533 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1536 if (!NT_STATUS_IS_OK(result_tmp)) {
1537 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1538 nt_errstr(result_tmp)));
1539 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1543 acct_flags = info->info16.acct_flags;
1545 if (acct_flags == 0) {
1546 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1550 my_info3->base.acct_flags = acct_flags;
1552 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1554 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1562 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1563 struct winbindd_cli_state *state)
1565 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1566 NTSTATUS krb5_result = NT_STATUS_OK;
1567 fstring name_domain, name_user;
1569 fstring domain_user;
1570 struct netr_SamInfo3 *info3 = NULL;
1571 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1573 /* Ensure null termination */
1574 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1576 /* Ensure null termination */
1577 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1579 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1580 state->request->data.auth.user));
1582 /* Parse domain and username */
1584 name_map_status = normalize_name_unmap(state->mem_ctx,
1585 state->request->data.auth.user,
1588 /* If the name normalization didnt' actually do anything,
1589 just use the original name */
1591 if (!NT_STATUS_IS_OK(name_map_status) &&
1592 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1594 mapped_user = state->request->data.auth.user;
1597 parse_domain_user(mapped_user, name_domain, name_user);
1599 if ( mapped_user != state->request->data.auth.user ) {
1600 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1601 *lp_winbind_separator(),
1603 strlcpy( state->request->data.auth.user, domain_user,
1604 sizeof(state->request->data.auth.user));
1607 if (!domain->online) {
1608 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1609 if (domain->startup) {
1610 /* Logons are very important to users. If we're offline and
1611 we get a request within the first 30 seconds of startup,
1612 try very hard to find a DC and go online. */
1614 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1615 "request in startup mode.\n", domain->name ));
1617 winbindd_flush_negative_conn_cache(domain);
1618 result = init_dc_connection(domain);
1622 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1624 /* Check for Kerberos authentication */
1625 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1627 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1628 /* save for later */
1629 krb5_result = result;
1632 if (NT_STATUS_IS_OK(result)) {
1633 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1634 goto process_result;
1636 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1639 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1640 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1641 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1642 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1643 set_domain_offline( domain );
1647 /* there are quite some NT_STATUS errors where there is no
1648 * point in retrying with a samlogon, we explictly have to take
1649 * care not to increase the bad logon counter on the DC */
1651 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1652 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1653 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1654 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1655 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1656 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1657 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1658 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1659 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1660 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1664 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1665 DEBUG(3,("falling back to samlogon\n"));
1673 /* Check for Samlogon authentication */
1674 if (domain->online) {
1675 result = winbindd_dual_pam_auth_samlogon(
1676 state->mem_ctx, domain,
1677 state->request->data.auth.user,
1678 state->request->data.auth.pass,
1679 state->request->flags,
1682 if (NT_STATUS_IS_OK(result)) {
1683 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1684 /* add the Krb5 err if we have one */
1685 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1686 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1688 goto process_result;
1691 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1692 nt_errstr(result)));
1694 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1695 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1696 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1698 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1699 set_domain_offline( domain );
1703 if (domain->online) {
1704 /* We're still online - fail. */
1710 /* Check for Cached logons */
1711 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1712 lp_winbind_offline_logon()) {
1714 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1716 if (NT_STATUS_IS_OK(result)) {
1717 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1718 goto process_result;
1720 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1727 if (NT_STATUS_IS_OK(result)) {
1729 struct dom_sid user_sid;
1731 /* In all codepaths where result == NT_STATUS_OK info3 must have
1732 been initialized. */
1734 result = NT_STATUS_INTERNAL_ERROR;
1738 sid_compose(&user_sid, info3->base.domain_sid,
1741 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1743 netsamlogon_cache_store(name_user, info3);
1745 /* save name_to_sid info as early as possible (only if
1746 this is our primary domain so we don't invalidate
1747 the cache entry by storing the seq_num for the wrong
1749 if ( domain->primary ) {
1750 cache_name2sid(domain, name_domain, name_user,
1751 SID_NAME_USER, &user_sid);
1754 /* Check if the user is in the right group */
1756 result = check_info3_in_group(
1758 state->request->data.auth.require_membership_of_sid);
1759 if (!NT_STATUS_IS_OK(result)) {
1760 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1761 state->request->data.auth.user,
1762 state->request->data.auth.require_membership_of_sid));
1766 result = append_auth_data(state->mem_ctx, state->response,
1767 state->request->flags, info3,
1768 name_domain, name_user);
1769 if (!NT_STATUS_IS_OK(result)) {
1773 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1774 && lp_winbind_offline_logon()) {
1776 result = winbindd_store_creds(domain,
1777 state->request->data.auth.user,
1778 state->request->data.auth.pass,
1782 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1783 struct winbindd_domain *our_domain = find_our_domain();
1785 /* This is not entirely correct I believe, but it is
1786 consistent. Only apply the password policy settings
1787 too warn users for our own domain. Cannot obtain these
1788 from trusted DCs all the time so don't do it at all.
1791 result = NT_STATUS_NOT_SUPPORTED;
1792 if (our_domain == domain ) {
1793 result = fillup_password_policy(
1794 our_domain, state->response);
1797 if (!NT_STATUS_IS_OK(result)
1798 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1800 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1801 domain->name, nt_errstr(result)));
1806 result = NT_STATUS_OK;
1810 /* give us a more useful (more correct?) error code */
1811 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1812 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1813 result = NT_STATUS_NO_LOGON_SERVERS;
1816 set_auth_errors(state->response, result);
1818 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1819 state->request->data.auth.user,
1820 state->response->data.auth.nt_status_string,
1821 state->response->data.auth.pam_error));
1823 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1826 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1827 struct winbindd_cli_state *state)
1830 struct netr_SamInfo3 *info3 = NULL;
1831 const char *name_user = NULL;
1832 const char *name_domain = NULL;
1833 const char *workstation;
1835 DATA_BLOB lm_resp, nt_resp;
1837 /* This is child-only, so no check for privileged access is needed
1840 /* Ensure null termination */
1841 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1842 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1844 name_user = state->request->data.auth_crap.user;
1845 name_domain = state->request->data.auth_crap.domain;
1846 workstation = state->request->data.auth_crap.workstation;
1848 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1849 name_domain, name_user));
1851 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1852 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1853 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1854 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1855 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1856 state->request->data.auth_crap.lm_resp_len,
1857 state->request->data.auth_crap.nt_resp_len));
1858 result = NT_STATUS_INVALID_PARAMETER;
1863 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1864 state->request->data.auth_crap.lm_resp_len);
1866 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1867 nt_resp = data_blob_talloc(state->mem_ctx,
1868 state->request->extra_data.data,
1869 state->request->data.auth_crap.nt_resp_len);
1871 nt_resp = data_blob_talloc(state->mem_ctx,
1872 state->request->data.auth_crap.nt_resp,
1873 state->request->data.auth_crap.nt_resp_len);
1876 if (strequal(name_domain, get_global_sam_name())) {
1877 DATA_BLOB chal_blob = data_blob_const(
1878 state->request->data.auth_crap.chal,
1879 sizeof(state->request->data.auth_crap.chal));
1881 result = winbindd_dual_auth_passdb(
1883 state->request->data.auth_crap.logon_parameters,
1884 name_domain, name_user,
1885 &chal_blob, &lm_resp, &nt_resp, &info3);
1886 goto process_result;
1889 result = winbind_samlogon_retry_loop(domain,
1891 state->request->data.auth_crap.logon_parameters,
1895 /* Bug #3248 - found by Stefan Burkei. */
1896 workstation, /* We carefully set this above so use it... */
1897 state->request->data.auth_crap.chal,
1901 if (!NT_STATUS_IS_OK(result)) {
1907 if (NT_STATUS_IS_OK(result)) {
1908 struct dom_sid user_sid;
1910 sid_compose(&user_sid, info3->base.domain_sid,
1912 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1914 netsamlogon_cache_store(name_user, info3);
1916 /* Check if the user is in the right group */
1918 result = check_info3_in_group(
1920 state->request->data.auth_crap.require_membership_of_sid);
1921 if (!NT_STATUS_IS_OK(result)) {
1922 DEBUG(3, ("User %s is not in the required group (%s), so "
1923 "crap authentication is rejected\n",
1924 state->request->data.auth_crap.user,
1925 state->request->data.auth_crap.require_membership_of_sid));
1929 result = append_auth_data(state->mem_ctx, state->response,
1930 state->request->flags, info3,
1931 name_domain, name_user);
1932 if (!NT_STATUS_IS_OK(result)) {
1939 /* give us a more useful (more correct?) error code */
1940 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1941 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1942 result = NT_STATUS_NO_LOGON_SERVERS;
1945 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1946 result = nt_status_squash(result);
1949 set_auth_errors(state->response, result);
1951 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1952 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1955 state->response->data.auth.nt_status_string,
1956 state->response->data.auth.pam_error));
1958 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1961 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1962 struct winbindd_cli_state *state)
1965 char *newpass = NULL;
1966 struct policy_handle dom_pol;
1967 struct rpc_pipe_client *cli = NULL;
1968 bool got_info = false;
1969 struct samr_DomInfo1 *info = NULL;
1970 struct userPwdChangeFailureInformation *reject = NULL;
1971 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1972 fstring domain, user;
1973 struct dcerpc_binding_handle *b = NULL;
1975 ZERO_STRUCT(dom_pol);
1977 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1978 state->request->data.auth.user));
1980 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1984 /* Change password */
1986 oldpass = state->request->data.chauthtok.oldpass;
1987 newpass = state->request->data.chauthtok.newpass;
1989 /* Initialize reject reason */
1990 state->response->data.auth.reject_reason = Undefined;
1992 /* Get sam handle */
1994 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1996 if (!NT_STATUS_IS_OK(result)) {
1997 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2001 b = cli->binding_handle;
2003 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2010 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2012 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2014 fill_in_password_policy(state->response, info);
2016 state->response->data.auth.reject_reason =
2017 reject->extendedFailureReason;
2022 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2023 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2024 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2025 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2027 /* only fallback when the chgpasswd_user3 call is not supported */
2028 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2029 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2030 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2031 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2033 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2034 nt_errstr(result)));
2036 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2038 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2039 Map to the same status code as Windows 2003. */
2041 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2042 result = NT_STATUS_PASSWORD_RESTRICTION;
2048 if (NT_STATUS_IS_OK(result)
2049 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2050 && lp_winbind_offline_logon()) {
2051 result = winbindd_update_creds_by_name(contact_domain, user,
2053 /* Again, this happens when we login from gdm or xdm
2054 * and the password expires, *BUT* cached crendentials
2055 * doesn't exist. winbindd_update_creds_by_name()
2056 * returns NT_STATUS_NO_SUCH_USER.
2057 * This is not a failure.
2060 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2061 result = NT_STATUS_OK;
2064 if (!NT_STATUS_IS_OK(result)) {
2065 DEBUG(10, ("Failed to store creds: %s\n",
2066 nt_errstr(result)));
2067 goto process_result;
2071 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2073 NTSTATUS policy_ret;
2075 policy_ret = fillup_password_policy(
2076 contact_domain, state->response);
2078 /* failure of this is non critical, it will just provide no
2079 * additional information to the client why the change has
2080 * failed - Guenther */
2082 if (!NT_STATUS_IS_OK(policy_ret)) {
2083 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2084 goto process_result;
2090 if (strequal(contact_domain->name, get_global_sam_name())) {
2091 /* FIXME: internal rpc pipe does not cache handles yet */
2093 if (is_valid_policy_hnd(&dom_pol)) {
2095 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2101 set_auth_errors(state->response, result);
2103 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2104 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2107 state->response->data.auth.nt_status_string,
2108 state->response->data.auth.pam_error));
2110 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2113 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2114 struct winbindd_cli_state *state)
2116 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2118 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2119 state->request->data.logoff.user));
2121 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2122 result = NT_STATUS_OK;
2123 goto process_result;
2126 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2127 result = NT_STATUS_OK;
2128 goto process_result;
2133 if (state->request->data.logoff.uid < 0) {
2134 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2135 goto process_result;
2138 /* what we need here is to find the corresponding krb5 ccache name *we*
2139 * created for a given username and destroy it */
2141 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2142 result = NT_STATUS_OK;
2143 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2144 goto process_result;
2147 if (!ccache_entry_identical(state->request->data.logoff.user,
2148 state->request->data.logoff.uid,
2149 state->request->data.logoff.krb5ccname)) {
2150 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2151 goto process_result;
2154 result = remove_ccache(state->request->data.logoff.user);
2155 if (!NT_STATUS_IS_OK(result)) {
2156 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2157 nt_errstr(result)));
2158 goto process_result;
2162 * Remove any mlock'ed memory creds in the child
2163 * we might be using for krb5 ticket renewal.
2166 winbindd_delete_memory_creds(state->request->data.logoff.user);
2169 result = NT_STATUS_NOT_SUPPORTED;
2175 set_auth_errors(state->response, result);
2177 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2180 /* Change user password with auth crap*/
2182 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2185 DATA_BLOB new_nt_password;
2186 DATA_BLOB old_nt_hash_enc;
2187 DATA_BLOB new_lm_password;
2188 DATA_BLOB old_lm_hash_enc;
2189 fstring domain,user;
2190 struct policy_handle dom_pol;
2191 struct winbindd_domain *contact_domain = domainSt;
2192 struct rpc_pipe_client *cli = NULL;
2193 struct dcerpc_binding_handle *b = NULL;
2195 ZERO_STRUCT(dom_pol);
2197 /* Ensure null termination */
2198 state->request->data.chng_pswd_auth_crap.user[
2199 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2200 state->request->data.chng_pswd_auth_crap.domain[
2201 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2205 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2206 (unsigned long)state->pid,
2207 state->request->data.chng_pswd_auth_crap.domain,
2208 state->request->data.chng_pswd_auth_crap.user));
2210 if (lp_winbind_offline_logon()) {
2211 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2212 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2213 result = NT_STATUS_ACCESS_DENIED;
2217 if (*state->request->data.chng_pswd_auth_crap.domain) {
2218 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2220 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2224 DEBUG(3,("no domain specified with username (%s) - "
2226 state->request->data.chng_pswd_auth_crap.user));
2227 result = NT_STATUS_NO_SUCH_USER;
2232 if (!*domain && lp_winbind_use_default_domain()) {
2233 fstrcpy(domain,lp_workgroup());
2237 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2240 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2241 (unsigned long)state->pid, domain, user));
2243 /* Change password */
2244 new_nt_password = data_blob_const(
2245 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2246 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2248 old_nt_hash_enc = data_blob_const(
2249 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2250 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2252 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2253 new_lm_password = data_blob_const(
2254 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2255 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2257 old_lm_hash_enc = data_blob_const(
2258 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2259 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2261 new_lm_password = data_blob_null;
2262 old_lm_hash_enc = data_blob_null;
2265 /* Get sam handle */
2267 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2268 if (!NT_STATUS_IS_OK(result)) {
2269 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2273 b = cli->binding_handle;
2275 result = rpccli_samr_chng_pswd_auth_crap(
2276 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2277 new_lm_password, old_lm_hash_enc);
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",
2297 state->response->data.auth.nt_status_string,
2298 state->response->data.auth.pam_error));
2300 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2304 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2305 struct PAC_LOGON_INFO **logon_info)
2307 krb5_context krbctx = NULL;
2308 krb5_error_code k5ret;
2310 krb5_kt_cursor cursor;
2311 krb5_keytab_entry entry;
2312 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2315 ZERO_STRUCT(cursor);
2317 k5ret = krb5_init_context(&krbctx);
2319 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2320 error_message(k5ret)));
2321 status = krb5_to_nt_status(k5ret);
2325 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2327 DEBUG(1, ("Failed to get keytab: %s\n",
2328 error_message(k5ret)));
2329 status = krb5_to_nt_status(k5ret);
2333 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2335 DEBUG(1, ("Failed to start seq: %s\n",
2336 error_message(k5ret)));
2337 status = krb5_to_nt_status(k5ret);
2341 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2342 while (k5ret == 0) {
2343 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2345 KRB5_KT_KEY(&entry), NULL, 0,
2347 if (NT_STATUS_IS_OK(status)) {
2350 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2351 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2354 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2356 DEBUG(1, ("Failed to end seq: %s\n",
2357 error_message(k5ret)));
2360 k5ret = krb5_kt_close(krbctx, keytab);
2362 DEBUG(1, ("Failed to close keytab: %s\n",
2363 error_message(k5ret)));
2366 krb5_free_context(krbctx);
2371 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2372 struct netr_SamInfo3 **info3)
2374 struct winbindd_request *req = state->request;
2376 struct PAC_LOGON_INFO *logon_info = NULL;
2379 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2380 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2381 if (!NT_STATUS_IS_OK(result) &&
2382 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2383 DEBUG(1, ("Error during PAC signature verification: %s\n",
2384 nt_errstr(result)));
2389 /* Signature verification succeeded, trust the PAC */
2390 netsamlogon_cache_store(NULL, &logon_info->info3);
2393 /* Try without signature verification */
2394 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2395 NULL, NULL, NULL, 0,
2397 if (!NT_STATUS_IS_OK(result)) {
2398 DEBUG(10, ("Could not extract PAC: %s\n",
2399 nt_errstr(result)));
2404 *info3 = &logon_info->info3;
2406 return NT_STATUS_OK;
2408 #else /* HAVE_KRB5 */
2409 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2410 struct netr_SamInfo3 **info3)
2412 return NT_STATUS_NO_SUCH_USER;
2414 #endif /* HAVE_KRB5 */