2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "../librpc/gen_ndr/ndr_netlogon.h"
33 #define DBGC_CLASS DBGC_WINBIND
35 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
37 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
38 struct winbindd_cli_state *state,
39 struct netr_SamInfo3 *info3)
44 state->response->data.auth.info3.logon_time =
45 nt_time_to_unix(info3->base.last_logon);
46 state->response->data.auth.info3.logoff_time =
47 nt_time_to_unix(info3->base.last_logoff);
48 state->response->data.auth.info3.kickoff_time =
49 nt_time_to_unix(info3->base.acct_expiry);
50 state->response->data.auth.info3.pass_last_set_time =
51 nt_time_to_unix(info3->base.last_password_change);
52 state->response->data.auth.info3.pass_can_change_time =
53 nt_time_to_unix(info3->base.allow_password_change);
54 state->response->data.auth.info3.pass_must_change_time =
55 nt_time_to_unix(info3->base.force_password_change);
57 state->response->data.auth.info3.logon_count = info3->base.logon_count;
58 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
60 state->response->data.auth.info3.user_rid = info3->base.rid;
61 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
62 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
64 state->response->data.auth.info3.num_groups = info3->base.groups.count;
65 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
67 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
68 state->response->data.auth.info3.num_other_sids = info3->sidcount;
70 fstrcpy(state->response->data.auth.info3.user_name,
71 info3->base.account_name.string);
72 fstrcpy(state->response->data.auth.info3.full_name,
73 info3->base.full_name.string);
74 fstrcpy(state->response->data.auth.info3.logon_script,
75 info3->base.logon_script.string);
76 fstrcpy(state->response->data.auth.info3.profile_path,
77 info3->base.profile_path.string);
78 fstrcpy(state->response->data.auth.info3.home_dir,
79 info3->base.home_directory.string);
80 fstrcpy(state->response->data.auth.info3.dir_drive,
81 info3->base.home_drive.string);
83 fstrcpy(state->response->data.auth.info3.logon_srv,
84 info3->base.logon_server.string);
85 fstrcpy(state->response->data.auth.info3.logon_dom,
86 info3->base.domain.string);
88 ex = talloc_strdup(state->mem_ctx, "");
89 NT_STATUS_HAVE_NO_MEMORY(ex);
91 for (i=0; i < info3->base.groups.count; i++) {
92 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
93 info3->base.groups.rids[i].rid,
94 info3->base.groups.rids[i].attributes);
95 NT_STATUS_HAVE_NO_MEMORY(ex);
98 for (i=0; i < info3->sidcount; i++) {
101 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
102 NT_STATUS_HAVE_NO_MEMORY(sid);
104 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
106 info3->sids[i].attributes);
107 NT_STATUS_HAVE_NO_MEMORY(ex);
112 state->response->extra_data.data = ex;
113 state->response->length += talloc_get_size(ex);
118 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
119 struct winbindd_cli_state *state,
120 struct netr_SamInfo3 *info3)
123 enum ndr_err_code ndr_err;
125 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
126 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
127 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
128 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
129 return ndr_map_error2ntstatus(ndr_err);
132 state->response->extra_data.data = blob.data;
133 state->response->length += blob.length;
138 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
139 struct winbindd_cli_state *state,
140 const struct netr_SamInfo3 *info3,
141 const char *name_domain,
142 const char *name_user)
144 /* We've been asked to return the unix username, per
145 'winbind use default domain' settings and the like */
147 const char *nt_username, *nt_domain;
149 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
151 /* If the server didn't give us one, just use the one
153 nt_domain = name_domain;
156 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
158 /* If the server didn't give us one, just use the one
160 nt_username = name_user;
163 fill_domain_username(state->response->data.auth.unix_username,
164 nt_domain, nt_username, true);
166 DEBUG(5,("Setting unix username to [%s]\n",
167 state->response->data.auth.unix_username));
172 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
173 struct winbindd_cli_state *state,
174 const struct netr_SamInfo3 *info3,
175 const char *name_domain,
176 const char *name_user)
178 char *afsname = NULL;
182 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
183 if (afsname == NULL) {
184 return NT_STATUS_NO_MEMORY;
187 afsname = talloc_string_sub(mem_ctx,
188 lp_afs_username_map(),
190 afsname = talloc_string_sub(mem_ctx, afsname,
192 afsname = talloc_string_sub(mem_ctx, afsname,
199 sid_compose(&user_sid, info3->base.domain_sid,
201 sid_to_fstring(sidstr, &user_sid);
202 afsname = talloc_string_sub(mem_ctx, afsname,
206 if (afsname == NULL) {
207 return NT_STATUS_NO_MEMORY;
212 DEBUG(10, ("Generating token for user %s\n", afsname));
214 cell = strchr(afsname, '@');
217 return NT_STATUS_NO_MEMORY;
223 token = afs_createtoken_str(afsname, cell);
227 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
229 if (state->response->extra_data.data == NULL) {
230 return NT_STATUS_NO_MEMORY;
232 state->response->length +=
233 strlen((const char *)state->response->extra_data.data)+1;
238 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
239 const char *group_sid)
241 * Check whether a user belongs to a group or list of groups.
243 * @param mem_ctx talloc memory context.
244 * @param info3 user information, including group membership info.
245 * @param group_sid One or more groups , separated by commas.
247 * @return NT_STATUS_OK on success,
248 * NT_STATUS_LOGON_FAILURE if the user does not belong,
249 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
252 DOM_SID *require_membership_of_sid;
253 size_t num_require_membership_of_sid;
258 struct nt_user_token *token;
259 TALLOC_CTX *frame = talloc_stackframe();
262 /* Parse the 'required group' SID */
264 if (!group_sid || !group_sid[0]) {
265 /* NO sid supplied, all users may access */
269 token = talloc_zero(talloc_tos(), struct nt_user_token);
271 DEBUG(0, ("talloc failed\n"));
273 return NT_STATUS_NO_MEMORY;
276 num_require_membership_of_sid = 0;
277 require_membership_of_sid = NULL;
281 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
282 if (!string_to_sid(&sid, req_sid)) {
283 DEBUG(0, ("check_info3_in_group: could not parse %s "
284 "as a SID!", req_sid));
286 return NT_STATUS_INVALID_PARAMETER;
289 status = add_sid_to_array(talloc_tos(), &sid,
290 &require_membership_of_sid,
291 &num_require_membership_of_sid);
292 if (!NT_STATUS_IS_OK(status)) {
293 DEBUG(0, ("add_sid_to_array failed\n"));
299 status = sid_array_from_info3(talloc_tos(), info3,
303 if (!NT_STATUS_IS_OK(status)) {
308 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
310 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
312 DEBUG(3, ("could not add aliases: %s\n",
318 debug_nt_user_token(DBGC_CLASS, 10, token);
320 for (i=0; i<num_require_membership_of_sid; i++) {
321 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
322 &require_membership_of_sid[i])));
323 if (nt_token_check_sid(&require_membership_of_sid[i],
325 DEBUG(10, ("Access ok\n"));
331 /* Do not distinguish this error from a wrong username/pw */
334 return NT_STATUS_LOGON_FAILURE;
337 struct winbindd_domain *find_auth_domain(uint8_t flags,
338 const char *domain_name)
340 struct winbindd_domain *domain;
343 domain = find_domain_from_name_noinit(domain_name);
344 if (domain == NULL) {
345 DEBUG(3, ("Authentication for domain [%s] refused "
346 "as it is not a trusted domain\n",
352 if (strequal(domain_name, get_global_sam_name())) {
353 return find_domain_from_name_noinit(domain_name);
356 /* we can auth against trusted domains */
357 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
358 domain = find_domain_from_name_noinit(domain_name);
359 if (domain == NULL) {
360 DEBUG(3, ("Authentication for domain [%s] skipped "
361 "as it is not a trusted domain\n",
368 return find_our_domain();
371 static void fill_in_password_policy(struct winbindd_response *r,
372 const struct samr_DomInfo1 *p)
374 r->data.auth.policy.min_length_password =
375 p->min_password_length;
376 r->data.auth.policy.password_history =
377 p->password_history_length;
378 r->data.auth.policy.password_properties =
379 p->password_properties;
380 r->data.auth.policy.expire =
381 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
382 r->data.auth.policy.min_passwordage =
383 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
386 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
387 struct winbindd_cli_state *state)
389 struct winbindd_methods *methods;
390 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
391 struct samr_DomInfo1 password_policy;
393 if ( !winbindd_can_contact_domain( domain ) ) {
394 DEBUG(5,("fillup_password_policy: No inbound trust to "
395 "contact domain %s\n", domain->name));
396 return NT_STATUS_NOT_SUPPORTED;
399 methods = domain->methods;
401 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
402 if (NT_STATUS_IS_ERR(status)) {
406 fill_in_password_policy(state->response, &password_policy);
411 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
413 uint16 *lockout_threshold)
415 struct winbindd_methods *methods;
416 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
417 struct samr_DomInfo12 lockout_policy;
419 *lockout_threshold = 0;
421 methods = domain->methods;
423 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
424 if (NT_STATUS_IS_ERR(status)) {
428 *lockout_threshold = lockout_policy.lockout_threshold;
433 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
435 uint32 *password_properties)
437 struct winbindd_methods *methods;
438 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
439 struct samr_DomInfo1 password_policy;
441 *password_properties = 0;
443 methods = domain->methods;
445 status = methods->password_policy(domain, mem_ctx, &password_policy);
446 if (NT_STATUS_IS_ERR(status)) {
450 *password_properties = password_policy.password_properties;
457 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
460 bool *internal_ccache)
462 /* accept FILE and WRFILE as krb5_cc_type from the client and then
463 * build the full ccname string based on the user's uid here -
466 const char *gen_cc = NULL;
468 *internal_ccache = true;
474 if (!type || type[0] == '\0') {
478 if (strequal(type, "FILE")) {
479 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
480 } else if (strequal(type, "WRFILE")) {
481 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
483 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
487 *internal_ccache = false;
491 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
494 if (gen_cc == NULL) {
495 DEBUG(0,("out of memory\n"));
499 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
504 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
506 const char *type = state->request->data.auth.krb5_cc_type;
508 state->response->data.auth.krb5ccname[0] = '\0';
510 if (type[0] == '\0') {
514 if (!strequal(type, "FILE") &&
515 !strequal(type, "WRFILE")) {
516 DEBUG(10,("won't return krbccname for a %s type ccache\n",
521 fstrcpy(state->response->data.auth.krb5ccname, cc);
526 uid_t get_uid_from_request(struct winbindd_request *request)
530 uid = request->data.auth.uid;
533 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
539 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
541 return get_uid_from_request(state->request);
544 /**********************************************************************
545 Authenticate a user with a clear text password using Kerberos and fill up
547 **********************************************************************/
549 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
550 struct winbindd_cli_state *state,
551 struct netr_SamInfo3 **info3)
554 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
555 krb5_error_code krb5_ret;
556 const char *cc = NULL;
557 const char *principal_s = NULL;
558 const char *service = NULL;
560 fstring name_domain, name_user;
561 time_t ticket_lifetime = 0;
562 time_t renewal_until = 0;
565 time_t time_offset = 0;
566 bool internal_ccache = true;
567 struct PAC_LOGON_INFO *logon_info = NULL;
572 * prepare a krb5_cc_cache string for the user */
574 uid = get_uid_from_state(state);
576 DEBUG(0,("no valid uid\n"));
579 cc = generate_krb5_ccache(state->mem_ctx,
580 state->request->data.auth.krb5_cc_type,
581 state->request->data.auth.uid,
584 return NT_STATUS_NO_MEMORY;
589 * get kerberos properties */
591 if (domain->private_data) {
592 ads = (ADS_STRUCT *)domain->private_data;
593 time_offset = ads->auth.time_offset;
598 * do kerberos auth and setup ccache as the user */
600 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
602 realm = domain->alt_name;
605 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
606 if (principal_s == NULL) {
607 return NT_STATUS_NO_MEMORY;
610 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
611 if (service == NULL) {
612 return NT_STATUS_NO_MEMORY;
615 /* if this is a user ccache, we need to act as the user to let the krb5
616 * library handle the chown, etc. */
618 /************************ ENTERING NON-ROOT **********************/
620 if (!internal_ccache) {
621 set_effective_uid(uid);
622 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
625 result = kerberos_return_pac(state->mem_ctx,
627 state->request->data.auth.pass,
634 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
637 if (!internal_ccache) {
638 gain_root_privilege();
641 /************************ RETURNED TO ROOT **********************/
643 if (!NT_STATUS_IS_OK(result)) {
647 *info3 = &logon_info->info3;
649 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
652 /* if we had a user's ccache then return that string for the pam
655 if (!internal_ccache) {
657 setup_return_cc_name(state, cc);
659 result = add_ccache_to_list(principal_s,
662 state->request->data.auth.user,
670 if (!NT_STATUS_IS_OK(result)) {
671 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
676 /* need to delete the memory cred cache, it is not used anymore */
678 krb5_ret = ads_kdestroy(cc);
680 DEBUG(3,("winbindd_raw_kerberos_login: "
681 "could not destroy krb5 credential cache: "
682 "%s\n", error_message(krb5_ret)));
691 /* we could have created a new credential cache with a valid tgt in it
692 * but we werent able to get or verify the service ticket for this
693 * local host and therefor didn't get the PAC, we need to remove that
694 * cache entirely now */
696 krb5_ret = ads_kdestroy(cc);
698 DEBUG(3,("winbindd_raw_kerberos_login: "
699 "could not destroy krb5 credential cache: "
700 "%s\n", error_message(krb5_ret)));
703 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
704 DEBUG(3,("winbindd_raw_kerberos_login: "
705 "could not remove ccache for user %s\n",
706 state->request->data.auth.user));
711 return NT_STATUS_NOT_SUPPORTED;
712 #endif /* HAVE_KRB5 */
715 /****************************************************************
716 ****************************************************************/
718 bool check_request_flags(uint32_t flags)
720 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
721 WBFLAG_PAM_INFO3_TEXT |
722 WBFLAG_PAM_INFO3_NDR;
724 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
725 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
726 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
727 !(flags & flags_edata) ) {
731 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
737 /****************************************************************
738 ****************************************************************/
740 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
741 struct netr_SamInfo3 *info3,
742 const char *name_domain,
743 const char *name_user)
746 uint32_t flags = state->request->flags;
748 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
749 memcpy(state->response->data.auth.user_session_key,
751 sizeof(state->response->data.auth.user_session_key)
755 if (flags & WBFLAG_PAM_LMKEY) {
756 memcpy(state->response->data.auth.first_8_lm_hash,
757 info3->base.LMSessKey.key,
758 sizeof(state->response->data.auth.first_8_lm_hash)
762 if (flags & WBFLAG_PAM_UNIX_NAME) {
763 result = append_unix_username(state->mem_ctx, state, info3,
764 name_domain, name_user);
765 if (!NT_STATUS_IS_OK(result)) {
766 DEBUG(10,("Failed to append Unix Username: %s\n",
772 /* currently, anything from here on potentially overwrites extra_data. */
774 if (flags & WBFLAG_PAM_INFO3_NDR) {
775 result = append_info3_as_ndr(state->mem_ctx, state, info3);
776 if (!NT_STATUS_IS_OK(result)) {
777 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
783 if (flags & WBFLAG_PAM_INFO3_TEXT) {
784 result = append_info3_as_txt(state->mem_ctx, state, info3);
785 if (!NT_STATUS_IS_OK(result)) {
786 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
792 if (flags & WBFLAG_PAM_AFS_TOKEN) {
793 result = append_afs_token(state->mem_ctx, state, info3,
794 name_domain, name_user);
795 if (!NT_STATUS_IS_OK(result)) {
796 DEBUG(10,("Failed to append AFS token: %s\n",
805 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
806 struct winbindd_cli_state *state,
807 struct netr_SamInfo3 **info3)
809 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
810 uint16 max_allowed_bad_attempts;
811 fstring name_domain, name_user;
813 enum lsa_SidType type;
814 uchar new_nt_pass[NT_HASH_LEN];
815 const uint8 *cached_nt_pass;
816 const uint8 *cached_salt;
817 struct netr_SamInfo3 *my_info3;
818 time_t kickoff_time, must_change_time;
819 bool password_good = false;
821 struct winbindd_tdc_domain *tdc_domain = NULL;
828 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
830 /* Parse domain and username */
832 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
835 if (!lookup_cached_name(state->mem_ctx,
840 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
841 return NT_STATUS_NO_SUCH_USER;
844 if (type != SID_NAME_USER) {
845 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
846 return NT_STATUS_LOGON_FAILURE;
849 result = winbindd_get_creds(domain,
855 if (!NT_STATUS_IS_OK(result)) {
856 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
862 E_md4hash(state->request->data.auth.pass, new_nt_pass);
864 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
865 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
867 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
871 /* In this case we didn't store the nt_hash itself,
872 but the MD5 combination of salt + nt_hash. */
873 uchar salted_hash[NT_HASH_LEN];
874 E_md5hash(cached_salt, new_nt_pass, salted_hash);
876 password_good = (memcmp(cached_nt_pass, salted_hash,
879 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
880 password_good = (memcmp(cached_nt_pass, new_nt_pass,
886 /* User *DOES* know the password, update logon_time and reset
889 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
891 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
892 return NT_STATUS_ACCOUNT_LOCKED_OUT;
895 if (my_info3->base.acct_flags & ACB_DISABLED) {
896 return NT_STATUS_ACCOUNT_DISABLED;
899 if (my_info3->base.acct_flags & ACB_WSTRUST) {
900 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
903 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
904 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
907 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
908 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
911 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
912 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
913 my_info3->base.acct_flags));
914 return NT_STATUS_LOGON_FAILURE;
917 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
918 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
919 return NT_STATUS_ACCOUNT_EXPIRED;
922 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
923 if (must_change_time != 0 && must_change_time < time(NULL)) {
924 /* we allow grace logons when the password has expired */
925 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
926 /* return NT_STATUS_PASSWORD_EXPIRED; */
931 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
932 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
933 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
934 /* used to cope with the case winbindd starting without network. */
935 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
938 const char *cc = NULL;
940 const char *principal_s = NULL;
941 const char *service = NULL;
942 bool internal_ccache = false;
944 uid = get_uid_from_state(state);
946 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
947 return NT_STATUS_INVALID_PARAMETER;
950 cc = generate_krb5_ccache(state->mem_ctx,
951 state->request->data.auth.krb5_cc_type,
952 state->request->data.auth.uid,
955 return NT_STATUS_NO_MEMORY;
958 realm = domain->alt_name;
961 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
962 if (principal_s == NULL) {
963 return NT_STATUS_NO_MEMORY;
966 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
967 if (service == NULL) {
968 return NT_STATUS_NO_MEMORY;
971 if (!internal_ccache) {
973 setup_return_cc_name(state, cc);
975 result = add_ccache_to_list(principal_s,
978 state->request->data.auth.user,
982 time(NULL) + lp_winbind_cache_time(),
983 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
986 if (!NT_STATUS_IS_OK(result)) {
987 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
988 "to add ccache to list: %s\n",
993 #endif /* HAVE_KRB5 */
995 /* FIXME: we possibly should handle logon hours as well (does xp when
996 * offline?) see auth/auth_sam.c:sam_account_ok for details */
998 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
999 my_info3->base.bad_password_count = 0;
1001 result = winbindd_update_creds_by_info3(domain,
1003 state->request->data.auth.user,
1004 state->request->data.auth.pass,
1006 if (!NT_STATUS_IS_OK(result)) {
1007 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1008 nt_errstr(result)));
1012 return NT_STATUS_OK;
1016 /* User does *NOT* know the correct password, modify info3 accordingly */
1018 /* failure of this is not critical */
1019 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1020 if (!NT_STATUS_IS_OK(result)) {
1021 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1022 "Won't be able to honour account lockout policies\n"));
1025 /* increase counter */
1026 my_info3->base.bad_password_count++;
1028 if (max_allowed_bad_attempts == 0) {
1033 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1035 uint32 password_properties;
1037 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1038 if (!NT_STATUS_IS_OK(result)) {
1039 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1042 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1043 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1044 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1049 result = winbindd_update_creds_by_info3(domain,
1051 state->request->data.auth.user,
1055 if (!NT_STATUS_IS_OK(result)) {
1056 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1057 nt_errstr(result)));
1060 return NT_STATUS_LOGON_FAILURE;
1063 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1064 struct winbindd_cli_state *state,
1065 struct netr_SamInfo3 **info3)
1067 struct winbindd_domain *contact_domain;
1068 fstring name_domain, name_user;
1071 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1073 /* Parse domain and username */
1075 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1077 /* what domain should we contact? */
1080 if (!(contact_domain = find_domain_from_name(name_domain))) {
1081 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1082 state->request->data.auth.user, name_domain, name_user, name_domain));
1083 result = NT_STATUS_NO_SUCH_USER;
1088 if (is_myname(name_domain)) {
1089 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1090 result = NT_STATUS_NO_SUCH_USER;
1094 contact_domain = find_domain_from_name(name_domain);
1095 if (contact_domain == NULL) {
1096 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1097 state->request->data.auth.user, name_domain, name_user, name_domain));
1099 contact_domain = find_our_domain();
1103 if (contact_domain->initialized &&
1104 contact_domain->active_directory) {
1108 if (!contact_domain->initialized) {
1109 init_dc_connection(contact_domain);
1112 if (!contact_domain->active_directory) {
1113 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1114 return NT_STATUS_INVALID_LOGON_TYPE;
1117 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1122 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1123 const char *domain, const char *user,
1124 const DATA_BLOB *challenge,
1125 const DATA_BLOB *lm_resp,
1126 const DATA_BLOB *nt_resp,
1127 struct netr_SamInfo3 **pinfo3)
1129 struct auth_usersupplied_info *user_info = NULL;
1130 struct auth_serversupplied_info *server_info = NULL;
1131 struct netr_SamInfo3 *info3;
1134 status = make_user_info(&user_info, user, user, domain, domain,
1135 global_myname(), lm_resp, nt_resp, NULL, NULL,
1137 if (!NT_STATUS_IS_OK(status)) {
1138 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1142 status = check_sam_security(challenge, talloc_tos(), user_info,
1144 free_user_info(&user_info);
1146 if (!NT_STATUS_IS_OK(status)) {
1147 DEBUG(10, ("check_ntlm_password failed: %s\n",
1148 nt_errstr(status)));
1152 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1153 if (info3 == NULL) {
1154 return NT_STATUS_NO_MEMORY;
1157 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1158 if (!NT_STATUS_IS_OK(status)) {
1159 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1160 nt_errstr(status)));
1164 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1166 return NT_STATUS_OK;
1169 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1170 TALLOC_CTX *mem_ctx,
1171 uint32 logon_parameters,
1173 const char *username,
1175 const char *workstation,
1176 const uint8 chal[8],
1177 DATA_BLOB lm_response,
1178 DATA_BLOB nt_response,
1179 struct netr_SamInfo3 **info3);
1181 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1182 struct winbindd_cli_state *state,
1183 struct netr_SamInfo3 **info3)
1186 struct rpc_pipe_client *netlogon_pipe;
1191 unsigned char local_lm_response[24];
1192 unsigned char local_nt_response[24];
1193 fstring name_domain, name_user;
1196 struct netr_SamInfo3 *my_info3 = NULL;
1200 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1202 /* Parse domain and username */
1204 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1206 /* do password magic */
1208 generate_random_buffer(chal, sizeof(chal));
1210 if (lp_client_ntlmv2_auth()) {
1211 DATA_BLOB server_chal;
1212 DATA_BLOB names_blob;
1213 DATA_BLOB nt_response;
1214 DATA_BLOB lm_response;
1215 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1217 /* note that the 'workgroup' here is a best guess - we don't know
1218 the server's domain at this point. The 'server name' is also
1221 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1223 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1224 state->request->data.auth.pass,
1227 &lm_response, &nt_response, NULL, NULL)) {
1228 data_blob_free(&names_blob);
1229 data_blob_free(&server_chal);
1230 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1231 result = NT_STATUS_NO_MEMORY;
1234 data_blob_free(&names_blob);
1235 data_blob_free(&server_chal);
1236 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1237 lm_response.length);
1238 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1239 nt_response.length);
1240 data_blob_free(&lm_response);
1241 data_blob_free(&nt_response);
1244 if (lp_client_lanman_auth()
1245 && SMBencrypt(state->request->data.auth.pass,
1247 local_lm_response)) {
1248 lm_resp = data_blob_talloc(state->mem_ctx,
1250 sizeof(local_lm_response));
1252 lm_resp = data_blob_null;
1254 SMBNTencrypt(state->request->data.auth.pass,
1258 nt_resp = data_blob_talloc(state->mem_ctx,
1260 sizeof(local_nt_response));
1263 if (strequal(name_domain, get_global_sam_name())) {
1264 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1266 result = winbindd_dual_auth_passdb(
1267 state->mem_ctx, name_domain, name_user,
1268 &chal_blob, &lm_resp, &nt_resp, info3);
1272 /* check authentication loop */
1275 netlogon_fn_t logon_fn;
1277 ZERO_STRUCTP(my_info3);
1280 result = cm_connect_netlogon(domain, &netlogon_pipe);
1282 if (!NT_STATUS_IS_OK(result)) {
1283 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1287 /* It is really important to try SamLogonEx here,
1288 * because in a clustered environment, we want to use
1289 * one machine account from multiple physical
1292 * With a normal SamLogon call, we must keep the
1293 * credentials chain updated and intact between all
1294 * users of the machine account (which would imply
1295 * cross-node communication for every NTLM logon).
1297 * (The credentials chain is not per NETLOGON pipe
1298 * connection, but globally on the server/client pair
1301 * When using SamLogonEx, the credentials are not
1302 * supplied, but the session key is implied by the
1303 * wrapping SamLogon context.
1305 * -- abartlet 21 April 2008
1308 logon_fn = domain->can_do_samlogon_ex
1309 ? rpccli_netlogon_sam_network_logon_ex
1310 : rpccli_netlogon_sam_network_logon;
1312 result = logon_fn(netlogon_pipe,
1315 domain->dcname, /* server name */
1316 name_user, /* user name */
1317 name_domain, /* target domain */
1318 global_myname(), /* workstation */
1325 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1326 && domain->can_do_samlogon_ex) {
1327 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1328 "retrying with NetSamLogon\n"));
1329 domain->can_do_samlogon_ex = false;
1334 /* We have to try a second time as cm_connect_netlogon
1335 might not yet have noticed that the DC has killed
1338 if (!rpccli_is_connected(netlogon_pipe)) {
1343 /* if we get access denied, a possible cause was that we had
1344 and open connection to the DC, but someone changed our
1345 machine account password out from underneath us using 'net
1346 rpc changetrustpw' */
1348 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1349 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1350 "ACCESS_DENIED. Maybe the trust account "
1351 "password was changed and we didn't know it. "
1352 "Killing connections to domain %s\n",
1354 invalidate_cm_connection(&domain->conn);
1358 } while ( (attempts < 2) && retry );
1360 /* handle the case where a NT4 DC does not fill in the acct_flags in
1361 * the samlogon reply info3. When accurate info3 is required by the
1362 * caller, we look up the account flags ourselve - gd */
1364 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1365 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1367 struct rpc_pipe_client *samr_pipe;
1368 struct policy_handle samr_domain_handle, user_pol;
1369 union samr_UserInfo *info = NULL;
1370 NTSTATUS status_tmp;
1373 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1374 &samr_pipe, &samr_domain_handle);
1376 if (!NT_STATUS_IS_OK(status_tmp)) {
1377 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1378 nt_errstr(status_tmp)));
1382 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1383 &samr_domain_handle,
1384 MAXIMUM_ALLOWED_ACCESS,
1388 if (!NT_STATUS_IS_OK(status_tmp)) {
1389 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1390 nt_errstr(status_tmp)));
1394 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1399 if (!NT_STATUS_IS_OK(status_tmp)) {
1400 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1401 nt_errstr(status_tmp)));
1402 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1406 acct_flags = info->info16.acct_flags;
1408 if (acct_flags == 0) {
1409 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1413 my_info3->base.acct_flags = acct_flags;
1415 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1417 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1425 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1426 struct winbindd_cli_state *state)
1428 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1429 NTSTATUS krb5_result = NT_STATUS_OK;
1430 fstring name_domain, name_user;
1432 fstring domain_user;
1433 struct netr_SamInfo3 *info3 = NULL;
1434 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1436 /* Ensure null termination */
1437 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1439 /* Ensure null termination */
1440 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1442 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1443 state->request->data.auth.user));
1445 if (!check_request_flags(state->request->flags)) {
1446 result = NT_STATUS_INVALID_PARAMETER_MIX;
1450 /* Parse domain and username */
1452 name_map_status = normalize_name_unmap(state->mem_ctx,
1453 state->request->data.auth.user,
1456 /* If the name normalization didnt' actually do anything,
1457 just use the original name */
1459 if (!NT_STATUS_IS_OK(name_map_status) &&
1460 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1462 mapped_user = state->request->data.auth.user;
1465 parse_domain_user(mapped_user, name_domain, name_user);
1467 if ( mapped_user != state->request->data.auth.user ) {
1468 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1469 safe_strcpy( state->request->data.auth.user, domain_user,
1470 sizeof(state->request->data.auth.user)-1 );
1473 if (domain->online == false) {
1474 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1475 if (domain->startup) {
1476 /* Logons are very important to users. If we're offline and
1477 we get a request within the first 30 seconds of startup,
1478 try very hard to find a DC and go online. */
1480 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1481 "request in startup mode.\n", domain->name ));
1483 winbindd_flush_negative_conn_cache(domain);
1484 result = init_dc_connection(domain);
1488 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1490 /* Check for Kerberos authentication */
1491 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1493 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1494 /* save for later */
1495 krb5_result = result;
1498 if (NT_STATUS_IS_OK(result)) {
1499 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1500 goto process_result;
1502 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1505 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1506 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1507 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1508 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1509 set_domain_offline( domain );
1513 /* there are quite some NT_STATUS errors where there is no
1514 * point in retrying with a samlogon, we explictly have to take
1515 * care not to increase the bad logon counter on the DC */
1517 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1518 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1519 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1520 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1530 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1531 DEBUG(3,("falling back to samlogon\n"));
1539 /* Check for Samlogon authentication */
1540 if (domain->online) {
1541 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1543 if (NT_STATUS_IS_OK(result)) {
1544 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1545 /* add the Krb5 err if we have one */
1546 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1547 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1549 goto process_result;
1552 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1553 nt_errstr(result)));
1555 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1557 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1559 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1560 set_domain_offline( domain );
1564 if (domain->online) {
1565 /* We're still online - fail. */
1571 /* Check for Cached logons */
1572 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1573 lp_winbind_offline_logon()) {
1575 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1577 if (NT_STATUS_IS_OK(result)) {
1578 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1579 goto process_result;
1581 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1588 if (NT_STATUS_IS_OK(result)) {
1592 /* In all codepaths where result == NT_STATUS_OK info3 must have
1593 been initialized. */
1595 result = NT_STATUS_INTERNAL_ERROR;
1599 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1600 netsamlogon_cache_store(name_user, info3);
1602 /* save name_to_sid info as early as possible (only if
1603 this is our primary domain so we don't invalidate
1604 the cache entry by storing the seq_num for the wrong
1606 if ( domain->primary ) {
1607 sid_compose(&user_sid, info3->base.domain_sid,
1609 cache_name2sid(domain, name_domain, name_user,
1610 SID_NAME_USER, &user_sid);
1613 /* Check if the user is in the right group */
1615 result = check_info3_in_group(
1617 state->request->data.auth.require_membership_of_sid);
1618 if (!NT_STATUS_IS_OK(result)) {
1619 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1620 state->request->data.auth.user,
1621 state->request->data.auth.require_membership_of_sid));
1625 result = append_auth_data(state, info3, name_domain,
1627 if (!NT_STATUS_IS_OK(result)) {
1631 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1633 if (lp_winbind_offline_logon()) {
1634 result = winbindd_store_creds(domain,
1636 state->request->data.auth.user,
1637 state->request->data.auth.pass,
1643 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1644 struct winbindd_domain *our_domain = find_our_domain();
1646 /* This is not entirely correct I believe, but it is
1647 consistent. Only apply the password policy settings
1648 too warn users for our own domain. Cannot obtain these
1649 from trusted DCs all the time so don't do it at all.
1652 result = NT_STATUS_NOT_SUPPORTED;
1653 if (our_domain == domain ) {
1654 result = fillup_password_policy(our_domain, state);
1657 if (!NT_STATUS_IS_OK(result)
1658 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1660 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1661 domain->name, nt_errstr(result)));
1666 result = NT_STATUS_OK;
1670 /* give us a more useful (more correct?) error code */
1671 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1672 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1673 result = NT_STATUS_NO_LOGON_SERVERS;
1676 set_auth_errors(state->response, result);
1678 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1679 state->request->data.auth.user,
1680 state->response->data.auth.nt_status_string,
1681 state->response->data.auth.pam_error));
1683 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1686 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1687 struct winbindd_cli_state *state)
1690 struct netr_SamInfo3 *info3 = NULL;
1691 struct rpc_pipe_client *netlogon_pipe;
1692 const char *name_user = NULL;
1693 const char *name_domain = NULL;
1694 const char *workstation;
1698 DATA_BLOB lm_resp, nt_resp;
1700 /* This is child-only, so no check for privileged access is needed
1703 /* Ensure null termination */
1704 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1705 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1707 if (!check_request_flags(state->request->flags)) {
1708 result = NT_STATUS_INVALID_PARAMETER_MIX;
1712 name_user = state->request->data.auth_crap.user;
1714 if (*state->request->data.auth_crap.domain) {
1715 name_domain = state->request->data.auth_crap.domain;
1716 } else if (lp_winbind_use_default_domain()) {
1717 name_domain = lp_workgroup();
1719 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1721 result = NT_STATUS_NO_SUCH_USER;
1725 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1726 name_domain, name_user));
1728 if (*state->request->data.auth_crap.workstation) {
1729 workstation = state->request->data.auth_crap.workstation;
1731 workstation = global_myname();
1734 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1735 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1736 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1737 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1738 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1739 state->request->data.auth_crap.lm_resp_len,
1740 state->request->data.auth_crap.nt_resp_len));
1741 result = NT_STATUS_INVALID_PARAMETER;
1746 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1747 state->request->data.auth_crap.lm_resp_len);
1749 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1750 nt_resp = data_blob_talloc(state->mem_ctx,
1751 state->request->extra_data.data,
1752 state->request->data.auth_crap.nt_resp_len);
1754 nt_resp = data_blob_talloc(state->mem_ctx,
1755 state->request->data.auth_crap.nt_resp,
1756 state->request->data.auth_crap.nt_resp_len);
1759 if (strequal(name_domain, get_global_sam_name())) {
1760 DATA_BLOB chal_blob = data_blob_const(
1761 state->request->data.auth_crap.chal,
1762 sizeof(state->request->data.auth_crap.chal));
1764 result = winbindd_dual_auth_passdb(
1765 state->mem_ctx, name_domain, name_user,
1766 &chal_blob, &lm_resp, &nt_resp, &info3);
1767 goto process_result;
1771 netlogon_fn_t logon_fn;
1775 netlogon_pipe = NULL;
1776 result = cm_connect_netlogon(domain, &netlogon_pipe);
1778 if (!NT_STATUS_IS_OK(result)) {
1779 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1780 nt_errstr(result)));
1784 logon_fn = domain->can_do_samlogon_ex
1785 ? rpccli_netlogon_sam_network_logon_ex
1786 : rpccli_netlogon_sam_network_logon;
1788 result = logon_fn(netlogon_pipe,
1790 state->request->data.auth_crap.logon_parameters,
1794 /* Bug #3248 - found by Stefan Burkei. */
1795 workstation, /* We carefully set this above so use it... */
1796 state->request->data.auth_crap.chal,
1801 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1802 && domain->can_do_samlogon_ex) {
1803 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1804 "retrying with NetSamLogon\n"));
1805 domain->can_do_samlogon_ex = false;
1812 /* We have to try a second time as cm_connect_netlogon
1813 might not yet have noticed that the DC has killed
1816 if (!rpccli_is_connected(netlogon_pipe)) {
1821 /* if we get access denied, a possible cause was that we had and open
1822 connection to the DC, but someone changed our machine account password
1823 out from underneath us using 'net rpc changetrustpw' */
1825 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1826 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1827 "ACCESS_DENIED. Maybe the trust account "
1828 "password was changed and we didn't know it. "
1829 "Killing connections to domain %s\n",
1831 invalidate_cm_connection(&domain->conn);
1835 } while ( (attempts < 2) && retry );
1839 if (NT_STATUS_IS_OK(result)) {
1841 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1842 netsamlogon_cache_store(name_user, info3);
1844 /* Check if the user is in the right group */
1846 result = check_info3_in_group(
1848 state->request->data.auth_crap.require_membership_of_sid);
1849 if (!NT_STATUS_IS_OK(result)) {
1850 DEBUG(3, ("User %s is not in the required group (%s), so "
1851 "crap authentication is rejected\n",
1852 state->request->data.auth_crap.user,
1853 state->request->data.auth_crap.require_membership_of_sid));
1857 result = append_auth_data(state, info3, name_domain,
1859 if (!NT_STATUS_IS_OK(result)) {
1866 /* give us a more useful (more correct?) error code */
1867 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1868 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1869 result = NT_STATUS_NO_LOGON_SERVERS;
1872 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1873 result = nt_status_squash(result);
1876 set_auth_errors(state->response, result);
1878 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1879 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1882 state->response->data.auth.nt_status_string,
1883 state->response->data.auth.pam_error));
1885 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1888 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1889 struct winbindd_cli_state *state)
1892 char *newpass = NULL;
1893 struct policy_handle dom_pol;
1894 struct rpc_pipe_client *cli;
1895 bool got_info = false;
1896 struct samr_DomInfo1 *info = NULL;
1897 struct userPwdChangeFailureInformation *reject = NULL;
1898 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1899 fstring domain, user;
1901 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1902 state->request->data.auth.user));
1904 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1908 /* Change password */
1910 oldpass = state->request->data.chauthtok.oldpass;
1911 newpass = state->request->data.chauthtok.newpass;
1913 /* Initialize reject reason */
1914 state->response->data.auth.reject_reason = Undefined;
1916 if (strequal(domain, get_global_sam_name())) {
1917 struct samr_CryptPassword new_nt_password;
1918 struct samr_CryptPassword new_lm_password;
1919 struct samr_Password old_nt_hash_enc;
1920 struct samr_Password old_lanman_hash_enc;
1921 enum samPwdChangeReason rejectReason;
1923 uchar old_nt_hash[16];
1924 uchar old_lanman_hash[16];
1925 uchar new_nt_hash[16];
1926 uchar new_lanman_hash[16];
1928 contact_domain = NULL;
1930 E_md4hash(oldpass, old_nt_hash);
1931 E_md4hash(newpass, new_nt_hash);
1933 if (lp_client_lanman_auth() &&
1934 E_deshash(newpass, new_lanman_hash) &&
1935 E_deshash(oldpass, old_lanman_hash)) {
1937 /* E_deshash returns false for 'long' passwords (> 14
1938 DOS chars). This allows us to match Win2k, which
1939 does not store a LM hash for these passwords (which
1940 would reduce the effective password length to 14) */
1942 encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
1943 arcfour_crypt(new_lm_password.data, old_nt_hash, 516);
1944 E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
1946 ZERO_STRUCT(new_lm_password);
1947 ZERO_STRUCT(old_lanman_hash_enc);
1950 encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
1952 arcfour_crypt(new_nt_password.data, old_nt_hash, 516);
1953 E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
1955 result = pass_oem_change(
1957 new_lm_password.data, old_lanman_hash_enc.hash,
1958 new_nt_password.data, old_nt_hash_enc.hash,
1963 /* Get sam handle */
1965 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1967 if (!NT_STATUS_IS_OK(result)) {
1968 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1972 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1979 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1981 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1983 fill_in_password_policy(state->response, info);
1985 state->response->data.auth.reject_reason =
1986 reject->extendedFailureReason;
1991 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1992 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1993 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1994 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1996 /* only fallback when the chgpasswd_user3 call is not supported */
1997 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1998 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1999 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2000 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2002 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2003 nt_errstr(result)));
2005 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2007 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2008 Map to the same status code as Windows 2003. */
2010 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2011 result = NT_STATUS_PASSWORD_RESTRICTION;
2017 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2018 if (lp_winbind_offline_logon()) {
2019 result = winbindd_update_creds_by_name(contact_domain,
2020 state->mem_ctx, user,
2022 /* Again, this happens when we login from gdm or xdm
2023 * and the password expires, *BUT* cached crendentials
2024 * doesn't exist. winbindd_update_creds_by_name()
2025 * returns NT_STATUS_NO_SUCH_USER.
2026 * This is not a failure.
2029 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2030 result = NT_STATUS_OK;
2033 if (!NT_STATUS_IS_OK(result)) {
2034 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2035 goto process_result;
2040 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2042 NTSTATUS policy_ret;
2044 policy_ret = fillup_password_policy(contact_domain, state);
2046 /* failure of this is non critical, it will just provide no
2047 * additional information to the client why the change has
2048 * failed - Guenther */
2050 if (!NT_STATUS_IS_OK(policy_ret)) {
2051 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2052 goto process_result;
2058 set_auth_errors(state->response, result);
2060 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2061 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2064 state->response->data.auth.nt_status_string,
2065 state->response->data.auth.pam_error));
2067 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2070 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2071 struct winbindd_cli_state *state)
2073 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2075 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2076 state->request->data.logoff.user));
2078 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2079 result = NT_STATUS_OK;
2080 goto process_result;
2083 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2084 result = NT_STATUS_OK;
2085 goto process_result;
2090 if (state->request->data.logoff.uid < 0) {
2091 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2092 goto process_result;
2095 /* what we need here is to find the corresponding krb5 ccache name *we*
2096 * created for a given username and destroy it */
2098 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2099 result = NT_STATUS_OK;
2100 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2101 goto process_result;
2104 if (!ccache_entry_identical(state->request->data.logoff.user,
2105 state->request->data.logoff.uid,
2106 state->request->data.logoff.krb5ccname)) {
2107 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2108 goto process_result;
2111 result = remove_ccache(state->request->data.logoff.user);
2112 if (!NT_STATUS_IS_OK(result)) {
2113 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2114 nt_errstr(result)));
2115 goto process_result;
2119 result = NT_STATUS_NOT_SUPPORTED;
2125 set_auth_errors(state->response, result);
2127 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2130 /* Change user password with auth crap*/
2132 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2135 DATA_BLOB new_nt_password;
2136 DATA_BLOB old_nt_hash_enc;
2137 DATA_BLOB new_lm_password;
2138 DATA_BLOB old_lm_hash_enc;
2139 fstring domain,user;
2140 struct policy_handle dom_pol;
2141 struct winbindd_domain *contact_domain = domainSt;
2142 struct rpc_pipe_client *cli;
2144 /* Ensure null termination */
2145 state->request->data.chng_pswd_auth_crap.user[
2146 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2147 state->request->data.chng_pswd_auth_crap.domain[
2148 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2152 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2153 (unsigned long)state->pid,
2154 state->request->data.chng_pswd_auth_crap.domain,
2155 state->request->data.chng_pswd_auth_crap.user));
2157 if (lp_winbind_offline_logon()) {
2158 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2159 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2160 result = NT_STATUS_ACCESS_DENIED;
2164 if (*state->request->data.chng_pswd_auth_crap.domain) {
2165 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2167 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2171 DEBUG(3,("no domain specified with username (%s) - "
2173 state->request->data.chng_pswd_auth_crap.user));
2174 result = NT_STATUS_NO_SUCH_USER;
2179 if (!*domain && lp_winbind_use_default_domain()) {
2180 fstrcpy(domain,(char *)lp_workgroup());
2184 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2187 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2188 (unsigned long)state->pid, domain, user));
2190 if (strequal(domain, get_global_sam_name())) {
2191 enum samPwdChangeReason reject_reason;
2193 result = pass_oem_change(
2195 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2196 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2197 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2198 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2200 DEBUG(10, ("pass_oem_change returned %s\n",
2201 nt_errstr(result)));
2205 /* Change password */
2206 new_nt_password = data_blob_const(
2207 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2208 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2210 old_nt_hash_enc = data_blob_const(
2211 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2212 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2214 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2215 new_lm_password = data_blob_const(
2216 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2217 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2219 old_lm_hash_enc = data_blob_const(
2220 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2221 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2223 new_lm_password.length = 0;
2224 old_lm_hash_enc.length = 0;
2227 /* Get sam handle */
2229 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2230 if (!NT_STATUS_IS_OK(result)) {
2231 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2235 result = rpccli_samr_chng_pswd_auth_crap(
2236 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2237 new_lm_password, old_lm_hash_enc);
2241 set_auth_errors(state->response, result);
2243 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2244 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2246 state->response->data.auth.nt_status_string,
2247 state->response->data.auth.pam_error));
2249 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;