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"
31 #include "rpc_client/cli_netlogon.h"
33 #include "../lib/crypto/arcfour.h"
34 #include "../libcli/security/security.h"
36 #include "../librpc/gen_ndr/krb5pac.h"
39 #define DBGC_CLASS DBGC_WINBIND
41 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
43 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
44 struct winbindd_response *resp,
45 struct netr_SamInfo3 *info3)
50 resp->data.auth.info3.logon_time =
51 nt_time_to_unix(info3->base.last_logon);
52 resp->data.auth.info3.logoff_time =
53 nt_time_to_unix(info3->base.last_logoff);
54 resp->data.auth.info3.kickoff_time =
55 nt_time_to_unix(info3->base.acct_expiry);
56 resp->data.auth.info3.pass_last_set_time =
57 nt_time_to_unix(info3->base.last_password_change);
58 resp->data.auth.info3.pass_can_change_time =
59 nt_time_to_unix(info3->base.allow_password_change);
60 resp->data.auth.info3.pass_must_change_time =
61 nt_time_to_unix(info3->base.force_password_change);
63 resp->data.auth.info3.logon_count = info3->base.logon_count;
64 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
66 resp->data.auth.info3.user_rid = info3->base.rid;
67 resp->data.auth.info3.group_rid = info3->base.primary_gid;
68 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
70 resp->data.auth.info3.num_groups = info3->base.groups.count;
71 resp->data.auth.info3.user_flgs = info3->base.user_flags;
73 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
74 resp->data.auth.info3.num_other_sids = info3->sidcount;
76 fstrcpy(resp->data.auth.info3.user_name,
77 info3->base.account_name.string);
78 fstrcpy(resp->data.auth.info3.full_name,
79 info3->base.full_name.string);
80 fstrcpy(resp->data.auth.info3.logon_script,
81 info3->base.logon_script.string);
82 fstrcpy(resp->data.auth.info3.profile_path,
83 info3->base.profile_path.string);
84 fstrcpy(resp->data.auth.info3.home_dir,
85 info3->base.home_directory.string);
86 fstrcpy(resp->data.auth.info3.dir_drive,
87 info3->base.home_drive.string);
89 fstrcpy(resp->data.auth.info3.logon_srv,
90 info3->base.logon_server.string);
91 fstrcpy(resp->data.auth.info3.logon_dom,
92 info3->base.domain.string);
94 ex = talloc_strdup(mem_ctx, "");
95 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->base.groups.count; i++) {
98 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
99 info3->base.groups.rids[i].rid,
100 info3->base.groups.rids[i].attributes);
101 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->sidcount; i++) {
107 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
108 NT_STATUS_HAVE_NO_MEMORY(sid);
110 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
112 info3->sids[i].attributes);
113 NT_STATUS_HAVE_NO_MEMORY(ex);
118 resp->extra_data.data = ex;
119 resp->length += talloc_get_size(ex);
124 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
125 struct winbindd_response *resp,
126 struct netr_SamInfo3 *info3)
129 enum ndr_err_code ndr_err;
131 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
132 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
133 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
134 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
135 return ndr_map_error2ntstatus(ndr_err);
138 resp->extra_data.data = blob.data;
139 resp->length += blob.length;
144 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
145 struct winbindd_response *resp,
146 const struct netr_SamInfo3 *info3,
147 const char *name_domain,
148 const char *name_user)
150 /* We've been asked to return the unix username, per
151 'winbind use default domain' settings and the like */
153 const char *nt_username, *nt_domain;
155 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
157 /* If the server didn't give us one, just use the one
159 nt_domain = name_domain;
162 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
164 /* If the server didn't give us one, just use the one
166 nt_username = name_user;
169 fill_domain_username(resp->data.auth.unix_username,
170 nt_domain, nt_username, true);
172 DEBUG(5, ("Setting unix username to [%s]\n",
173 resp->data.auth.unix_username));
178 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
179 struct winbindd_response *resp,
180 const struct netr_SamInfo3 *info3,
181 const char *name_domain,
182 const char *name_user)
184 char *afsname = NULL;
188 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
189 if (afsname == NULL) {
190 return NT_STATUS_NO_MEMORY;
193 afsname = talloc_string_sub(mem_ctx,
194 lp_afs_username_map(),
196 afsname = talloc_string_sub(mem_ctx, afsname,
198 afsname = talloc_string_sub(mem_ctx, afsname,
202 struct dom_sid user_sid;
205 sid_compose(&user_sid, info3->base.domain_sid,
207 sid_to_fstring(sidstr, &user_sid);
208 afsname = talloc_string_sub(mem_ctx, afsname,
212 if (afsname == NULL) {
213 return NT_STATUS_NO_MEMORY;
218 DEBUG(10, ("Generating token for user %s\n", afsname));
220 cell = strchr(afsname, '@');
223 return NT_STATUS_NO_MEMORY;
229 token = afs_createtoken_str(afsname, cell);
233 resp->extra_data.data = talloc_strdup(mem_ctx, token);
234 if (resp->extra_data.data == NULL) {
235 return NT_STATUS_NO_MEMORY;
237 resp->length += strlen((const char *)resp->extra_data.data)+1;
242 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
243 const char *group_sid)
245 * Check whether a user belongs to a group or list of groups.
247 * @param mem_ctx talloc memory context.
248 * @param info3 user information, including group membership info.
249 * @param group_sid One or more groups , separated by commas.
251 * @return NT_STATUS_OK on success,
252 * NT_STATUS_LOGON_FAILURE if the user does not belong,
253 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
256 struct dom_sid *require_membership_of_sid;
257 uint32_t num_require_membership_of_sid;
262 struct security_token *token;
263 TALLOC_CTX *frame = talloc_stackframe();
266 /* Parse the 'required group' SID */
268 if (!group_sid || !group_sid[0]) {
269 /* NO sid supplied, all users may access */
273 token = talloc_zero(talloc_tos(), struct security_token);
275 DEBUG(0, ("talloc failed\n"));
277 return NT_STATUS_NO_MEMORY;
280 num_require_membership_of_sid = 0;
281 require_membership_of_sid = NULL;
285 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
286 if (!string_to_sid(&sid, req_sid)) {
287 DEBUG(0, ("check_info3_in_group: could not parse %s "
288 "as a SID!", req_sid));
290 return NT_STATUS_INVALID_PARAMETER;
293 status = add_sid_to_array(talloc_tos(), &sid,
294 &require_membership_of_sid,
295 &num_require_membership_of_sid);
296 if (!NT_STATUS_IS_OK(status)) {
297 DEBUG(0, ("add_sid_to_array failed\n"));
303 status = sid_array_from_info3(talloc_tos(), info3,
307 if (!NT_STATUS_IS_OK(status)) {
312 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
314 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
316 DEBUG(3, ("could not add aliases: %s\n",
322 security_token_debug(DBGC_CLASS, 10, token);
324 for (i=0; i<num_require_membership_of_sid; i++) {
325 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
326 &require_membership_of_sid[i])));
327 if (nt_token_check_sid(&require_membership_of_sid[i],
329 DEBUG(10, ("Access ok\n"));
335 /* Do not distinguish this error from a wrong username/pw */
338 return NT_STATUS_LOGON_FAILURE;
341 struct winbindd_domain *find_auth_domain(uint8_t flags,
342 const char *domain_name)
344 struct winbindd_domain *domain;
347 domain = find_domain_from_name_noinit(domain_name);
348 if (domain == NULL) {
349 DEBUG(3, ("Authentication for domain [%s] refused "
350 "as it is not a trusted domain\n",
356 if (strequal(domain_name, get_global_sam_name())) {
357 return find_domain_from_name_noinit(domain_name);
360 /* we can auth against trusted domains */
361 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
362 domain = find_domain_from_name_noinit(domain_name);
363 if (domain == NULL) {
364 DEBUG(3, ("Authentication for domain [%s] skipped "
365 "as it is not a trusted domain\n",
372 return find_our_domain();
375 static void fill_in_password_policy(struct winbindd_response *r,
376 const struct samr_DomInfo1 *p)
378 r->data.auth.policy.min_length_password =
379 p->min_password_length;
380 r->data.auth.policy.password_history =
381 p->password_history_length;
382 r->data.auth.policy.password_properties =
383 p->password_properties;
384 r->data.auth.policy.expire =
385 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
386 r->data.auth.policy.min_passwordage =
387 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
390 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
391 struct winbindd_cli_state *state)
393 struct winbindd_methods *methods;
394 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
395 struct samr_DomInfo1 password_policy;
397 if ( !winbindd_can_contact_domain( domain ) ) {
398 DEBUG(5,("fillup_password_policy: No inbound trust to "
399 "contact domain %s\n", domain->name));
400 return NT_STATUS_NOT_SUPPORTED;
403 methods = domain->methods;
405 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
406 if (NT_STATUS_IS_ERR(status)) {
410 fill_in_password_policy(state->response, &password_policy);
415 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
417 uint16 *lockout_threshold)
419 struct winbindd_methods *methods;
420 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
421 struct samr_DomInfo12 lockout_policy;
423 *lockout_threshold = 0;
425 methods = domain->methods;
427 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
428 if (NT_STATUS_IS_ERR(status)) {
432 *lockout_threshold = lockout_policy.lockout_threshold;
437 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
439 uint32 *password_properties)
441 struct winbindd_methods *methods;
442 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
443 struct samr_DomInfo1 password_policy;
445 *password_properties = 0;
447 methods = domain->methods;
449 status = methods->password_policy(domain, mem_ctx, &password_policy);
450 if (NT_STATUS_IS_ERR(status)) {
454 *password_properties = password_policy.password_properties;
461 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
464 const char **user_ccache_file)
466 /* accept FILE and WRFILE as krb5_cc_type from the client and then
467 * build the full ccname string based on the user's uid here -
470 const char *gen_cc = NULL;
473 if (strequal(type, "FILE")) {
474 gen_cc = talloc_asprintf(
475 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
477 if (strequal(type, "WRFILE")) {
478 gen_cc = talloc_asprintf(
479 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
483 *user_ccache_file = gen_cc;
485 if (gen_cc == NULL) {
486 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
488 if (gen_cc == NULL) {
489 DEBUG(0,("out of memory\n"));
493 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
494 (*user_ccache_file == NULL) ? " (internal)":""));
501 uid_t get_uid_from_request(struct winbindd_request *request)
505 uid = request->data.auth.uid;
508 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
514 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
516 return get_uid_from_request(state->request);
519 /**********************************************************************
520 Authenticate a user with a clear text password using Kerberos and fill up
522 **********************************************************************/
524 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
525 struct winbindd_domain *domain,
528 const char *krb5_cc_type,
530 struct netr_SamInfo3 **info3,
534 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
535 krb5_error_code krb5_ret;
536 const char *cc = NULL;
537 const char *principal_s = NULL;
538 const char *service = NULL;
540 fstring name_domain, name_user;
541 time_t ticket_lifetime = 0;
542 time_t renewal_until = 0;
544 time_t time_offset = 0;
545 const char *user_ccache_file;
546 struct PAC_LOGON_INFO *logon_info = NULL;
551 * prepare a krb5_cc_cache string for the user */
554 DEBUG(0,("no valid uid\n"));
557 cc = generate_krb5_ccache(mem_ctx,
562 return NT_STATUS_NO_MEMORY;
567 * get kerberos properties */
569 if (domain->private_data) {
570 ads = (ADS_STRUCT *)domain->private_data;
571 time_offset = ads->auth.time_offset;
576 * do kerberos auth and setup ccache as the user */
578 parse_domain_user(user, name_domain, name_user);
580 realm = domain->alt_name;
583 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
584 if (principal_s == NULL) {
585 return NT_STATUS_NO_MEMORY;
588 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
589 if (service == NULL) {
590 return NT_STATUS_NO_MEMORY;
593 /* if this is a user ccache, we need to act as the user to let the krb5
594 * library handle the chown, etc. */
596 /************************ ENTERING NON-ROOT **********************/
598 if (user_ccache_file != NULL) {
599 set_effective_uid(uid);
600 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
603 result = kerberos_return_pac(mem_ctx,
612 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
615 if (user_ccache_file != NULL) {
616 gain_root_privilege();
619 /************************ RETURNED TO ROOT **********************/
621 if (!NT_STATUS_IS_OK(result)) {
625 *info3 = &logon_info->info3;
627 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
630 /* if we had a user's ccache then return that string for the pam
633 if (user_ccache_file != NULL) {
635 fstrcpy(krb5ccname, user_ccache_file);
637 result = add_ccache_to_list(principal_s,
648 if (!NT_STATUS_IS_OK(result)) {
649 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
654 /* need to delete the memory cred cache, it is not used anymore */
656 krb5_ret = ads_kdestroy(cc);
658 DEBUG(3,("winbindd_raw_kerberos_login: "
659 "could not destroy krb5 credential cache: "
660 "%s\n", error_message(krb5_ret)));
669 /* we could have created a new credential cache with a valid tgt in it
670 * but we werent able to get or verify the service ticket for this
671 * local host and therefor didn't get the PAC, we need to remove that
672 * cache entirely now */
674 krb5_ret = ads_kdestroy(cc);
676 DEBUG(3,("winbindd_raw_kerberos_login: "
677 "could not destroy krb5 credential cache: "
678 "%s\n", error_message(krb5_ret)));
681 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
682 DEBUG(3,("winbindd_raw_kerberos_login: "
683 "could not remove ccache for user %s\n",
689 return NT_STATUS_NOT_SUPPORTED;
690 #endif /* HAVE_KRB5 */
693 /****************************************************************
694 ****************************************************************/
696 bool check_request_flags(uint32_t flags)
698 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
699 WBFLAG_PAM_INFO3_TEXT |
700 WBFLAG_PAM_INFO3_NDR;
702 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
703 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
704 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
705 !(flags & flags_edata) ) {
709 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
715 /****************************************************************
716 ****************************************************************/
718 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
719 struct winbindd_response *resp,
720 uint32_t request_flags,
721 struct netr_SamInfo3 *info3,
722 const char *name_domain,
723 const char *name_user)
727 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
728 memcpy(resp->data.auth.user_session_key,
730 sizeof(resp->data.auth.user_session_key)
734 if (request_flags & WBFLAG_PAM_LMKEY) {
735 memcpy(resp->data.auth.first_8_lm_hash,
736 info3->base.LMSessKey.key,
737 sizeof(resp->data.auth.first_8_lm_hash)
741 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
742 result = append_unix_username(mem_ctx, resp,
743 info3, name_domain, name_user);
744 if (!NT_STATUS_IS_OK(result)) {
745 DEBUG(10,("Failed to append Unix Username: %s\n",
751 /* currently, anything from here on potentially overwrites extra_data. */
753 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
754 result = append_info3_as_ndr(mem_ctx, resp, info3);
755 if (!NT_STATUS_IS_OK(result)) {
756 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
762 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
763 result = append_info3_as_txt(mem_ctx, resp, info3);
764 if (!NT_STATUS_IS_OK(result)) {
765 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
771 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
772 result = append_afs_token(mem_ctx, resp,
773 info3, name_domain, name_user);
774 if (!NT_STATUS_IS_OK(result)) {
775 DEBUG(10,("Failed to append AFS token: %s\n",
784 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
785 struct winbindd_cli_state *state,
786 struct netr_SamInfo3 **info3)
788 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
789 uint16 max_allowed_bad_attempts;
790 fstring name_domain, name_user;
792 enum lsa_SidType type;
793 uchar new_nt_pass[NT_HASH_LEN];
794 const uint8 *cached_nt_pass;
795 const uint8 *cached_salt;
796 struct netr_SamInfo3 *my_info3;
797 time_t kickoff_time, must_change_time;
798 bool password_good = false;
800 struct winbindd_tdc_domain *tdc_domain = NULL;
807 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
809 /* Parse domain and username */
811 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
814 if (!lookup_cached_name(name_domain,
818 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
819 return NT_STATUS_NO_SUCH_USER;
822 if (type != SID_NAME_USER) {
823 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
824 return NT_STATUS_LOGON_FAILURE;
827 result = winbindd_get_creds(domain,
833 if (!NT_STATUS_IS_OK(result)) {
834 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
840 E_md4hash(state->request->data.auth.pass, new_nt_pass);
842 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
843 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
845 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
849 /* In this case we didn't store the nt_hash itself,
850 but the MD5 combination of salt + nt_hash. */
851 uchar salted_hash[NT_HASH_LEN];
852 E_md5hash(cached_salt, new_nt_pass, salted_hash);
854 password_good = (memcmp(cached_nt_pass, salted_hash,
857 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
858 password_good = (memcmp(cached_nt_pass, new_nt_pass,
864 /* User *DOES* know the password, update logon_time and reset
867 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
869 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
870 return NT_STATUS_ACCOUNT_LOCKED_OUT;
873 if (my_info3->base.acct_flags & ACB_DISABLED) {
874 return NT_STATUS_ACCOUNT_DISABLED;
877 if (my_info3->base.acct_flags & ACB_WSTRUST) {
878 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
881 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
882 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
885 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
886 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
889 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
890 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
891 my_info3->base.acct_flags));
892 return NT_STATUS_LOGON_FAILURE;
895 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
896 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
897 return NT_STATUS_ACCOUNT_EXPIRED;
900 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
901 if (must_change_time != 0 && must_change_time < time(NULL)) {
902 /* we allow grace logons when the password has expired */
903 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
904 /* return NT_STATUS_PASSWORD_EXPIRED; */
909 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
910 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
911 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
912 /* used to cope with the case winbindd starting without network. */
913 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
916 const char *cc = NULL;
918 const char *principal_s = NULL;
919 const char *service = NULL;
920 const char *user_ccache_file;
922 uid = get_uid_from_state(state);
924 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
925 return NT_STATUS_INVALID_PARAMETER;
928 cc = generate_krb5_ccache(state->mem_ctx,
929 state->request->data.auth.krb5_cc_type,
930 state->request->data.auth.uid,
933 return NT_STATUS_NO_MEMORY;
936 realm = domain->alt_name;
939 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
940 if (principal_s == NULL) {
941 return NT_STATUS_NO_MEMORY;
944 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
945 if (service == NULL) {
946 return NT_STATUS_NO_MEMORY;
949 if (user_ccache_file != NULL) {
951 fstrcpy(state->response->data.auth.krb5ccname,
954 result = add_ccache_to_list(principal_s,
957 state->request->data.auth.user,
961 time(NULL) + lp_winbind_cache_time(),
962 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
965 if (!NT_STATUS_IS_OK(result)) {
966 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
967 "to add ccache to list: %s\n",
972 #endif /* HAVE_KRB5 */
974 /* FIXME: we possibly should handle logon hours as well (does xp when
975 * offline?) see auth/auth_sam.c:sam_account_ok for details */
977 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
978 my_info3->base.bad_password_count = 0;
980 result = winbindd_update_creds_by_info3(domain,
981 state->request->data.auth.user,
982 state->request->data.auth.pass,
984 if (!NT_STATUS_IS_OK(result)) {
985 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
994 /* User does *NOT* know the correct password, modify info3 accordingly */
996 /* failure of this is not critical */
997 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
998 if (!NT_STATUS_IS_OK(result)) {
999 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1000 "Won't be able to honour account lockout policies\n"));
1003 /* increase counter */
1004 my_info3->base.bad_password_count++;
1006 if (max_allowed_bad_attempts == 0) {
1011 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1013 uint32 password_properties;
1015 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1016 if (!NT_STATUS_IS_OK(result)) {
1017 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1020 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1021 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1022 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1027 result = winbindd_update_creds_by_info3(domain,
1028 state->request->data.auth.user,
1032 if (!NT_STATUS_IS_OK(result)) {
1033 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1034 nt_errstr(result)));
1037 return NT_STATUS_LOGON_FAILURE;
1040 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1041 struct winbindd_cli_state *state,
1042 struct netr_SamInfo3 **info3)
1044 struct winbindd_domain *contact_domain;
1045 fstring name_domain, name_user;
1048 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1050 /* Parse domain and username */
1052 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1054 /* what domain should we contact? */
1057 if (!(contact_domain = find_domain_from_name(name_domain))) {
1058 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1059 state->request->data.auth.user, name_domain, name_user, name_domain));
1060 result = NT_STATUS_NO_SUCH_USER;
1065 if (is_myname(name_domain)) {
1066 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1067 result = NT_STATUS_NO_SUCH_USER;
1071 contact_domain = find_domain_from_name(name_domain);
1072 if (contact_domain == NULL) {
1073 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1074 state->request->data.auth.user, name_domain, name_user, name_domain));
1076 contact_domain = find_our_domain();
1080 if (contact_domain->initialized &&
1081 contact_domain->active_directory) {
1085 if (!contact_domain->initialized) {
1086 init_dc_connection(contact_domain);
1089 if (!contact_domain->active_directory) {
1090 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1091 return NT_STATUS_INVALID_LOGON_TYPE;
1094 result = winbindd_raw_kerberos_login(
1095 state->mem_ctx, contact_domain,
1096 state->request->data.auth.user,
1097 state->request->data.auth.pass,
1098 state->request->data.auth.krb5_cc_type,
1099 get_uid_from_request(state->request),
1100 info3, state->response->data.auth.krb5ccname);
1105 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1106 const char *domain, const char *user,
1107 const DATA_BLOB *challenge,
1108 const DATA_BLOB *lm_resp,
1109 const DATA_BLOB *nt_resp,
1110 struct netr_SamInfo3 **pinfo3)
1112 struct auth_usersupplied_info *user_info = NULL;
1115 status = make_user_info(&user_info, user, user, domain, domain,
1116 global_myname(), lm_resp, nt_resp, NULL, NULL,
1117 NULL, AUTH_PASSWORD_RESPONSE);
1118 if (!NT_STATUS_IS_OK(status)) {
1119 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1123 /* We don't want any more mapping of the username */
1124 user_info->mapped_state = True;
1126 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1128 free_user_info(&user_info);
1129 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1130 user, nt_errstr(status)));
1134 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1135 TALLOC_CTX *mem_ctx,
1136 uint32_t logon_parameters,
1138 const char *username,
1139 const char *domainname,
1140 const char *workstation,
1141 const uint8_t chal[8],
1142 DATA_BLOB lm_response,
1143 DATA_BLOB nt_response,
1144 struct netr_SamInfo3 **info3)
1151 struct rpc_pipe_client *netlogon_pipe;
1153 ZERO_STRUCTP(info3);
1156 result = cm_connect_netlogon(domain, &netlogon_pipe);
1158 if (!NT_STATUS_IS_OK(result)) {
1159 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1160 nt_errstr(result)));
1164 /* It is really important to try SamLogonEx here,
1165 * because in a clustered environment, we want to use
1166 * one machine account from multiple physical
1169 * With a normal SamLogon call, we must keep the
1170 * credentials chain updated and intact between all
1171 * users of the machine account (which would imply
1172 * cross-node communication for every NTLM logon).
1174 * (The credentials chain is not per NETLOGON pipe
1175 * connection, but globally on the server/client pair
1178 * When using SamLogonEx, the credentials are not
1179 * supplied, but the session key is implied by the
1180 * wrapping SamLogon context.
1182 * -- abartlet 21 April 2008
1185 if (domain->can_do_samlogon_ex) {
1186 result = rpccli_netlogon_sam_network_logon_ex(
1190 server, /* server name */
1191 username, /* user name */
1192 domainname, /* target domain */
1193 workstation, /* workstation */
1199 result = rpccli_netlogon_sam_network_logon(
1203 server, /* server name */
1204 username, /* user name */
1205 domainname, /* target domain */
1206 workstation, /* workstation */
1215 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1216 && domain->can_do_samlogon_ex) {
1217 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1218 "retrying with NetSamLogon\n"));
1219 domain->can_do_samlogon_ex = false;
1224 /* We have to try a second time as cm_connect_netlogon
1225 might not yet have noticed that the DC has killed
1228 if (!rpccli_is_connected(netlogon_pipe)) {
1233 /* if we get access denied, a possible cause was that we had
1234 and open connection to the DC, but someone changed our
1235 machine account password out from underneath us using 'net
1236 rpc changetrustpw' */
1238 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1239 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1240 "ACCESS_DENIED. Maybe the trust account "
1241 "password was changed and we didn't know it. "
1242 "Killing connections to domain %s\n",
1244 invalidate_cm_connection(&domain->conn);
1248 } while ( (attempts < 2) && retry );
1253 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1254 struct winbindd_cli_state *state,
1255 struct netr_SamInfo3 **info3)
1261 unsigned char local_nt_response[24];
1262 fstring name_domain, name_user;
1264 struct netr_SamInfo3 *my_info3 = NULL;
1268 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1270 /* Parse domain and username */
1272 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1274 /* do password magic */
1276 generate_random_buffer(chal, sizeof(chal));
1278 if (lp_client_ntlmv2_auth()) {
1279 DATA_BLOB server_chal;
1280 DATA_BLOB names_blob;
1281 server_chal = data_blob_const(chal, 8);
1283 /* note that the 'workgroup' here is for the local
1284 machine. The 'server name' must match the
1285 'workstation' passed to the actual SamLogon call.
1287 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1289 if (!SMBNTLMv2encrypt(state->mem_ctx, name_user, name_domain,
1290 state->request->data.auth.pass,
1293 &lm_resp, &nt_resp, NULL, NULL)) {
1294 data_blob_free(&names_blob);
1295 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1296 result = NT_STATUS_NO_MEMORY;
1299 data_blob_free(&names_blob);
1301 lm_resp = data_blob_null;
1302 SMBNTencrypt(state->request->data.auth.pass,
1306 nt_resp = data_blob_talloc(state->mem_ctx,
1308 sizeof(local_nt_response));
1311 if (strequal(name_domain, get_global_sam_name())) {
1312 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1314 result = winbindd_dual_auth_passdb(
1315 state->mem_ctx, name_domain, name_user,
1316 &chal_blob, &lm_resp, &nt_resp, info3);
1320 /* check authentication loop */
1322 result = winbind_samlogon_retry_loop(domain,
1333 if (!NT_STATUS_IS_OK(result)) {
1337 /* handle the case where a NT4 DC does not fill in the acct_flags in
1338 * the samlogon reply info3. When accurate info3 is required by the
1339 * caller, we look up the account flags ourselve - gd */
1341 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1342 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1344 struct rpc_pipe_client *samr_pipe;
1345 struct policy_handle samr_domain_handle, user_pol;
1346 union samr_UserInfo *info = NULL;
1347 NTSTATUS status_tmp;
1350 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1351 &samr_pipe, &samr_domain_handle);
1353 if (!NT_STATUS_IS_OK(status_tmp)) {
1354 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1355 nt_errstr(status_tmp)));
1359 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1360 &samr_domain_handle,
1361 MAXIMUM_ALLOWED_ACCESS,
1365 if (!NT_STATUS_IS_OK(status_tmp)) {
1366 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1367 nt_errstr(status_tmp)));
1371 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1376 if (!NT_STATUS_IS_OK(status_tmp)) {
1377 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1378 nt_errstr(status_tmp)));
1379 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1383 acct_flags = info->info16.acct_flags;
1385 if (acct_flags == 0) {
1386 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1390 my_info3->base.acct_flags = acct_flags;
1392 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1394 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1402 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1403 struct winbindd_cli_state *state)
1405 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1406 NTSTATUS krb5_result = NT_STATUS_OK;
1407 fstring name_domain, name_user;
1409 fstring domain_user;
1410 struct netr_SamInfo3 *info3 = NULL;
1411 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1413 /* Ensure null termination */
1414 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1416 /* Ensure null termination */
1417 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1419 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1420 state->request->data.auth.user));
1422 /* Parse domain and username */
1424 name_map_status = normalize_name_unmap(state->mem_ctx,
1425 state->request->data.auth.user,
1428 /* If the name normalization didnt' actually do anything,
1429 just use the original name */
1431 if (!NT_STATUS_IS_OK(name_map_status) &&
1432 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1434 mapped_user = state->request->data.auth.user;
1437 parse_domain_user(mapped_user, name_domain, name_user);
1439 if ( mapped_user != state->request->data.auth.user ) {
1440 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1441 *lp_winbind_separator(),
1443 safe_strcpy( state->request->data.auth.user, domain_user,
1444 sizeof(state->request->data.auth.user)-1 );
1447 if (!domain->online) {
1448 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1449 if (domain->startup) {
1450 /* Logons are very important to users. If we're offline and
1451 we get a request within the first 30 seconds of startup,
1452 try very hard to find a DC and go online. */
1454 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1455 "request in startup mode.\n", domain->name ));
1457 winbindd_flush_negative_conn_cache(domain);
1458 result = init_dc_connection(domain);
1462 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1464 /* Check for Kerberos authentication */
1465 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1467 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1468 /* save for later */
1469 krb5_result = result;
1472 if (NT_STATUS_IS_OK(result)) {
1473 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1474 goto process_result;
1476 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1479 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1480 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1481 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1482 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1483 set_domain_offline( domain );
1487 /* there are quite some NT_STATUS errors where there is no
1488 * point in retrying with a samlogon, we explictly have to take
1489 * care not to increase the bad logon counter on the DC */
1491 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1492 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1493 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1494 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1495 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1496 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1497 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1498 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1499 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1500 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1504 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1505 DEBUG(3,("falling back to samlogon\n"));
1513 /* Check for Samlogon authentication */
1514 if (domain->online) {
1515 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1517 if (NT_STATUS_IS_OK(result)) {
1518 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1519 /* add the Krb5 err if we have one */
1520 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1521 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1523 goto process_result;
1526 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1527 nt_errstr(result)));
1529 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1530 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1531 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1533 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1534 set_domain_offline( domain );
1538 if (domain->online) {
1539 /* We're still online - fail. */
1545 /* Check for Cached logons */
1546 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1547 lp_winbind_offline_logon()) {
1549 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1551 if (NT_STATUS_IS_OK(result)) {
1552 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1553 goto process_result;
1555 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1562 if (NT_STATUS_IS_OK(result)) {
1564 struct dom_sid user_sid;
1566 /* In all codepaths where result == NT_STATUS_OK info3 must have
1567 been initialized. */
1569 result = NT_STATUS_INTERNAL_ERROR;
1573 sid_compose(&user_sid, info3->base.domain_sid,
1576 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1578 netsamlogon_cache_store(name_user, info3);
1580 /* save name_to_sid info as early as possible (only if
1581 this is our primary domain so we don't invalidate
1582 the cache entry by storing the seq_num for the wrong
1584 if ( domain->primary ) {
1585 cache_name2sid(domain, name_domain, name_user,
1586 SID_NAME_USER, &user_sid);
1589 /* Check if the user is in the right group */
1591 result = check_info3_in_group(
1593 state->request->data.auth.require_membership_of_sid);
1594 if (!NT_STATUS_IS_OK(result)) {
1595 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1596 state->request->data.auth.user,
1597 state->request->data.auth.require_membership_of_sid));
1601 result = append_auth_data(state->mem_ctx, state->response,
1602 state->request->flags, info3,
1603 name_domain, name_user);
1604 if (!NT_STATUS_IS_OK(result)) {
1608 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1609 && lp_winbind_offline_logon()) {
1611 result = winbindd_store_creds(domain,
1612 state->request->data.auth.user,
1613 state->request->data.auth.pass,
1617 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1618 struct winbindd_domain *our_domain = find_our_domain();
1620 /* This is not entirely correct I believe, but it is
1621 consistent. Only apply the password policy settings
1622 too warn users for our own domain. Cannot obtain these
1623 from trusted DCs all the time so don't do it at all.
1626 result = NT_STATUS_NOT_SUPPORTED;
1627 if (our_domain == domain ) {
1628 result = fillup_password_policy(our_domain, state);
1631 if (!NT_STATUS_IS_OK(result)
1632 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1634 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1635 domain->name, nt_errstr(result)));
1640 result = NT_STATUS_OK;
1644 /* give us a more useful (more correct?) error code */
1645 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1646 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1647 result = NT_STATUS_NO_LOGON_SERVERS;
1650 set_auth_errors(state->response, result);
1652 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1653 state->request->data.auth.user,
1654 state->response->data.auth.nt_status_string,
1655 state->response->data.auth.pam_error));
1657 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1660 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1661 struct winbindd_cli_state *state)
1664 struct netr_SamInfo3 *info3 = NULL;
1665 const char *name_user = NULL;
1666 const char *name_domain = NULL;
1667 const char *workstation;
1669 DATA_BLOB lm_resp, nt_resp;
1671 /* This is child-only, so no check for privileged access is needed
1674 /* Ensure null termination */
1675 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1676 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1678 name_user = state->request->data.auth_crap.user;
1679 name_domain = state->request->data.auth_crap.domain;
1680 workstation = state->request->data.auth_crap.workstation;
1682 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1683 name_domain, name_user));
1685 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1686 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1687 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1688 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1689 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1690 state->request->data.auth_crap.lm_resp_len,
1691 state->request->data.auth_crap.nt_resp_len));
1692 result = NT_STATUS_INVALID_PARAMETER;
1697 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1698 state->request->data.auth_crap.lm_resp_len);
1700 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1701 nt_resp = data_blob_talloc(state->mem_ctx,
1702 state->request->extra_data.data,
1703 state->request->data.auth_crap.nt_resp_len);
1705 nt_resp = data_blob_talloc(state->mem_ctx,
1706 state->request->data.auth_crap.nt_resp,
1707 state->request->data.auth_crap.nt_resp_len);
1710 if (strequal(name_domain, get_global_sam_name())) {
1711 DATA_BLOB chal_blob = data_blob_const(
1712 state->request->data.auth_crap.chal,
1713 sizeof(state->request->data.auth_crap.chal));
1715 result = winbindd_dual_auth_passdb(
1716 state->mem_ctx, name_domain, name_user,
1717 &chal_blob, &lm_resp, &nt_resp, &info3);
1718 goto process_result;
1721 result = winbind_samlogon_retry_loop(domain,
1723 state->request->data.auth_crap.logon_parameters,
1727 /* Bug #3248 - found by Stefan Burkei. */
1728 workstation, /* We carefully set this above so use it... */
1729 state->request->data.auth_crap.chal,
1733 if (!NT_STATUS_IS_OK(result)) {
1739 if (NT_STATUS_IS_OK(result)) {
1740 struct dom_sid user_sid;
1742 sid_compose(&user_sid, info3->base.domain_sid,
1744 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1746 netsamlogon_cache_store(name_user, info3);
1748 /* Check if the user is in the right group */
1750 result = check_info3_in_group(
1752 state->request->data.auth_crap.require_membership_of_sid);
1753 if (!NT_STATUS_IS_OK(result)) {
1754 DEBUG(3, ("User %s is not in the required group (%s), so "
1755 "crap authentication is rejected\n",
1756 state->request->data.auth_crap.user,
1757 state->request->data.auth_crap.require_membership_of_sid));
1761 result = append_auth_data(state->mem_ctx, state->response,
1762 state->request->flags, info3,
1763 name_domain, name_user);
1764 if (!NT_STATUS_IS_OK(result)) {
1771 /* give us a more useful (more correct?) error code */
1772 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1773 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1774 result = NT_STATUS_NO_LOGON_SERVERS;
1777 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1778 result = nt_status_squash(result);
1781 set_auth_errors(state->response, result);
1783 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1784 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1787 state->response->data.auth.nt_status_string,
1788 state->response->data.auth.pam_error));
1790 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1793 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1794 struct winbindd_cli_state *state)
1797 char *newpass = NULL;
1798 struct policy_handle dom_pol;
1799 struct rpc_pipe_client *cli = NULL;
1800 bool got_info = false;
1801 struct samr_DomInfo1 *info = NULL;
1802 struct userPwdChangeFailureInformation *reject = NULL;
1803 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1804 fstring domain, user;
1806 ZERO_STRUCT(dom_pol);
1808 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1809 state->request->data.auth.user));
1811 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1815 /* Change password */
1817 oldpass = state->request->data.chauthtok.oldpass;
1818 newpass = state->request->data.chauthtok.newpass;
1820 /* Initialize reject reason */
1821 state->response->data.auth.reject_reason = Undefined;
1823 /* Get sam handle */
1825 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1827 if (!NT_STATUS_IS_OK(result)) {
1828 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1832 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1839 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1841 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1843 fill_in_password_policy(state->response, info);
1845 state->response->data.auth.reject_reason =
1846 reject->extendedFailureReason;
1851 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1852 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1853 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1854 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1856 /* only fallback when the chgpasswd_user3 call is not supported */
1857 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1858 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1859 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1860 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1862 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1863 nt_errstr(result)));
1865 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1867 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1868 Map to the same status code as Windows 2003. */
1870 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1871 result = NT_STATUS_PASSWORD_RESTRICTION;
1877 if (NT_STATUS_IS_OK(result)
1878 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1879 && lp_winbind_offline_logon()) {
1880 result = winbindd_update_creds_by_name(contact_domain, user,
1882 /* Again, this happens when we login from gdm or xdm
1883 * and the password expires, *BUT* cached crendentials
1884 * doesn't exist. winbindd_update_creds_by_name()
1885 * returns NT_STATUS_NO_SUCH_USER.
1886 * This is not a failure.
1889 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1890 result = NT_STATUS_OK;
1893 if (!NT_STATUS_IS_OK(result)) {
1894 DEBUG(10, ("Failed to store creds: %s\n",
1895 nt_errstr(result)));
1896 goto process_result;
1900 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1902 NTSTATUS policy_ret;
1904 policy_ret = fillup_password_policy(contact_domain, state);
1906 /* failure of this is non critical, it will just provide no
1907 * additional information to the client why the change has
1908 * failed - Guenther */
1910 if (!NT_STATUS_IS_OK(policy_ret)) {
1911 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1912 goto process_result;
1918 if (strequal(contact_domain->name, get_global_sam_name())) {
1919 /* FIXME: internal rpc pipe does not cache handles yet */
1921 if (is_valid_policy_hnd(&dom_pol)) {
1922 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1928 set_auth_errors(state->response, result);
1930 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1931 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1934 state->response->data.auth.nt_status_string,
1935 state->response->data.auth.pam_error));
1937 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1940 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1941 struct winbindd_cli_state *state)
1943 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1945 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1946 state->request->data.logoff.user));
1948 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1949 result = NT_STATUS_OK;
1950 goto process_result;
1953 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1954 result = NT_STATUS_OK;
1955 goto process_result;
1960 if (state->request->data.logoff.uid < 0) {
1961 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1962 goto process_result;
1965 /* what we need here is to find the corresponding krb5 ccache name *we*
1966 * created for a given username and destroy it */
1968 if (!ccache_entry_exists(state->request->data.logoff.user)) {
1969 result = NT_STATUS_OK;
1970 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1971 goto process_result;
1974 if (!ccache_entry_identical(state->request->data.logoff.user,
1975 state->request->data.logoff.uid,
1976 state->request->data.logoff.krb5ccname)) {
1977 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
1978 goto process_result;
1981 result = remove_ccache(state->request->data.logoff.user);
1982 if (!NT_STATUS_IS_OK(result)) {
1983 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
1984 nt_errstr(result)));
1985 goto process_result;
1989 result = NT_STATUS_NOT_SUPPORTED;
1995 set_auth_errors(state->response, result);
1997 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2000 /* Change user password with auth crap*/
2002 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2005 DATA_BLOB new_nt_password;
2006 DATA_BLOB old_nt_hash_enc;
2007 DATA_BLOB new_lm_password;
2008 DATA_BLOB old_lm_hash_enc;
2009 fstring domain,user;
2010 struct policy_handle dom_pol;
2011 struct winbindd_domain *contact_domain = domainSt;
2012 struct rpc_pipe_client *cli = NULL;
2014 ZERO_STRUCT(dom_pol);
2016 /* Ensure null termination */
2017 state->request->data.chng_pswd_auth_crap.user[
2018 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2019 state->request->data.chng_pswd_auth_crap.domain[
2020 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2024 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2025 (unsigned long)state->pid,
2026 state->request->data.chng_pswd_auth_crap.domain,
2027 state->request->data.chng_pswd_auth_crap.user));
2029 if (lp_winbind_offline_logon()) {
2030 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2031 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2032 result = NT_STATUS_ACCESS_DENIED;
2036 if (*state->request->data.chng_pswd_auth_crap.domain) {
2037 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2039 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2043 DEBUG(3,("no domain specified with username (%s) - "
2045 state->request->data.chng_pswd_auth_crap.user));
2046 result = NT_STATUS_NO_SUCH_USER;
2051 if (!*domain && lp_winbind_use_default_domain()) {
2052 fstrcpy(domain,(char *)lp_workgroup());
2056 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2059 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2060 (unsigned long)state->pid, domain, user));
2062 /* Change password */
2063 new_nt_password = data_blob_const(
2064 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2065 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2067 old_nt_hash_enc = data_blob_const(
2068 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2069 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2071 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2072 new_lm_password = data_blob_const(
2073 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2074 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2076 old_lm_hash_enc = data_blob_const(
2077 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2078 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2080 new_lm_password.length = 0;
2081 old_lm_hash_enc.length = 0;
2084 /* Get sam handle */
2086 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2087 if (!NT_STATUS_IS_OK(result)) {
2088 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2092 result = rpccli_samr_chng_pswd_auth_crap(
2093 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2094 new_lm_password, old_lm_hash_enc);
2098 if (strequal(contact_domain->name, get_global_sam_name())) {
2099 /* FIXME: internal rpc pipe does not cache handles yet */
2101 if (is_valid_policy_hnd(&dom_pol)) {
2102 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2108 set_auth_errors(state->response, result);
2110 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2111 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2113 state->response->data.auth.nt_status_string,
2114 state->response->data.auth.pam_error));
2116 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;