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(struct winbindd_domain *domain,
525 struct winbindd_cli_state *state,
526 struct netr_SamInfo3 **info3)
529 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
530 krb5_error_code krb5_ret;
531 const char *cc = NULL;
532 const char *principal_s = NULL;
533 const char *service = NULL;
535 fstring name_domain, name_user;
536 time_t ticket_lifetime = 0;
537 time_t renewal_until = 0;
540 time_t time_offset = 0;
541 const char *user_ccache_file;
542 struct PAC_LOGON_INFO *logon_info = NULL;
547 * prepare a krb5_cc_cache string for the user */
549 uid = get_uid_from_state(state);
551 DEBUG(0,("no valid uid\n"));
554 cc = generate_krb5_ccache(state->mem_ctx,
555 state->request->data.auth.krb5_cc_type,
556 state->request->data.auth.uid,
559 return NT_STATUS_NO_MEMORY;
564 * get kerberos properties */
566 if (domain->private_data) {
567 ads = (ADS_STRUCT *)domain->private_data;
568 time_offset = ads->auth.time_offset;
573 * do kerberos auth and setup ccache as the user */
575 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
577 realm = domain->alt_name;
580 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
581 if (principal_s == NULL) {
582 return NT_STATUS_NO_MEMORY;
585 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
586 if (service == NULL) {
587 return NT_STATUS_NO_MEMORY;
590 /* if this is a user ccache, we need to act as the user to let the krb5
591 * library handle the chown, etc. */
593 /************************ ENTERING NON-ROOT **********************/
595 if (user_ccache_file != NULL) {
596 set_effective_uid(uid);
597 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
600 result = kerberos_return_pac(state->mem_ctx,
602 state->request->data.auth.pass,
609 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
612 if (user_ccache_file != NULL) {
613 gain_root_privilege();
616 /************************ RETURNED TO ROOT **********************/
618 if (!NT_STATUS_IS_OK(result)) {
622 *info3 = &logon_info->info3;
624 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
627 /* if we had a user's ccache then return that string for the pam
630 if (user_ccache_file != NULL) {
632 fstrcpy(state->response->data.auth.krb5ccname,
635 result = add_ccache_to_list(principal_s,
638 state->request->data.auth.user,
646 if (!NT_STATUS_IS_OK(result)) {
647 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
652 /* need to delete the memory cred cache, it is not used anymore */
654 krb5_ret = ads_kdestroy(cc);
656 DEBUG(3,("winbindd_raw_kerberos_login: "
657 "could not destroy krb5 credential cache: "
658 "%s\n", error_message(krb5_ret)));
667 /* we could have created a new credential cache with a valid tgt in it
668 * but we werent able to get or verify the service ticket for this
669 * local host and therefor didn't get the PAC, we need to remove that
670 * cache entirely now */
672 krb5_ret = ads_kdestroy(cc);
674 DEBUG(3,("winbindd_raw_kerberos_login: "
675 "could not destroy krb5 credential cache: "
676 "%s\n", error_message(krb5_ret)));
679 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
680 DEBUG(3,("winbindd_raw_kerberos_login: "
681 "could not remove ccache for user %s\n",
682 state->request->data.auth.user));
687 return NT_STATUS_NOT_SUPPORTED;
688 #endif /* HAVE_KRB5 */
691 /****************************************************************
692 ****************************************************************/
694 bool check_request_flags(uint32_t flags)
696 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
697 WBFLAG_PAM_INFO3_TEXT |
698 WBFLAG_PAM_INFO3_NDR;
700 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
701 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
702 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
703 !(flags & flags_edata) ) {
707 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
713 /****************************************************************
714 ****************************************************************/
716 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
717 struct winbindd_response *resp,
718 uint32_t request_flags,
719 struct netr_SamInfo3 *info3,
720 const char *name_domain,
721 const char *name_user)
725 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
726 memcpy(resp->data.auth.user_session_key,
728 sizeof(resp->data.auth.user_session_key)
732 if (request_flags & WBFLAG_PAM_LMKEY) {
733 memcpy(resp->data.auth.first_8_lm_hash,
734 info3->base.LMSessKey.key,
735 sizeof(resp->data.auth.first_8_lm_hash)
739 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
740 result = append_unix_username(mem_ctx, resp,
741 info3, name_domain, name_user);
742 if (!NT_STATUS_IS_OK(result)) {
743 DEBUG(10,("Failed to append Unix Username: %s\n",
749 /* currently, anything from here on potentially overwrites extra_data. */
751 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
752 result = append_info3_as_ndr(mem_ctx, resp, info3);
753 if (!NT_STATUS_IS_OK(result)) {
754 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
760 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
761 result = append_info3_as_txt(mem_ctx, resp, info3);
762 if (!NT_STATUS_IS_OK(result)) {
763 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
769 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
770 result = append_afs_token(mem_ctx, resp,
771 info3, name_domain, name_user);
772 if (!NT_STATUS_IS_OK(result)) {
773 DEBUG(10,("Failed to append AFS token: %s\n",
782 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
783 struct winbindd_cli_state *state,
784 struct netr_SamInfo3 **info3)
786 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
787 uint16 max_allowed_bad_attempts;
788 fstring name_domain, name_user;
790 enum lsa_SidType type;
791 uchar new_nt_pass[NT_HASH_LEN];
792 const uint8 *cached_nt_pass;
793 const uint8 *cached_salt;
794 struct netr_SamInfo3 *my_info3;
795 time_t kickoff_time, must_change_time;
796 bool password_good = false;
798 struct winbindd_tdc_domain *tdc_domain = NULL;
805 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
807 /* Parse domain and username */
809 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
812 if (!lookup_cached_name(name_domain,
816 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
817 return NT_STATUS_NO_SUCH_USER;
820 if (type != SID_NAME_USER) {
821 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
822 return NT_STATUS_LOGON_FAILURE;
825 result = winbindd_get_creds(domain,
831 if (!NT_STATUS_IS_OK(result)) {
832 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
838 E_md4hash(state->request->data.auth.pass, new_nt_pass);
840 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
841 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
843 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
847 /* In this case we didn't store the nt_hash itself,
848 but the MD5 combination of salt + nt_hash. */
849 uchar salted_hash[NT_HASH_LEN];
850 E_md5hash(cached_salt, new_nt_pass, salted_hash);
852 password_good = (memcmp(cached_nt_pass, salted_hash,
855 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
856 password_good = (memcmp(cached_nt_pass, new_nt_pass,
862 /* User *DOES* know the password, update logon_time and reset
865 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
867 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
868 return NT_STATUS_ACCOUNT_LOCKED_OUT;
871 if (my_info3->base.acct_flags & ACB_DISABLED) {
872 return NT_STATUS_ACCOUNT_DISABLED;
875 if (my_info3->base.acct_flags & ACB_WSTRUST) {
876 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
879 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
880 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
883 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
884 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
887 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
888 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
889 my_info3->base.acct_flags));
890 return NT_STATUS_LOGON_FAILURE;
893 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
894 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
895 return NT_STATUS_ACCOUNT_EXPIRED;
898 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
899 if (must_change_time != 0 && must_change_time < time(NULL)) {
900 /* we allow grace logons when the password has expired */
901 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
902 /* return NT_STATUS_PASSWORD_EXPIRED; */
907 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
908 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
909 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
910 /* used to cope with the case winbindd starting without network. */
911 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
914 const char *cc = NULL;
916 const char *principal_s = NULL;
917 const char *service = NULL;
918 const char *user_ccache_file;
920 uid = get_uid_from_state(state);
922 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
923 return NT_STATUS_INVALID_PARAMETER;
926 cc = generate_krb5_ccache(state->mem_ctx,
927 state->request->data.auth.krb5_cc_type,
928 state->request->data.auth.uid,
931 return NT_STATUS_NO_MEMORY;
934 realm = domain->alt_name;
937 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
938 if (principal_s == NULL) {
939 return NT_STATUS_NO_MEMORY;
942 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
943 if (service == NULL) {
944 return NT_STATUS_NO_MEMORY;
947 if (user_ccache_file != NULL) {
949 fstrcpy(state->response->data.auth.krb5ccname,
952 result = add_ccache_to_list(principal_s,
955 state->request->data.auth.user,
959 time(NULL) + lp_winbind_cache_time(),
960 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
963 if (!NT_STATUS_IS_OK(result)) {
964 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
965 "to add ccache to list: %s\n",
970 #endif /* HAVE_KRB5 */
972 /* FIXME: we possibly should handle logon hours as well (does xp when
973 * offline?) see auth/auth_sam.c:sam_account_ok for details */
975 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
976 my_info3->base.bad_password_count = 0;
978 result = winbindd_update_creds_by_info3(domain,
979 state->request->data.auth.user,
980 state->request->data.auth.pass,
982 if (!NT_STATUS_IS_OK(result)) {
983 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
992 /* User does *NOT* know the correct password, modify info3 accordingly */
994 /* failure of this is not critical */
995 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
996 if (!NT_STATUS_IS_OK(result)) {
997 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
998 "Won't be able to honour account lockout policies\n"));
1001 /* increase counter */
1002 my_info3->base.bad_password_count++;
1004 if (max_allowed_bad_attempts == 0) {
1009 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1011 uint32 password_properties;
1013 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1014 if (!NT_STATUS_IS_OK(result)) {
1015 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1018 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1019 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1020 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1025 result = winbindd_update_creds_by_info3(domain,
1026 state->request->data.auth.user,
1030 if (!NT_STATUS_IS_OK(result)) {
1031 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1032 nt_errstr(result)));
1035 return NT_STATUS_LOGON_FAILURE;
1038 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1039 struct winbindd_cli_state *state,
1040 struct netr_SamInfo3 **info3)
1042 struct winbindd_domain *contact_domain;
1043 fstring name_domain, name_user;
1046 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1048 /* Parse domain and username */
1050 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1052 /* what domain should we contact? */
1055 if (!(contact_domain = find_domain_from_name(name_domain))) {
1056 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1057 state->request->data.auth.user, name_domain, name_user, name_domain));
1058 result = NT_STATUS_NO_SUCH_USER;
1063 if (is_myname(name_domain)) {
1064 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1065 result = NT_STATUS_NO_SUCH_USER;
1069 contact_domain = find_domain_from_name(name_domain);
1070 if (contact_domain == NULL) {
1071 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1072 state->request->data.auth.user, name_domain, name_user, name_domain));
1074 contact_domain = find_our_domain();
1078 if (contact_domain->initialized &&
1079 contact_domain->active_directory) {
1083 if (!contact_domain->initialized) {
1084 init_dc_connection(contact_domain);
1087 if (!contact_domain->active_directory) {
1088 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1089 return NT_STATUS_INVALID_LOGON_TYPE;
1092 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1097 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1098 const char *domain, const char *user,
1099 const DATA_BLOB *challenge,
1100 const DATA_BLOB *lm_resp,
1101 const DATA_BLOB *nt_resp,
1102 struct netr_SamInfo3 **pinfo3)
1104 struct auth_usersupplied_info *user_info = NULL;
1107 status = make_user_info(&user_info, user, user, domain, domain,
1108 global_myname(), lm_resp, nt_resp, NULL, NULL,
1109 NULL, AUTH_PASSWORD_RESPONSE);
1110 if (!NT_STATUS_IS_OK(status)) {
1111 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1115 /* We don't want any more mapping of the username */
1116 user_info->mapped_state = True;
1118 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1120 free_user_info(&user_info);
1121 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1122 user, nt_errstr(status)));
1126 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1127 TALLOC_CTX *mem_ctx,
1128 uint32 logon_parameters,
1130 const char *username,
1132 const char *workstation,
1133 const uint8 chal[8],
1134 DATA_BLOB lm_response,
1135 DATA_BLOB nt_response,
1136 struct netr_SamInfo3 **info3);
1138 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1139 struct winbindd_cli_state *state,
1140 struct netr_SamInfo3 **info3)
1143 struct rpc_pipe_client *netlogon_pipe;
1148 unsigned char local_lm_response[24];
1149 unsigned char local_nt_response[24];
1150 fstring name_domain, name_user;
1153 struct netr_SamInfo3 *my_info3 = NULL;
1157 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1159 /* Parse domain and username */
1161 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1163 /* do password magic */
1165 generate_random_buffer(chal, sizeof(chal));
1167 if (lp_client_ntlmv2_auth()) {
1168 DATA_BLOB server_chal;
1169 DATA_BLOB names_blob;
1170 DATA_BLOB nt_response;
1171 DATA_BLOB lm_response;
1172 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1174 /* note that the 'workgroup' here is a best guess - we don't know
1175 the server's domain at this point. The 'server name' is also
1178 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1180 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1181 state->request->data.auth.pass,
1184 &lm_response, &nt_response, NULL, NULL)) {
1185 data_blob_free(&names_blob);
1186 data_blob_free(&server_chal);
1187 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1188 result = NT_STATUS_NO_MEMORY;
1191 data_blob_free(&names_blob);
1192 data_blob_free(&server_chal);
1193 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1194 lm_response.length);
1195 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1196 nt_response.length);
1197 data_blob_free(&lm_response);
1198 data_blob_free(&nt_response);
1201 if (lp_client_lanman_auth()
1202 && SMBencrypt(state->request->data.auth.pass,
1204 local_lm_response)) {
1205 lm_resp = data_blob_talloc(state->mem_ctx,
1207 sizeof(local_lm_response));
1209 lm_resp = data_blob_null;
1211 SMBNTencrypt(state->request->data.auth.pass,
1215 nt_resp = data_blob_talloc(state->mem_ctx,
1217 sizeof(local_nt_response));
1220 if (strequal(name_domain, get_global_sam_name())) {
1221 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1223 result = winbindd_dual_auth_passdb(
1224 state->mem_ctx, name_domain, name_user,
1225 &chal_blob, &lm_resp, &nt_resp, info3);
1229 /* check authentication loop */
1232 netlogon_fn_t logon_fn;
1234 ZERO_STRUCTP(my_info3);
1237 result = cm_connect_netlogon(domain, &netlogon_pipe);
1239 if (!NT_STATUS_IS_OK(result)) {
1240 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1244 /* It is really important to try SamLogonEx here,
1245 * because in a clustered environment, we want to use
1246 * one machine account from multiple physical
1249 * With a normal SamLogon call, we must keep the
1250 * credentials chain updated and intact between all
1251 * users of the machine account (which would imply
1252 * cross-node communication for every NTLM logon).
1254 * (The credentials chain is not per NETLOGON pipe
1255 * connection, but globally on the server/client pair
1258 * When using SamLogonEx, the credentials are not
1259 * supplied, but the session key is implied by the
1260 * wrapping SamLogon context.
1262 * -- abartlet 21 April 2008
1265 logon_fn = domain->can_do_samlogon_ex
1266 ? rpccli_netlogon_sam_network_logon_ex
1267 : rpccli_netlogon_sam_network_logon;
1269 result = logon_fn(netlogon_pipe,
1272 domain->dcname, /* server name */
1273 name_user, /* user name */
1274 name_domain, /* target domain */
1275 global_myname(), /* workstation */
1282 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1283 && domain->can_do_samlogon_ex) {
1284 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1285 "retrying with NetSamLogon\n"));
1286 domain->can_do_samlogon_ex = false;
1290 /* We have to try a second time as cm_connect_netlogon
1291 might not yet have noticed that the DC has killed
1294 if (!rpccli_is_connected(netlogon_pipe)) {
1298 /* if we get access denied, a possible cause was that we had
1299 and open connection to the DC, but someone changed our
1300 machine account password out from underneath us using 'net
1301 rpc changetrustpw' */
1303 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1304 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1305 "ACCESS_DENIED. Maybe the trust account "
1306 "password was changed and we didn't know it. "
1307 "Killing connections to domain %s\n",
1309 invalidate_cm_connection(&domain->conn);
1313 } while ( (attempts < 2) && retry );
1315 /* handle the case where a NT4 DC does not fill in the acct_flags in
1316 * the samlogon reply info3. When accurate info3 is required by the
1317 * caller, we look up the account flags ourselve - gd */
1319 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1320 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1322 struct rpc_pipe_client *samr_pipe;
1323 struct policy_handle samr_domain_handle, user_pol;
1324 union samr_UserInfo *info = NULL;
1325 NTSTATUS status_tmp;
1328 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1329 &samr_pipe, &samr_domain_handle);
1331 if (!NT_STATUS_IS_OK(status_tmp)) {
1332 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1333 nt_errstr(status_tmp)));
1337 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1338 &samr_domain_handle,
1339 MAXIMUM_ALLOWED_ACCESS,
1343 if (!NT_STATUS_IS_OK(status_tmp)) {
1344 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1345 nt_errstr(status_tmp)));
1349 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1354 if (!NT_STATUS_IS_OK(status_tmp)) {
1355 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1356 nt_errstr(status_tmp)));
1357 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1361 acct_flags = info->info16.acct_flags;
1363 if (acct_flags == 0) {
1364 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1368 my_info3->base.acct_flags = acct_flags;
1370 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1372 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1380 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1381 struct winbindd_cli_state *state)
1383 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1384 NTSTATUS krb5_result = NT_STATUS_OK;
1385 fstring name_domain, name_user;
1387 fstring domain_user;
1388 struct netr_SamInfo3 *info3 = NULL;
1389 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1391 /* Ensure null termination */
1392 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1394 /* Ensure null termination */
1395 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1397 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1398 state->request->data.auth.user));
1400 /* Parse domain and username */
1402 name_map_status = normalize_name_unmap(state->mem_ctx,
1403 state->request->data.auth.user,
1406 /* If the name normalization didnt' actually do anything,
1407 just use the original name */
1409 if (!NT_STATUS_IS_OK(name_map_status) &&
1410 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1412 mapped_user = state->request->data.auth.user;
1415 parse_domain_user(mapped_user, name_domain, name_user);
1417 if ( mapped_user != state->request->data.auth.user ) {
1418 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1419 *lp_winbind_separator(),
1421 safe_strcpy( state->request->data.auth.user, domain_user,
1422 sizeof(state->request->data.auth.user)-1 );
1425 if (!domain->online) {
1426 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1427 if (domain->startup) {
1428 /* Logons are very important to users. If we're offline and
1429 we get a request within the first 30 seconds of startup,
1430 try very hard to find a DC and go online. */
1432 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1433 "request in startup mode.\n", domain->name ));
1435 winbindd_flush_negative_conn_cache(domain);
1436 result = init_dc_connection(domain);
1440 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1442 /* Check for Kerberos authentication */
1443 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1445 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1446 /* save for later */
1447 krb5_result = result;
1450 if (NT_STATUS_IS_OK(result)) {
1451 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1452 goto process_result;
1454 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1457 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1458 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1459 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1460 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1461 set_domain_offline( domain );
1465 /* there are quite some NT_STATUS errors where there is no
1466 * point in retrying with a samlogon, we explictly have to take
1467 * care not to increase the bad logon counter on the DC */
1469 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1470 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1471 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1472 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1473 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1474 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1475 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1476 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1477 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1478 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1482 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1483 DEBUG(3,("falling back to samlogon\n"));
1491 /* Check for Samlogon authentication */
1492 if (domain->online) {
1493 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1495 if (NT_STATUS_IS_OK(result)) {
1496 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1497 /* add the Krb5 err if we have one */
1498 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1499 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1501 goto process_result;
1504 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1505 nt_errstr(result)));
1507 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1509 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1511 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1512 set_domain_offline( domain );
1516 if (domain->online) {
1517 /* We're still online - fail. */
1523 /* Check for Cached logons */
1524 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1525 lp_winbind_offline_logon()) {
1527 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1529 if (NT_STATUS_IS_OK(result)) {
1530 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1531 goto process_result;
1533 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1540 if (NT_STATUS_IS_OK(result)) {
1542 struct dom_sid user_sid;
1544 /* In all codepaths where result == NT_STATUS_OK info3 must have
1545 been initialized. */
1547 result = NT_STATUS_INTERNAL_ERROR;
1551 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1552 netsamlogon_cache_store(name_user, info3);
1554 /* save name_to_sid info as early as possible (only if
1555 this is our primary domain so we don't invalidate
1556 the cache entry by storing the seq_num for the wrong
1558 if ( domain->primary ) {
1559 sid_compose(&user_sid, info3->base.domain_sid,
1561 cache_name2sid(domain, name_domain, name_user,
1562 SID_NAME_USER, &user_sid);
1565 /* Check if the user is in the right group */
1567 result = check_info3_in_group(
1569 state->request->data.auth.require_membership_of_sid);
1570 if (!NT_STATUS_IS_OK(result)) {
1571 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1572 state->request->data.auth.user,
1573 state->request->data.auth.require_membership_of_sid));
1577 result = append_auth_data(state->mem_ctx, state->response,
1578 state->request->flags, info3,
1579 name_domain, name_user);
1580 if (!NT_STATUS_IS_OK(result)) {
1584 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1585 && lp_winbind_offline_logon()) {
1587 result = winbindd_store_creds(domain,
1588 state->request->data.auth.user,
1589 state->request->data.auth.pass,
1593 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1594 struct winbindd_domain *our_domain = find_our_domain();
1596 /* This is not entirely correct I believe, but it is
1597 consistent. Only apply the password policy settings
1598 too warn users for our own domain. Cannot obtain these
1599 from trusted DCs all the time so don't do it at all.
1602 result = NT_STATUS_NOT_SUPPORTED;
1603 if (our_domain == domain ) {
1604 result = fillup_password_policy(our_domain, state);
1607 if (!NT_STATUS_IS_OK(result)
1608 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1610 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1611 domain->name, nt_errstr(result)));
1616 result = NT_STATUS_OK;
1620 /* give us a more useful (more correct?) error code */
1621 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1622 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1623 result = NT_STATUS_NO_LOGON_SERVERS;
1626 set_auth_errors(state->response, result);
1628 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1629 state->request->data.auth.user,
1630 state->response->data.auth.nt_status_string,
1631 state->response->data.auth.pam_error));
1633 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1636 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1637 struct winbindd_cli_state *state)
1640 struct netr_SamInfo3 *info3 = NULL;
1641 struct rpc_pipe_client *netlogon_pipe;
1642 const char *name_user = NULL;
1643 const char *name_domain = NULL;
1644 const char *workstation;
1648 DATA_BLOB lm_resp, nt_resp;
1650 /* This is child-only, so no check for privileged access is needed
1653 /* Ensure null termination */
1654 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1655 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1657 name_user = state->request->data.auth_crap.user;
1658 name_domain = state->request->data.auth_crap.domain;
1659 workstation = state->request->data.auth_crap.workstation;
1661 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1662 name_domain, name_user));
1664 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1665 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1666 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1667 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1668 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1669 state->request->data.auth_crap.lm_resp_len,
1670 state->request->data.auth_crap.nt_resp_len));
1671 result = NT_STATUS_INVALID_PARAMETER;
1676 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1677 state->request->data.auth_crap.lm_resp_len);
1679 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1680 nt_resp = data_blob_talloc(state->mem_ctx,
1681 state->request->extra_data.data,
1682 state->request->data.auth_crap.nt_resp_len);
1684 nt_resp = data_blob_talloc(state->mem_ctx,
1685 state->request->data.auth_crap.nt_resp,
1686 state->request->data.auth_crap.nt_resp_len);
1689 if (strequal(name_domain, get_global_sam_name())) {
1690 DATA_BLOB chal_blob = data_blob_const(
1691 state->request->data.auth_crap.chal,
1692 sizeof(state->request->data.auth_crap.chal));
1694 result = winbindd_dual_auth_passdb(
1695 state->mem_ctx, name_domain, name_user,
1696 &chal_blob, &lm_resp, &nt_resp, &info3);
1697 goto process_result;
1701 netlogon_fn_t logon_fn;
1705 netlogon_pipe = NULL;
1706 result = cm_connect_netlogon(domain, &netlogon_pipe);
1708 if (!NT_STATUS_IS_OK(result)) {
1709 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1710 nt_errstr(result)));
1714 logon_fn = domain->can_do_samlogon_ex
1715 ? rpccli_netlogon_sam_network_logon_ex
1716 : rpccli_netlogon_sam_network_logon;
1718 result = logon_fn(netlogon_pipe,
1720 state->request->data.auth_crap.logon_parameters,
1724 /* Bug #3248 - found by Stefan Burkei. */
1725 workstation, /* We carefully set this above so use it... */
1726 state->request->data.auth_crap.chal,
1731 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1732 && domain->can_do_samlogon_ex) {
1733 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1734 "retrying with NetSamLogon\n"));
1735 domain->can_do_samlogon_ex = false;
1741 /* We have to try a second time as cm_connect_netlogon
1742 might not yet have noticed that the DC has killed
1745 if (!rpccli_is_connected(netlogon_pipe)) {
1749 /* if we get access denied, a possible cause was that we had and open
1750 connection to the DC, but someone changed our machine account password
1751 out from underneath us using 'net rpc changetrustpw' */
1753 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1754 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1755 "ACCESS_DENIED. Maybe the trust account "
1756 "password was changed and we didn't know it. "
1757 "Killing connections to domain %s\n",
1759 invalidate_cm_connection(&domain->conn);
1763 } while ( (attempts < 2) && retry );
1767 if (NT_STATUS_IS_OK(result)) {
1769 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1770 netsamlogon_cache_store(name_user, info3);
1772 /* Check if the user is in the right group */
1774 result = check_info3_in_group(
1776 state->request->data.auth_crap.require_membership_of_sid);
1777 if (!NT_STATUS_IS_OK(result)) {
1778 DEBUG(3, ("User %s is not in the required group (%s), so "
1779 "crap authentication is rejected\n",
1780 state->request->data.auth_crap.user,
1781 state->request->data.auth_crap.require_membership_of_sid));
1785 result = append_auth_data(state->mem_ctx, state->response,
1786 state->request->flags, info3,
1787 name_domain, name_user);
1788 if (!NT_STATUS_IS_OK(result)) {
1795 /* give us a more useful (more correct?) error code */
1796 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1797 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1798 result = NT_STATUS_NO_LOGON_SERVERS;
1801 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1802 result = nt_status_squash(result);
1805 set_auth_errors(state->response, result);
1807 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1808 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1811 state->response->data.auth.nt_status_string,
1812 state->response->data.auth.pam_error));
1814 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1817 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1818 struct winbindd_cli_state *state)
1821 char *newpass = NULL;
1822 struct policy_handle dom_pol;
1823 struct rpc_pipe_client *cli = NULL;
1824 bool got_info = false;
1825 struct samr_DomInfo1 *info = NULL;
1826 struct userPwdChangeFailureInformation *reject = NULL;
1827 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1828 fstring domain, user;
1830 ZERO_STRUCT(dom_pol);
1832 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1833 state->request->data.auth.user));
1835 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1839 /* Change password */
1841 oldpass = state->request->data.chauthtok.oldpass;
1842 newpass = state->request->data.chauthtok.newpass;
1844 /* Initialize reject reason */
1845 state->response->data.auth.reject_reason = Undefined;
1847 /* Get sam handle */
1849 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1851 if (!NT_STATUS_IS_OK(result)) {
1852 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1856 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1863 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1865 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1867 fill_in_password_policy(state->response, info);
1869 state->response->data.auth.reject_reason =
1870 reject->extendedFailureReason;
1875 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1876 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1877 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1878 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1880 /* only fallback when the chgpasswd_user3 call is not supported */
1881 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1882 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1883 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1884 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1886 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1887 nt_errstr(result)));
1889 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1891 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1892 Map to the same status code as Windows 2003. */
1894 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1895 result = NT_STATUS_PASSWORD_RESTRICTION;
1901 if (NT_STATUS_IS_OK(result)
1902 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1903 && lp_winbind_offline_logon()) {
1904 result = winbindd_update_creds_by_name(contact_domain, user,
1906 /* Again, this happens when we login from gdm or xdm
1907 * and the password expires, *BUT* cached crendentials
1908 * doesn't exist. winbindd_update_creds_by_name()
1909 * returns NT_STATUS_NO_SUCH_USER.
1910 * This is not a failure.
1913 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1914 result = NT_STATUS_OK;
1917 if (!NT_STATUS_IS_OK(result)) {
1918 DEBUG(10, ("Failed to store creds: %s\n",
1919 nt_errstr(result)));
1920 goto process_result;
1924 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1926 NTSTATUS policy_ret;
1928 policy_ret = fillup_password_policy(contact_domain, state);
1930 /* failure of this is non critical, it will just provide no
1931 * additional information to the client why the change has
1932 * failed - Guenther */
1934 if (!NT_STATUS_IS_OK(policy_ret)) {
1935 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1936 goto process_result;
1942 if (strequal(contact_domain->name, get_global_sam_name())) {
1943 /* FIXME: internal rpc pipe does not cache handles yet */
1945 if (is_valid_policy_hnd(&dom_pol)) {
1946 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1952 set_auth_errors(state->response, result);
1954 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1955 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1958 state->response->data.auth.nt_status_string,
1959 state->response->data.auth.pam_error));
1961 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1964 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1965 struct winbindd_cli_state *state)
1967 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1969 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1970 state->request->data.logoff.user));
1972 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1973 result = NT_STATUS_OK;
1974 goto process_result;
1977 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1978 result = NT_STATUS_OK;
1979 goto process_result;
1984 if (state->request->data.logoff.uid < 0) {
1985 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1986 goto process_result;
1989 /* what we need here is to find the corresponding krb5 ccache name *we*
1990 * created for a given username and destroy it */
1992 if (!ccache_entry_exists(state->request->data.logoff.user)) {
1993 result = NT_STATUS_OK;
1994 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1995 goto process_result;
1998 if (!ccache_entry_identical(state->request->data.logoff.user,
1999 state->request->data.logoff.uid,
2000 state->request->data.logoff.krb5ccname)) {
2001 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2002 goto process_result;
2005 result = remove_ccache(state->request->data.logoff.user);
2006 if (!NT_STATUS_IS_OK(result)) {
2007 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2008 nt_errstr(result)));
2009 goto process_result;
2013 result = NT_STATUS_NOT_SUPPORTED;
2019 set_auth_errors(state->response, result);
2021 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2024 /* Change user password with auth crap*/
2026 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2029 DATA_BLOB new_nt_password;
2030 DATA_BLOB old_nt_hash_enc;
2031 DATA_BLOB new_lm_password;
2032 DATA_BLOB old_lm_hash_enc;
2033 fstring domain,user;
2034 struct policy_handle dom_pol;
2035 struct winbindd_domain *contact_domain = domainSt;
2036 struct rpc_pipe_client *cli = NULL;
2038 ZERO_STRUCT(dom_pol);
2040 /* Ensure null termination */
2041 state->request->data.chng_pswd_auth_crap.user[
2042 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2043 state->request->data.chng_pswd_auth_crap.domain[
2044 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2048 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2049 (unsigned long)state->pid,
2050 state->request->data.chng_pswd_auth_crap.domain,
2051 state->request->data.chng_pswd_auth_crap.user));
2053 if (lp_winbind_offline_logon()) {
2054 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2055 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2056 result = NT_STATUS_ACCESS_DENIED;
2060 if (*state->request->data.chng_pswd_auth_crap.domain) {
2061 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2063 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2067 DEBUG(3,("no domain specified with username (%s) - "
2069 state->request->data.chng_pswd_auth_crap.user));
2070 result = NT_STATUS_NO_SUCH_USER;
2075 if (!*domain && lp_winbind_use_default_domain()) {
2076 fstrcpy(domain,(char *)lp_workgroup());
2080 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2083 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2084 (unsigned long)state->pid, domain, user));
2086 /* Change password */
2087 new_nt_password = data_blob_const(
2088 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2089 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2091 old_nt_hash_enc = data_blob_const(
2092 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2093 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2095 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2096 new_lm_password = data_blob_const(
2097 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2098 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2100 old_lm_hash_enc = data_blob_const(
2101 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2102 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2104 new_lm_password.length = 0;
2105 old_lm_hash_enc.length = 0;
2108 /* Get sam handle */
2110 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2111 if (!NT_STATUS_IS_OK(result)) {
2112 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2116 result = rpccli_samr_chng_pswd_auth_crap(
2117 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2118 new_lm_password, old_lm_hash_enc);
2122 if (strequal(contact_domain->name, get_global_sam_name())) {
2123 /* FIXME: internal rpc pipe does not cache handles yet */
2125 if (is_valid_policy_hnd(&dom_pol)) {
2126 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2132 set_auth_errors(state->response, result);
2134 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2135 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2137 state->response->data.auth.nt_status_string,
2138 state->response->data.auth.pam_error));
2140 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;