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"
47 #define DBGC_CLASS DBGC_WINBIND
49 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
51 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
52 struct winbindd_response *resp,
53 struct netr_SamInfo3 *info3)
58 resp->data.auth.info3.logon_time =
59 nt_time_to_unix(info3->base.logon_time);
60 resp->data.auth.info3.logoff_time =
61 nt_time_to_unix(info3->base.logoff_time);
62 resp->data.auth.info3.kickoff_time =
63 nt_time_to_unix(info3->base.kickoff_time);
64 resp->data.auth.info3.pass_last_set_time =
65 nt_time_to_unix(info3->base.last_password_change);
66 resp->data.auth.info3.pass_can_change_time =
67 nt_time_to_unix(info3->base.allow_password_change);
68 resp->data.auth.info3.pass_must_change_time =
69 nt_time_to_unix(info3->base.force_password_change);
71 resp->data.auth.info3.logon_count = info3->base.logon_count;
72 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
74 resp->data.auth.info3.user_rid = info3->base.rid;
75 resp->data.auth.info3.group_rid = info3->base.primary_gid;
76 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
78 resp->data.auth.info3.num_groups = info3->base.groups.count;
79 resp->data.auth.info3.user_flgs = info3->base.user_flags;
81 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
82 resp->data.auth.info3.num_other_sids = info3->sidcount;
84 fstrcpy(resp->data.auth.info3.user_name,
85 info3->base.account_name.string);
86 fstrcpy(resp->data.auth.info3.full_name,
87 info3->base.full_name.string);
88 fstrcpy(resp->data.auth.info3.logon_script,
89 info3->base.logon_script.string);
90 fstrcpy(resp->data.auth.info3.profile_path,
91 info3->base.profile_path.string);
92 fstrcpy(resp->data.auth.info3.home_dir,
93 info3->base.home_directory.string);
94 fstrcpy(resp->data.auth.info3.dir_drive,
95 info3->base.home_drive.string);
97 fstrcpy(resp->data.auth.info3.logon_srv,
98 info3->base.logon_server.string);
99 fstrcpy(resp->data.auth.info3.logon_dom,
100 info3->base.logon_domain.string);
102 ex = talloc_strdup(mem_ctx, "");
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 for (i=0; i < info3->base.groups.count; i++) {
106 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
107 info3->base.groups.rids[i].rid,
108 info3->base.groups.rids[i].attributes);
109 NT_STATUS_HAVE_NO_MEMORY(ex);
112 for (i=0; i < info3->sidcount; i++) {
115 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
116 NT_STATUS_HAVE_NO_MEMORY(sid);
118 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
120 info3->sids[i].attributes);
121 NT_STATUS_HAVE_NO_MEMORY(ex);
126 resp->extra_data.data = ex;
127 resp->length += talloc_get_size(ex);
132 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
133 struct winbindd_response *resp,
134 struct netr_SamInfo3 *info3)
137 enum ndr_err_code ndr_err;
139 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
140 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
141 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
142 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
143 return ndr_map_error2ntstatus(ndr_err);
146 resp->extra_data.data = blob.data;
147 resp->length += blob.length;
152 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
153 struct winbindd_response *resp,
154 const struct netr_SamInfo3 *info3,
155 const char *name_domain,
156 const char *name_user)
158 /* We've been asked to return the unix username, per
159 'winbind use default domain' settings and the like */
161 const char *nt_username, *nt_domain;
163 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
165 /* If the server didn't give us one, just use the one
167 nt_domain = name_domain;
170 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
172 /* If the server didn't give us one, just use the one
174 nt_username = name_user;
177 fill_domain_username(resp->data.auth.unix_username,
178 nt_domain, nt_username, true);
180 DEBUG(5, ("Setting unix username to [%s]\n",
181 resp->data.auth.unix_username));
186 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
187 struct winbindd_response *resp,
188 const struct netr_SamInfo3 *info3,
189 const char *name_domain,
190 const char *name_user)
192 char *afsname = NULL;
196 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197 if (afsname == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 afsname = talloc_string_sub(mem_ctx,
202 lp_afs_username_map(),
204 afsname = talloc_string_sub(mem_ctx, afsname,
206 afsname = talloc_string_sub(mem_ctx, afsname,
210 struct dom_sid user_sid;
213 sid_compose(&user_sid, info3->base.domain_sid,
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 if (!strlower_m(afsname)) {
225 return NT_STATUS_INVALID_PARAMETER;
228 DEBUG(10, ("Generating token for user %s\n", afsname));
230 cell = strchr(afsname, '@');
233 return NT_STATUS_NO_MEMORY;
239 token = afs_createtoken_str(afsname, cell);
243 resp->extra_data.data = talloc_strdup(mem_ctx, token);
244 if (resp->extra_data.data == NULL) {
245 return NT_STATUS_NO_MEMORY;
247 resp->length += strlen((const char *)resp->extra_data.data)+1;
252 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
253 const char *group_sid)
255 * Check whether a user belongs to a group or list of groups.
257 * @param mem_ctx talloc memory context.
258 * @param info3 user information, including group membership info.
259 * @param group_sid One or more groups , separated by commas.
261 * @return NT_STATUS_OK on success,
262 * NT_STATUS_LOGON_FAILURE if the user does not belong,
263 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
266 struct dom_sid *require_membership_of_sid;
267 uint32_t num_require_membership_of_sid;
272 struct security_token *token;
273 TALLOC_CTX *frame = talloc_stackframe();
276 /* Parse the 'required group' SID */
278 if (!group_sid || !group_sid[0]) {
279 /* NO sid supplied, all users may access */
284 token = talloc_zero(talloc_tos(), struct security_token);
286 DEBUG(0, ("talloc failed\n"));
288 return NT_STATUS_NO_MEMORY;
291 num_require_membership_of_sid = 0;
292 require_membership_of_sid = NULL;
296 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
297 if (!string_to_sid(&sid, req_sid)) {
298 DEBUG(0, ("check_info3_in_group: could not parse %s "
299 "as a SID!", req_sid));
301 return NT_STATUS_INVALID_PARAMETER;
304 status = add_sid_to_array(talloc_tos(), &sid,
305 &require_membership_of_sid,
306 &num_require_membership_of_sid);
307 if (!NT_STATUS_IS_OK(status)) {
308 DEBUG(0, ("add_sid_to_array failed\n"));
314 status = sid_array_from_info3(talloc_tos(), info3,
318 if (!NT_STATUS_IS_OK(status)) {
323 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
325 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
327 DEBUG(3, ("could not add aliases: %s\n",
333 security_token_debug(DBGC_CLASS, 10, token);
335 for (i=0; i<num_require_membership_of_sid; i++) {
336 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
337 &require_membership_of_sid[i])));
338 if (nt_token_check_sid(&require_membership_of_sid[i],
340 DEBUG(10, ("Access ok\n"));
346 /* Do not distinguish this error from a wrong username/pw */
349 return NT_STATUS_LOGON_FAILURE;
352 struct winbindd_domain *find_auth_domain(uint8_t flags,
353 const char *domain_name)
355 struct winbindd_domain *domain;
358 domain = find_domain_from_name_noinit(domain_name);
359 if (domain == NULL) {
360 DEBUG(3, ("Authentication for domain [%s] refused "
361 "as it is not a trusted domain\n",
367 if (strequal(domain_name, get_global_sam_name())) {
368 return find_domain_from_name_noinit(domain_name);
371 /* we can auth against trusted domains */
372 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
373 domain = find_domain_from_name_noinit(domain_name);
374 if (domain == NULL) {
375 DEBUG(3, ("Authentication for domain [%s] skipped "
376 "as it is not a trusted domain\n",
383 return find_our_domain();
386 static void fill_in_password_policy(struct winbindd_response *r,
387 const struct samr_DomInfo1 *p)
389 r->data.auth.policy.min_length_password =
390 p->min_password_length;
391 r->data.auth.policy.password_history =
392 p->password_history_length;
393 r->data.auth.policy.password_properties =
394 p->password_properties;
395 r->data.auth.policy.expire =
396 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
397 r->data.auth.policy.min_passwordage =
398 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
401 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
402 struct winbindd_response *response)
404 TALLOC_CTX *frame = talloc_stackframe();
405 struct winbindd_methods *methods;
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 methods = domain->methods;
418 status = methods->password_policy(domain, talloc_tos(), &password_policy);
419 if (NT_STATUS_IS_ERR(status)) {
423 fill_in_password_policy(response, &password_policy);
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
432 uint16 *lockout_threshold)
434 struct winbindd_methods *methods;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 struct samr_DomInfo12 lockout_policy;
438 *lockout_threshold = 0;
440 methods = domain->methods;
442 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
443 if (NT_STATUS_IS_ERR(status)) {
447 *lockout_threshold = lockout_policy.lockout_threshold;
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
454 uint32 *password_properties)
456 struct winbindd_methods *methods;
457 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458 struct samr_DomInfo1 password_policy;
460 *password_properties = 0;
462 methods = domain->methods;
464 status = methods->password_policy(domain, mem_ctx, &password_policy);
465 if (NT_STATUS_IS_ERR(status)) {
469 *password_properties = password_policy.password_properties;
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
479 const char **user_ccache_file)
481 /* accept FILE and WRFILE as krb5_cc_type from the client and then
482 * build the full ccname string based on the user's uid here -
485 const char *gen_cc = NULL;
488 if (strequal(type, "FILE")) {
489 gen_cc = talloc_asprintf(
490 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
492 if (strequal(type, "WRFILE")) {
493 gen_cc = talloc_asprintf(
494 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
496 if (strequal(type, "KEYRING")) {
497 gen_cc = talloc_asprintf(
498 mem_ctx, "KEYRING:persistent:%d", uid);
501 if (strnequal(type, "FILE:/", 6) ||
502 strnequal(type, "WRFILE:/", 8) ||
503 strnequal(type, "DIR:/", 5)) {
505 /* we allow only one "%u" substitution */
509 p = strchr(type, '%');
514 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
515 gen_cc = talloc_asprintf(mem_ctx, type, uid);
521 *user_ccache_file = gen_cc;
523 if (gen_cc == NULL) {
524 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
526 if (gen_cc == NULL) {
527 DEBUG(0,("out of memory\n"));
531 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
532 (*user_ccache_file == NULL) ? " (internal)":""));
539 uid_t get_uid_from_request(struct winbindd_request *request)
543 uid = request->data.auth.uid;
546 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
552 /**********************************************************************
553 Authenticate a user with a clear text password using Kerberos and fill up
555 **********************************************************************/
557 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
558 struct winbindd_domain *domain,
561 const char *krb5_cc_type,
563 struct netr_SamInfo3 **info3,
567 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
568 krb5_error_code krb5_ret;
569 const char *cc = NULL;
570 const char *principal_s = NULL;
571 const char *service = NULL;
573 fstring name_domain, name_user;
574 time_t ticket_lifetime = 0;
575 time_t renewal_until = 0;
577 time_t time_offset = 0;
578 const char *user_ccache_file;
579 struct PAC_LOGON_INFO *logon_info = NULL;
580 struct PAC_DATA *pac_data = NULL;
581 struct PAC_DATA_CTR *pac_data_ctr = NULL;
582 const char *local_service;
587 if (domain->alt_name == NULL) {
588 return NT_STATUS_INVALID_PARAMETER;
592 * prepare a krb5_cc_cache string for the user */
595 DEBUG(0,("no valid uid\n"));
598 cc = generate_krb5_ccache(mem_ctx,
603 return NT_STATUS_NO_MEMORY;
608 * get kerberos properties */
610 if (domain->private_data) {
611 ads = (ADS_STRUCT *)domain->private_data;
612 time_offset = ads->auth.time_offset;
617 * do kerberos auth and setup ccache as the user */
619 parse_domain_user(user, name_domain, name_user);
621 realm = talloc_strdup(mem_ctx, domain->alt_name);
623 return NT_STATUS_NO_MEMORY;
626 if (!strupper_m(realm)) {
627 return NT_STATUS_INVALID_PARAMETER;
630 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
631 if (principal_s == NULL) {
632 return NT_STATUS_NO_MEMORY;
635 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
636 if (service == NULL) {
637 return NT_STATUS_NO_MEMORY;
640 local_service = talloc_asprintf(mem_ctx, "%s$@%s",
641 lp_netbios_name(), lp_realm());
642 if (local_service == NULL) {
643 return NT_STATUS_NO_MEMORY;
647 /* if this is a user ccache, we need to act as the user to let the krb5
648 * library handle the chown, etc. */
650 /************************ ENTERING NON-ROOT **********************/
652 if (user_ccache_file != NULL) {
653 set_effective_uid(uid);
654 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
657 result = kerberos_return_pac(mem_ctx,
666 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
670 if (user_ccache_file != NULL) {
671 gain_root_privilege();
674 /************************ RETURNED TO ROOT **********************/
676 if (!NT_STATUS_IS_OK(result)) {
680 if (pac_data_ctr == NULL) {
684 pac_data = pac_data_ctr->pac_data;
685 if (pac_data == NULL) {
689 for (i=0; i < pac_data->num_buffers; i++) {
691 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
695 logon_info = pac_data->buffers[i].info->logon_info.info;
697 return NT_STATUS_INVALID_PARAMETER;
703 *info3 = &logon_info->info3;
705 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
708 /* if we had a user's ccache then return that string for the pam
711 if (user_ccache_file != NULL) {
713 fstrcpy(krb5ccname, user_ccache_file);
715 result = add_ccache_to_list(principal_s,
727 if (!NT_STATUS_IS_OK(result)) {
728 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
733 /* need to delete the memory cred cache, it is not used anymore */
735 krb5_ret = ads_kdestroy(cc);
737 DEBUG(3,("winbindd_raw_kerberos_login: "
738 "could not destroy krb5 credential cache: "
739 "%s\n", error_message(krb5_ret)));
748 * Do not delete an existing valid credential cache, if the user
749 * e.g. enters a wrong password
751 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
752 && user_ccache_file != NULL) {
756 /* we could have created a new credential cache with a valid tgt in it
757 * but we werent able to get or verify the service ticket for this
758 * local host and therefor didn't get the PAC, we need to remove that
759 * cache entirely now */
761 krb5_ret = ads_kdestroy(cc);
763 DEBUG(3,("winbindd_raw_kerberos_login: "
764 "could not destroy krb5 credential cache: "
765 "%s\n", error_message(krb5_ret)));
768 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
769 DEBUG(3,("winbindd_raw_kerberos_login: "
770 "could not remove ccache for user %s\n",
776 return NT_STATUS_NOT_SUPPORTED;
777 #endif /* HAVE_KRB5 */
780 /****************************************************************
781 ****************************************************************/
783 bool check_request_flags(uint32_t flags)
785 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
786 WBFLAG_PAM_INFO3_TEXT |
787 WBFLAG_PAM_INFO3_NDR;
789 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
790 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
791 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
792 !(flags & flags_edata) ) {
796 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
802 /****************************************************************
803 ****************************************************************/
805 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
806 struct winbindd_response *resp,
807 uint32_t request_flags,
808 struct netr_SamInfo3 *info3,
809 const char *name_domain,
810 const char *name_user)
814 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
815 memcpy(resp->data.auth.user_session_key,
817 sizeof(resp->data.auth.user_session_key)
821 if (request_flags & WBFLAG_PAM_LMKEY) {
822 memcpy(resp->data.auth.first_8_lm_hash,
823 info3->base.LMSessKey.key,
824 sizeof(resp->data.auth.first_8_lm_hash)
828 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
829 result = append_unix_username(mem_ctx, resp,
830 info3, name_domain, name_user);
831 if (!NT_STATUS_IS_OK(result)) {
832 DEBUG(10,("Failed to append Unix Username: %s\n",
838 /* currently, anything from here on potentially overwrites extra_data. */
840 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
841 result = append_info3_as_ndr(mem_ctx, resp, info3);
842 if (!NT_STATUS_IS_OK(result)) {
843 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
849 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
850 result = append_info3_as_txt(mem_ctx, resp, info3);
851 if (!NT_STATUS_IS_OK(result)) {
852 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
858 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
859 result = append_afs_token(mem_ctx, resp,
860 info3, name_domain, name_user);
861 if (!NT_STATUS_IS_OK(result)) {
862 DEBUG(10,("Failed to append AFS token: %s\n",
871 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
872 struct winbindd_cli_state *state,
873 struct netr_SamInfo3 **info3)
875 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
876 uint16 max_allowed_bad_attempts;
877 fstring name_domain, name_user;
879 enum lsa_SidType type;
880 uchar new_nt_pass[NT_HASH_LEN];
881 const uint8 *cached_nt_pass;
882 const uint8 *cached_salt;
883 struct netr_SamInfo3 *my_info3;
884 time_t kickoff_time, must_change_time;
885 bool password_good = false;
887 struct winbindd_tdc_domain *tdc_domain = NULL;
894 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
896 /* Parse domain and username */
898 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
901 if (!lookup_cached_name(name_domain,
905 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
906 return NT_STATUS_NO_SUCH_USER;
909 if (type != SID_NAME_USER) {
910 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
911 return NT_STATUS_LOGON_FAILURE;
914 result = winbindd_get_creds(domain,
920 if (!NT_STATUS_IS_OK(result)) {
921 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
927 E_md4hash(state->request->data.auth.pass, new_nt_pass);
929 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
930 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
932 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
936 /* In this case we didn't store the nt_hash itself,
937 but the MD5 combination of salt + nt_hash. */
938 uchar salted_hash[NT_HASH_LEN];
939 E_md5hash(cached_salt, new_nt_pass, salted_hash);
941 password_good = (memcmp(cached_nt_pass, salted_hash,
944 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
945 password_good = (memcmp(cached_nt_pass, new_nt_pass,
951 /* User *DOES* know the password, update logon_time and reset
954 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
956 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
957 return NT_STATUS_ACCOUNT_LOCKED_OUT;
960 if (my_info3->base.acct_flags & ACB_DISABLED) {
961 return NT_STATUS_ACCOUNT_DISABLED;
964 if (my_info3->base.acct_flags & ACB_WSTRUST) {
965 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
968 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
969 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
972 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
973 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
976 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
977 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
978 my_info3->base.acct_flags));
979 return NT_STATUS_LOGON_FAILURE;
982 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
983 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
984 return NT_STATUS_ACCOUNT_EXPIRED;
987 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
988 if (must_change_time != 0 && must_change_time < time(NULL)) {
989 /* we allow grace logons when the password has expired */
990 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
991 /* return NT_STATUS_PASSWORD_EXPIRED; */
996 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
997 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
998 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
999 /* used to cope with the case winbindd starting without network. */
1000 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
1003 const char *cc = NULL;
1005 const char *principal_s = NULL;
1006 const char *service = NULL;
1007 const char *user_ccache_file;
1009 if (domain->alt_name == NULL) {
1010 return NT_STATUS_INVALID_PARAMETER;
1013 uid = get_uid_from_request(state->request);
1015 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1016 return NT_STATUS_INVALID_PARAMETER;
1019 cc = generate_krb5_ccache(state->mem_ctx,
1020 state->request->data.auth.krb5_cc_type,
1021 state->request->data.auth.uid,
1024 return NT_STATUS_NO_MEMORY;
1027 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
1028 if (realm == NULL) {
1029 return NT_STATUS_NO_MEMORY;
1032 if (!strupper_m(realm)) {
1033 return NT_STATUS_INVALID_PARAMETER;
1036 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1037 if (principal_s == NULL) {
1038 return NT_STATUS_NO_MEMORY;
1041 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1042 if (service == NULL) {
1043 return NT_STATUS_NO_MEMORY;
1046 if (user_ccache_file != NULL) {
1048 fstrcpy(state->response->data.auth.krb5ccname,
1051 result = add_ccache_to_list(principal_s,
1054 state->request->data.auth.user,
1055 state->request->data.auth.pass,
1059 time(NULL) + lp_winbind_cache_time(),
1060 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1063 if (!NT_STATUS_IS_OK(result)) {
1064 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1065 "to add ccache to list: %s\n",
1066 nt_errstr(result)));
1070 #endif /* HAVE_KRB5 */
1072 /* FIXME: we possibly should handle logon hours as well (does xp when
1073 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1075 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1076 my_info3->base.bad_password_count = 0;
1078 result = winbindd_update_creds_by_info3(domain,
1079 state->request->data.auth.user,
1080 state->request->data.auth.pass,
1082 if (!NT_STATUS_IS_OK(result)) {
1083 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1084 nt_errstr(result)));
1088 return NT_STATUS_OK;
1092 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1093 if (domain->online == false) {
1097 /* failure of this is not critical */
1098 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1099 if (!NT_STATUS_IS_OK(result)) {
1100 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1101 "Won't be able to honour account lockout policies\n"));
1104 /* increase counter */
1105 my_info3->base.bad_password_count++;
1107 if (max_allowed_bad_attempts == 0) {
1112 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1114 uint32 password_properties;
1116 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1117 if (!NT_STATUS_IS_OK(result)) {
1118 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1121 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1122 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1123 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1128 result = winbindd_update_creds_by_info3(domain,
1129 state->request->data.auth.user,
1133 if (!NT_STATUS_IS_OK(result)) {
1134 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1135 nt_errstr(result)));
1138 return NT_STATUS_LOGON_FAILURE;
1141 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1142 struct winbindd_cli_state *state,
1143 struct netr_SamInfo3 **info3)
1145 struct winbindd_domain *contact_domain;
1146 fstring name_domain, name_user;
1149 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1151 /* Parse domain and username */
1153 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1155 /* what domain should we contact? */
1158 if (!(contact_domain = find_domain_from_name(name_domain))) {
1159 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1160 state->request->data.auth.user, name_domain, name_user, name_domain));
1161 result = NT_STATUS_NO_SUCH_USER;
1166 if (is_myname(name_domain)) {
1167 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1168 result = NT_STATUS_NO_SUCH_USER;
1172 contact_domain = find_domain_from_name(name_domain);
1173 if (contact_domain == NULL) {
1174 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1175 state->request->data.auth.user, name_domain, name_user, name_domain));
1177 result = NT_STATUS_NO_SUCH_USER;
1182 if (contact_domain->initialized &&
1183 contact_domain->active_directory) {
1187 if (!contact_domain->initialized) {
1188 init_dc_connection(contact_domain);
1191 if (!contact_domain->active_directory) {
1192 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1193 return NT_STATUS_INVALID_LOGON_TYPE;
1196 result = winbindd_raw_kerberos_login(
1197 state->mem_ctx, contact_domain,
1198 state->request->data.auth.user,
1199 state->request->data.auth.pass,
1200 state->request->data.auth.krb5_cc_type,
1201 get_uid_from_request(state->request),
1202 info3, state->response->data.auth.krb5ccname);
1207 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1208 uint32_t logon_parameters,
1209 const char *domain, const char *user,
1210 const DATA_BLOB *challenge,
1211 const DATA_BLOB *lm_resp,
1212 const DATA_BLOB *nt_resp,
1213 struct netr_SamInfo3 **pinfo3)
1215 struct auth_context *auth_context;
1216 struct auth_serversupplied_info *server_info;
1217 struct auth_usersupplied_info *user_info = NULL;
1218 struct tsocket_address *local;
1219 struct netr_SamInfo3 *info3;
1222 TALLOC_CTX *frame = talloc_stackframe();
1224 rc = tsocket_address_inet_from_strings(frame,
1231 return NT_STATUS_NO_MEMORY;
1233 status = make_user_info(frame, &user_info, user, user, domain, domain,
1234 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1235 NULL, AUTH_PASSWORD_RESPONSE);
1236 if (!NT_STATUS_IS_OK(status)) {
1237 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1242 user_info->logon_parameters = logon_parameters;
1244 /* We don't want any more mapping of the username */
1245 user_info->mapped_state = True;
1247 /* We don't want to come back to winbindd or to do PAM account checks */
1248 user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
1250 status = make_auth_context_fixed(frame, &auth_context, challenge->data);
1252 if (!NT_STATUS_IS_OK(status)) {
1253 DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
1258 status = auth_check_ntlm_password(mem_ctx,
1263 if (!NT_STATUS_IS_OK(status)) {
1268 info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1269 if (info3 == NULL) {
1271 return NT_STATUS_NO_MEMORY;
1274 status = serverinfo_to_SamInfo3(server_info, info3);
1275 if (!NT_STATUS_IS_OK(status)) {
1278 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1279 nt_errstr(status)));
1284 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1285 user, nt_errstr(status)));
1290 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1291 TALLOC_CTX *mem_ctx,
1292 uint32_t logon_parameters,
1294 const char *username,
1295 const char *domainname,
1296 const char *workstation,
1297 const uint8_t chal[8],
1298 DATA_BLOB lm_response,
1299 DATA_BLOB nt_response,
1300 struct netr_SamInfo3 **info3)
1303 int netr_attempts = 0;
1308 struct rpc_pipe_client *netlogon_pipe;
1309 uint8_t authoritative = 0;
1312 ZERO_STRUCTP(info3);
1315 result = cm_connect_netlogon(domain, &netlogon_pipe);
1317 if (!NT_STATUS_IS_OK(result)) {
1318 DEBUG(3,("Could not open handle to NETLOGON pipe "
1319 "(error: %s, attempts: %d)\n",
1320 nt_errstr(result), netr_attempts));
1322 /* After the first retry always close the connection */
1323 if (netr_attempts > 0) {
1324 DEBUG(3, ("This is again a problem for this "
1325 "particular call, forcing the close "
1326 "of this connection\n"));
1327 invalidate_cm_connection(&domain->conn);
1330 /* After the second retry failover to the next DC */
1331 if (netr_attempts > 1) {
1333 * If the netlogon server is not reachable then
1334 * it is possible that the DC is rebuilding
1335 * sysvol and shutdown netlogon for that time.
1336 * We should failover to the next dc.
1338 DEBUG(3, ("This is the third problem for this "
1339 "particular call, adding DC to the "
1340 "negative cache list\n"));
1341 add_failed_connection_entry(domain->name,
1344 saf_delete(domain->name);
1347 /* Only allow 3 retries */
1348 if (netr_attempts < 3) {
1349 DEBUG(3, ("The connection to netlogon "
1350 "failed, retrying\n"));
1359 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1360 netlogon_pipe->binding_handle,
1374 * we increment this after the "feature negotiation"
1375 * for can_do_samlogon_ex and can_do_validation6
1379 /* We have to try a second time as cm_connect_netlogon
1380 might not yet have noticed that the DC has killed
1383 if (!rpccli_is_connected(netlogon_pipe)) {
1388 /* if we get access denied, a possible cause was that we had
1389 and open connection to the DC, but someone changed our
1390 machine account password out from underneath us using 'net
1391 rpc changetrustpw' */
1393 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1394 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1395 "ACCESS_DENIED. Maybe the trust account "
1396 "password was changed and we didn't know it. "
1397 "Killing connections to domain %s\n",
1399 invalidate_cm_connection(&domain->conn);
1403 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1405 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1406 * (no Ex). This happens against old Samba
1407 * DCs, if LogonSamLogonEx() fails with an error
1408 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1410 * The server will log something like this:
1411 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1413 * This sets the whole connection into a fault_state mode
1414 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1416 * This also happens to our retry with LogonSamLogonWithFlags()
1417 * and LogonSamLogon().
1419 * In order to recover from this situation, we need to
1420 * drop the connection.
1422 invalidate_cm_connection(&domain->conn);
1423 result = NT_STATUS_LOGON_FAILURE;
1427 } while ( (attempts < 2) && retry );
1429 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1430 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1431 "returned NT_STATUS_IO_TIMEOUT after the retry."
1432 "Killing connections to domain %s\n",
1434 invalidate_cm_connection(&domain->conn);
1439 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1440 struct winbindd_domain *domain,
1443 uint32_t request_flags,
1444 struct netr_SamInfo3 **info3)
1450 unsigned char local_nt_response[24];
1451 fstring name_domain, name_user;
1453 struct netr_SamInfo3 *my_info3 = NULL;
1457 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1459 /* Parse domain and username */
1461 parse_domain_user(user, name_domain, name_user);
1463 /* do password magic */
1465 generate_random_buffer(chal, sizeof(chal));
1467 if (lp_client_ntlmv2_auth()) {
1468 DATA_BLOB server_chal;
1469 DATA_BLOB names_blob;
1470 server_chal = data_blob_const(chal, 8);
1472 /* note that the 'workgroup' here is for the local
1473 machine. The 'server name' must match the
1474 'workstation' passed to the actual SamLogon call.
1476 names_blob = NTLMv2_generate_names_blob(
1477 mem_ctx, lp_netbios_name(), lp_workgroup());
1479 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1483 &lm_resp, &nt_resp, NULL, NULL)) {
1484 data_blob_free(&names_blob);
1485 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1486 result = NT_STATUS_NO_MEMORY;
1489 data_blob_free(&names_blob);
1491 lm_resp = data_blob_null;
1492 SMBNTencrypt(pass, chal, local_nt_response);
1494 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1495 sizeof(local_nt_response));
1498 if (strequal(name_domain, get_global_sam_name())) {
1499 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1501 result = winbindd_dual_auth_passdb(
1502 mem_ctx, 0, name_domain, name_user,
1503 &chal_blob, &lm_resp, &nt_resp, info3);
1507 /* check authentication loop */
1509 result = winbind_samlogon_retry_loop(domain,
1520 if (!NT_STATUS_IS_OK(result)) {
1524 /* handle the case where a NT4 DC does not fill in the acct_flags in
1525 * the samlogon reply info3. When accurate info3 is required by the
1526 * caller, we look up the account flags ourselve - gd */
1528 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1529 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1531 struct rpc_pipe_client *samr_pipe;
1532 struct policy_handle samr_domain_handle, user_pol;
1533 union samr_UserInfo *info = NULL;
1534 NTSTATUS status_tmp, result_tmp;
1536 struct dcerpc_binding_handle *b;
1538 status_tmp = cm_connect_sam(domain, mem_ctx,
1539 &samr_pipe, &samr_domain_handle);
1541 if (!NT_STATUS_IS_OK(status_tmp)) {
1542 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1543 nt_errstr(status_tmp)));
1547 b = samr_pipe->binding_handle;
1549 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1550 &samr_domain_handle,
1551 MAXIMUM_ALLOWED_ACCESS,
1556 if (!NT_STATUS_IS_OK(status_tmp)) {
1557 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1558 nt_errstr(status_tmp)));
1561 if (!NT_STATUS_IS_OK(result_tmp)) {
1562 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1563 nt_errstr(result_tmp)));
1567 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1573 if (!NT_STATUS_IS_OK(status_tmp)) {
1574 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1575 nt_errstr(status_tmp)));
1576 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1579 if (!NT_STATUS_IS_OK(result_tmp)) {
1580 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1581 nt_errstr(result_tmp)));
1582 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1586 acct_flags = info->info16.acct_flags;
1588 if (acct_flags == 0) {
1589 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1593 my_info3->base.acct_flags = acct_flags;
1595 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1597 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1605 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1606 struct winbindd_cli_state *state)
1608 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1609 NTSTATUS krb5_result = NT_STATUS_OK;
1610 fstring name_domain, name_user;
1612 fstring domain_user;
1613 struct netr_SamInfo3 *info3 = NULL;
1614 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1616 /* Ensure null termination */
1617 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1619 /* Ensure null termination */
1620 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1622 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1623 state->request->data.auth.user));
1625 /* Parse domain and username */
1627 name_map_status = normalize_name_unmap(state->mem_ctx,
1628 state->request->data.auth.user,
1631 /* If the name normalization didnt' actually do anything,
1632 just use the original name */
1634 if (!NT_STATUS_IS_OK(name_map_status) &&
1635 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1637 mapped_user = state->request->data.auth.user;
1640 parse_domain_user(mapped_user, name_domain, name_user);
1642 if ( mapped_user != state->request->data.auth.user ) {
1643 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1644 *lp_winbind_separator(),
1646 strlcpy( state->request->data.auth.user, domain_user,
1647 sizeof(state->request->data.auth.user));
1650 if (!domain->online) {
1651 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1652 if (domain->startup) {
1653 /* Logons are very important to users. If we're offline and
1654 we get a request within the first 30 seconds of startup,
1655 try very hard to find a DC and go online. */
1657 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1658 "request in startup mode.\n", domain->name ));
1660 winbindd_flush_negative_conn_cache(domain);
1661 result = init_dc_connection(domain);
1665 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1667 /* Check for Kerberos authentication */
1668 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1670 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1671 /* save for later */
1672 krb5_result = result;
1675 if (NT_STATUS_IS_OK(result)) {
1676 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1677 goto process_result;
1679 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1682 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1683 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1684 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1685 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1686 set_domain_offline( domain );
1690 /* there are quite some NT_STATUS errors where there is no
1691 * point in retrying with a samlogon, we explictly have to take
1692 * care not to increase the bad logon counter on the DC */
1694 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1695 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1696 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1697 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1698 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1699 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1700 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1701 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1702 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1703 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1707 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1708 DEBUG(3,("falling back to samlogon\n"));
1716 /* Check for Samlogon authentication */
1717 if (domain->online) {
1718 result = winbindd_dual_pam_auth_samlogon(
1719 state->mem_ctx, domain,
1720 state->request->data.auth.user,
1721 state->request->data.auth.pass,
1722 state->request->flags,
1725 if (NT_STATUS_IS_OK(result)) {
1726 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1727 /* add the Krb5 err if we have one */
1728 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1729 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1731 goto process_result;
1734 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1735 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))
1741 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1742 set_domain_offline( domain );
1746 if (domain->online) {
1747 /* We're still online - fail. */
1753 /* Check for Cached logons */
1754 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1755 lp_winbind_offline_logon()) {
1757 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1759 if (NT_STATUS_IS_OK(result)) {
1760 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1761 goto process_result;
1763 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1770 if (NT_STATUS_IS_OK(result)) {
1772 struct dom_sid user_sid;
1774 /* In all codepaths where result == NT_STATUS_OK info3 must have
1775 been initialized. */
1777 result = NT_STATUS_INTERNAL_ERROR;
1781 sid_compose(&user_sid, info3->base.domain_sid,
1784 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1786 netsamlogon_cache_store(name_user, info3);
1788 /* save name_to_sid info as early as possible (only if
1789 this is our primary domain so we don't invalidate
1790 the cache entry by storing the seq_num for the wrong
1792 if ( domain->primary ) {
1793 cache_name2sid(domain, name_domain, name_user,
1794 SID_NAME_USER, &user_sid);
1797 /* Check if the user is in the right group */
1799 result = check_info3_in_group(
1801 state->request->data.auth.require_membership_of_sid);
1802 if (!NT_STATUS_IS_OK(result)) {
1803 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1804 state->request->data.auth.user,
1805 state->request->data.auth.require_membership_of_sid));
1809 result = append_auth_data(state->mem_ctx, state->response,
1810 state->request->flags, info3,
1811 name_domain, name_user);
1812 if (!NT_STATUS_IS_OK(result)) {
1816 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1817 && lp_winbind_offline_logon()) {
1819 result = winbindd_store_creds(domain,
1820 state->request->data.auth.user,
1821 state->request->data.auth.pass,
1825 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1826 struct winbindd_domain *our_domain = find_our_domain();
1828 /* This is not entirely correct I believe, but it is
1829 consistent. Only apply the password policy settings
1830 too warn users for our own domain. Cannot obtain these
1831 from trusted DCs all the time so don't do it at all.
1834 result = NT_STATUS_NOT_SUPPORTED;
1835 if (our_domain == domain ) {
1836 result = fillup_password_policy(
1837 our_domain, state->response);
1840 if (!NT_STATUS_IS_OK(result)
1841 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1843 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1844 domain->name, nt_errstr(result)));
1849 result = NT_STATUS_OK;
1853 /* give us a more useful (more correct?) error code */
1854 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1855 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1856 result = NT_STATUS_NO_LOGON_SERVERS;
1859 set_auth_errors(state->response, result);
1861 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1862 state->request->data.auth.user,
1863 state->response->data.auth.nt_status_string,
1864 state->response->data.auth.pam_error));
1866 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1869 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1870 struct winbindd_cli_state *state)
1873 struct netr_SamInfo3 *info3 = NULL;
1874 const char *name_user = NULL;
1875 const char *name_domain = NULL;
1876 const char *workstation;
1878 DATA_BLOB lm_resp, nt_resp;
1880 /* This is child-only, so no check for privileged access is needed
1883 /* Ensure null termination */
1884 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1885 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1887 name_user = state->request->data.auth_crap.user;
1888 name_domain = state->request->data.auth_crap.domain;
1889 workstation = state->request->data.auth_crap.workstation;
1891 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1892 name_domain, name_user));
1894 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1895 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1896 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1897 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1898 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1899 state->request->data.auth_crap.lm_resp_len,
1900 state->request->data.auth_crap.nt_resp_len));
1901 result = NT_STATUS_INVALID_PARAMETER;
1906 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1907 state->request->data.auth_crap.lm_resp_len);
1909 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1910 nt_resp = data_blob_talloc(state->mem_ctx,
1911 state->request->extra_data.data,
1912 state->request->data.auth_crap.nt_resp_len);
1914 nt_resp = data_blob_talloc(state->mem_ctx,
1915 state->request->data.auth_crap.nt_resp,
1916 state->request->data.auth_crap.nt_resp_len);
1919 if (strequal(name_domain, get_global_sam_name())) {
1920 DATA_BLOB chal_blob = data_blob_const(
1921 state->request->data.auth_crap.chal,
1922 sizeof(state->request->data.auth_crap.chal));
1924 result = winbindd_dual_auth_passdb(
1926 state->request->data.auth_crap.logon_parameters,
1927 name_domain, name_user,
1928 &chal_blob, &lm_resp, &nt_resp, &info3);
1929 goto process_result;
1932 result = winbind_samlogon_retry_loop(domain,
1934 state->request->data.auth_crap.logon_parameters,
1938 /* Bug #3248 - found by Stefan Burkei. */
1939 workstation, /* We carefully set this above so use it... */
1940 state->request->data.auth_crap.chal,
1944 if (!NT_STATUS_IS_OK(result)) {
1950 if (NT_STATUS_IS_OK(result)) {
1951 struct dom_sid user_sid;
1953 sid_compose(&user_sid, info3->base.domain_sid,
1955 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1957 netsamlogon_cache_store(name_user, info3);
1959 /* Check if the user is in the right group */
1961 result = check_info3_in_group(
1963 state->request->data.auth_crap.require_membership_of_sid);
1964 if (!NT_STATUS_IS_OK(result)) {
1965 DEBUG(3, ("User %s is not in the required group (%s), so "
1966 "crap authentication is rejected\n",
1967 state->request->data.auth_crap.user,
1968 state->request->data.auth_crap.require_membership_of_sid));
1972 result = append_auth_data(state->mem_ctx, state->response,
1973 state->request->flags, info3,
1974 name_domain, name_user);
1975 if (!NT_STATUS_IS_OK(result)) {
1982 /* give us a more useful (more correct?) error code */
1983 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1984 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1985 result = NT_STATUS_NO_LOGON_SERVERS;
1988 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1989 result = nt_status_squash(result);
1992 set_auth_errors(state->response, result);
1994 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1995 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1998 state->response->data.auth.nt_status_string,
1999 state->response->data.auth.pam_error));
2001 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2004 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2005 struct winbindd_cli_state *state)
2008 char *newpass = NULL;
2009 struct policy_handle dom_pol;
2010 struct rpc_pipe_client *cli = NULL;
2011 bool got_info = false;
2012 struct samr_DomInfo1 *info = NULL;
2013 struct userPwdChangeFailureInformation *reject = NULL;
2014 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2015 fstring domain, user;
2016 struct dcerpc_binding_handle *b = NULL;
2018 ZERO_STRUCT(dom_pol);
2020 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2021 state->request->data.auth.user));
2023 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2027 /* Change password */
2029 oldpass = state->request->data.chauthtok.oldpass;
2030 newpass = state->request->data.chauthtok.newpass;
2032 /* Initialize reject reason */
2033 state->response->data.auth.reject_reason = Undefined;
2035 /* Get sam handle */
2037 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2039 if (!NT_STATUS_IS_OK(result)) {
2040 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2044 b = cli->binding_handle;
2046 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2053 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2055 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2057 fill_in_password_policy(state->response, info);
2059 state->response->data.auth.reject_reason =
2060 reject->extendedFailureReason;
2065 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2066 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2067 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2068 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2070 /* only fallback when the chgpasswd_user3 call is not supported */
2071 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2072 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2073 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2074 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2076 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2077 nt_errstr(result)));
2079 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2081 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2082 Map to the same status code as Windows 2003. */
2084 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2085 result = NT_STATUS_PASSWORD_RESTRICTION;
2091 if (NT_STATUS_IS_OK(result)
2092 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2093 && lp_winbind_offline_logon()) {
2094 result = winbindd_update_creds_by_name(contact_domain, user,
2096 /* Again, this happens when we login from gdm or xdm
2097 * and the password expires, *BUT* cached crendentials
2098 * doesn't exist. winbindd_update_creds_by_name()
2099 * returns NT_STATUS_NO_SUCH_USER.
2100 * This is not a failure.
2103 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2104 result = NT_STATUS_OK;
2107 if (!NT_STATUS_IS_OK(result)) {
2108 DEBUG(10, ("Failed to store creds: %s\n",
2109 nt_errstr(result)));
2110 goto process_result;
2114 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2116 NTSTATUS policy_ret;
2118 policy_ret = fillup_password_policy(
2119 contact_domain, state->response);
2121 /* failure of this is non critical, it will just provide no
2122 * additional information to the client why the change has
2123 * failed - Guenther */
2125 if (!NT_STATUS_IS_OK(policy_ret)) {
2126 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2127 goto process_result;
2133 if (strequal(contact_domain->name, get_global_sam_name())) {
2134 /* FIXME: internal rpc pipe does not cache handles yet */
2136 if (is_valid_policy_hnd(&dom_pol)) {
2138 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2144 set_auth_errors(state->response, result);
2146 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2147 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2150 state->response->data.auth.nt_status_string,
2151 state->response->data.auth.pam_error));
2153 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2156 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2157 struct winbindd_cli_state *state)
2159 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2161 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2162 state->request->data.logoff.user));
2164 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2165 result = NT_STATUS_OK;
2166 goto process_result;
2169 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2170 result = NT_STATUS_OK;
2171 goto process_result;
2176 if (state->request->data.logoff.uid < 0) {
2177 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2178 goto process_result;
2181 /* what we need here is to find the corresponding krb5 ccache name *we*
2182 * created for a given username and destroy it */
2184 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2185 result = NT_STATUS_OK;
2186 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2187 goto process_result;
2190 if (!ccache_entry_identical(state->request->data.logoff.user,
2191 state->request->data.logoff.uid,
2192 state->request->data.logoff.krb5ccname)) {
2193 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2194 goto process_result;
2197 result = remove_ccache(state->request->data.logoff.user);
2198 if (!NT_STATUS_IS_OK(result)) {
2199 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2200 nt_errstr(result)));
2201 goto process_result;
2205 * Remove any mlock'ed memory creds in the child
2206 * we might be using for krb5 ticket renewal.
2209 winbindd_delete_memory_creds(state->request->data.logoff.user);
2212 result = NT_STATUS_NOT_SUPPORTED;
2218 set_auth_errors(state->response, result);
2220 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2223 /* Change user password with auth crap*/
2225 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2228 DATA_BLOB new_nt_password;
2229 DATA_BLOB old_nt_hash_enc;
2230 DATA_BLOB new_lm_password;
2231 DATA_BLOB old_lm_hash_enc;
2232 fstring domain,user;
2233 struct policy_handle dom_pol;
2234 struct winbindd_domain *contact_domain = domainSt;
2235 struct rpc_pipe_client *cli = NULL;
2236 struct dcerpc_binding_handle *b = NULL;
2238 ZERO_STRUCT(dom_pol);
2240 /* Ensure null termination */
2241 state->request->data.chng_pswd_auth_crap.user[
2242 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2243 state->request->data.chng_pswd_auth_crap.domain[
2244 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2248 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2249 (unsigned long)state->pid,
2250 state->request->data.chng_pswd_auth_crap.domain,
2251 state->request->data.chng_pswd_auth_crap.user));
2253 if (lp_winbind_offline_logon()) {
2254 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2255 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2256 result = NT_STATUS_ACCESS_DENIED;
2260 if (*state->request->data.chng_pswd_auth_crap.domain) {
2261 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2263 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2267 DEBUG(3,("no domain specified with username (%s) - "
2269 state->request->data.chng_pswd_auth_crap.user));
2270 result = NT_STATUS_NO_SUCH_USER;
2275 if (!*domain && lp_winbind_use_default_domain()) {
2276 fstrcpy(domain,lp_workgroup());
2280 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2283 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2284 (unsigned long)state->pid, domain, user));
2286 /* Change password */
2287 new_nt_password = data_blob_const(
2288 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2289 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2291 old_nt_hash_enc = data_blob_const(
2292 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2293 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2295 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2296 new_lm_password = data_blob_const(
2297 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2298 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2300 old_lm_hash_enc = data_blob_const(
2301 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2302 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2304 new_lm_password = data_blob_null;
2305 old_lm_hash_enc = data_blob_null;
2308 /* Get sam handle */
2310 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2311 if (!NT_STATUS_IS_OK(result)) {
2312 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2316 b = cli->binding_handle;
2318 result = rpccli_samr_chng_pswd_auth_crap(
2319 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2320 new_lm_password, old_lm_hash_enc);
2324 if (strequal(contact_domain->name, get_global_sam_name())) {
2325 /* FIXME: internal rpc pipe does not cache handles yet */
2327 if (is_valid_policy_hnd(&dom_pol)) {
2329 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2335 set_auth_errors(state->response, result);
2337 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2338 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2340 state->response->data.auth.nt_status_string,
2341 state->response->data.auth.pam_error));
2343 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2347 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2348 struct PAC_LOGON_INFO **logon_info)
2350 krb5_context krbctx = NULL;
2351 krb5_error_code k5ret;
2353 krb5_kt_cursor cursor;
2354 krb5_keytab_entry entry;
2355 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2358 ZERO_STRUCT(cursor);
2360 k5ret = krb5_init_context(&krbctx);
2362 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2363 error_message(k5ret)));
2364 status = krb5_to_nt_status(k5ret);
2368 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2370 DEBUG(1, ("Failed to get keytab: %s\n",
2371 error_message(k5ret)));
2372 status = krb5_to_nt_status(k5ret);
2376 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2378 DEBUG(1, ("Failed to start seq: %s\n",
2379 error_message(k5ret)));
2380 status = krb5_to_nt_status(k5ret);
2384 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2385 while (k5ret == 0) {
2386 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2388 KRB5_KT_KEY(&entry), NULL, 0,
2390 if (NT_STATUS_IS_OK(status)) {
2393 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2394 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2397 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2399 DEBUG(1, ("Failed to end seq: %s\n",
2400 error_message(k5ret)));
2403 k5ret = krb5_kt_close(krbctx, keytab);
2405 DEBUG(1, ("Failed to close keytab: %s\n",
2406 error_message(k5ret)));
2409 krb5_free_context(krbctx);
2414 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2415 struct netr_SamInfo3 **info3)
2417 struct winbindd_request *req = state->request;
2419 struct PAC_LOGON_INFO *logon_info = NULL;
2422 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2423 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2424 if (!NT_STATUS_IS_OK(result) &&
2425 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2426 DEBUG(1, ("Error during PAC signature verification: %s\n",
2427 nt_errstr(result)));
2432 /* Signature verification succeeded, trust the PAC */
2433 netsamlogon_cache_store(NULL, &logon_info->info3);
2436 /* Try without signature verification */
2437 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2438 NULL, NULL, NULL, 0,
2440 if (!NT_STATUS_IS_OK(result)) {
2441 DEBUG(10, ("Could not extract PAC: %s\n",
2442 nt_errstr(result)));
2447 *info3 = &logon_info->info3;
2449 return NT_STATUS_OK;
2451 #else /* HAVE_KRB5 */
2452 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2453 struct netr_SamInfo3 **info3)
2455 return NT_STATUS_NO_SUCH_USER;
2457 #endif /* HAVE_KRB5 */