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 "rpc_client/cli_samr.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "../lib/crypto/arcfour.h"
35 #define DBGC_CLASS DBGC_WINBIND
37 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
39 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
40 struct winbindd_cli_state *state,
41 struct netr_SamInfo3 *info3)
46 state->response->data.auth.info3.logon_time =
47 nt_time_to_unix(info3->base.last_logon);
48 state->response->data.auth.info3.logoff_time =
49 nt_time_to_unix(info3->base.last_logoff);
50 state->response->data.auth.info3.kickoff_time =
51 nt_time_to_unix(info3->base.acct_expiry);
52 state->response->data.auth.info3.pass_last_set_time =
53 nt_time_to_unix(info3->base.last_password_change);
54 state->response->data.auth.info3.pass_can_change_time =
55 nt_time_to_unix(info3->base.allow_password_change);
56 state->response->data.auth.info3.pass_must_change_time =
57 nt_time_to_unix(info3->base.force_password_change);
59 state->response->data.auth.info3.logon_count = info3->base.logon_count;
60 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
62 state->response->data.auth.info3.user_rid = info3->base.rid;
63 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
64 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
66 state->response->data.auth.info3.num_groups = info3->base.groups.count;
67 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
69 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
70 state->response->data.auth.info3.num_other_sids = info3->sidcount;
72 fstrcpy(state->response->data.auth.info3.user_name,
73 info3->base.account_name.string);
74 fstrcpy(state->response->data.auth.info3.full_name,
75 info3->base.full_name.string);
76 fstrcpy(state->response->data.auth.info3.logon_script,
77 info3->base.logon_script.string);
78 fstrcpy(state->response->data.auth.info3.profile_path,
79 info3->base.profile_path.string);
80 fstrcpy(state->response->data.auth.info3.home_dir,
81 info3->base.home_directory.string);
82 fstrcpy(state->response->data.auth.info3.dir_drive,
83 info3->base.home_drive.string);
85 fstrcpy(state->response->data.auth.info3.logon_srv,
86 info3->base.logon_server.string);
87 fstrcpy(state->response->data.auth.info3.logon_dom,
88 info3->base.domain.string);
90 ex = talloc_strdup(state->mem_ctx, "");
91 NT_STATUS_HAVE_NO_MEMORY(ex);
93 for (i=0; i < info3->base.groups.count; i++) {
94 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
95 info3->base.groups.rids[i].rid,
96 info3->base.groups.rids[i].attributes);
97 NT_STATUS_HAVE_NO_MEMORY(ex);
100 for (i=0; i < info3->sidcount; i++) {
103 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
104 NT_STATUS_HAVE_NO_MEMORY(sid);
106 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
108 info3->sids[i].attributes);
109 NT_STATUS_HAVE_NO_MEMORY(ex);
114 state->response->extra_data.data = ex;
115 state->response->length += talloc_get_size(ex);
120 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
121 struct winbindd_cli_state *state,
122 struct netr_SamInfo3 *info3)
125 enum ndr_err_code ndr_err;
127 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
128 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
129 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
130 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
131 return ndr_map_error2ntstatus(ndr_err);
134 state->response->extra_data.data = blob.data;
135 state->response->length += blob.length;
140 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
141 struct winbindd_cli_state *state,
142 const struct netr_SamInfo3 *info3,
143 const char *name_domain,
144 const char *name_user)
146 /* We've been asked to return the unix username, per
147 'winbind use default domain' settings and the like */
149 const char *nt_username, *nt_domain;
151 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
153 /* If the server didn't give us one, just use the one
155 nt_domain = name_domain;
158 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
160 /* If the server didn't give us one, just use the one
162 nt_username = name_user;
165 fill_domain_username(state->response->data.auth.unix_username,
166 nt_domain, nt_username, true);
168 DEBUG(5,("Setting unix username to [%s]\n",
169 state->response->data.auth.unix_username));
174 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
175 struct winbindd_cli_state *state,
176 const struct netr_SamInfo3 *info3,
177 const char *name_domain,
178 const char *name_user)
180 char *afsname = NULL;
184 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
185 if (afsname == NULL) {
186 return NT_STATUS_NO_MEMORY;
189 afsname = talloc_string_sub(mem_ctx,
190 lp_afs_username_map(),
192 afsname = talloc_string_sub(mem_ctx, afsname,
194 afsname = talloc_string_sub(mem_ctx, afsname,
201 sid_compose(&user_sid, info3->base.domain_sid,
203 sid_to_fstring(sidstr, &user_sid);
204 afsname = talloc_string_sub(mem_ctx, afsname,
208 if (afsname == NULL) {
209 return NT_STATUS_NO_MEMORY;
214 DEBUG(10, ("Generating token for user %s\n", afsname));
216 cell = strchr(afsname, '@');
219 return NT_STATUS_NO_MEMORY;
225 token = afs_createtoken_str(afsname, cell);
229 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
231 if (state->response->extra_data.data == NULL) {
232 return NT_STATUS_NO_MEMORY;
234 state->response->length +=
235 strlen((const char *)state->response->extra_data.data)+1;
240 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
241 const char *group_sid)
243 * Check whether a user belongs to a group or list of groups.
245 * @param mem_ctx talloc memory context.
246 * @param info3 user information, including group membership info.
247 * @param group_sid One or more groups , separated by commas.
249 * @return NT_STATUS_OK on success,
250 * NT_STATUS_LOGON_FAILURE if the user does not belong,
251 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
254 DOM_SID *require_membership_of_sid;
255 size_t num_require_membership_of_sid;
260 struct nt_user_token *token;
261 TALLOC_CTX *frame = talloc_stackframe();
264 /* Parse the 'required group' SID */
266 if (!group_sid || !group_sid[0]) {
267 /* NO sid supplied, all users may access */
271 token = talloc_zero(talloc_tos(), struct nt_user_token);
273 DEBUG(0, ("talloc failed\n"));
275 return NT_STATUS_NO_MEMORY;
278 num_require_membership_of_sid = 0;
279 require_membership_of_sid = NULL;
283 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
284 if (!string_to_sid(&sid, req_sid)) {
285 DEBUG(0, ("check_info3_in_group: could not parse %s "
286 "as a SID!", req_sid));
288 return NT_STATUS_INVALID_PARAMETER;
291 status = add_sid_to_array(talloc_tos(), &sid,
292 &require_membership_of_sid,
293 &num_require_membership_of_sid);
294 if (!NT_STATUS_IS_OK(status)) {
295 DEBUG(0, ("add_sid_to_array failed\n"));
301 status = sid_array_from_info3(talloc_tos(), info3,
305 if (!NT_STATUS_IS_OK(status)) {
310 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
312 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
314 DEBUG(3, ("could not add aliases: %s\n",
320 debug_nt_user_token(DBGC_CLASS, 10, token);
322 for (i=0; i<num_require_membership_of_sid; i++) {
323 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
324 &require_membership_of_sid[i])));
325 if (nt_token_check_sid(&require_membership_of_sid[i],
327 DEBUG(10, ("Access ok\n"));
333 /* Do not distinguish this error from a wrong username/pw */
336 return NT_STATUS_LOGON_FAILURE;
339 struct winbindd_domain *find_auth_domain(uint8_t flags,
340 const char *domain_name)
342 struct winbindd_domain *domain;
345 domain = find_domain_from_name_noinit(domain_name);
346 if (domain == NULL) {
347 DEBUG(3, ("Authentication for domain [%s] refused "
348 "as it is not a trusted domain\n",
354 if (strequal(domain_name, get_global_sam_name())) {
355 return find_domain_from_name_noinit(domain_name);
358 /* we can auth against trusted domains */
359 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
360 domain = find_domain_from_name_noinit(domain_name);
361 if (domain == NULL) {
362 DEBUG(3, ("Authentication for domain [%s] skipped "
363 "as it is not a trusted domain\n",
370 return find_our_domain();
373 static void fill_in_password_policy(struct winbindd_response *r,
374 const struct samr_DomInfo1 *p)
376 r->data.auth.policy.min_length_password =
377 p->min_password_length;
378 r->data.auth.policy.password_history =
379 p->password_history_length;
380 r->data.auth.policy.password_properties =
381 p->password_properties;
382 r->data.auth.policy.expire =
383 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
384 r->data.auth.policy.min_passwordage =
385 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
388 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
389 struct winbindd_cli_state *state)
391 struct winbindd_methods *methods;
392 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
393 struct samr_DomInfo1 password_policy;
395 if ( !winbindd_can_contact_domain( domain ) ) {
396 DEBUG(5,("fillup_password_policy: No inbound trust to "
397 "contact domain %s\n", domain->name));
398 return NT_STATUS_NOT_SUPPORTED;
401 methods = domain->methods;
403 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
404 if (NT_STATUS_IS_ERR(status)) {
408 fill_in_password_policy(state->response, &password_policy);
413 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
415 uint16 *lockout_threshold)
417 struct winbindd_methods *methods;
418 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
419 struct samr_DomInfo12 lockout_policy;
421 *lockout_threshold = 0;
423 methods = domain->methods;
425 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
426 if (NT_STATUS_IS_ERR(status)) {
430 *lockout_threshold = lockout_policy.lockout_threshold;
435 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
437 uint32 *password_properties)
439 struct winbindd_methods *methods;
440 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
441 struct samr_DomInfo1 password_policy;
443 *password_properties = 0;
445 methods = domain->methods;
447 status = methods->password_policy(domain, mem_ctx, &password_policy);
448 if (NT_STATUS_IS_ERR(status)) {
452 *password_properties = password_policy.password_properties;
459 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
462 bool *internal_ccache)
464 /* accept FILE and WRFILE as krb5_cc_type from the client and then
465 * build the full ccname string based on the user's uid here -
468 const char *gen_cc = NULL;
470 *internal_ccache = true;
476 if (!type || type[0] == '\0') {
480 if (strequal(type, "FILE")) {
481 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
482 } else if (strequal(type, "WRFILE")) {
483 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
485 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
489 *internal_ccache = false;
493 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
496 if (gen_cc == NULL) {
497 DEBUG(0,("out of memory\n"));
501 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
506 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
508 const char *type = state->request->data.auth.krb5_cc_type;
510 state->response->data.auth.krb5ccname[0] = '\0';
512 if (type[0] == '\0') {
516 if (!strequal(type, "FILE") &&
517 !strequal(type, "WRFILE")) {
518 DEBUG(10,("won't return krbccname for a %s type ccache\n",
523 fstrcpy(state->response->data.auth.krb5ccname, cc);
528 uid_t get_uid_from_request(struct winbindd_request *request)
532 uid = request->data.auth.uid;
535 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
541 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
543 return get_uid_from_request(state->request);
546 /**********************************************************************
547 Authenticate a user with a clear text password using Kerberos and fill up
549 **********************************************************************/
551 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
552 struct winbindd_cli_state *state,
553 struct netr_SamInfo3 **info3)
556 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
557 krb5_error_code krb5_ret;
558 const char *cc = NULL;
559 const char *principal_s = NULL;
560 const char *service = NULL;
562 fstring name_domain, name_user;
563 time_t ticket_lifetime = 0;
564 time_t renewal_until = 0;
567 time_t time_offset = 0;
568 bool internal_ccache = true;
569 struct PAC_LOGON_INFO *logon_info = NULL;
574 * prepare a krb5_cc_cache string for the user */
576 uid = get_uid_from_state(state);
578 DEBUG(0,("no valid uid\n"));
581 cc = generate_krb5_ccache(state->mem_ctx,
582 state->request->data.auth.krb5_cc_type,
583 state->request->data.auth.uid,
586 return NT_STATUS_NO_MEMORY;
591 * get kerberos properties */
593 if (domain->private_data) {
594 ads = (ADS_STRUCT *)domain->private_data;
595 time_offset = ads->auth.time_offset;
600 * do kerberos auth and setup ccache as the user */
602 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
604 realm = domain->alt_name;
607 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
608 if (principal_s == NULL) {
609 return NT_STATUS_NO_MEMORY;
612 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
613 if (service == NULL) {
614 return NT_STATUS_NO_MEMORY;
617 /* if this is a user ccache, we need to act as the user to let the krb5
618 * library handle the chown, etc. */
620 /************************ ENTERING NON-ROOT **********************/
622 if (!internal_ccache) {
623 set_effective_uid(uid);
624 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
627 result = kerberos_return_pac(state->mem_ctx,
629 state->request->data.auth.pass,
636 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
639 if (!internal_ccache) {
640 gain_root_privilege();
643 /************************ RETURNED TO ROOT **********************/
645 if (!NT_STATUS_IS_OK(result)) {
649 *info3 = &logon_info->info3;
651 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
654 /* if we had a user's ccache then return that string for the pam
657 if (!internal_ccache) {
659 setup_return_cc_name(state, cc);
661 result = add_ccache_to_list(principal_s,
664 state->request->data.auth.user,
672 if (!NT_STATUS_IS_OK(result)) {
673 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
678 /* need to delete the memory cred cache, it is not used anymore */
680 krb5_ret = ads_kdestroy(cc);
682 DEBUG(3,("winbindd_raw_kerberos_login: "
683 "could not destroy krb5 credential cache: "
684 "%s\n", error_message(krb5_ret)));
693 /* we could have created a new credential cache with a valid tgt in it
694 * but we werent able to get or verify the service ticket for this
695 * local host and therefor didn't get the PAC, we need to remove that
696 * cache entirely now */
698 krb5_ret = ads_kdestroy(cc);
700 DEBUG(3,("winbindd_raw_kerberos_login: "
701 "could not destroy krb5 credential cache: "
702 "%s\n", error_message(krb5_ret)));
705 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
706 DEBUG(3,("winbindd_raw_kerberos_login: "
707 "could not remove ccache for user %s\n",
708 state->request->data.auth.user));
713 return NT_STATUS_NOT_SUPPORTED;
714 #endif /* HAVE_KRB5 */
717 /****************************************************************
718 ****************************************************************/
720 bool check_request_flags(uint32_t flags)
722 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
723 WBFLAG_PAM_INFO3_TEXT |
724 WBFLAG_PAM_INFO3_NDR;
726 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
727 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
728 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
729 !(flags & flags_edata) ) {
733 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
739 /****************************************************************
740 ****************************************************************/
742 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
743 struct netr_SamInfo3 *info3,
744 const char *name_domain,
745 const char *name_user)
748 uint32_t flags = state->request->flags;
750 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
751 memcpy(state->response->data.auth.user_session_key,
753 sizeof(state->response->data.auth.user_session_key)
757 if (flags & WBFLAG_PAM_LMKEY) {
758 memcpy(state->response->data.auth.first_8_lm_hash,
759 info3->base.LMSessKey.key,
760 sizeof(state->response->data.auth.first_8_lm_hash)
764 if (flags & WBFLAG_PAM_UNIX_NAME) {
765 result = append_unix_username(state->mem_ctx, state, info3,
766 name_domain, name_user);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append Unix Username: %s\n",
774 /* currently, anything from here on potentially overwrites extra_data. */
776 if (flags & WBFLAG_PAM_INFO3_NDR) {
777 result = append_info3_as_ndr(state->mem_ctx, state, info3);
778 if (!NT_STATUS_IS_OK(result)) {
779 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
785 if (flags & WBFLAG_PAM_INFO3_TEXT) {
786 result = append_info3_as_txt(state->mem_ctx, state, info3);
787 if (!NT_STATUS_IS_OK(result)) {
788 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
794 if (flags & WBFLAG_PAM_AFS_TOKEN) {
795 result = append_afs_token(state->mem_ctx, state, info3,
796 name_domain, name_user);
797 if (!NT_STATUS_IS_OK(result)) {
798 DEBUG(10,("Failed to append AFS token: %s\n",
807 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
808 struct winbindd_cli_state *state,
809 struct netr_SamInfo3 **info3)
811 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
812 uint16 max_allowed_bad_attempts;
813 fstring name_domain, name_user;
815 enum lsa_SidType type;
816 uchar new_nt_pass[NT_HASH_LEN];
817 const uint8 *cached_nt_pass;
818 const uint8 *cached_salt;
819 struct netr_SamInfo3 *my_info3;
820 time_t kickoff_time, must_change_time;
821 bool password_good = false;
823 struct winbindd_tdc_domain *tdc_domain = NULL;
830 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
832 /* Parse domain and username */
834 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
837 if (!lookup_cached_name(state->mem_ctx,
842 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
843 return NT_STATUS_NO_SUCH_USER;
846 if (type != SID_NAME_USER) {
847 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
848 return NT_STATUS_LOGON_FAILURE;
851 result = winbindd_get_creds(domain,
857 if (!NT_STATUS_IS_OK(result)) {
858 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
864 E_md4hash(state->request->data.auth.pass, new_nt_pass);
866 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
867 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
869 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
873 /* In this case we didn't store the nt_hash itself,
874 but the MD5 combination of salt + nt_hash. */
875 uchar salted_hash[NT_HASH_LEN];
876 E_md5hash(cached_salt, new_nt_pass, salted_hash);
878 password_good = (memcmp(cached_nt_pass, salted_hash,
881 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
882 password_good = (memcmp(cached_nt_pass, new_nt_pass,
888 /* User *DOES* know the password, update logon_time and reset
891 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
893 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
894 return NT_STATUS_ACCOUNT_LOCKED_OUT;
897 if (my_info3->base.acct_flags & ACB_DISABLED) {
898 return NT_STATUS_ACCOUNT_DISABLED;
901 if (my_info3->base.acct_flags & ACB_WSTRUST) {
902 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
905 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
906 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
909 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
910 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
913 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
914 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
915 my_info3->base.acct_flags));
916 return NT_STATUS_LOGON_FAILURE;
919 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
920 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
921 return NT_STATUS_ACCOUNT_EXPIRED;
924 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
925 if (must_change_time != 0 && must_change_time < time(NULL)) {
926 /* we allow grace logons when the password has expired */
927 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
928 /* return NT_STATUS_PASSWORD_EXPIRED; */
933 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
934 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
935 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
936 /* used to cope with the case winbindd starting without network. */
937 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
940 const char *cc = NULL;
942 const char *principal_s = NULL;
943 const char *service = NULL;
944 bool internal_ccache = false;
946 uid = get_uid_from_state(state);
948 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
949 return NT_STATUS_INVALID_PARAMETER;
952 cc = generate_krb5_ccache(state->mem_ctx,
953 state->request->data.auth.krb5_cc_type,
954 state->request->data.auth.uid,
957 return NT_STATUS_NO_MEMORY;
960 realm = domain->alt_name;
963 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
964 if (principal_s == NULL) {
965 return NT_STATUS_NO_MEMORY;
968 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
969 if (service == NULL) {
970 return NT_STATUS_NO_MEMORY;
973 if (!internal_ccache) {
975 setup_return_cc_name(state, cc);
977 result = add_ccache_to_list(principal_s,
980 state->request->data.auth.user,
984 time(NULL) + lp_winbind_cache_time(),
985 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
988 if (!NT_STATUS_IS_OK(result)) {
989 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
990 "to add ccache to list: %s\n",
995 #endif /* HAVE_KRB5 */
997 /* FIXME: we possibly should handle logon hours as well (does xp when
998 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1000 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1001 my_info3->base.bad_password_count = 0;
1003 result = winbindd_update_creds_by_info3(domain,
1005 state->request->data.auth.user,
1006 state->request->data.auth.pass,
1008 if (!NT_STATUS_IS_OK(result)) {
1009 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1010 nt_errstr(result)));
1014 return NT_STATUS_OK;
1018 /* User does *NOT* know the correct password, modify info3 accordingly */
1020 /* failure of this is not critical */
1021 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1022 if (!NT_STATUS_IS_OK(result)) {
1023 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1024 "Won't be able to honour account lockout policies\n"));
1027 /* increase counter */
1028 my_info3->base.bad_password_count++;
1030 if (max_allowed_bad_attempts == 0) {
1035 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1037 uint32 password_properties;
1039 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1040 if (!NT_STATUS_IS_OK(result)) {
1041 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1044 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1045 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1046 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1051 result = winbindd_update_creds_by_info3(domain,
1053 state->request->data.auth.user,
1057 if (!NT_STATUS_IS_OK(result)) {
1058 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1059 nt_errstr(result)));
1062 return NT_STATUS_LOGON_FAILURE;
1065 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1066 struct winbindd_cli_state *state,
1067 struct netr_SamInfo3 **info3)
1069 struct winbindd_domain *contact_domain;
1070 fstring name_domain, name_user;
1073 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1075 /* Parse domain and username */
1077 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1079 /* what domain should we contact? */
1082 if (!(contact_domain = find_domain_from_name(name_domain))) {
1083 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1084 state->request->data.auth.user, name_domain, name_user, name_domain));
1085 result = NT_STATUS_NO_SUCH_USER;
1090 if (is_myname(name_domain)) {
1091 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1092 result = NT_STATUS_NO_SUCH_USER;
1096 contact_domain = find_domain_from_name(name_domain);
1097 if (contact_domain == NULL) {
1098 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1099 state->request->data.auth.user, name_domain, name_user, name_domain));
1101 contact_domain = find_our_domain();
1105 if (contact_domain->initialized &&
1106 contact_domain->active_directory) {
1110 if (!contact_domain->initialized) {
1111 init_dc_connection(contact_domain);
1114 if (!contact_domain->active_directory) {
1115 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1116 return NT_STATUS_INVALID_LOGON_TYPE;
1119 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1124 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1125 const char *domain, const char *user,
1126 const DATA_BLOB *challenge,
1127 const DATA_BLOB *lm_resp,
1128 const DATA_BLOB *nt_resp,
1129 struct netr_SamInfo3 **pinfo3)
1131 struct auth_usersupplied_info *user_info = NULL;
1132 struct auth_serversupplied_info *server_info = NULL;
1133 struct netr_SamInfo3 *info3;
1136 status = make_user_info(&user_info, user, user, domain, domain,
1137 global_myname(), lm_resp, nt_resp, NULL, NULL,
1139 if (!NT_STATUS_IS_OK(status)) {
1140 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1144 status = check_sam_security(challenge, talloc_tos(), user_info,
1146 free_user_info(&user_info);
1148 if (!NT_STATUS_IS_OK(status)) {
1149 DEBUG(10, ("check_ntlm_password failed: %s\n",
1150 nt_errstr(status)));
1154 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1155 if (info3 == NULL) {
1156 return NT_STATUS_NO_MEMORY;
1159 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1160 if (!NT_STATUS_IS_OK(status)) {
1161 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1162 nt_errstr(status)));
1166 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1168 return NT_STATUS_OK;
1171 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1172 TALLOC_CTX *mem_ctx,
1173 uint32 logon_parameters,
1175 const char *username,
1177 const char *workstation,
1178 const uint8 chal[8],
1179 DATA_BLOB lm_response,
1180 DATA_BLOB nt_response,
1181 struct netr_SamInfo3 **info3);
1183 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1184 struct winbindd_cli_state *state,
1185 struct netr_SamInfo3 **info3)
1188 struct rpc_pipe_client *netlogon_pipe;
1193 unsigned char local_lm_response[24];
1194 unsigned char local_nt_response[24];
1195 fstring name_domain, name_user;
1198 struct netr_SamInfo3 *my_info3 = NULL;
1202 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1204 /* Parse domain and username */
1206 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1208 /* do password magic */
1210 generate_random_buffer(chal, sizeof(chal));
1212 if (lp_client_ntlmv2_auth()) {
1213 DATA_BLOB server_chal;
1214 DATA_BLOB names_blob;
1215 DATA_BLOB nt_response;
1216 DATA_BLOB lm_response;
1217 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1219 /* note that the 'workgroup' here is a best guess - we don't know
1220 the server's domain at this point. The 'server name' is also
1223 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1225 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1226 state->request->data.auth.pass,
1229 &lm_response, &nt_response, NULL, NULL)) {
1230 data_blob_free(&names_blob);
1231 data_blob_free(&server_chal);
1232 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1233 result = NT_STATUS_NO_MEMORY;
1236 data_blob_free(&names_blob);
1237 data_blob_free(&server_chal);
1238 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1239 lm_response.length);
1240 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1241 nt_response.length);
1242 data_blob_free(&lm_response);
1243 data_blob_free(&nt_response);
1246 if (lp_client_lanman_auth()
1247 && SMBencrypt(state->request->data.auth.pass,
1249 local_lm_response)) {
1250 lm_resp = data_blob_talloc(state->mem_ctx,
1252 sizeof(local_lm_response));
1254 lm_resp = data_blob_null;
1256 SMBNTencrypt(state->request->data.auth.pass,
1260 nt_resp = data_blob_talloc(state->mem_ctx,
1262 sizeof(local_nt_response));
1265 if (strequal(name_domain, get_global_sam_name())) {
1266 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1268 result = winbindd_dual_auth_passdb(
1269 state->mem_ctx, name_domain, name_user,
1270 &chal_blob, &lm_resp, &nt_resp, info3);
1274 /* check authentication loop */
1277 netlogon_fn_t logon_fn;
1279 ZERO_STRUCTP(my_info3);
1282 result = cm_connect_netlogon(domain, &netlogon_pipe);
1284 if (!NT_STATUS_IS_OK(result)) {
1285 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1289 /* It is really important to try SamLogonEx here,
1290 * because in a clustered environment, we want to use
1291 * one machine account from multiple physical
1294 * With a normal SamLogon call, we must keep the
1295 * credentials chain updated and intact between all
1296 * users of the machine account (which would imply
1297 * cross-node communication for every NTLM logon).
1299 * (The credentials chain is not per NETLOGON pipe
1300 * connection, but globally on the server/client pair
1303 * When using SamLogonEx, the credentials are not
1304 * supplied, but the session key is implied by the
1305 * wrapping SamLogon context.
1307 * -- abartlet 21 April 2008
1310 logon_fn = domain->can_do_samlogon_ex
1311 ? rpccli_netlogon_sam_network_logon_ex
1312 : rpccli_netlogon_sam_network_logon;
1314 result = logon_fn(netlogon_pipe,
1317 domain->dcname, /* server name */
1318 name_user, /* user name */
1319 name_domain, /* target domain */
1320 global_myname(), /* workstation */
1327 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1328 && domain->can_do_samlogon_ex) {
1329 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1330 "retrying with NetSamLogon\n"));
1331 domain->can_do_samlogon_ex = false;
1336 /* We have to try a second time as cm_connect_netlogon
1337 might not yet have noticed that the DC has killed
1340 if (!rpccli_is_connected(netlogon_pipe)) {
1345 /* if we get access denied, a possible cause was that we had
1346 and open connection to the DC, but someone changed our
1347 machine account password out from underneath us using 'net
1348 rpc changetrustpw' */
1350 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1351 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1352 "ACCESS_DENIED. Maybe the trust account "
1353 "password was changed and we didn't know it. "
1354 "Killing connections to domain %s\n",
1356 invalidate_cm_connection(&domain->conn);
1360 } while ( (attempts < 2) && retry );
1362 /* handle the case where a NT4 DC does not fill in the acct_flags in
1363 * the samlogon reply info3. When accurate info3 is required by the
1364 * caller, we look up the account flags ourselve - gd */
1366 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1367 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1369 struct rpc_pipe_client *samr_pipe;
1370 struct policy_handle samr_domain_handle, user_pol;
1371 union samr_UserInfo *info = NULL;
1372 NTSTATUS status_tmp;
1375 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1376 &samr_pipe, &samr_domain_handle);
1378 if (!NT_STATUS_IS_OK(status_tmp)) {
1379 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1380 nt_errstr(status_tmp)));
1384 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1385 &samr_domain_handle,
1386 MAXIMUM_ALLOWED_ACCESS,
1390 if (!NT_STATUS_IS_OK(status_tmp)) {
1391 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1392 nt_errstr(status_tmp)));
1396 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1401 if (!NT_STATUS_IS_OK(status_tmp)) {
1402 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1403 nt_errstr(status_tmp)));
1404 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1408 acct_flags = info->info16.acct_flags;
1410 if (acct_flags == 0) {
1411 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1415 my_info3->base.acct_flags = acct_flags;
1417 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1419 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1427 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1428 struct winbindd_cli_state *state)
1430 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1431 NTSTATUS krb5_result = NT_STATUS_OK;
1432 fstring name_domain, name_user;
1434 fstring domain_user;
1435 struct netr_SamInfo3 *info3 = NULL;
1436 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1438 /* Ensure null termination */
1439 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1441 /* Ensure null termination */
1442 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1444 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1445 state->request->data.auth.user));
1447 if (!check_request_flags(state->request->flags)) {
1448 result = NT_STATUS_INVALID_PARAMETER_MIX;
1452 /* Parse domain and username */
1454 name_map_status = normalize_name_unmap(state->mem_ctx,
1455 state->request->data.auth.user,
1458 /* If the name normalization didnt' actually do anything,
1459 just use the original name */
1461 if (!NT_STATUS_IS_OK(name_map_status) &&
1462 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1464 mapped_user = state->request->data.auth.user;
1467 parse_domain_user(mapped_user, name_domain, name_user);
1469 if ( mapped_user != state->request->data.auth.user ) {
1470 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1471 safe_strcpy( state->request->data.auth.user, domain_user,
1472 sizeof(state->request->data.auth.user)-1 );
1475 if (domain->online == false) {
1476 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1477 if (domain->startup) {
1478 /* Logons are very important to users. If we're offline and
1479 we get a request within the first 30 seconds of startup,
1480 try very hard to find a DC and go online. */
1482 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1483 "request in startup mode.\n", domain->name ));
1485 winbindd_flush_negative_conn_cache(domain);
1486 result = init_dc_connection(domain);
1490 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1492 /* Check for Kerberos authentication */
1493 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1495 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1496 /* save for later */
1497 krb5_result = result;
1500 if (NT_STATUS_IS_OK(result)) {
1501 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1502 goto process_result;
1504 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1507 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1509 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1510 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1511 set_domain_offline( domain );
1515 /* there are quite some NT_STATUS errors where there is no
1516 * point in retrying with a samlogon, we explictly have to take
1517 * care not to increase the bad logon counter on the DC */
1519 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1520 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1527 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1528 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1532 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1533 DEBUG(3,("falling back to samlogon\n"));
1541 /* Check for Samlogon authentication */
1542 if (domain->online) {
1543 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1545 if (NT_STATUS_IS_OK(result)) {
1546 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1547 /* add the Krb5 err if we have one */
1548 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1549 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1551 goto process_result;
1554 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1555 nt_errstr(result)));
1557 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1558 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1559 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1561 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1562 set_domain_offline( domain );
1566 if (domain->online) {
1567 /* We're still online - fail. */
1573 /* Check for Cached logons */
1574 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1575 lp_winbind_offline_logon()) {
1577 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1579 if (NT_STATUS_IS_OK(result)) {
1580 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1581 goto process_result;
1583 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1590 if (NT_STATUS_IS_OK(result)) {
1594 /* In all codepaths where result == NT_STATUS_OK info3 must have
1595 been initialized. */
1597 result = NT_STATUS_INTERNAL_ERROR;
1601 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1602 netsamlogon_cache_store(name_user, info3);
1604 /* save name_to_sid info as early as possible (only if
1605 this is our primary domain so we don't invalidate
1606 the cache entry by storing the seq_num for the wrong
1608 if ( domain->primary ) {
1609 sid_compose(&user_sid, info3->base.domain_sid,
1611 cache_name2sid(domain, name_domain, name_user,
1612 SID_NAME_USER, &user_sid);
1615 /* Check if the user is in the right group */
1617 result = check_info3_in_group(
1619 state->request->data.auth.require_membership_of_sid);
1620 if (!NT_STATUS_IS_OK(result)) {
1621 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1622 state->request->data.auth.user,
1623 state->request->data.auth.require_membership_of_sid));
1627 result = append_auth_data(state, info3, name_domain,
1629 if (!NT_STATUS_IS_OK(result)) {
1633 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1635 if (lp_winbind_offline_logon()) {
1636 result = winbindd_store_creds(domain,
1638 state->request->data.auth.user,
1639 state->request->data.auth.pass,
1645 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1646 struct winbindd_domain *our_domain = find_our_domain();
1648 /* This is not entirely correct I believe, but it is
1649 consistent. Only apply the password policy settings
1650 too warn users for our own domain. Cannot obtain these
1651 from trusted DCs all the time so don't do it at all.
1654 result = NT_STATUS_NOT_SUPPORTED;
1655 if (our_domain == domain ) {
1656 result = fillup_password_policy(our_domain, state);
1659 if (!NT_STATUS_IS_OK(result)
1660 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1662 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1663 domain->name, nt_errstr(result)));
1668 result = NT_STATUS_OK;
1672 /* give us a more useful (more correct?) error code */
1673 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1674 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1675 result = NT_STATUS_NO_LOGON_SERVERS;
1678 set_auth_errors(state->response, result);
1680 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1681 state->request->data.auth.user,
1682 state->response->data.auth.nt_status_string,
1683 state->response->data.auth.pam_error));
1685 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1688 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1689 struct winbindd_cli_state *state)
1692 struct netr_SamInfo3 *info3 = NULL;
1693 struct rpc_pipe_client *netlogon_pipe;
1694 const char *name_user = NULL;
1695 const char *name_domain = NULL;
1696 const char *workstation;
1700 DATA_BLOB lm_resp, nt_resp;
1702 /* This is child-only, so no check for privileged access is needed
1705 /* Ensure null termination */
1706 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1707 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1709 if (!check_request_flags(state->request->flags)) {
1710 result = NT_STATUS_INVALID_PARAMETER_MIX;
1714 name_user = state->request->data.auth_crap.user;
1716 if (*state->request->data.auth_crap.domain) {
1717 name_domain = state->request->data.auth_crap.domain;
1718 } else if (lp_winbind_use_default_domain()) {
1719 name_domain = lp_workgroup();
1721 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1723 result = NT_STATUS_NO_SUCH_USER;
1727 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1728 name_domain, name_user));
1730 if (*state->request->data.auth_crap.workstation) {
1731 workstation = state->request->data.auth_crap.workstation;
1733 workstation = global_myname();
1736 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1737 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1738 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1739 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1740 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1741 state->request->data.auth_crap.lm_resp_len,
1742 state->request->data.auth_crap.nt_resp_len));
1743 result = NT_STATUS_INVALID_PARAMETER;
1748 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1749 state->request->data.auth_crap.lm_resp_len);
1751 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1752 nt_resp = data_blob_talloc(state->mem_ctx,
1753 state->request->extra_data.data,
1754 state->request->data.auth_crap.nt_resp_len);
1756 nt_resp = data_blob_talloc(state->mem_ctx,
1757 state->request->data.auth_crap.nt_resp,
1758 state->request->data.auth_crap.nt_resp_len);
1761 if (strequal(name_domain, get_global_sam_name())) {
1762 DATA_BLOB chal_blob = data_blob_const(
1763 state->request->data.auth_crap.chal,
1764 sizeof(state->request->data.auth_crap.chal));
1766 result = winbindd_dual_auth_passdb(
1767 state->mem_ctx, name_domain, name_user,
1768 &chal_blob, &lm_resp, &nt_resp, &info3);
1769 goto process_result;
1773 netlogon_fn_t logon_fn;
1777 netlogon_pipe = NULL;
1778 result = cm_connect_netlogon(domain, &netlogon_pipe);
1780 if (!NT_STATUS_IS_OK(result)) {
1781 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1782 nt_errstr(result)));
1786 logon_fn = domain->can_do_samlogon_ex
1787 ? rpccli_netlogon_sam_network_logon_ex
1788 : rpccli_netlogon_sam_network_logon;
1790 result = logon_fn(netlogon_pipe,
1792 state->request->data.auth_crap.logon_parameters,
1796 /* Bug #3248 - found by Stefan Burkei. */
1797 workstation, /* We carefully set this above so use it... */
1798 state->request->data.auth_crap.chal,
1803 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1804 && domain->can_do_samlogon_ex) {
1805 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1806 "retrying with NetSamLogon\n"));
1807 domain->can_do_samlogon_ex = false;
1814 /* We have to try a second time as cm_connect_netlogon
1815 might not yet have noticed that the DC has killed
1818 if (!rpccli_is_connected(netlogon_pipe)) {
1823 /* if we get access denied, a possible cause was that we had and open
1824 connection to the DC, but someone changed our machine account password
1825 out from underneath us using 'net rpc changetrustpw' */
1827 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1828 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1829 "ACCESS_DENIED. Maybe the trust account "
1830 "password was changed and we didn't know it. "
1831 "Killing connections to domain %s\n",
1833 invalidate_cm_connection(&domain->conn);
1837 } while ( (attempts < 2) && retry );
1841 if (NT_STATUS_IS_OK(result)) {
1843 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1844 netsamlogon_cache_store(name_user, info3);
1846 /* Check if the user is in the right group */
1848 result = check_info3_in_group(
1850 state->request->data.auth_crap.require_membership_of_sid);
1851 if (!NT_STATUS_IS_OK(result)) {
1852 DEBUG(3, ("User %s is not in the required group (%s), so "
1853 "crap authentication is rejected\n",
1854 state->request->data.auth_crap.user,
1855 state->request->data.auth_crap.require_membership_of_sid));
1859 result = append_auth_data(state, info3, name_domain,
1861 if (!NT_STATUS_IS_OK(result)) {
1868 /* give us a more useful (more correct?) error code */
1869 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1870 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1871 result = NT_STATUS_NO_LOGON_SERVERS;
1874 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1875 result = nt_status_squash(result);
1878 set_auth_errors(state->response, result);
1880 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1881 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1884 state->response->data.auth.nt_status_string,
1885 state->response->data.auth.pam_error));
1887 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1890 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1891 struct winbindd_cli_state *state)
1894 char *newpass = NULL;
1895 struct policy_handle dom_pol;
1896 struct rpc_pipe_client *cli;
1897 bool got_info = false;
1898 struct samr_DomInfo1 *info = NULL;
1899 struct userPwdChangeFailureInformation *reject = NULL;
1900 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1901 fstring domain, user;
1903 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1904 state->request->data.auth.user));
1906 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1910 /* Change password */
1912 oldpass = state->request->data.chauthtok.oldpass;
1913 newpass = state->request->data.chauthtok.newpass;
1915 /* Initialize reject reason */
1916 state->response->data.auth.reject_reason = Undefined;
1918 if (strequal(domain, get_global_sam_name())) {
1919 struct samr_CryptPassword new_nt_password;
1920 struct samr_CryptPassword new_lm_password;
1921 struct samr_Password old_nt_hash_enc;
1922 struct samr_Password old_lanman_hash_enc;
1923 enum samPwdChangeReason rejectReason;
1925 uchar old_nt_hash[16];
1926 uchar old_lanman_hash[16];
1927 uchar new_nt_hash[16];
1928 uchar new_lanman_hash[16];
1930 contact_domain = NULL;
1932 E_md4hash(oldpass, old_nt_hash);
1933 E_md4hash(newpass, new_nt_hash);
1935 if (lp_client_lanman_auth() &&
1936 E_deshash(newpass, new_lanman_hash) &&
1937 E_deshash(oldpass, old_lanman_hash)) {
1939 /* E_deshash returns false for 'long' passwords (> 14
1940 DOS chars). This allows us to match Win2k, which
1941 does not store a LM hash for these passwords (which
1942 would reduce the effective password length to 14) */
1944 encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
1945 arcfour_crypt(new_lm_password.data, old_nt_hash, 516);
1946 E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
1948 ZERO_STRUCT(new_lm_password);
1949 ZERO_STRUCT(old_lanman_hash_enc);
1952 encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
1954 arcfour_crypt(new_nt_password.data, old_nt_hash, 516);
1955 E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
1957 result = pass_oem_change(
1959 new_lm_password.data, old_lanman_hash_enc.hash,
1960 new_nt_password.data, old_nt_hash_enc.hash,
1965 /* Get sam handle */
1967 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1969 if (!NT_STATUS_IS_OK(result)) {
1970 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1974 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1981 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1983 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1985 fill_in_password_policy(state->response, info);
1987 state->response->data.auth.reject_reason =
1988 reject->extendedFailureReason;
1993 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1994 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1995 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1996 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1998 /* only fallback when the chgpasswd_user3 call is not supported */
1999 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2000 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2001 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2002 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2004 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2005 nt_errstr(result)));
2007 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2009 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2010 Map to the same status code as Windows 2003. */
2012 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2013 result = NT_STATUS_PASSWORD_RESTRICTION;
2019 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2020 if (lp_winbind_offline_logon()) {
2021 result = winbindd_update_creds_by_name(contact_domain,
2022 state->mem_ctx, user,
2024 /* Again, this happens when we login from gdm or xdm
2025 * and the password expires, *BUT* cached crendentials
2026 * doesn't exist. winbindd_update_creds_by_name()
2027 * returns NT_STATUS_NO_SUCH_USER.
2028 * This is not a failure.
2031 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2032 result = NT_STATUS_OK;
2035 if (!NT_STATUS_IS_OK(result)) {
2036 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2037 goto process_result;
2042 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2044 NTSTATUS policy_ret;
2046 policy_ret = fillup_password_policy(contact_domain, state);
2048 /* failure of this is non critical, it will just provide no
2049 * additional information to the client why the change has
2050 * failed - Guenther */
2052 if (!NT_STATUS_IS_OK(policy_ret)) {
2053 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2054 goto process_result;
2060 set_auth_errors(state->response, result);
2062 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2063 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2066 state->response->data.auth.nt_status_string,
2067 state->response->data.auth.pam_error));
2069 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2072 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2073 struct winbindd_cli_state *state)
2075 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2077 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2078 state->request->data.logoff.user));
2080 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2081 result = NT_STATUS_OK;
2082 goto process_result;
2085 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2086 result = NT_STATUS_OK;
2087 goto process_result;
2092 if (state->request->data.logoff.uid < 0) {
2093 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2094 goto process_result;
2097 /* what we need here is to find the corresponding krb5 ccache name *we*
2098 * created for a given username and destroy it */
2100 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2101 result = NT_STATUS_OK;
2102 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2103 goto process_result;
2106 if (!ccache_entry_identical(state->request->data.logoff.user,
2107 state->request->data.logoff.uid,
2108 state->request->data.logoff.krb5ccname)) {
2109 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2110 goto process_result;
2113 result = remove_ccache(state->request->data.logoff.user);
2114 if (!NT_STATUS_IS_OK(result)) {
2115 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2116 nt_errstr(result)));
2117 goto process_result;
2121 result = NT_STATUS_NOT_SUPPORTED;
2127 set_auth_errors(state->response, result);
2129 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2132 /* Change user password with auth crap*/
2134 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2137 DATA_BLOB new_nt_password;
2138 DATA_BLOB old_nt_hash_enc;
2139 DATA_BLOB new_lm_password;
2140 DATA_BLOB old_lm_hash_enc;
2141 fstring domain,user;
2142 struct policy_handle dom_pol;
2143 struct winbindd_domain *contact_domain = domainSt;
2144 struct rpc_pipe_client *cli;
2146 /* Ensure null termination */
2147 state->request->data.chng_pswd_auth_crap.user[
2148 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2149 state->request->data.chng_pswd_auth_crap.domain[
2150 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2154 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2155 (unsigned long)state->pid,
2156 state->request->data.chng_pswd_auth_crap.domain,
2157 state->request->data.chng_pswd_auth_crap.user));
2159 if (lp_winbind_offline_logon()) {
2160 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2161 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2162 result = NT_STATUS_ACCESS_DENIED;
2166 if (*state->request->data.chng_pswd_auth_crap.domain) {
2167 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2169 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2173 DEBUG(3,("no domain specified with username (%s) - "
2175 state->request->data.chng_pswd_auth_crap.user));
2176 result = NT_STATUS_NO_SUCH_USER;
2181 if (!*domain && lp_winbind_use_default_domain()) {
2182 fstrcpy(domain,(char *)lp_workgroup());
2186 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2189 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2190 (unsigned long)state->pid, domain, user));
2192 if (strequal(domain, get_global_sam_name())) {
2193 enum samPwdChangeReason reject_reason;
2195 result = pass_oem_change(
2197 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2198 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2199 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2200 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2202 DEBUG(10, ("pass_oem_change returned %s\n",
2203 nt_errstr(result)));
2207 /* Change password */
2208 new_nt_password = data_blob_const(
2209 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2210 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2212 old_nt_hash_enc = data_blob_const(
2213 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2214 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2216 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2217 new_lm_password = data_blob_const(
2218 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2219 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2221 old_lm_hash_enc = data_blob_const(
2222 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2223 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2225 new_lm_password.length = 0;
2226 old_lm_hash_enc.length = 0;
2229 /* Get sam handle */
2231 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2232 if (!NT_STATUS_IS_OK(result)) {
2233 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2237 result = rpccli_samr_chng_pswd_auth_crap(
2238 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2239 new_lm_password, old_lm_hash_enc);
2243 set_auth_errors(state->response, result);
2245 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2246 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2248 state->response->data.auth.nt_status_string,
2249 state->response->data.auth.pam_error));
2251 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;