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/cli_samr.h"
29 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "../lib/crypto/arcfour.h"
34 #define DBGC_CLASS DBGC_WINBIND
36 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
38 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
39 struct winbindd_cli_state *state,
40 struct netr_SamInfo3 *info3)
45 state->response->data.auth.info3.logon_time =
46 nt_time_to_unix(info3->base.last_logon);
47 state->response->data.auth.info3.logoff_time =
48 nt_time_to_unix(info3->base.last_logoff);
49 state->response->data.auth.info3.kickoff_time =
50 nt_time_to_unix(info3->base.acct_expiry);
51 state->response->data.auth.info3.pass_last_set_time =
52 nt_time_to_unix(info3->base.last_password_change);
53 state->response->data.auth.info3.pass_can_change_time =
54 nt_time_to_unix(info3->base.allow_password_change);
55 state->response->data.auth.info3.pass_must_change_time =
56 nt_time_to_unix(info3->base.force_password_change);
58 state->response->data.auth.info3.logon_count = info3->base.logon_count;
59 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
61 state->response->data.auth.info3.user_rid = info3->base.rid;
62 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
63 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
65 state->response->data.auth.info3.num_groups = info3->base.groups.count;
66 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
68 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
69 state->response->data.auth.info3.num_other_sids = info3->sidcount;
71 fstrcpy(state->response->data.auth.info3.user_name,
72 info3->base.account_name.string);
73 fstrcpy(state->response->data.auth.info3.full_name,
74 info3->base.full_name.string);
75 fstrcpy(state->response->data.auth.info3.logon_script,
76 info3->base.logon_script.string);
77 fstrcpy(state->response->data.auth.info3.profile_path,
78 info3->base.profile_path.string);
79 fstrcpy(state->response->data.auth.info3.home_dir,
80 info3->base.home_directory.string);
81 fstrcpy(state->response->data.auth.info3.dir_drive,
82 info3->base.home_drive.string);
84 fstrcpy(state->response->data.auth.info3.logon_srv,
85 info3->base.logon_server.string);
86 fstrcpy(state->response->data.auth.info3.logon_dom,
87 info3->base.domain.string);
89 ex = talloc_strdup(state->mem_ctx, "");
90 NT_STATUS_HAVE_NO_MEMORY(ex);
92 for (i=0; i < info3->base.groups.count; i++) {
93 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
94 info3->base.groups.rids[i].rid,
95 info3->base.groups.rids[i].attributes);
96 NT_STATUS_HAVE_NO_MEMORY(ex);
99 for (i=0; i < info3->sidcount; i++) {
102 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
103 NT_STATUS_HAVE_NO_MEMORY(sid);
105 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
107 info3->sids[i].attributes);
108 NT_STATUS_HAVE_NO_MEMORY(ex);
113 state->response->extra_data.data = ex;
114 state->response->length += talloc_get_size(ex);
119 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
120 struct winbindd_cli_state *state,
121 struct netr_SamInfo3 *info3)
124 enum ndr_err_code ndr_err;
126 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
127 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
128 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
129 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
130 return ndr_map_error2ntstatus(ndr_err);
133 state->response->extra_data.data = blob.data;
134 state->response->length += blob.length;
139 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
140 struct winbindd_cli_state *state,
141 const struct netr_SamInfo3 *info3,
142 const char *name_domain,
143 const char *name_user)
145 /* We've been asked to return the unix username, per
146 'winbind use default domain' settings and the like */
148 const char *nt_username, *nt_domain;
150 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
152 /* If the server didn't give us one, just use the one
154 nt_domain = name_domain;
157 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
159 /* If the server didn't give us one, just use the one
161 nt_username = name_user;
164 fill_domain_username(state->response->data.auth.unix_username,
165 nt_domain, nt_username, true);
167 DEBUG(5,("Setting unix username to [%s]\n",
168 state->response->data.auth.unix_username));
173 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
174 struct winbindd_cli_state *state,
175 const struct netr_SamInfo3 *info3,
176 const char *name_domain,
177 const char *name_user)
179 char *afsname = NULL;
183 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
184 if (afsname == NULL) {
185 return NT_STATUS_NO_MEMORY;
188 afsname = talloc_string_sub(mem_ctx,
189 lp_afs_username_map(),
191 afsname = talloc_string_sub(mem_ctx, afsname,
193 afsname = talloc_string_sub(mem_ctx, afsname,
200 sid_compose(&user_sid, info3->base.domain_sid,
202 sid_to_fstring(sidstr, &user_sid);
203 afsname = talloc_string_sub(mem_ctx, afsname,
207 if (afsname == NULL) {
208 return NT_STATUS_NO_MEMORY;
213 DEBUG(10, ("Generating token for user %s\n", afsname));
215 cell = strchr(afsname, '@');
218 return NT_STATUS_NO_MEMORY;
224 token = afs_createtoken_str(afsname, cell);
228 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
230 if (state->response->extra_data.data == NULL) {
231 return NT_STATUS_NO_MEMORY;
233 state->response->length +=
234 strlen((const char *)state->response->extra_data.data)+1;
239 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
240 const char *group_sid)
242 * Check whether a user belongs to a group or list of groups.
244 * @param mem_ctx talloc memory context.
245 * @param info3 user information, including group membership info.
246 * @param group_sid One or more groups , separated by commas.
248 * @return NT_STATUS_OK on success,
249 * NT_STATUS_LOGON_FAILURE if the user does not belong,
250 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
253 DOM_SID *require_membership_of_sid;
254 size_t num_require_membership_of_sid;
259 struct nt_user_token *token;
260 TALLOC_CTX *frame = talloc_stackframe();
263 /* Parse the 'required group' SID */
265 if (!group_sid || !group_sid[0]) {
266 /* NO sid supplied, all users may access */
270 token = talloc_zero(talloc_tos(), struct nt_user_token);
272 DEBUG(0, ("talloc failed\n"));
274 return NT_STATUS_NO_MEMORY;
277 num_require_membership_of_sid = 0;
278 require_membership_of_sid = NULL;
282 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
283 if (!string_to_sid(&sid, req_sid)) {
284 DEBUG(0, ("check_info3_in_group: could not parse %s "
285 "as a SID!", req_sid));
287 return NT_STATUS_INVALID_PARAMETER;
290 status = add_sid_to_array(talloc_tos(), &sid,
291 &require_membership_of_sid,
292 &num_require_membership_of_sid);
293 if (!NT_STATUS_IS_OK(status)) {
294 DEBUG(0, ("add_sid_to_array failed\n"));
300 status = sid_array_from_info3(talloc_tos(), info3,
304 if (!NT_STATUS_IS_OK(status)) {
309 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
311 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
313 DEBUG(3, ("could not add aliases: %s\n",
319 debug_nt_user_token(DBGC_CLASS, 10, token);
321 for (i=0; i<num_require_membership_of_sid; i++) {
322 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
323 &require_membership_of_sid[i])));
324 if (nt_token_check_sid(&require_membership_of_sid[i],
326 DEBUG(10, ("Access ok\n"));
332 /* Do not distinguish this error from a wrong username/pw */
335 return NT_STATUS_LOGON_FAILURE;
338 struct winbindd_domain *find_auth_domain(uint8_t flags,
339 const char *domain_name)
341 struct winbindd_domain *domain;
344 domain = find_domain_from_name_noinit(domain_name);
345 if (domain == NULL) {
346 DEBUG(3, ("Authentication for domain [%s] refused "
347 "as it is not a trusted domain\n",
353 if (strequal(domain_name, get_global_sam_name())) {
354 return find_domain_from_name_noinit(domain_name);
357 /* we can auth against trusted domains */
358 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
359 domain = find_domain_from_name_noinit(domain_name);
360 if (domain == NULL) {
361 DEBUG(3, ("Authentication for domain [%s] skipped "
362 "as it is not a trusted domain\n",
369 return find_our_domain();
372 static void fill_in_password_policy(struct winbindd_response *r,
373 const struct samr_DomInfo1 *p)
375 r->data.auth.policy.min_length_password =
376 p->min_password_length;
377 r->data.auth.policy.password_history =
378 p->password_history_length;
379 r->data.auth.policy.password_properties =
380 p->password_properties;
381 r->data.auth.policy.expire =
382 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
383 r->data.auth.policy.min_passwordage =
384 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
387 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
388 struct winbindd_cli_state *state)
390 struct winbindd_methods *methods;
391 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
392 struct samr_DomInfo1 password_policy;
394 if ( !winbindd_can_contact_domain( domain ) ) {
395 DEBUG(5,("fillup_password_policy: No inbound trust to "
396 "contact domain %s\n", domain->name));
397 return NT_STATUS_NOT_SUPPORTED;
400 methods = domain->methods;
402 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
403 if (NT_STATUS_IS_ERR(status)) {
407 fill_in_password_policy(state->response, &password_policy);
412 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
414 uint16 *lockout_threshold)
416 struct winbindd_methods *methods;
417 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
418 struct samr_DomInfo12 lockout_policy;
420 *lockout_threshold = 0;
422 methods = domain->methods;
424 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
425 if (NT_STATUS_IS_ERR(status)) {
429 *lockout_threshold = lockout_policy.lockout_threshold;
434 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
436 uint32 *password_properties)
438 struct winbindd_methods *methods;
439 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
440 struct samr_DomInfo1 password_policy;
442 *password_properties = 0;
444 methods = domain->methods;
446 status = methods->password_policy(domain, mem_ctx, &password_policy);
447 if (NT_STATUS_IS_ERR(status)) {
451 *password_properties = password_policy.password_properties;
458 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
461 bool *internal_ccache)
463 /* accept FILE and WRFILE as krb5_cc_type from the client and then
464 * build the full ccname string based on the user's uid here -
467 const char *gen_cc = NULL;
469 *internal_ccache = true;
475 if (!type || type[0] == '\0') {
479 if (strequal(type, "FILE")) {
480 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
481 } else if (strequal(type, "WRFILE")) {
482 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
484 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
488 *internal_ccache = false;
492 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
495 if (gen_cc == NULL) {
496 DEBUG(0,("out of memory\n"));
500 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
505 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
507 const char *type = state->request->data.auth.krb5_cc_type;
509 state->response->data.auth.krb5ccname[0] = '\0';
511 if (type[0] == '\0') {
515 if (!strequal(type, "FILE") &&
516 !strequal(type, "WRFILE")) {
517 DEBUG(10,("won't return krbccname for a %s type ccache\n",
522 fstrcpy(state->response->data.auth.krb5ccname, cc);
527 uid_t get_uid_from_request(struct winbindd_request *request)
531 uid = request->data.auth.uid;
534 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
540 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
542 return get_uid_from_request(state->request);
545 /**********************************************************************
546 Authenticate a user with a clear text password using Kerberos and fill up
548 **********************************************************************/
550 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
551 struct winbindd_cli_state *state,
552 struct netr_SamInfo3 **info3)
555 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
556 krb5_error_code krb5_ret;
557 const char *cc = NULL;
558 const char *principal_s = NULL;
559 const char *service = NULL;
561 fstring name_domain, name_user;
562 time_t ticket_lifetime = 0;
563 time_t renewal_until = 0;
566 time_t time_offset = 0;
567 bool internal_ccache = true;
568 struct PAC_LOGON_INFO *logon_info = NULL;
573 * prepare a krb5_cc_cache string for the user */
575 uid = get_uid_from_state(state);
577 DEBUG(0,("no valid uid\n"));
580 cc = generate_krb5_ccache(state->mem_ctx,
581 state->request->data.auth.krb5_cc_type,
582 state->request->data.auth.uid,
585 return NT_STATUS_NO_MEMORY;
590 * get kerberos properties */
592 if (domain->private_data) {
593 ads = (ADS_STRUCT *)domain->private_data;
594 time_offset = ads->auth.time_offset;
599 * do kerberos auth and setup ccache as the user */
601 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
603 realm = domain->alt_name;
606 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
607 if (principal_s == NULL) {
608 return NT_STATUS_NO_MEMORY;
611 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
612 if (service == NULL) {
613 return NT_STATUS_NO_MEMORY;
616 /* if this is a user ccache, we need to act as the user to let the krb5
617 * library handle the chown, etc. */
619 /************************ ENTERING NON-ROOT **********************/
621 if (!internal_ccache) {
622 set_effective_uid(uid);
623 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
626 result = kerberos_return_pac(state->mem_ctx,
628 state->request->data.auth.pass,
635 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
638 if (!internal_ccache) {
639 gain_root_privilege();
642 /************************ RETURNED TO ROOT **********************/
644 if (!NT_STATUS_IS_OK(result)) {
648 *info3 = &logon_info->info3;
650 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
653 /* if we had a user's ccache then return that string for the pam
656 if (!internal_ccache) {
658 setup_return_cc_name(state, cc);
660 result = add_ccache_to_list(principal_s,
663 state->request->data.auth.user,
671 if (!NT_STATUS_IS_OK(result)) {
672 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
677 /* need to delete the memory cred cache, it is not used anymore */
679 krb5_ret = ads_kdestroy(cc);
681 DEBUG(3,("winbindd_raw_kerberos_login: "
682 "could not destroy krb5 credential cache: "
683 "%s\n", error_message(krb5_ret)));
692 /* we could have created a new credential cache with a valid tgt in it
693 * but we werent able to get or verify the service ticket for this
694 * local host and therefor didn't get the PAC, we need to remove that
695 * cache entirely now */
697 krb5_ret = ads_kdestroy(cc);
699 DEBUG(3,("winbindd_raw_kerberos_login: "
700 "could not destroy krb5 credential cache: "
701 "%s\n", error_message(krb5_ret)));
704 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
705 DEBUG(3,("winbindd_raw_kerberos_login: "
706 "could not remove ccache for user %s\n",
707 state->request->data.auth.user));
712 return NT_STATUS_NOT_SUPPORTED;
713 #endif /* HAVE_KRB5 */
716 /****************************************************************
717 ****************************************************************/
719 bool check_request_flags(uint32_t flags)
721 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
722 WBFLAG_PAM_INFO3_TEXT |
723 WBFLAG_PAM_INFO3_NDR;
725 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
726 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
727 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
728 !(flags & flags_edata) ) {
732 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
738 /****************************************************************
739 ****************************************************************/
741 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
742 struct netr_SamInfo3 *info3,
743 const char *name_domain,
744 const char *name_user)
747 uint32_t flags = state->request->flags;
749 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
750 memcpy(state->response->data.auth.user_session_key,
752 sizeof(state->response->data.auth.user_session_key)
756 if (flags & WBFLAG_PAM_LMKEY) {
757 memcpy(state->response->data.auth.first_8_lm_hash,
758 info3->base.LMSessKey.key,
759 sizeof(state->response->data.auth.first_8_lm_hash)
763 if (flags & WBFLAG_PAM_UNIX_NAME) {
764 result = append_unix_username(state->mem_ctx, state, info3,
765 name_domain, name_user);
766 if (!NT_STATUS_IS_OK(result)) {
767 DEBUG(10,("Failed to append Unix Username: %s\n",
773 /* currently, anything from here on potentially overwrites extra_data. */
775 if (flags & WBFLAG_PAM_INFO3_NDR) {
776 result = append_info3_as_ndr(state->mem_ctx, state, info3);
777 if (!NT_STATUS_IS_OK(result)) {
778 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
784 if (flags & WBFLAG_PAM_INFO3_TEXT) {
785 result = append_info3_as_txt(state->mem_ctx, state, info3);
786 if (!NT_STATUS_IS_OK(result)) {
787 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
793 if (flags & WBFLAG_PAM_AFS_TOKEN) {
794 result = append_afs_token(state->mem_ctx, state, info3,
795 name_domain, name_user);
796 if (!NT_STATUS_IS_OK(result)) {
797 DEBUG(10,("Failed to append AFS token: %s\n",
806 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
807 struct winbindd_cli_state *state,
808 struct netr_SamInfo3 **info3)
810 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
811 uint16 max_allowed_bad_attempts;
812 fstring name_domain, name_user;
814 enum lsa_SidType type;
815 uchar new_nt_pass[NT_HASH_LEN];
816 const uint8 *cached_nt_pass;
817 const uint8 *cached_salt;
818 struct netr_SamInfo3 *my_info3;
819 time_t kickoff_time, must_change_time;
820 bool password_good = false;
822 struct winbindd_tdc_domain *tdc_domain = NULL;
829 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
831 /* Parse domain and username */
833 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
836 if (!lookup_cached_name(state->mem_ctx,
841 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
842 return NT_STATUS_NO_SUCH_USER;
845 if (type != SID_NAME_USER) {
846 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
847 return NT_STATUS_LOGON_FAILURE;
850 result = winbindd_get_creds(domain,
856 if (!NT_STATUS_IS_OK(result)) {
857 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
863 E_md4hash(state->request->data.auth.pass, new_nt_pass);
865 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
866 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
868 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
872 /* In this case we didn't store the nt_hash itself,
873 but the MD5 combination of salt + nt_hash. */
874 uchar salted_hash[NT_HASH_LEN];
875 E_md5hash(cached_salt, new_nt_pass, salted_hash);
877 password_good = (memcmp(cached_nt_pass, salted_hash,
880 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
881 password_good = (memcmp(cached_nt_pass, new_nt_pass,
887 /* User *DOES* know the password, update logon_time and reset
890 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
892 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
893 return NT_STATUS_ACCOUNT_LOCKED_OUT;
896 if (my_info3->base.acct_flags & ACB_DISABLED) {
897 return NT_STATUS_ACCOUNT_DISABLED;
900 if (my_info3->base.acct_flags & ACB_WSTRUST) {
901 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
904 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
905 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
908 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
909 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
912 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
913 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
914 my_info3->base.acct_flags));
915 return NT_STATUS_LOGON_FAILURE;
918 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
919 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
920 return NT_STATUS_ACCOUNT_EXPIRED;
923 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
924 if (must_change_time != 0 && must_change_time < time(NULL)) {
925 /* we allow grace logons when the password has expired */
926 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
927 /* return NT_STATUS_PASSWORD_EXPIRED; */
932 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
933 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
934 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
935 /* used to cope with the case winbindd starting without network. */
936 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
939 const char *cc = NULL;
941 const char *principal_s = NULL;
942 const char *service = NULL;
943 bool internal_ccache = false;
945 uid = get_uid_from_state(state);
947 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
948 return NT_STATUS_INVALID_PARAMETER;
951 cc = generate_krb5_ccache(state->mem_ctx,
952 state->request->data.auth.krb5_cc_type,
953 state->request->data.auth.uid,
956 return NT_STATUS_NO_MEMORY;
959 realm = domain->alt_name;
962 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
963 if (principal_s == NULL) {
964 return NT_STATUS_NO_MEMORY;
967 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
968 if (service == NULL) {
969 return NT_STATUS_NO_MEMORY;
972 if (!internal_ccache) {
974 setup_return_cc_name(state, cc);
976 result = add_ccache_to_list(principal_s,
979 state->request->data.auth.user,
983 time(NULL) + lp_winbind_cache_time(),
984 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
987 if (!NT_STATUS_IS_OK(result)) {
988 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
989 "to add ccache to list: %s\n",
994 #endif /* HAVE_KRB5 */
996 /* FIXME: we possibly should handle logon hours as well (does xp when
997 * offline?) see auth/auth_sam.c:sam_account_ok for details */
999 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1000 my_info3->base.bad_password_count = 0;
1002 result = winbindd_update_creds_by_info3(domain,
1004 state->request->data.auth.user,
1005 state->request->data.auth.pass,
1007 if (!NT_STATUS_IS_OK(result)) {
1008 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1009 nt_errstr(result)));
1013 return NT_STATUS_OK;
1017 /* User does *NOT* know the correct password, modify info3 accordingly */
1019 /* failure of this is not critical */
1020 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1021 if (!NT_STATUS_IS_OK(result)) {
1022 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1023 "Won't be able to honour account lockout policies\n"));
1026 /* increase counter */
1027 my_info3->base.bad_password_count++;
1029 if (max_allowed_bad_attempts == 0) {
1034 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1036 uint32 password_properties;
1038 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1039 if (!NT_STATUS_IS_OK(result)) {
1040 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1043 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1044 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1045 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1050 result = winbindd_update_creds_by_info3(domain,
1052 state->request->data.auth.user,
1056 if (!NT_STATUS_IS_OK(result)) {
1057 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1058 nt_errstr(result)));
1061 return NT_STATUS_LOGON_FAILURE;
1064 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1065 struct winbindd_cli_state *state,
1066 struct netr_SamInfo3 **info3)
1068 struct winbindd_domain *contact_domain;
1069 fstring name_domain, name_user;
1072 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1074 /* Parse domain and username */
1076 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1078 /* what domain should we contact? */
1081 if (!(contact_domain = find_domain_from_name(name_domain))) {
1082 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1083 state->request->data.auth.user, name_domain, name_user, name_domain));
1084 result = NT_STATUS_NO_SUCH_USER;
1089 if (is_myname(name_domain)) {
1090 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1091 result = NT_STATUS_NO_SUCH_USER;
1095 contact_domain = find_domain_from_name(name_domain);
1096 if (contact_domain == NULL) {
1097 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1098 state->request->data.auth.user, name_domain, name_user, name_domain));
1100 contact_domain = find_our_domain();
1104 if (contact_domain->initialized &&
1105 contact_domain->active_directory) {
1109 if (!contact_domain->initialized) {
1110 init_dc_connection(contact_domain);
1113 if (!contact_domain->active_directory) {
1114 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1115 return NT_STATUS_INVALID_LOGON_TYPE;
1118 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1123 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1124 const char *domain, const char *user,
1125 const DATA_BLOB *challenge,
1126 const DATA_BLOB *lm_resp,
1127 const DATA_BLOB *nt_resp,
1128 struct netr_SamInfo3 **pinfo3)
1130 struct auth_usersupplied_info *user_info = NULL;
1131 struct auth_serversupplied_info *server_info = NULL;
1132 struct netr_SamInfo3 *info3;
1135 status = make_user_info(&user_info, user, user, domain, domain,
1136 global_myname(), lm_resp, nt_resp, NULL, NULL,
1138 if (!NT_STATUS_IS_OK(status)) {
1139 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1143 status = check_sam_security(challenge, talloc_tos(), user_info,
1145 free_user_info(&user_info);
1147 if (!NT_STATUS_IS_OK(status)) {
1148 DEBUG(10, ("check_ntlm_password failed: %s\n",
1149 nt_errstr(status)));
1153 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1154 if (info3 == NULL) {
1155 return NT_STATUS_NO_MEMORY;
1158 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1159 if (!NT_STATUS_IS_OK(status)) {
1160 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1161 nt_errstr(status)));
1165 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1167 return NT_STATUS_OK;
1170 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1171 TALLOC_CTX *mem_ctx,
1172 uint32 logon_parameters,
1174 const char *username,
1176 const char *workstation,
1177 const uint8 chal[8],
1178 DATA_BLOB lm_response,
1179 DATA_BLOB nt_response,
1180 struct netr_SamInfo3 **info3);
1182 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1183 struct winbindd_cli_state *state,
1184 struct netr_SamInfo3 **info3)
1187 struct rpc_pipe_client *netlogon_pipe;
1192 unsigned char local_lm_response[24];
1193 unsigned char local_nt_response[24];
1194 fstring name_domain, name_user;
1197 struct netr_SamInfo3 *my_info3 = NULL;
1201 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1203 /* Parse domain and username */
1205 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1207 /* do password magic */
1209 generate_random_buffer(chal, sizeof(chal));
1211 if (lp_client_ntlmv2_auth()) {
1212 DATA_BLOB server_chal;
1213 DATA_BLOB names_blob;
1214 DATA_BLOB nt_response;
1215 DATA_BLOB lm_response;
1216 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1218 /* note that the 'workgroup' here is a best guess - we don't know
1219 the server's domain at this point. The 'server name' is also
1222 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1224 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1225 state->request->data.auth.pass,
1228 &lm_response, &nt_response, NULL, NULL)) {
1229 data_blob_free(&names_blob);
1230 data_blob_free(&server_chal);
1231 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1232 result = NT_STATUS_NO_MEMORY;
1235 data_blob_free(&names_blob);
1236 data_blob_free(&server_chal);
1237 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1238 lm_response.length);
1239 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1240 nt_response.length);
1241 data_blob_free(&lm_response);
1242 data_blob_free(&nt_response);
1245 if (lp_client_lanman_auth()
1246 && SMBencrypt(state->request->data.auth.pass,
1248 local_lm_response)) {
1249 lm_resp = data_blob_talloc(state->mem_ctx,
1251 sizeof(local_lm_response));
1253 lm_resp = data_blob_null;
1255 SMBNTencrypt(state->request->data.auth.pass,
1259 nt_resp = data_blob_talloc(state->mem_ctx,
1261 sizeof(local_nt_response));
1264 if (strequal(name_domain, get_global_sam_name())) {
1265 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1267 result = winbindd_dual_auth_passdb(
1268 state->mem_ctx, name_domain, name_user,
1269 &chal_blob, &lm_resp, &nt_resp, info3);
1273 /* check authentication loop */
1276 netlogon_fn_t logon_fn;
1278 ZERO_STRUCTP(my_info3);
1281 result = cm_connect_netlogon(domain, &netlogon_pipe);
1283 if (!NT_STATUS_IS_OK(result)) {
1284 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1288 /* It is really important to try SamLogonEx here,
1289 * because in a clustered environment, we want to use
1290 * one machine account from multiple physical
1293 * With a normal SamLogon call, we must keep the
1294 * credentials chain updated and intact between all
1295 * users of the machine account (which would imply
1296 * cross-node communication for every NTLM logon).
1298 * (The credentials chain is not per NETLOGON pipe
1299 * connection, but globally on the server/client pair
1302 * When using SamLogonEx, the credentials are not
1303 * supplied, but the session key is implied by the
1304 * wrapping SamLogon context.
1306 * -- abartlet 21 April 2008
1309 logon_fn = domain->can_do_samlogon_ex
1310 ? rpccli_netlogon_sam_network_logon_ex
1311 : rpccli_netlogon_sam_network_logon;
1313 result = logon_fn(netlogon_pipe,
1316 domain->dcname, /* server name */
1317 name_user, /* user name */
1318 name_domain, /* target domain */
1319 global_myname(), /* workstation */
1326 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1327 && domain->can_do_samlogon_ex) {
1328 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1329 "retrying with NetSamLogon\n"));
1330 domain->can_do_samlogon_ex = false;
1335 /* We have to try a second time as cm_connect_netlogon
1336 might not yet have noticed that the DC has killed
1339 if (!rpccli_is_connected(netlogon_pipe)) {
1344 /* if we get access denied, a possible cause was that we had
1345 and open connection to the DC, but someone changed our
1346 machine account password out from underneath us using 'net
1347 rpc changetrustpw' */
1349 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1350 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1351 "ACCESS_DENIED. Maybe the trust account "
1352 "password was changed and we didn't know it. "
1353 "Killing connections to domain %s\n",
1355 invalidate_cm_connection(&domain->conn);
1359 } while ( (attempts < 2) && retry );
1361 /* handle the case where a NT4 DC does not fill in the acct_flags in
1362 * the samlogon reply info3. When accurate info3 is required by the
1363 * caller, we look up the account flags ourselve - gd */
1365 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1366 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1368 struct rpc_pipe_client *samr_pipe;
1369 struct policy_handle samr_domain_handle, user_pol;
1370 union samr_UserInfo *info = NULL;
1371 NTSTATUS status_tmp;
1374 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1375 &samr_pipe, &samr_domain_handle);
1377 if (!NT_STATUS_IS_OK(status_tmp)) {
1378 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1379 nt_errstr(status_tmp)));
1383 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1384 &samr_domain_handle,
1385 MAXIMUM_ALLOWED_ACCESS,
1389 if (!NT_STATUS_IS_OK(status_tmp)) {
1390 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1391 nt_errstr(status_tmp)));
1395 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1400 if (!NT_STATUS_IS_OK(status_tmp)) {
1401 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1402 nt_errstr(status_tmp)));
1403 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1407 acct_flags = info->info16.acct_flags;
1409 if (acct_flags == 0) {
1410 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1414 my_info3->base.acct_flags = acct_flags;
1416 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1418 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1426 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1427 struct winbindd_cli_state *state)
1429 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1430 NTSTATUS krb5_result = NT_STATUS_OK;
1431 fstring name_domain, name_user;
1433 fstring domain_user;
1434 struct netr_SamInfo3 *info3 = NULL;
1435 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1437 /* Ensure null termination */
1438 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1440 /* Ensure null termination */
1441 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1443 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1444 state->request->data.auth.user));
1446 if (!check_request_flags(state->request->flags)) {
1447 result = NT_STATUS_INVALID_PARAMETER_MIX;
1451 /* Parse domain and username */
1453 name_map_status = normalize_name_unmap(state->mem_ctx,
1454 state->request->data.auth.user,
1457 /* If the name normalization didnt' actually do anything,
1458 just use the original name */
1460 if (!NT_STATUS_IS_OK(name_map_status) &&
1461 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1463 mapped_user = state->request->data.auth.user;
1466 parse_domain_user(mapped_user, name_domain, name_user);
1468 if ( mapped_user != state->request->data.auth.user ) {
1469 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1470 safe_strcpy( state->request->data.auth.user, domain_user,
1471 sizeof(state->request->data.auth.user)-1 );
1474 if (domain->online == false) {
1475 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1476 if (domain->startup) {
1477 /* Logons are very important to users. If we're offline and
1478 we get a request within the first 30 seconds of startup,
1479 try very hard to find a DC and go online. */
1481 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1482 "request in startup mode.\n", domain->name ));
1484 winbindd_flush_negative_conn_cache(domain);
1485 result = init_dc_connection(domain);
1489 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1491 /* Check for Kerberos authentication */
1492 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1494 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1495 /* save for later */
1496 krb5_result = result;
1499 if (NT_STATUS_IS_OK(result)) {
1500 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1501 goto process_result;
1503 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1506 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1507 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1509 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1510 set_domain_offline( domain );
1514 /* there are quite some NT_STATUS errors where there is no
1515 * point in retrying with a samlogon, we explictly have to take
1516 * care not to increase the bad logon counter on the DC */
1518 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1519 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1520 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1527 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1531 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1532 DEBUG(3,("falling back to samlogon\n"));
1540 /* Check for Samlogon authentication */
1541 if (domain->online) {
1542 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1544 if (NT_STATUS_IS_OK(result)) {
1545 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1546 /* add the Krb5 err if we have one */
1547 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1548 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1550 goto process_result;
1553 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1554 nt_errstr(result)));
1556 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1557 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1558 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1560 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1561 set_domain_offline( domain );
1565 if (domain->online) {
1566 /* We're still online - fail. */
1572 /* Check for Cached logons */
1573 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1574 lp_winbind_offline_logon()) {
1576 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1578 if (NT_STATUS_IS_OK(result)) {
1579 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1580 goto process_result;
1582 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1589 if (NT_STATUS_IS_OK(result)) {
1593 /* In all codepaths where result == NT_STATUS_OK info3 must have
1594 been initialized. */
1596 result = NT_STATUS_INTERNAL_ERROR;
1600 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1601 netsamlogon_cache_store(name_user, info3);
1603 /* save name_to_sid info as early as possible (only if
1604 this is our primary domain so we don't invalidate
1605 the cache entry by storing the seq_num for the wrong
1607 if ( domain->primary ) {
1608 sid_compose(&user_sid, info3->base.domain_sid,
1610 cache_name2sid(domain, name_domain, name_user,
1611 SID_NAME_USER, &user_sid);
1614 /* Check if the user is in the right group */
1616 result = check_info3_in_group(
1618 state->request->data.auth.require_membership_of_sid);
1619 if (!NT_STATUS_IS_OK(result)) {
1620 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1621 state->request->data.auth.user,
1622 state->request->data.auth.require_membership_of_sid));
1626 result = append_auth_data(state, info3, name_domain,
1628 if (!NT_STATUS_IS_OK(result)) {
1632 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1634 if (lp_winbind_offline_logon()) {
1635 result = winbindd_store_creds(domain,
1637 state->request->data.auth.user,
1638 state->request->data.auth.pass,
1644 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1645 struct winbindd_domain *our_domain = find_our_domain();
1647 /* This is not entirely correct I believe, but it is
1648 consistent. Only apply the password policy settings
1649 too warn users for our own domain. Cannot obtain these
1650 from trusted DCs all the time so don't do it at all.
1653 result = NT_STATUS_NOT_SUPPORTED;
1654 if (our_domain == domain ) {
1655 result = fillup_password_policy(our_domain, state);
1658 if (!NT_STATUS_IS_OK(result)
1659 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1661 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1662 domain->name, nt_errstr(result)));
1667 result = NT_STATUS_OK;
1671 /* give us a more useful (more correct?) error code */
1672 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1673 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1674 result = NT_STATUS_NO_LOGON_SERVERS;
1677 set_auth_errors(state->response, result);
1679 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1680 state->request->data.auth.user,
1681 state->response->data.auth.nt_status_string,
1682 state->response->data.auth.pam_error));
1684 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1687 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1688 struct winbindd_cli_state *state)
1691 struct netr_SamInfo3 *info3 = NULL;
1692 struct rpc_pipe_client *netlogon_pipe;
1693 const char *name_user = NULL;
1694 const char *name_domain = NULL;
1695 const char *workstation;
1699 DATA_BLOB lm_resp, nt_resp;
1701 /* This is child-only, so no check for privileged access is needed
1704 /* Ensure null termination */
1705 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1706 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1708 if (!check_request_flags(state->request->flags)) {
1709 result = NT_STATUS_INVALID_PARAMETER_MIX;
1713 name_user = state->request->data.auth_crap.user;
1715 if (*state->request->data.auth_crap.domain) {
1716 name_domain = state->request->data.auth_crap.domain;
1717 } else if (lp_winbind_use_default_domain()) {
1718 name_domain = lp_workgroup();
1720 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1722 result = NT_STATUS_NO_SUCH_USER;
1726 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1727 name_domain, name_user));
1729 if (*state->request->data.auth_crap.workstation) {
1730 workstation = state->request->data.auth_crap.workstation;
1732 workstation = global_myname();
1735 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1736 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1737 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1738 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1739 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1740 state->request->data.auth_crap.lm_resp_len,
1741 state->request->data.auth_crap.nt_resp_len));
1742 result = NT_STATUS_INVALID_PARAMETER;
1747 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1748 state->request->data.auth_crap.lm_resp_len);
1750 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1751 nt_resp = data_blob_talloc(state->mem_ctx,
1752 state->request->extra_data.data,
1753 state->request->data.auth_crap.nt_resp_len);
1755 nt_resp = data_blob_talloc(state->mem_ctx,
1756 state->request->data.auth_crap.nt_resp,
1757 state->request->data.auth_crap.nt_resp_len);
1760 if (strequal(name_domain, get_global_sam_name())) {
1761 DATA_BLOB chal_blob = data_blob_const(
1762 state->request->data.auth_crap.chal,
1763 sizeof(state->request->data.auth_crap.chal));
1765 result = winbindd_dual_auth_passdb(
1766 state->mem_ctx, name_domain, name_user,
1767 &chal_blob, &lm_resp, &nt_resp, &info3);
1768 goto process_result;
1772 netlogon_fn_t logon_fn;
1776 netlogon_pipe = NULL;
1777 result = cm_connect_netlogon(domain, &netlogon_pipe);
1779 if (!NT_STATUS_IS_OK(result)) {
1780 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1781 nt_errstr(result)));
1785 logon_fn = domain->can_do_samlogon_ex
1786 ? rpccli_netlogon_sam_network_logon_ex
1787 : rpccli_netlogon_sam_network_logon;
1789 result = logon_fn(netlogon_pipe,
1791 state->request->data.auth_crap.logon_parameters,
1795 /* Bug #3248 - found by Stefan Burkei. */
1796 workstation, /* We carefully set this above so use it... */
1797 state->request->data.auth_crap.chal,
1802 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1803 && domain->can_do_samlogon_ex) {
1804 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1805 "retrying with NetSamLogon\n"));
1806 domain->can_do_samlogon_ex = false;
1813 /* We have to try a second time as cm_connect_netlogon
1814 might not yet have noticed that the DC has killed
1817 if (!rpccli_is_connected(netlogon_pipe)) {
1822 /* if we get access denied, a possible cause was that we had and open
1823 connection to the DC, but someone changed our machine account password
1824 out from underneath us using 'net rpc changetrustpw' */
1826 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1827 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1828 "ACCESS_DENIED. Maybe the trust account "
1829 "password was changed and we didn't know it. "
1830 "Killing connections to domain %s\n",
1832 invalidate_cm_connection(&domain->conn);
1836 } while ( (attempts < 2) && retry );
1840 if (NT_STATUS_IS_OK(result)) {
1842 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1843 netsamlogon_cache_store(name_user, info3);
1845 /* Check if the user is in the right group */
1847 result = check_info3_in_group(
1849 state->request->data.auth_crap.require_membership_of_sid);
1850 if (!NT_STATUS_IS_OK(result)) {
1851 DEBUG(3, ("User %s is not in the required group (%s), so "
1852 "crap authentication is rejected\n",
1853 state->request->data.auth_crap.user,
1854 state->request->data.auth_crap.require_membership_of_sid));
1858 result = append_auth_data(state, info3, name_domain,
1860 if (!NT_STATUS_IS_OK(result)) {
1867 /* give us a more useful (more correct?) error code */
1868 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1869 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1870 result = NT_STATUS_NO_LOGON_SERVERS;
1873 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1874 result = nt_status_squash(result);
1877 set_auth_errors(state->response, result);
1879 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1880 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1883 state->response->data.auth.nt_status_string,
1884 state->response->data.auth.pam_error));
1886 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1889 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1890 struct winbindd_cli_state *state)
1893 char *newpass = NULL;
1894 struct policy_handle dom_pol;
1895 struct rpc_pipe_client *cli;
1896 bool got_info = false;
1897 struct samr_DomInfo1 *info = NULL;
1898 struct userPwdChangeFailureInformation *reject = NULL;
1899 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1900 fstring domain, user;
1902 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1903 state->request->data.auth.user));
1905 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1909 /* Change password */
1911 oldpass = state->request->data.chauthtok.oldpass;
1912 newpass = state->request->data.chauthtok.newpass;
1914 /* Initialize reject reason */
1915 state->response->data.auth.reject_reason = Undefined;
1917 if (strequal(domain, get_global_sam_name())) {
1918 struct samr_CryptPassword new_nt_password;
1919 struct samr_CryptPassword new_lm_password;
1920 struct samr_Password old_nt_hash_enc;
1921 struct samr_Password old_lanman_hash_enc;
1922 enum samPwdChangeReason rejectReason;
1924 uchar old_nt_hash[16];
1925 uchar old_lanman_hash[16];
1926 uchar new_nt_hash[16];
1927 uchar new_lanman_hash[16];
1929 contact_domain = NULL;
1931 E_md4hash(oldpass, old_nt_hash);
1932 E_md4hash(newpass, new_nt_hash);
1934 if (lp_client_lanman_auth() &&
1935 E_deshash(newpass, new_lanman_hash) &&
1936 E_deshash(oldpass, old_lanman_hash)) {
1938 /* E_deshash returns false for 'long' passwords (> 14
1939 DOS chars). This allows us to match Win2k, which
1940 does not store a LM hash for these passwords (which
1941 would reduce the effective password length to 14) */
1943 encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
1944 arcfour_crypt(new_lm_password.data, old_nt_hash, 516);
1945 E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
1947 ZERO_STRUCT(new_lm_password);
1948 ZERO_STRUCT(old_lanman_hash_enc);
1951 encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
1953 arcfour_crypt(new_nt_password.data, old_nt_hash, 516);
1954 E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
1956 result = pass_oem_change(
1958 new_lm_password.data, old_lanman_hash_enc.hash,
1959 new_nt_password.data, old_nt_hash_enc.hash,
1964 /* Get sam handle */
1966 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1968 if (!NT_STATUS_IS_OK(result)) {
1969 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1973 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1980 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1982 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1984 fill_in_password_policy(state->response, info);
1986 state->response->data.auth.reject_reason =
1987 reject->extendedFailureReason;
1992 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1993 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1994 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1995 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1997 /* only fallback when the chgpasswd_user3 call is not supported */
1998 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1999 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2000 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2001 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2003 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2004 nt_errstr(result)));
2006 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2008 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2009 Map to the same status code as Windows 2003. */
2011 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2012 result = NT_STATUS_PASSWORD_RESTRICTION;
2018 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2019 if (lp_winbind_offline_logon()) {
2020 result = winbindd_update_creds_by_name(contact_domain,
2021 state->mem_ctx, user,
2023 /* Again, this happens when we login from gdm or xdm
2024 * and the password expires, *BUT* cached crendentials
2025 * doesn't exist. winbindd_update_creds_by_name()
2026 * returns NT_STATUS_NO_SUCH_USER.
2027 * This is not a failure.
2030 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2031 result = NT_STATUS_OK;
2034 if (!NT_STATUS_IS_OK(result)) {
2035 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2036 goto process_result;
2041 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2043 NTSTATUS policy_ret;
2045 policy_ret = fillup_password_policy(contact_domain, state);
2047 /* failure of this is non critical, it will just provide no
2048 * additional information to the client why the change has
2049 * failed - Guenther */
2051 if (!NT_STATUS_IS_OK(policy_ret)) {
2052 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2053 goto process_result;
2059 set_auth_errors(state->response, result);
2061 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2062 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2065 state->response->data.auth.nt_status_string,
2066 state->response->data.auth.pam_error));
2068 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2071 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2072 struct winbindd_cli_state *state)
2074 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2076 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2077 state->request->data.logoff.user));
2079 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2080 result = NT_STATUS_OK;
2081 goto process_result;
2084 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2085 result = NT_STATUS_OK;
2086 goto process_result;
2091 if (state->request->data.logoff.uid < 0) {
2092 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2093 goto process_result;
2096 /* what we need here is to find the corresponding krb5 ccache name *we*
2097 * created for a given username and destroy it */
2099 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2100 result = NT_STATUS_OK;
2101 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2102 goto process_result;
2105 if (!ccache_entry_identical(state->request->data.logoff.user,
2106 state->request->data.logoff.uid,
2107 state->request->data.logoff.krb5ccname)) {
2108 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2109 goto process_result;
2112 result = remove_ccache(state->request->data.logoff.user);
2113 if (!NT_STATUS_IS_OK(result)) {
2114 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2115 nt_errstr(result)));
2116 goto process_result;
2120 result = NT_STATUS_NOT_SUPPORTED;
2126 set_auth_errors(state->response, result);
2128 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2131 /* Change user password with auth crap*/
2133 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2136 DATA_BLOB new_nt_password;
2137 DATA_BLOB old_nt_hash_enc;
2138 DATA_BLOB new_lm_password;
2139 DATA_BLOB old_lm_hash_enc;
2140 fstring domain,user;
2141 struct policy_handle dom_pol;
2142 struct winbindd_domain *contact_domain = domainSt;
2143 struct rpc_pipe_client *cli;
2145 /* Ensure null termination */
2146 state->request->data.chng_pswd_auth_crap.user[
2147 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2148 state->request->data.chng_pswd_auth_crap.domain[
2149 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2153 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2154 (unsigned long)state->pid,
2155 state->request->data.chng_pswd_auth_crap.domain,
2156 state->request->data.chng_pswd_auth_crap.user));
2158 if (lp_winbind_offline_logon()) {
2159 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2160 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2161 result = NT_STATUS_ACCESS_DENIED;
2165 if (*state->request->data.chng_pswd_auth_crap.domain) {
2166 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2168 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2172 DEBUG(3,("no domain specified with username (%s) - "
2174 state->request->data.chng_pswd_auth_crap.user));
2175 result = NT_STATUS_NO_SUCH_USER;
2180 if (!*domain && lp_winbind_use_default_domain()) {
2181 fstrcpy(domain,(char *)lp_workgroup());
2185 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2188 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2189 (unsigned long)state->pid, domain, user));
2191 if (strequal(domain, get_global_sam_name())) {
2192 enum samPwdChangeReason reject_reason;
2194 result = pass_oem_change(
2196 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2197 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2198 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2199 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2201 DEBUG(10, ("pass_oem_change returned %s\n",
2202 nt_errstr(result)));
2206 /* Change password */
2207 new_nt_password = data_blob_const(
2208 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2209 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2211 old_nt_hash_enc = data_blob_const(
2212 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2213 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2215 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2216 new_lm_password = data_blob_const(
2217 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2218 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2220 old_lm_hash_enc = data_blob_const(
2221 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2222 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2224 new_lm_password.length = 0;
2225 old_lm_hash_enc.length = 0;
2228 /* Get sam handle */
2230 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2231 if (!NT_STATUS_IS_OK(result)) {
2232 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2236 result = rpccli_samr_chng_pswd_auth_crap(
2237 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2238 new_lm_password, old_lm_hash_enc);
2242 set_auth_errors(state->response, result);
2244 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2245 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2247 state->response->data.auth.nt_status_string,
2248 state->response->data.auth.pam_error));
2250 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;