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_response *response)
393 TALLOC_CTX *frame = talloc_stackframe();
394 struct winbindd_methods *methods;
396 struct samr_DomInfo1 password_policy;
398 if ( !winbindd_can_contact_domain( domain ) ) {
399 DEBUG(5,("fillup_password_policy: No inbound trust to "
400 "contact domain %s\n", domain->name));
401 status = NT_STATUS_NOT_SUPPORTED;
405 methods = domain->methods;
407 status = methods->password_policy(domain, talloc_tos(), &password_policy);
408 if (NT_STATUS_IS_ERR(status)) {
412 fill_in_password_policy(response, &password_policy);
419 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
421 uint16 *lockout_threshold)
423 struct winbindd_methods *methods;
424 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
425 struct samr_DomInfo12 lockout_policy;
427 *lockout_threshold = 0;
429 methods = domain->methods;
431 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
432 if (NT_STATUS_IS_ERR(status)) {
436 *lockout_threshold = lockout_policy.lockout_threshold;
441 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
443 uint32 *password_properties)
445 struct winbindd_methods *methods;
446 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
447 struct samr_DomInfo1 password_policy;
449 *password_properties = 0;
451 methods = domain->methods;
453 status = methods->password_policy(domain, mem_ctx, &password_policy);
454 if (NT_STATUS_IS_ERR(status)) {
458 *password_properties = password_policy.password_properties;
465 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
468 const char **user_ccache_file)
470 /* accept FILE and WRFILE as krb5_cc_type from the client and then
471 * build the full ccname string based on the user's uid here -
474 const char *gen_cc = NULL;
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(
479 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
481 if (strequal(type, "WRFILE")) {
482 gen_cc = talloc_asprintf(
483 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
487 *user_ccache_file = gen_cc;
489 if (gen_cc == NULL) {
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
492 if (gen_cc == NULL) {
493 DEBUG(0,("out of memory\n"));
497 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
498 (*user_ccache_file == NULL) ? " (internal)":""));
505 uid_t get_uid_from_request(struct winbindd_request *request)
509 uid = request->data.auth.uid;
512 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
518 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
520 return get_uid_from_request(state->request);
523 /**********************************************************************
524 Authenticate a user with a clear text password using Kerberos and fill up
526 **********************************************************************/
528 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
529 struct winbindd_domain *domain,
532 const char *krb5_cc_type,
534 struct netr_SamInfo3 **info3,
538 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
539 krb5_error_code krb5_ret;
540 const char *cc = NULL;
541 const char *principal_s = NULL;
542 const char *service = NULL;
544 fstring name_domain, name_user;
545 time_t ticket_lifetime = 0;
546 time_t renewal_until = 0;
548 time_t time_offset = 0;
549 const char *user_ccache_file;
550 struct PAC_LOGON_INFO *logon_info = NULL;
555 * prepare a krb5_cc_cache string for the user */
558 DEBUG(0,("no valid uid\n"));
561 cc = generate_krb5_ccache(mem_ctx,
566 return NT_STATUS_NO_MEMORY;
571 * get kerberos properties */
573 if (domain->private_data) {
574 ads = (ADS_STRUCT *)domain->private_data;
575 time_offset = ads->auth.time_offset;
580 * do kerberos auth and setup ccache as the user */
582 parse_domain_user(user, name_domain, name_user);
584 realm = domain->alt_name;
587 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
588 if (principal_s == NULL) {
589 return NT_STATUS_NO_MEMORY;
592 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
593 if (service == NULL) {
594 return NT_STATUS_NO_MEMORY;
597 /* if this is a user ccache, we need to act as the user to let the krb5
598 * library handle the chown, etc. */
600 /************************ ENTERING NON-ROOT **********************/
602 if (user_ccache_file != NULL) {
603 set_effective_uid(uid);
604 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
607 result = kerberos_return_pac(mem_ctx,
616 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
619 if (user_ccache_file != NULL) {
620 gain_root_privilege();
623 /************************ RETURNED TO ROOT **********************/
625 if (!NT_STATUS_IS_OK(result)) {
629 *info3 = &logon_info->info3;
631 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
634 /* if we had a user's ccache then return that string for the pam
637 if (user_ccache_file != NULL) {
639 fstrcpy(krb5ccname, user_ccache_file);
641 result = add_ccache_to_list(principal_s,
652 if (!NT_STATUS_IS_OK(result)) {
653 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
658 /* need to delete the memory cred cache, it is not used anymore */
660 krb5_ret = ads_kdestroy(cc);
662 DEBUG(3,("winbindd_raw_kerberos_login: "
663 "could not destroy krb5 credential cache: "
664 "%s\n", error_message(krb5_ret)));
673 /* we could have created a new credential cache with a valid tgt in it
674 * but we werent able to get or verify the service ticket for this
675 * local host and therefor didn't get the PAC, we need to remove that
676 * cache entirely now */
678 krb5_ret = ads_kdestroy(cc);
680 DEBUG(3,("winbindd_raw_kerberos_login: "
681 "could not destroy krb5 credential cache: "
682 "%s\n", error_message(krb5_ret)));
685 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
686 DEBUG(3,("winbindd_raw_kerberos_login: "
687 "could not remove ccache for user %s\n",
693 return NT_STATUS_NOT_SUPPORTED;
694 #endif /* HAVE_KRB5 */
697 /****************************************************************
698 ****************************************************************/
700 bool check_request_flags(uint32_t flags)
702 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
703 WBFLAG_PAM_INFO3_TEXT |
704 WBFLAG_PAM_INFO3_NDR;
706 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
707 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
708 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
709 !(flags & flags_edata) ) {
713 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
719 /****************************************************************
720 ****************************************************************/
722 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
723 struct winbindd_response *resp,
724 uint32_t request_flags,
725 struct netr_SamInfo3 *info3,
726 const char *name_domain,
727 const char *name_user)
731 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
732 memcpy(resp->data.auth.user_session_key,
734 sizeof(resp->data.auth.user_session_key)
738 if (request_flags & WBFLAG_PAM_LMKEY) {
739 memcpy(resp->data.auth.first_8_lm_hash,
740 info3->base.LMSessKey.key,
741 sizeof(resp->data.auth.first_8_lm_hash)
745 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
746 result = append_unix_username(mem_ctx, resp,
747 info3, name_domain, name_user);
748 if (!NT_STATUS_IS_OK(result)) {
749 DEBUG(10,("Failed to append Unix Username: %s\n",
755 /* currently, anything from here on potentially overwrites extra_data. */
757 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
758 result = append_info3_as_ndr(mem_ctx, resp, info3);
759 if (!NT_STATUS_IS_OK(result)) {
760 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
766 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
767 result = append_info3_as_txt(mem_ctx, resp, info3);
768 if (!NT_STATUS_IS_OK(result)) {
769 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
775 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
776 result = append_afs_token(mem_ctx, resp,
777 info3, name_domain, name_user);
778 if (!NT_STATUS_IS_OK(result)) {
779 DEBUG(10,("Failed to append AFS token: %s\n",
788 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
789 struct winbindd_cli_state *state,
790 struct netr_SamInfo3 **info3)
792 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
793 uint16 max_allowed_bad_attempts;
794 fstring name_domain, name_user;
796 enum lsa_SidType type;
797 uchar new_nt_pass[NT_HASH_LEN];
798 const uint8 *cached_nt_pass;
799 const uint8 *cached_salt;
800 struct netr_SamInfo3 *my_info3;
801 time_t kickoff_time, must_change_time;
802 bool password_good = false;
804 struct winbindd_tdc_domain *tdc_domain = NULL;
811 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
813 /* Parse domain and username */
815 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
818 if (!lookup_cached_name(name_domain,
822 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
823 return NT_STATUS_NO_SUCH_USER;
826 if (type != SID_NAME_USER) {
827 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
828 return NT_STATUS_LOGON_FAILURE;
831 result = winbindd_get_creds(domain,
837 if (!NT_STATUS_IS_OK(result)) {
838 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
844 E_md4hash(state->request->data.auth.pass, new_nt_pass);
846 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
847 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
849 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
853 /* In this case we didn't store the nt_hash itself,
854 but the MD5 combination of salt + nt_hash. */
855 uchar salted_hash[NT_HASH_LEN];
856 E_md5hash(cached_salt, new_nt_pass, salted_hash);
858 password_good = (memcmp(cached_nt_pass, salted_hash,
861 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
862 password_good = (memcmp(cached_nt_pass, new_nt_pass,
868 /* User *DOES* know the password, update logon_time and reset
871 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
873 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
874 return NT_STATUS_ACCOUNT_LOCKED_OUT;
877 if (my_info3->base.acct_flags & ACB_DISABLED) {
878 return NT_STATUS_ACCOUNT_DISABLED;
881 if (my_info3->base.acct_flags & ACB_WSTRUST) {
882 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
885 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
886 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
889 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
890 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
893 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
894 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
895 my_info3->base.acct_flags));
896 return NT_STATUS_LOGON_FAILURE;
899 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
900 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
901 return NT_STATUS_ACCOUNT_EXPIRED;
904 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
905 if (must_change_time != 0 && must_change_time < time(NULL)) {
906 /* we allow grace logons when the password has expired */
907 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
908 /* return NT_STATUS_PASSWORD_EXPIRED; */
913 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
914 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
915 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
916 /* used to cope with the case winbindd starting without network. */
917 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
920 const char *cc = NULL;
922 const char *principal_s = NULL;
923 const char *service = NULL;
924 const char *user_ccache_file;
926 uid = get_uid_from_state(state);
928 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
929 return NT_STATUS_INVALID_PARAMETER;
932 cc = generate_krb5_ccache(state->mem_ctx,
933 state->request->data.auth.krb5_cc_type,
934 state->request->data.auth.uid,
937 return NT_STATUS_NO_MEMORY;
940 realm = domain->alt_name;
943 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
944 if (principal_s == NULL) {
945 return NT_STATUS_NO_MEMORY;
948 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
949 if (service == NULL) {
950 return NT_STATUS_NO_MEMORY;
953 if (user_ccache_file != NULL) {
955 fstrcpy(state->response->data.auth.krb5ccname,
958 result = add_ccache_to_list(principal_s,
961 state->request->data.auth.user,
965 time(NULL) + lp_winbind_cache_time(),
966 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
969 if (!NT_STATUS_IS_OK(result)) {
970 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
971 "to add ccache to list: %s\n",
976 #endif /* HAVE_KRB5 */
978 /* FIXME: we possibly should handle logon hours as well (does xp when
979 * offline?) see auth/auth_sam.c:sam_account_ok for details */
981 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
982 my_info3->base.bad_password_count = 0;
984 result = winbindd_update_creds_by_info3(domain,
985 state->request->data.auth.user,
986 state->request->data.auth.pass,
988 if (!NT_STATUS_IS_OK(result)) {
989 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
998 /* User does *NOT* know the correct password, modify info3 accordingly */
1000 /* failure of this is not critical */
1001 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1002 if (!NT_STATUS_IS_OK(result)) {
1003 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1004 "Won't be able to honour account lockout policies\n"));
1007 /* increase counter */
1008 my_info3->base.bad_password_count++;
1010 if (max_allowed_bad_attempts == 0) {
1015 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1017 uint32 password_properties;
1019 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1020 if (!NT_STATUS_IS_OK(result)) {
1021 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1024 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1025 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1026 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1031 result = winbindd_update_creds_by_info3(domain,
1032 state->request->data.auth.user,
1036 if (!NT_STATUS_IS_OK(result)) {
1037 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1038 nt_errstr(result)));
1041 return NT_STATUS_LOGON_FAILURE;
1044 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1045 struct winbindd_cli_state *state,
1046 struct netr_SamInfo3 **info3)
1048 struct winbindd_domain *contact_domain;
1049 fstring name_domain, name_user;
1052 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1054 /* Parse domain and username */
1056 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1058 /* what domain should we contact? */
1061 if (!(contact_domain = find_domain_from_name(name_domain))) {
1062 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1063 state->request->data.auth.user, name_domain, name_user, name_domain));
1064 result = NT_STATUS_NO_SUCH_USER;
1069 if (is_myname(name_domain)) {
1070 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1071 result = NT_STATUS_NO_SUCH_USER;
1075 contact_domain = find_domain_from_name(name_domain);
1076 if (contact_domain == NULL) {
1077 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1078 state->request->data.auth.user, name_domain, name_user, name_domain));
1080 contact_domain = find_our_domain();
1084 if (contact_domain->initialized &&
1085 contact_domain->active_directory) {
1089 if (!contact_domain->initialized) {
1090 init_dc_connection(contact_domain);
1093 if (!contact_domain->active_directory) {
1094 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1095 return NT_STATUS_INVALID_LOGON_TYPE;
1098 result = winbindd_raw_kerberos_login(
1099 state->mem_ctx, contact_domain,
1100 state->request->data.auth.user,
1101 state->request->data.auth.pass,
1102 state->request->data.auth.krb5_cc_type,
1103 get_uid_from_request(state->request),
1104 info3, state->response->data.auth.krb5ccname);
1109 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1110 const char *domain, const char *user,
1111 const DATA_BLOB *challenge,
1112 const DATA_BLOB *lm_resp,
1113 const DATA_BLOB *nt_resp,
1114 struct netr_SamInfo3 **pinfo3)
1116 struct auth_usersupplied_info *user_info = NULL;
1119 status = make_user_info(&user_info, user, user, domain, domain,
1120 global_myname(), lm_resp, nt_resp, NULL, NULL,
1121 NULL, AUTH_PASSWORD_RESPONSE);
1122 if (!NT_STATUS_IS_OK(status)) {
1123 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1127 /* We don't want any more mapping of the username */
1128 user_info->mapped_state = True;
1130 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1132 free_user_info(&user_info);
1133 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1134 user, nt_errstr(status)));
1138 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1139 TALLOC_CTX *mem_ctx,
1140 uint32_t logon_parameters,
1142 const char *username,
1143 const char *domainname,
1144 const char *workstation,
1145 const uint8_t chal[8],
1146 DATA_BLOB lm_response,
1147 DATA_BLOB nt_response,
1148 struct netr_SamInfo3 **info3)
1155 struct rpc_pipe_client *netlogon_pipe;
1157 ZERO_STRUCTP(info3);
1160 result = cm_connect_netlogon(domain, &netlogon_pipe);
1162 if (!NT_STATUS_IS_OK(result)) {
1163 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1164 nt_errstr(result)));
1168 /* It is really important to try SamLogonEx here,
1169 * because in a clustered environment, we want to use
1170 * one machine account from multiple physical
1173 * With a normal SamLogon call, we must keep the
1174 * credentials chain updated and intact between all
1175 * users of the machine account (which would imply
1176 * cross-node communication for every NTLM logon).
1178 * (The credentials chain is not per NETLOGON pipe
1179 * connection, but globally on the server/client pair
1182 * When using SamLogonEx, the credentials are not
1183 * supplied, but the session key is implied by the
1184 * wrapping SamLogon context.
1186 * -- abartlet 21 April 2008
1189 if (domain->can_do_samlogon_ex) {
1190 result = rpccli_netlogon_sam_network_logon_ex(
1194 server, /* server name */
1195 username, /* user name */
1196 domainname, /* target domain */
1197 workstation, /* workstation */
1203 result = rpccli_netlogon_sam_network_logon(
1207 server, /* server name */
1208 username, /* user name */
1209 domainname, /* target domain */
1210 workstation, /* workstation */
1219 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1220 && domain->can_do_samlogon_ex) {
1221 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1222 "retrying with NetSamLogon\n"));
1223 domain->can_do_samlogon_ex = false;
1228 /* We have to try a second time as cm_connect_netlogon
1229 might not yet have noticed that the DC has killed
1232 if (!rpccli_is_connected(netlogon_pipe)) {
1237 /* if we get access denied, a possible cause was that we had
1238 and open connection to the DC, but someone changed our
1239 machine account password out from underneath us using 'net
1240 rpc changetrustpw' */
1242 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1243 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1244 "ACCESS_DENIED. Maybe the trust account "
1245 "password was changed and we didn't know it. "
1246 "Killing connections to domain %s\n",
1248 invalidate_cm_connection(&domain->conn);
1252 } while ( (attempts < 2) && retry );
1257 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1258 struct winbindd_domain *domain,
1261 uint32_t request_flags,
1262 struct netr_SamInfo3 **info3)
1268 unsigned char local_nt_response[24];
1269 fstring name_domain, name_user;
1271 struct netr_SamInfo3 *my_info3 = NULL;
1275 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1277 /* Parse domain and username */
1279 parse_domain_user(user, name_domain, name_user);
1281 /* do password magic */
1283 generate_random_buffer(chal, sizeof(chal));
1285 if (lp_client_ntlmv2_auth()) {
1286 DATA_BLOB server_chal;
1287 DATA_BLOB names_blob;
1288 server_chal = data_blob_const(chal, 8);
1290 /* note that the 'workgroup' here is for the local
1291 machine. The 'server name' must match the
1292 'workstation' passed to the actual SamLogon call.
1294 names_blob = NTLMv2_generate_names_blob(
1295 mem_ctx, global_myname(), lp_workgroup());
1297 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1301 &lm_resp, &nt_resp, NULL, NULL)) {
1302 data_blob_free(&names_blob);
1303 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1304 result = NT_STATUS_NO_MEMORY;
1307 data_blob_free(&names_blob);
1309 lm_resp = data_blob_null;
1310 SMBNTencrypt(pass, chal, local_nt_response);
1312 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1313 sizeof(local_nt_response));
1316 if (strequal(name_domain, get_global_sam_name())) {
1317 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1319 result = winbindd_dual_auth_passdb(
1320 mem_ctx, name_domain, name_user,
1321 &chal_blob, &lm_resp, &nt_resp, info3);
1325 /* check authentication loop */
1327 result = winbind_samlogon_retry_loop(domain,
1338 if (!NT_STATUS_IS_OK(result)) {
1342 /* handle the case where a NT4 DC does not fill in the acct_flags in
1343 * the samlogon reply info3. When accurate info3 is required by the
1344 * caller, we look up the account flags ourselve - gd */
1346 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1347 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1349 struct rpc_pipe_client *samr_pipe;
1350 struct policy_handle samr_domain_handle, user_pol;
1351 union samr_UserInfo *info = NULL;
1352 NTSTATUS status_tmp;
1355 status_tmp = cm_connect_sam(domain, mem_ctx,
1356 &samr_pipe, &samr_domain_handle);
1358 if (!NT_STATUS_IS_OK(status_tmp)) {
1359 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1360 nt_errstr(status_tmp)));
1364 status_tmp = rpccli_samr_OpenUser(samr_pipe, mem_ctx,
1365 &samr_domain_handle,
1366 MAXIMUM_ALLOWED_ACCESS,
1370 if (!NT_STATUS_IS_OK(status_tmp)) {
1371 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1372 nt_errstr(status_tmp)));
1376 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, mem_ctx,
1381 if (!NT_STATUS_IS_OK(status_tmp)) {
1382 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1383 nt_errstr(status_tmp)));
1384 rpccli_samr_Close(samr_pipe, mem_ctx, &user_pol);
1388 acct_flags = info->info16.acct_flags;
1390 if (acct_flags == 0) {
1391 rpccli_samr_Close(samr_pipe, mem_ctx, &user_pol);
1395 my_info3->base.acct_flags = acct_flags;
1397 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1399 rpccli_samr_Close(samr_pipe, mem_ctx, &user_pol);
1407 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1408 struct winbindd_cli_state *state)
1410 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1411 NTSTATUS krb5_result = NT_STATUS_OK;
1412 fstring name_domain, name_user;
1414 fstring domain_user;
1415 struct netr_SamInfo3 *info3 = NULL;
1416 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1418 /* Ensure null termination */
1419 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1421 /* Ensure null termination */
1422 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1424 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1425 state->request->data.auth.user));
1427 /* Parse domain and username */
1429 name_map_status = normalize_name_unmap(state->mem_ctx,
1430 state->request->data.auth.user,
1433 /* If the name normalization didnt' actually do anything,
1434 just use the original name */
1436 if (!NT_STATUS_IS_OK(name_map_status) &&
1437 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1439 mapped_user = state->request->data.auth.user;
1442 parse_domain_user(mapped_user, name_domain, name_user);
1444 if ( mapped_user != state->request->data.auth.user ) {
1445 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1446 *lp_winbind_separator(),
1448 safe_strcpy( state->request->data.auth.user, domain_user,
1449 sizeof(state->request->data.auth.user)-1 );
1452 if (!domain->online) {
1453 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1454 if (domain->startup) {
1455 /* Logons are very important to users. If we're offline and
1456 we get a request within the first 30 seconds of startup,
1457 try very hard to find a DC and go online. */
1459 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1460 "request in startup mode.\n", domain->name ));
1462 winbindd_flush_negative_conn_cache(domain);
1463 result = init_dc_connection(domain);
1467 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1469 /* Check for Kerberos authentication */
1470 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1472 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1473 /* save for later */
1474 krb5_result = result;
1477 if (NT_STATUS_IS_OK(result)) {
1478 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1479 goto process_result;
1481 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1484 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1485 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1486 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1487 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1488 set_domain_offline( domain );
1492 /* there are quite some NT_STATUS errors where there is no
1493 * point in retrying with a samlogon, we explictly have to take
1494 * care not to increase the bad logon counter on the DC */
1496 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1497 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1498 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1499 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1500 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1501 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1502 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1503 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1504 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1505 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1509 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1510 DEBUG(3,("falling back to samlogon\n"));
1518 /* Check for Samlogon authentication */
1519 if (domain->online) {
1520 result = winbindd_dual_pam_auth_samlogon(
1521 state->mem_ctx, domain,
1522 state->request->data.auth.user,
1523 state->request->data.auth.pass,
1524 state->request->flags,
1527 if (NT_STATUS_IS_OK(result)) {
1528 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1529 /* add the Krb5 err if we have one */
1530 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1531 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1533 goto process_result;
1536 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1537 nt_errstr(result)));
1539 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1543 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1544 set_domain_offline( domain );
1548 if (domain->online) {
1549 /* We're still online - fail. */
1555 /* Check for Cached logons */
1556 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1557 lp_winbind_offline_logon()) {
1559 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1561 if (NT_STATUS_IS_OK(result)) {
1562 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1563 goto process_result;
1565 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1572 if (NT_STATUS_IS_OK(result)) {
1574 struct dom_sid user_sid;
1576 /* In all codepaths where result == NT_STATUS_OK info3 must have
1577 been initialized. */
1579 result = NT_STATUS_INTERNAL_ERROR;
1583 sid_compose(&user_sid, info3->base.domain_sid,
1586 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1588 netsamlogon_cache_store(name_user, info3);
1590 /* save name_to_sid info as early as possible (only if
1591 this is our primary domain so we don't invalidate
1592 the cache entry by storing the seq_num for the wrong
1594 if ( domain->primary ) {
1595 cache_name2sid(domain, name_domain, name_user,
1596 SID_NAME_USER, &user_sid);
1599 /* Check if the user is in the right group */
1601 result = check_info3_in_group(
1603 state->request->data.auth.require_membership_of_sid);
1604 if (!NT_STATUS_IS_OK(result)) {
1605 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1606 state->request->data.auth.user,
1607 state->request->data.auth.require_membership_of_sid));
1611 result = append_auth_data(state->mem_ctx, state->response,
1612 state->request->flags, info3,
1613 name_domain, name_user);
1614 if (!NT_STATUS_IS_OK(result)) {
1618 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1619 && lp_winbind_offline_logon()) {
1621 result = winbindd_store_creds(domain,
1622 state->request->data.auth.user,
1623 state->request->data.auth.pass,
1627 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1628 struct winbindd_domain *our_domain = find_our_domain();
1630 /* This is not entirely correct I believe, but it is
1631 consistent. Only apply the password policy settings
1632 too warn users for our own domain. Cannot obtain these
1633 from trusted DCs all the time so don't do it at all.
1636 result = NT_STATUS_NOT_SUPPORTED;
1637 if (our_domain == domain ) {
1638 result = fillup_password_policy(
1639 our_domain, state->response);
1642 if (!NT_STATUS_IS_OK(result)
1643 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1645 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1646 domain->name, nt_errstr(result)));
1651 result = NT_STATUS_OK;
1655 /* give us a more useful (more correct?) error code */
1656 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1657 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1658 result = NT_STATUS_NO_LOGON_SERVERS;
1661 set_auth_errors(state->response, result);
1663 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1664 state->request->data.auth.user,
1665 state->response->data.auth.nt_status_string,
1666 state->response->data.auth.pam_error));
1668 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1671 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1672 struct winbindd_cli_state *state)
1675 struct netr_SamInfo3 *info3 = NULL;
1676 const char *name_user = NULL;
1677 const char *name_domain = NULL;
1678 const char *workstation;
1680 DATA_BLOB lm_resp, nt_resp;
1682 /* This is child-only, so no check for privileged access is needed
1685 /* Ensure null termination */
1686 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1687 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1689 name_user = state->request->data.auth_crap.user;
1690 name_domain = state->request->data.auth_crap.domain;
1691 workstation = state->request->data.auth_crap.workstation;
1693 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1694 name_domain, name_user));
1696 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1697 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1698 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1699 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1700 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1701 state->request->data.auth_crap.lm_resp_len,
1702 state->request->data.auth_crap.nt_resp_len));
1703 result = NT_STATUS_INVALID_PARAMETER;
1708 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1709 state->request->data.auth_crap.lm_resp_len);
1711 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1712 nt_resp = data_blob_talloc(state->mem_ctx,
1713 state->request->extra_data.data,
1714 state->request->data.auth_crap.nt_resp_len);
1716 nt_resp = data_blob_talloc(state->mem_ctx,
1717 state->request->data.auth_crap.nt_resp,
1718 state->request->data.auth_crap.nt_resp_len);
1721 if (strequal(name_domain, get_global_sam_name())) {
1722 DATA_BLOB chal_blob = data_blob_const(
1723 state->request->data.auth_crap.chal,
1724 sizeof(state->request->data.auth_crap.chal));
1726 result = winbindd_dual_auth_passdb(
1727 state->mem_ctx, name_domain, name_user,
1728 &chal_blob, &lm_resp, &nt_resp, &info3);
1729 goto process_result;
1732 result = winbind_samlogon_retry_loop(domain,
1734 state->request->data.auth_crap.logon_parameters,
1738 /* Bug #3248 - found by Stefan Burkei. */
1739 workstation, /* We carefully set this above so use it... */
1740 state->request->data.auth_crap.chal,
1744 if (!NT_STATUS_IS_OK(result)) {
1750 if (NT_STATUS_IS_OK(result)) {
1751 struct dom_sid user_sid;
1753 sid_compose(&user_sid, info3->base.domain_sid,
1755 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1757 netsamlogon_cache_store(name_user, info3);
1759 /* Check if the user is in the right group */
1761 result = check_info3_in_group(
1763 state->request->data.auth_crap.require_membership_of_sid);
1764 if (!NT_STATUS_IS_OK(result)) {
1765 DEBUG(3, ("User %s is not in the required group (%s), so "
1766 "crap authentication is rejected\n",
1767 state->request->data.auth_crap.user,
1768 state->request->data.auth_crap.require_membership_of_sid));
1772 result = append_auth_data(state->mem_ctx, state->response,
1773 state->request->flags, info3,
1774 name_domain, name_user);
1775 if (!NT_STATUS_IS_OK(result)) {
1782 /* give us a more useful (more correct?) error code */
1783 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1784 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1785 result = NT_STATUS_NO_LOGON_SERVERS;
1788 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1789 result = nt_status_squash(result);
1792 set_auth_errors(state->response, result);
1794 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1795 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1798 state->response->data.auth.nt_status_string,
1799 state->response->data.auth.pam_error));
1801 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1804 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1805 struct winbindd_cli_state *state)
1808 char *newpass = NULL;
1809 struct policy_handle dom_pol;
1810 struct rpc_pipe_client *cli = NULL;
1811 bool got_info = false;
1812 struct samr_DomInfo1 *info = NULL;
1813 struct userPwdChangeFailureInformation *reject = NULL;
1814 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1815 fstring domain, user;
1817 ZERO_STRUCT(dom_pol);
1819 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1820 state->request->data.auth.user));
1822 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1826 /* Change password */
1828 oldpass = state->request->data.chauthtok.oldpass;
1829 newpass = state->request->data.chauthtok.newpass;
1831 /* Initialize reject reason */
1832 state->response->data.auth.reject_reason = Undefined;
1834 /* Get sam handle */
1836 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1838 if (!NT_STATUS_IS_OK(result)) {
1839 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1843 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1850 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1852 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1854 fill_in_password_policy(state->response, info);
1856 state->response->data.auth.reject_reason =
1857 reject->extendedFailureReason;
1862 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1863 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1864 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1865 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1867 /* only fallback when the chgpasswd_user3 call is not supported */
1868 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1869 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1870 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1871 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1873 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1874 nt_errstr(result)));
1876 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1878 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1879 Map to the same status code as Windows 2003. */
1881 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1882 result = NT_STATUS_PASSWORD_RESTRICTION;
1888 if (NT_STATUS_IS_OK(result)
1889 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1890 && lp_winbind_offline_logon()) {
1891 result = winbindd_update_creds_by_name(contact_domain, user,
1893 /* Again, this happens when we login from gdm or xdm
1894 * and the password expires, *BUT* cached crendentials
1895 * doesn't exist. winbindd_update_creds_by_name()
1896 * returns NT_STATUS_NO_SUCH_USER.
1897 * This is not a failure.
1900 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1901 result = NT_STATUS_OK;
1904 if (!NT_STATUS_IS_OK(result)) {
1905 DEBUG(10, ("Failed to store creds: %s\n",
1906 nt_errstr(result)));
1907 goto process_result;
1911 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1913 NTSTATUS policy_ret;
1915 policy_ret = fillup_password_policy(
1916 contact_domain, state->response);
1918 /* failure of this is non critical, it will just provide no
1919 * additional information to the client why the change has
1920 * failed - Guenther */
1922 if (!NT_STATUS_IS_OK(policy_ret)) {
1923 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1924 goto process_result;
1930 if (strequal(contact_domain->name, get_global_sam_name())) {
1931 /* FIXME: internal rpc pipe does not cache handles yet */
1933 if (is_valid_policy_hnd(&dom_pol)) {
1934 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1940 set_auth_errors(state->response, result);
1942 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1943 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1946 state->response->data.auth.nt_status_string,
1947 state->response->data.auth.pam_error));
1949 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1952 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1953 struct winbindd_cli_state *state)
1955 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1957 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1958 state->request->data.logoff.user));
1960 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1961 result = NT_STATUS_OK;
1962 goto process_result;
1965 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1966 result = NT_STATUS_OK;
1967 goto process_result;
1972 if (state->request->data.logoff.uid < 0) {
1973 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1974 goto process_result;
1977 /* what we need here is to find the corresponding krb5 ccache name *we*
1978 * created for a given username and destroy it */
1980 if (!ccache_entry_exists(state->request->data.logoff.user)) {
1981 result = NT_STATUS_OK;
1982 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1983 goto process_result;
1986 if (!ccache_entry_identical(state->request->data.logoff.user,
1987 state->request->data.logoff.uid,
1988 state->request->data.logoff.krb5ccname)) {
1989 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
1990 goto process_result;
1993 result = remove_ccache(state->request->data.logoff.user);
1994 if (!NT_STATUS_IS_OK(result)) {
1995 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
1996 nt_errstr(result)));
1997 goto process_result;
2001 result = NT_STATUS_NOT_SUPPORTED;
2007 set_auth_errors(state->response, result);
2009 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2012 /* Change user password with auth crap*/
2014 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2017 DATA_BLOB new_nt_password;
2018 DATA_BLOB old_nt_hash_enc;
2019 DATA_BLOB new_lm_password;
2020 DATA_BLOB old_lm_hash_enc;
2021 fstring domain,user;
2022 struct policy_handle dom_pol;
2023 struct winbindd_domain *contact_domain = domainSt;
2024 struct rpc_pipe_client *cli = NULL;
2026 ZERO_STRUCT(dom_pol);
2028 /* Ensure null termination */
2029 state->request->data.chng_pswd_auth_crap.user[
2030 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2031 state->request->data.chng_pswd_auth_crap.domain[
2032 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2036 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2037 (unsigned long)state->pid,
2038 state->request->data.chng_pswd_auth_crap.domain,
2039 state->request->data.chng_pswd_auth_crap.user));
2041 if (lp_winbind_offline_logon()) {
2042 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2043 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2044 result = NT_STATUS_ACCESS_DENIED;
2048 if (*state->request->data.chng_pswd_auth_crap.domain) {
2049 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2051 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2055 DEBUG(3,("no domain specified with username (%s) - "
2057 state->request->data.chng_pswd_auth_crap.user));
2058 result = NT_STATUS_NO_SUCH_USER;
2063 if (!*domain && lp_winbind_use_default_domain()) {
2064 fstrcpy(domain,(char *)lp_workgroup());
2068 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2071 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2072 (unsigned long)state->pid, domain, user));
2074 /* Change password */
2075 new_nt_password = data_blob_const(
2076 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2077 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2079 old_nt_hash_enc = data_blob_const(
2080 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2081 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2083 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2084 new_lm_password = data_blob_const(
2085 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2086 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2088 old_lm_hash_enc = data_blob_const(
2089 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2090 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2092 new_lm_password.length = 0;
2093 old_lm_hash_enc.length = 0;
2096 /* Get sam handle */
2098 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2099 if (!NT_STATUS_IS_OK(result)) {
2100 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2104 result = rpccli_samr_chng_pswd_auth_crap(
2105 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2106 new_lm_password, old_lm_hash_enc);
2110 if (strequal(contact_domain->name, get_global_sam_name())) {
2111 /* FIXME: internal rpc pipe does not cache handles yet */
2113 if (is_valid_policy_hnd(&dom_pol)) {
2114 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2120 set_auth_errors(state->response, result);
2122 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2123 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2125 state->response->data.auth.nt_status_string,
2126 state->response->data.auth.pam_error));
2128 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;