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_samr.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "rpc_client/cli_netlogon.h"
33 #include "../lib/crypto/arcfour.h"
34 #include "../libcli/security/security.h"
36 #include "../librpc/gen_ndr/krb5pac.h"
39 #define DBGC_CLASS DBGC_WINBIND
41 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
43 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
44 struct winbindd_response *resp,
45 struct netr_SamInfo3 *info3)
50 resp->data.auth.info3.logon_time =
51 nt_time_to_unix(info3->base.last_logon);
52 resp->data.auth.info3.logoff_time =
53 nt_time_to_unix(info3->base.last_logoff);
54 resp->data.auth.info3.kickoff_time =
55 nt_time_to_unix(info3->base.acct_expiry);
56 resp->data.auth.info3.pass_last_set_time =
57 nt_time_to_unix(info3->base.last_password_change);
58 resp->data.auth.info3.pass_can_change_time =
59 nt_time_to_unix(info3->base.allow_password_change);
60 resp->data.auth.info3.pass_must_change_time =
61 nt_time_to_unix(info3->base.force_password_change);
63 resp->data.auth.info3.logon_count = info3->base.logon_count;
64 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
66 resp->data.auth.info3.user_rid = info3->base.rid;
67 resp->data.auth.info3.group_rid = info3->base.primary_gid;
68 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
70 resp->data.auth.info3.num_groups = info3->base.groups.count;
71 resp->data.auth.info3.user_flgs = info3->base.user_flags;
73 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
74 resp->data.auth.info3.num_other_sids = info3->sidcount;
76 fstrcpy(resp->data.auth.info3.user_name,
77 info3->base.account_name.string);
78 fstrcpy(resp->data.auth.info3.full_name,
79 info3->base.full_name.string);
80 fstrcpy(resp->data.auth.info3.logon_script,
81 info3->base.logon_script.string);
82 fstrcpy(resp->data.auth.info3.profile_path,
83 info3->base.profile_path.string);
84 fstrcpy(resp->data.auth.info3.home_dir,
85 info3->base.home_directory.string);
86 fstrcpy(resp->data.auth.info3.dir_drive,
87 info3->base.home_drive.string);
89 fstrcpy(resp->data.auth.info3.logon_srv,
90 info3->base.logon_server.string);
91 fstrcpy(resp->data.auth.info3.logon_dom,
92 info3->base.domain.string);
94 ex = talloc_strdup(mem_ctx, "");
95 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->base.groups.count; i++) {
98 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
99 info3->base.groups.rids[i].rid,
100 info3->base.groups.rids[i].attributes);
101 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->sidcount; i++) {
107 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
108 NT_STATUS_HAVE_NO_MEMORY(sid);
110 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
112 info3->sids[i].attributes);
113 NT_STATUS_HAVE_NO_MEMORY(ex);
118 resp->extra_data.data = ex;
119 resp->length += talloc_get_size(ex);
124 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
125 struct winbindd_response *resp,
126 struct netr_SamInfo3 *info3)
129 enum ndr_err_code ndr_err;
131 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
132 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
133 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
134 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
135 return ndr_map_error2ntstatus(ndr_err);
138 resp->extra_data.data = blob.data;
139 resp->length += blob.length;
144 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
145 struct winbindd_response *resp,
146 const struct netr_SamInfo3 *info3,
147 const char *name_domain,
148 const char *name_user)
150 /* We've been asked to return the unix username, per
151 'winbind use default domain' settings and the like */
153 const char *nt_username, *nt_domain;
155 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
157 /* If the server didn't give us one, just use the one
159 nt_domain = name_domain;
162 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
164 /* If the server didn't give us one, just use the one
166 nt_username = name_user;
169 fill_domain_username(resp->data.auth.unix_username,
170 nt_domain, nt_username, true);
172 DEBUG(5, ("Setting unix username to [%s]\n",
173 resp->data.auth.unix_username));
178 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
179 struct winbindd_response *resp,
180 const struct netr_SamInfo3 *info3,
181 const char *name_domain,
182 const char *name_user)
184 char *afsname = NULL;
188 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
189 if (afsname == NULL) {
190 return NT_STATUS_NO_MEMORY;
193 afsname = talloc_string_sub(mem_ctx,
194 lp_afs_username_map(),
196 afsname = talloc_string_sub(mem_ctx, afsname,
198 afsname = talloc_string_sub(mem_ctx, afsname,
202 struct dom_sid user_sid;
205 sid_compose(&user_sid, info3->base.domain_sid,
207 sid_to_fstring(sidstr, &user_sid);
208 afsname = talloc_string_sub(mem_ctx, afsname,
212 if (afsname == NULL) {
213 return NT_STATUS_NO_MEMORY;
218 DEBUG(10, ("Generating token for user %s\n", afsname));
220 cell = strchr(afsname, '@');
223 return NT_STATUS_NO_MEMORY;
229 token = afs_createtoken_str(afsname, cell);
233 resp->extra_data.data = talloc_strdup(mem_ctx, token);
234 if (resp->extra_data.data == NULL) {
235 return NT_STATUS_NO_MEMORY;
237 resp->length += strlen((const char *)resp->extra_data.data)+1;
242 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
243 const char *group_sid)
245 * Check whether a user belongs to a group or list of groups.
247 * @param mem_ctx talloc memory context.
248 * @param info3 user information, including group membership info.
249 * @param group_sid One or more groups , separated by commas.
251 * @return NT_STATUS_OK on success,
252 * NT_STATUS_LOGON_FAILURE if the user does not belong,
253 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
256 struct dom_sid *require_membership_of_sid;
257 uint32_t num_require_membership_of_sid;
262 struct security_token *token;
263 TALLOC_CTX *frame = talloc_stackframe();
266 /* Parse the 'required group' SID */
268 if (!group_sid || !group_sid[0]) {
269 /* NO sid supplied, all users may access */
273 token = talloc_zero(talloc_tos(), struct security_token);
275 DEBUG(0, ("talloc failed\n"));
277 return NT_STATUS_NO_MEMORY;
280 num_require_membership_of_sid = 0;
281 require_membership_of_sid = NULL;
285 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
286 if (!string_to_sid(&sid, req_sid)) {
287 DEBUG(0, ("check_info3_in_group: could not parse %s "
288 "as a SID!", req_sid));
290 return NT_STATUS_INVALID_PARAMETER;
293 status = add_sid_to_array(talloc_tos(), &sid,
294 &require_membership_of_sid,
295 &num_require_membership_of_sid);
296 if (!NT_STATUS_IS_OK(status)) {
297 DEBUG(0, ("add_sid_to_array failed\n"));
303 status = sid_array_from_info3(talloc_tos(), info3,
307 if (!NT_STATUS_IS_OK(status)) {
312 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
314 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
316 DEBUG(3, ("could not add aliases: %s\n",
322 security_token_debug(DBGC_CLASS, 10, token);
324 for (i=0; i<num_require_membership_of_sid; i++) {
325 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
326 &require_membership_of_sid[i])));
327 if (nt_token_check_sid(&require_membership_of_sid[i],
329 DEBUG(10, ("Access ok\n"));
335 /* Do not distinguish this error from a wrong username/pw */
338 return NT_STATUS_LOGON_FAILURE;
341 struct winbindd_domain *find_auth_domain(uint8_t flags,
342 const char *domain_name)
344 struct winbindd_domain *domain;
347 domain = find_domain_from_name_noinit(domain_name);
348 if (domain == NULL) {
349 DEBUG(3, ("Authentication for domain [%s] refused "
350 "as it is not a trusted domain\n",
356 if (strequal(domain_name, get_global_sam_name())) {
357 return find_domain_from_name_noinit(domain_name);
360 /* we can auth against trusted domains */
361 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
362 domain = find_domain_from_name_noinit(domain_name);
363 if (domain == NULL) {
364 DEBUG(3, ("Authentication for domain [%s] skipped "
365 "as it is not a trusted domain\n",
372 return find_our_domain();
375 static void fill_in_password_policy(struct winbindd_response *r,
376 const struct samr_DomInfo1 *p)
378 r->data.auth.policy.min_length_password =
379 p->min_password_length;
380 r->data.auth.policy.password_history =
381 p->password_history_length;
382 r->data.auth.policy.password_properties =
383 p->password_properties;
384 r->data.auth.policy.expire =
385 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
386 r->data.auth.policy.min_passwordage =
387 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
390 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
391 struct winbindd_response *response)
393 TALLOC_CTX *frame = talloc_stackframe();
394 struct winbindd_methods *methods;
396 struct samr_DomInfo1 password_policy;
398 if ( !winbindd_can_contact_domain( domain ) ) {
399 DEBUG(5,("fillup_password_policy: No inbound trust to "
400 "contact domain %s\n", domain->name));
401 status = NT_STATUS_NOT_SUPPORTED;
405 methods = domain->methods;
407 status = methods->password_policy(domain, talloc_tos(), &password_policy);
408 if (NT_STATUS_IS_ERR(status)) {
412 fill_in_password_policy(response, &password_policy);
419 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
421 uint16 *lockout_threshold)
423 struct winbindd_methods *methods;
424 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
425 struct samr_DomInfo12 lockout_policy;
427 *lockout_threshold = 0;
429 methods = domain->methods;
431 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
432 if (NT_STATUS_IS_ERR(status)) {
436 *lockout_threshold = lockout_policy.lockout_threshold;
441 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
443 uint32 *password_properties)
445 struct winbindd_methods *methods;
446 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
447 struct samr_DomInfo1 password_policy;
449 *password_properties = 0;
451 methods = domain->methods;
453 status = methods->password_policy(domain, mem_ctx, &password_policy);
454 if (NT_STATUS_IS_ERR(status)) {
458 *password_properties = password_policy.password_properties;
465 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
468 const char **user_ccache_file)
470 /* accept FILE and WRFILE as krb5_cc_type from the client and then
471 * build the full ccname string based on the user's uid here -
474 const char *gen_cc = NULL;
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(
479 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
481 if (strequal(type, "WRFILE")) {
482 gen_cc = talloc_asprintf(
483 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
487 *user_ccache_file = gen_cc;
489 if (gen_cc == NULL) {
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
492 if (gen_cc == NULL) {
493 DEBUG(0,("out of memory\n"));
497 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
498 (*user_ccache_file == NULL) ? " (internal)":""));
505 uid_t get_uid_from_request(struct winbindd_request *request)
509 uid = request->data.auth.uid;
512 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
518 /**********************************************************************
519 Authenticate a user with a clear text password using Kerberos and fill up
521 **********************************************************************/
523 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
524 struct winbindd_domain *domain,
527 const char *krb5_cc_type,
529 struct netr_SamInfo3 **info3,
533 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
534 krb5_error_code krb5_ret;
535 const char *cc = NULL;
536 const char *principal_s = NULL;
537 const char *service = NULL;
539 fstring name_domain, name_user;
540 time_t ticket_lifetime = 0;
541 time_t renewal_until = 0;
543 time_t time_offset = 0;
544 const char *user_ccache_file;
545 struct PAC_LOGON_INFO *logon_info = NULL;
550 * prepare a krb5_cc_cache string for the user */
553 DEBUG(0,("no valid uid\n"));
556 cc = generate_krb5_ccache(mem_ctx,
561 return NT_STATUS_NO_MEMORY;
566 * get kerberos properties */
568 if (domain->private_data) {
569 ads = (ADS_STRUCT *)domain->private_data;
570 time_offset = ads->auth.time_offset;
575 * do kerberos auth and setup ccache as the user */
577 parse_domain_user(user, name_domain, name_user);
579 realm = domain->alt_name;
582 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
583 if (principal_s == NULL) {
584 return NT_STATUS_NO_MEMORY;
587 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
588 if (service == NULL) {
589 return NT_STATUS_NO_MEMORY;
592 /* if this is a user ccache, we need to act as the user to let the krb5
593 * library handle the chown, etc. */
595 /************************ ENTERING NON-ROOT **********************/
597 if (user_ccache_file != NULL) {
598 set_effective_uid(uid);
599 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
602 result = kerberos_return_pac(mem_ctx,
611 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
614 if (user_ccache_file != NULL) {
615 gain_root_privilege();
618 /************************ RETURNED TO ROOT **********************/
620 if (!NT_STATUS_IS_OK(result)) {
624 *info3 = &logon_info->info3;
626 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
629 /* if we had a user's ccache then return that string for the pam
632 if (user_ccache_file != NULL) {
634 fstrcpy(krb5ccname, user_ccache_file);
636 result = add_ccache_to_list(principal_s,
647 if (!NT_STATUS_IS_OK(result)) {
648 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
653 /* need to delete the memory cred cache, it is not used anymore */
655 krb5_ret = ads_kdestroy(cc);
657 DEBUG(3,("winbindd_raw_kerberos_login: "
658 "could not destroy krb5 credential cache: "
659 "%s\n", error_message(krb5_ret)));
668 /* we could have created a new credential cache with a valid tgt in it
669 * but we werent able to get or verify the service ticket for this
670 * local host and therefor didn't get the PAC, we need to remove that
671 * cache entirely now */
673 krb5_ret = ads_kdestroy(cc);
675 DEBUG(3,("winbindd_raw_kerberos_login: "
676 "could not destroy krb5 credential cache: "
677 "%s\n", error_message(krb5_ret)));
680 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
681 DEBUG(3,("winbindd_raw_kerberos_login: "
682 "could not remove ccache for user %s\n",
688 return NT_STATUS_NOT_SUPPORTED;
689 #endif /* HAVE_KRB5 */
692 /****************************************************************
693 ****************************************************************/
695 bool check_request_flags(uint32_t flags)
697 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
698 WBFLAG_PAM_INFO3_TEXT |
699 WBFLAG_PAM_INFO3_NDR;
701 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
702 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
703 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
704 !(flags & flags_edata) ) {
708 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
714 /****************************************************************
715 ****************************************************************/
717 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
718 struct winbindd_response *resp,
719 uint32_t request_flags,
720 struct netr_SamInfo3 *info3,
721 const char *name_domain,
722 const char *name_user)
726 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
727 memcpy(resp->data.auth.user_session_key,
729 sizeof(resp->data.auth.user_session_key)
733 if (request_flags & WBFLAG_PAM_LMKEY) {
734 memcpy(resp->data.auth.first_8_lm_hash,
735 info3->base.LMSessKey.key,
736 sizeof(resp->data.auth.first_8_lm_hash)
740 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
741 result = append_unix_username(mem_ctx, resp,
742 info3, name_domain, name_user);
743 if (!NT_STATUS_IS_OK(result)) {
744 DEBUG(10,("Failed to append Unix Username: %s\n",
750 /* currently, anything from here on potentially overwrites extra_data. */
752 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
753 result = append_info3_as_ndr(mem_ctx, resp, info3);
754 if (!NT_STATUS_IS_OK(result)) {
755 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
761 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
762 result = append_info3_as_txt(mem_ctx, resp, info3);
763 if (!NT_STATUS_IS_OK(result)) {
764 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
770 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
771 result = append_afs_token(mem_ctx, resp,
772 info3, name_domain, name_user);
773 if (!NT_STATUS_IS_OK(result)) {
774 DEBUG(10,("Failed to append AFS token: %s\n",
783 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
784 struct winbindd_cli_state *state,
785 struct netr_SamInfo3 **info3)
787 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
788 uint16 max_allowed_bad_attempts;
789 fstring name_domain, name_user;
791 enum lsa_SidType type;
792 uchar new_nt_pass[NT_HASH_LEN];
793 const uint8 *cached_nt_pass;
794 const uint8 *cached_salt;
795 struct netr_SamInfo3 *my_info3;
796 time_t kickoff_time, must_change_time;
797 bool password_good = false;
799 struct winbindd_tdc_domain *tdc_domain = NULL;
806 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
808 /* Parse domain and username */
810 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
813 if (!lookup_cached_name(name_domain,
817 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
818 return NT_STATUS_NO_SUCH_USER;
821 if (type != SID_NAME_USER) {
822 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
823 return NT_STATUS_LOGON_FAILURE;
826 result = winbindd_get_creds(domain,
832 if (!NT_STATUS_IS_OK(result)) {
833 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
839 E_md4hash(state->request->data.auth.pass, new_nt_pass);
841 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
842 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
844 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
848 /* In this case we didn't store the nt_hash itself,
849 but the MD5 combination of salt + nt_hash. */
850 uchar salted_hash[NT_HASH_LEN];
851 E_md5hash(cached_salt, new_nt_pass, salted_hash);
853 password_good = (memcmp(cached_nt_pass, salted_hash,
856 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
857 password_good = (memcmp(cached_nt_pass, new_nt_pass,
863 /* User *DOES* know the password, update logon_time and reset
866 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
868 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
869 return NT_STATUS_ACCOUNT_LOCKED_OUT;
872 if (my_info3->base.acct_flags & ACB_DISABLED) {
873 return NT_STATUS_ACCOUNT_DISABLED;
876 if (my_info3->base.acct_flags & ACB_WSTRUST) {
877 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
880 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
881 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
884 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
885 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
888 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
889 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
890 my_info3->base.acct_flags));
891 return NT_STATUS_LOGON_FAILURE;
894 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
895 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
896 return NT_STATUS_ACCOUNT_EXPIRED;
899 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
900 if (must_change_time != 0 && must_change_time < time(NULL)) {
901 /* we allow grace logons when the password has expired */
902 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
903 /* return NT_STATUS_PASSWORD_EXPIRED; */
908 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
909 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
910 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
911 /* used to cope with the case winbindd starting without network. */
912 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
915 const char *cc = NULL;
917 const char *principal_s = NULL;
918 const char *service = NULL;
919 const char *user_ccache_file;
921 uid = get_uid_from_request(state->request);
923 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
924 return NT_STATUS_INVALID_PARAMETER;
927 cc = generate_krb5_ccache(state->mem_ctx,
928 state->request->data.auth.krb5_cc_type,
929 state->request->data.auth.uid,
932 return NT_STATUS_NO_MEMORY;
935 realm = domain->alt_name;
938 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
939 if (principal_s == NULL) {
940 return NT_STATUS_NO_MEMORY;
943 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
944 if (service == NULL) {
945 return NT_STATUS_NO_MEMORY;
948 if (user_ccache_file != NULL) {
950 fstrcpy(state->response->data.auth.krb5ccname,
953 result = add_ccache_to_list(principal_s,
956 state->request->data.auth.user,
960 time(NULL) + lp_winbind_cache_time(),
961 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
964 if (!NT_STATUS_IS_OK(result)) {
965 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
966 "to add ccache to list: %s\n",
971 #endif /* HAVE_KRB5 */
973 /* FIXME: we possibly should handle logon hours as well (does xp when
974 * offline?) see auth/auth_sam.c:sam_account_ok for details */
976 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
977 my_info3->base.bad_password_count = 0;
979 result = winbindd_update_creds_by_info3(domain,
980 state->request->data.auth.user,
981 state->request->data.auth.pass,
983 if (!NT_STATUS_IS_OK(result)) {
984 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
993 /* User does *NOT* know the correct password, modify info3 accordingly */
995 /* failure of this is not critical */
996 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
997 if (!NT_STATUS_IS_OK(result)) {
998 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
999 "Won't be able to honour account lockout policies\n"));
1002 /* increase counter */
1003 my_info3->base.bad_password_count++;
1005 if (max_allowed_bad_attempts == 0) {
1010 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1012 uint32 password_properties;
1014 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1015 if (!NT_STATUS_IS_OK(result)) {
1016 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1019 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1020 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1021 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1026 result = winbindd_update_creds_by_info3(domain,
1027 state->request->data.auth.user,
1031 if (!NT_STATUS_IS_OK(result)) {
1032 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1033 nt_errstr(result)));
1036 return NT_STATUS_LOGON_FAILURE;
1039 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1040 struct winbindd_cli_state *state,
1041 struct netr_SamInfo3 **info3)
1043 struct winbindd_domain *contact_domain;
1044 fstring name_domain, name_user;
1047 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1049 /* Parse domain and username */
1051 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1053 /* what domain should we contact? */
1056 if (!(contact_domain = find_domain_from_name(name_domain))) {
1057 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1058 state->request->data.auth.user, name_domain, name_user, name_domain));
1059 result = NT_STATUS_NO_SUCH_USER;
1064 if (is_myname(name_domain)) {
1065 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1066 result = NT_STATUS_NO_SUCH_USER;
1070 contact_domain = find_domain_from_name(name_domain);
1071 if (contact_domain == NULL) {
1072 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1073 state->request->data.auth.user, name_domain, name_user, name_domain));
1075 contact_domain = find_our_domain();
1079 if (contact_domain->initialized &&
1080 contact_domain->active_directory) {
1084 if (!contact_domain->initialized) {
1085 init_dc_connection(contact_domain);
1088 if (!contact_domain->active_directory) {
1089 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1090 return NT_STATUS_INVALID_LOGON_TYPE;
1093 result = winbindd_raw_kerberos_login(
1094 state->mem_ctx, contact_domain,
1095 state->request->data.auth.user,
1096 state->request->data.auth.pass,
1097 state->request->data.auth.krb5_cc_type,
1098 get_uid_from_request(state->request),
1099 info3, state->response->data.auth.krb5ccname);
1104 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1105 const char *domain, const char *user,
1106 const DATA_BLOB *challenge,
1107 const DATA_BLOB *lm_resp,
1108 const DATA_BLOB *nt_resp,
1109 struct netr_SamInfo3 **pinfo3)
1111 struct auth_usersupplied_info *user_info = NULL;
1114 status = make_user_info(&user_info, user, user, domain, domain,
1115 global_myname(), lm_resp, nt_resp, NULL, NULL,
1116 NULL, AUTH_PASSWORD_RESPONSE);
1117 if (!NT_STATUS_IS_OK(status)) {
1118 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1122 /* We don't want any more mapping of the username */
1123 user_info->mapped_state = True;
1125 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1127 free_user_info(&user_info);
1128 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1129 user, nt_errstr(status)));
1133 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1134 TALLOC_CTX *mem_ctx,
1135 uint32_t logon_parameters,
1137 const char *username,
1138 const char *domainname,
1139 const char *workstation,
1140 const uint8_t chal[8],
1141 DATA_BLOB lm_response,
1142 DATA_BLOB nt_response,
1143 struct netr_SamInfo3 **info3)
1150 struct rpc_pipe_client *netlogon_pipe;
1152 ZERO_STRUCTP(info3);
1155 result = cm_connect_netlogon(domain, &netlogon_pipe);
1157 if (!NT_STATUS_IS_OK(result)) {
1158 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1159 nt_errstr(result)));
1163 /* It is really important to try SamLogonEx here,
1164 * because in a clustered environment, we want to use
1165 * one machine account from multiple physical
1168 * With a normal SamLogon call, we must keep the
1169 * credentials chain updated and intact between all
1170 * users of the machine account (which would imply
1171 * cross-node communication for every NTLM logon).
1173 * (The credentials chain is not per NETLOGON pipe
1174 * connection, but globally on the server/client pair
1177 * When using SamLogonEx, the credentials are not
1178 * supplied, but the session key is implied by the
1179 * wrapping SamLogon context.
1181 * -- abartlet 21 April 2008
1184 if (domain->can_do_samlogon_ex) {
1185 result = rpccli_netlogon_sam_network_logon_ex(
1189 server, /* server name */
1190 username, /* user name */
1191 domainname, /* target domain */
1192 workstation, /* workstation */
1198 result = rpccli_netlogon_sam_network_logon(
1202 server, /* server name */
1203 username, /* user name */
1204 domainname, /* target domain */
1205 workstation, /* workstation */
1214 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1215 && domain->can_do_samlogon_ex) {
1216 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1217 "retrying with NetSamLogon\n"));
1218 domain->can_do_samlogon_ex = false;
1223 /* We have to try a second time as cm_connect_netlogon
1224 might not yet have noticed that the DC has killed
1227 if (!rpccli_is_connected(netlogon_pipe)) {
1232 /* if we get access denied, a possible cause was that we had
1233 and open connection to the DC, but someone changed our
1234 machine account password out from underneath us using 'net
1235 rpc changetrustpw' */
1237 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1238 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1239 "ACCESS_DENIED. Maybe the trust account "
1240 "password was changed and we didn't know it. "
1241 "Killing connections to domain %s\n",
1243 invalidate_cm_connection(&domain->conn);
1247 } while ( (attempts < 2) && retry );
1252 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1253 struct winbindd_domain *domain,
1256 uint32_t request_flags,
1257 struct netr_SamInfo3 **info3)
1263 unsigned char local_nt_response[24];
1264 fstring name_domain, name_user;
1266 struct netr_SamInfo3 *my_info3 = NULL;
1270 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1272 /* Parse domain and username */
1274 parse_domain_user(user, name_domain, name_user);
1276 /* do password magic */
1278 generate_random_buffer(chal, sizeof(chal));
1280 if (lp_client_ntlmv2_auth()) {
1281 DATA_BLOB server_chal;
1282 DATA_BLOB names_blob;
1283 server_chal = data_blob_const(chal, 8);
1285 /* note that the 'workgroup' here is for the local
1286 machine. The 'server name' must match the
1287 'workstation' passed to the actual SamLogon call.
1289 names_blob = NTLMv2_generate_names_blob(
1290 mem_ctx, global_myname(), lp_workgroup());
1292 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1296 &lm_resp, &nt_resp, NULL, NULL)) {
1297 data_blob_free(&names_blob);
1298 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1299 result = NT_STATUS_NO_MEMORY;
1302 data_blob_free(&names_blob);
1304 lm_resp = data_blob_null;
1305 SMBNTencrypt(pass, chal, local_nt_response);
1307 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1308 sizeof(local_nt_response));
1311 if (strequal(name_domain, get_global_sam_name())) {
1312 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1314 result = winbindd_dual_auth_passdb(
1315 mem_ctx, name_domain, name_user,
1316 &chal_blob, &lm_resp, &nt_resp, info3);
1320 /* check authentication loop */
1322 result = winbind_samlogon_retry_loop(domain,
1333 if (!NT_STATUS_IS_OK(result)) {
1337 /* handle the case where a NT4 DC does not fill in the acct_flags in
1338 * the samlogon reply info3. When accurate info3 is required by the
1339 * caller, we look up the account flags ourselve - gd */
1341 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1342 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1344 struct rpc_pipe_client *samr_pipe;
1345 struct policy_handle samr_domain_handle, user_pol;
1346 union samr_UserInfo *info = NULL;
1347 NTSTATUS status_tmp, result_tmp;
1349 struct dcerpc_binding_handle *b;
1351 status_tmp = cm_connect_sam(domain, mem_ctx,
1352 &samr_pipe, &samr_domain_handle);
1354 if (!NT_STATUS_IS_OK(status_tmp)) {
1355 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1356 nt_errstr(status_tmp)));
1360 b = samr_pipe->binding_handle;
1362 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1363 &samr_domain_handle,
1364 MAXIMUM_ALLOWED_ACCESS,
1369 if (!NT_STATUS_IS_OK(status_tmp)) {
1370 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1371 nt_errstr(status_tmp)));
1374 if (!NT_STATUS_IS_OK(result_tmp)) {
1375 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1376 nt_errstr(result_tmp)));
1380 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1386 if (!NT_STATUS_IS_OK(status_tmp)) {
1387 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1388 nt_errstr(status_tmp)));
1389 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1392 if (!NT_STATUS_IS_OK(result_tmp)) {
1393 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1394 nt_errstr(result_tmp)));
1395 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1399 acct_flags = info->info16.acct_flags;
1401 if (acct_flags == 0) {
1402 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1406 my_info3->base.acct_flags = acct_flags;
1408 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1410 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1418 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1419 struct winbindd_cli_state *state)
1421 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1422 NTSTATUS krb5_result = NT_STATUS_OK;
1423 fstring name_domain, name_user;
1425 fstring domain_user;
1426 struct netr_SamInfo3 *info3 = NULL;
1427 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1429 /* Ensure null termination */
1430 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1432 /* Ensure null termination */
1433 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1435 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1436 state->request->data.auth.user));
1438 /* Parse domain and username */
1440 name_map_status = normalize_name_unmap(state->mem_ctx,
1441 state->request->data.auth.user,
1444 /* If the name normalization didnt' actually do anything,
1445 just use the original name */
1447 if (!NT_STATUS_IS_OK(name_map_status) &&
1448 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1450 mapped_user = state->request->data.auth.user;
1453 parse_domain_user(mapped_user, name_domain, name_user);
1455 if ( mapped_user != state->request->data.auth.user ) {
1456 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1457 *lp_winbind_separator(),
1459 safe_strcpy( state->request->data.auth.user, domain_user,
1460 sizeof(state->request->data.auth.user)-1 );
1463 if (!domain->online) {
1464 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1465 if (domain->startup) {
1466 /* Logons are very important to users. If we're offline and
1467 we get a request within the first 30 seconds of startup,
1468 try very hard to find a DC and go online. */
1470 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1471 "request in startup mode.\n", domain->name ));
1473 winbindd_flush_negative_conn_cache(domain);
1474 result = init_dc_connection(domain);
1478 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1480 /* Check for Kerberos authentication */
1481 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1483 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1484 /* save for later */
1485 krb5_result = result;
1488 if (NT_STATUS_IS_OK(result)) {
1489 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1490 goto process_result;
1492 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1495 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1496 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1497 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1498 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1499 set_domain_offline( domain );
1503 /* there are quite some NT_STATUS errors where there is no
1504 * point in retrying with a samlogon, we explictly have to take
1505 * care not to increase the bad logon counter on the DC */
1507 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1509 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1510 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1511 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1512 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1513 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1514 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1515 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1516 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1520 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1521 DEBUG(3,("falling back to samlogon\n"));
1529 /* Check for Samlogon authentication */
1530 if (domain->online) {
1531 result = winbindd_dual_pam_auth_samlogon(
1532 state->mem_ctx, domain,
1533 state->request->data.auth.user,
1534 state->request->data.auth.pass,
1535 state->request->flags,
1538 if (NT_STATUS_IS_OK(result)) {
1539 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1540 /* add the Krb5 err if we have one */
1541 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1542 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1544 goto process_result;
1547 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1548 nt_errstr(result)));
1550 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1551 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1554 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1555 set_domain_offline( domain );
1559 if (domain->online) {
1560 /* We're still online - fail. */
1566 /* Check for Cached logons */
1567 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1568 lp_winbind_offline_logon()) {
1570 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1572 if (NT_STATUS_IS_OK(result)) {
1573 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1574 goto process_result;
1576 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1583 if (NT_STATUS_IS_OK(result)) {
1585 struct dom_sid user_sid;
1587 /* In all codepaths where result == NT_STATUS_OK info3 must have
1588 been initialized. */
1590 result = NT_STATUS_INTERNAL_ERROR;
1594 sid_compose(&user_sid, info3->base.domain_sid,
1597 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1599 netsamlogon_cache_store(name_user, info3);
1601 /* save name_to_sid info as early as possible (only if
1602 this is our primary domain so we don't invalidate
1603 the cache entry by storing the seq_num for the wrong
1605 if ( domain->primary ) {
1606 cache_name2sid(domain, name_domain, name_user,
1607 SID_NAME_USER, &user_sid);
1610 /* Check if the user is in the right group */
1612 result = check_info3_in_group(
1614 state->request->data.auth.require_membership_of_sid);
1615 if (!NT_STATUS_IS_OK(result)) {
1616 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1617 state->request->data.auth.user,
1618 state->request->data.auth.require_membership_of_sid));
1622 result = append_auth_data(state->mem_ctx, state->response,
1623 state->request->flags, info3,
1624 name_domain, name_user);
1625 if (!NT_STATUS_IS_OK(result)) {
1629 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1630 && lp_winbind_offline_logon()) {
1632 result = winbindd_store_creds(domain,
1633 state->request->data.auth.user,
1634 state->request->data.auth.pass,
1638 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1639 struct winbindd_domain *our_domain = find_our_domain();
1641 /* This is not entirely correct I believe, but it is
1642 consistent. Only apply the password policy settings
1643 too warn users for our own domain. Cannot obtain these
1644 from trusted DCs all the time so don't do it at all.
1647 result = NT_STATUS_NOT_SUPPORTED;
1648 if (our_domain == domain ) {
1649 result = fillup_password_policy(
1650 our_domain, state->response);
1653 if (!NT_STATUS_IS_OK(result)
1654 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1656 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1657 domain->name, nt_errstr(result)));
1662 result = NT_STATUS_OK;
1666 /* give us a more useful (more correct?) error code */
1667 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1668 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1669 result = NT_STATUS_NO_LOGON_SERVERS;
1672 set_auth_errors(state->response, result);
1674 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1675 state->request->data.auth.user,
1676 state->response->data.auth.nt_status_string,
1677 state->response->data.auth.pam_error));
1679 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1682 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1683 struct winbindd_cli_state *state)
1686 struct netr_SamInfo3 *info3 = NULL;
1687 const char *name_user = NULL;
1688 const char *name_domain = NULL;
1689 const char *workstation;
1691 DATA_BLOB lm_resp, nt_resp;
1693 /* This is child-only, so no check for privileged access is needed
1696 /* Ensure null termination */
1697 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1698 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1700 name_user = state->request->data.auth_crap.user;
1701 name_domain = state->request->data.auth_crap.domain;
1702 workstation = state->request->data.auth_crap.workstation;
1704 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1705 name_domain, name_user));
1707 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1708 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1709 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1710 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1711 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1712 state->request->data.auth_crap.lm_resp_len,
1713 state->request->data.auth_crap.nt_resp_len));
1714 result = NT_STATUS_INVALID_PARAMETER;
1719 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1720 state->request->data.auth_crap.lm_resp_len);
1722 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1723 nt_resp = data_blob_talloc(state->mem_ctx,
1724 state->request->extra_data.data,
1725 state->request->data.auth_crap.nt_resp_len);
1727 nt_resp = data_blob_talloc(state->mem_ctx,
1728 state->request->data.auth_crap.nt_resp,
1729 state->request->data.auth_crap.nt_resp_len);
1732 if (strequal(name_domain, get_global_sam_name())) {
1733 DATA_BLOB chal_blob = data_blob_const(
1734 state->request->data.auth_crap.chal,
1735 sizeof(state->request->data.auth_crap.chal));
1737 result = winbindd_dual_auth_passdb(
1738 state->mem_ctx, name_domain, name_user,
1739 &chal_blob, &lm_resp, &nt_resp, &info3);
1740 goto process_result;
1743 result = winbind_samlogon_retry_loop(domain,
1745 state->request->data.auth_crap.logon_parameters,
1749 /* Bug #3248 - found by Stefan Burkei. */
1750 workstation, /* We carefully set this above so use it... */
1751 state->request->data.auth_crap.chal,
1755 if (!NT_STATUS_IS_OK(result)) {
1761 if (NT_STATUS_IS_OK(result)) {
1762 struct dom_sid user_sid;
1764 sid_compose(&user_sid, info3->base.domain_sid,
1766 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1768 netsamlogon_cache_store(name_user, info3);
1770 /* Check if the user is in the right group */
1772 result = check_info3_in_group(
1774 state->request->data.auth_crap.require_membership_of_sid);
1775 if (!NT_STATUS_IS_OK(result)) {
1776 DEBUG(3, ("User %s is not in the required group (%s), so "
1777 "crap authentication is rejected\n",
1778 state->request->data.auth_crap.user,
1779 state->request->data.auth_crap.require_membership_of_sid));
1783 result = append_auth_data(state->mem_ctx, state->response,
1784 state->request->flags, info3,
1785 name_domain, name_user);
1786 if (!NT_STATUS_IS_OK(result)) {
1793 /* give us a more useful (more correct?) error code */
1794 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1795 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1796 result = NT_STATUS_NO_LOGON_SERVERS;
1799 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1800 result = nt_status_squash(result);
1803 set_auth_errors(state->response, result);
1805 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1806 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1809 state->response->data.auth.nt_status_string,
1810 state->response->data.auth.pam_error));
1812 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1815 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1816 struct winbindd_cli_state *state)
1819 char *newpass = NULL;
1820 struct policy_handle dom_pol;
1821 struct rpc_pipe_client *cli = NULL;
1822 bool got_info = false;
1823 struct samr_DomInfo1 *info = NULL;
1824 struct userPwdChangeFailureInformation *reject = NULL;
1825 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1826 fstring domain, user;
1827 struct dcerpc_binding_handle *b = NULL;
1829 ZERO_STRUCT(dom_pol);
1831 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1832 state->request->data.auth.user));
1834 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1838 /* Change password */
1840 oldpass = state->request->data.chauthtok.oldpass;
1841 newpass = state->request->data.chauthtok.newpass;
1843 /* Initialize reject reason */
1844 state->response->data.auth.reject_reason = Undefined;
1846 /* Get sam handle */
1848 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1850 if (!NT_STATUS_IS_OK(result)) {
1851 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1855 b = cli->binding_handle;
1857 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1864 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1866 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1868 fill_in_password_policy(state->response, info);
1870 state->response->data.auth.reject_reason =
1871 reject->extendedFailureReason;
1876 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1877 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1878 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1879 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1881 /* only fallback when the chgpasswd_user3 call is not supported */
1882 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1883 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1884 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1885 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1887 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1888 nt_errstr(result)));
1890 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1892 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1893 Map to the same status code as Windows 2003. */
1895 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1896 result = NT_STATUS_PASSWORD_RESTRICTION;
1902 if (NT_STATUS_IS_OK(result)
1903 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1904 && lp_winbind_offline_logon()) {
1905 result = winbindd_update_creds_by_name(contact_domain, user,
1907 /* Again, this happens when we login from gdm or xdm
1908 * and the password expires, *BUT* cached crendentials
1909 * doesn't exist. winbindd_update_creds_by_name()
1910 * returns NT_STATUS_NO_SUCH_USER.
1911 * This is not a failure.
1914 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1915 result = NT_STATUS_OK;
1918 if (!NT_STATUS_IS_OK(result)) {
1919 DEBUG(10, ("Failed to store creds: %s\n",
1920 nt_errstr(result)));
1921 goto process_result;
1925 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1927 NTSTATUS policy_ret;
1929 policy_ret = fillup_password_policy(
1930 contact_domain, state->response);
1932 /* failure of this is non critical, it will just provide no
1933 * additional information to the client why the change has
1934 * failed - Guenther */
1936 if (!NT_STATUS_IS_OK(policy_ret)) {
1937 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1938 goto process_result;
1944 if (strequal(contact_domain->name, get_global_sam_name())) {
1945 /* FIXME: internal rpc pipe does not cache handles yet */
1947 if (is_valid_policy_hnd(&dom_pol)) {
1949 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
1955 set_auth_errors(state->response, result);
1957 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1958 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1961 state->response->data.auth.nt_status_string,
1962 state->response->data.auth.pam_error));
1964 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1967 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1968 struct winbindd_cli_state *state)
1970 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1972 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1973 state->request->data.logoff.user));
1975 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1976 result = NT_STATUS_OK;
1977 goto process_result;
1980 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1981 result = NT_STATUS_OK;
1982 goto process_result;
1987 if (state->request->data.logoff.uid < 0) {
1988 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1989 goto process_result;
1992 /* what we need here is to find the corresponding krb5 ccache name *we*
1993 * created for a given username and destroy it */
1995 if (!ccache_entry_exists(state->request->data.logoff.user)) {
1996 result = NT_STATUS_OK;
1997 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1998 goto process_result;
2001 if (!ccache_entry_identical(state->request->data.logoff.user,
2002 state->request->data.logoff.uid,
2003 state->request->data.logoff.krb5ccname)) {
2004 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2005 goto process_result;
2008 result = remove_ccache(state->request->data.logoff.user);
2009 if (!NT_STATUS_IS_OK(result)) {
2010 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2011 nt_errstr(result)));
2012 goto process_result;
2016 result = NT_STATUS_NOT_SUPPORTED;
2022 set_auth_errors(state->response, result);
2024 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2027 /* Change user password with auth crap*/
2029 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2032 DATA_BLOB new_nt_password;
2033 DATA_BLOB old_nt_hash_enc;
2034 DATA_BLOB new_lm_password;
2035 DATA_BLOB old_lm_hash_enc;
2036 fstring domain,user;
2037 struct policy_handle dom_pol;
2038 struct winbindd_domain *contact_domain = domainSt;
2039 struct rpc_pipe_client *cli = NULL;
2040 struct dcerpc_binding_handle *b = NULL;
2042 ZERO_STRUCT(dom_pol);
2044 /* Ensure null termination */
2045 state->request->data.chng_pswd_auth_crap.user[
2046 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2047 state->request->data.chng_pswd_auth_crap.domain[
2048 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2052 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2053 (unsigned long)state->pid,
2054 state->request->data.chng_pswd_auth_crap.domain,
2055 state->request->data.chng_pswd_auth_crap.user));
2057 if (lp_winbind_offline_logon()) {
2058 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2059 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2060 result = NT_STATUS_ACCESS_DENIED;
2064 if (*state->request->data.chng_pswd_auth_crap.domain) {
2065 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2067 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2071 DEBUG(3,("no domain specified with username (%s) - "
2073 state->request->data.chng_pswd_auth_crap.user));
2074 result = NT_STATUS_NO_SUCH_USER;
2079 if (!*domain && lp_winbind_use_default_domain()) {
2080 fstrcpy(domain,(char *)lp_workgroup());
2084 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2087 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2088 (unsigned long)state->pid, domain, user));
2090 /* Change password */
2091 new_nt_password = data_blob_const(
2092 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2093 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2095 old_nt_hash_enc = data_blob_const(
2096 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2097 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2099 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2100 new_lm_password = data_blob_const(
2101 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2102 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2104 old_lm_hash_enc = data_blob_const(
2105 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2106 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2108 new_lm_password.length = 0;
2109 old_lm_hash_enc.length = 0;
2112 /* Get sam handle */
2114 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2115 if (!NT_STATUS_IS_OK(result)) {
2116 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2120 b = cli->binding_handle;
2122 result = rpccli_samr_chng_pswd_auth_crap(
2123 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2124 new_lm_password, old_lm_hash_enc);
2128 if (strequal(contact_domain->name, get_global_sam_name())) {
2129 /* FIXME: internal rpc pipe does not cache handles yet */
2131 if (is_valid_policy_hnd(&dom_pol)) {
2133 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2139 set_auth_errors(state->response, result);
2141 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2142 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2144 state->response->data.auth.nt_status_string,
2145 state->response->data.auth.pam_error));
2147 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;