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/ndr_samr_c.h"
29 #include "rpc_client/cli_pipe.h"
30 #include "rpc_client/cli_samr.h"
31 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "rpc_client/cli_netlogon.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
40 #include "../lib/tsocket/tsocket.h"
41 #include "auth/kerberos/pac_utils.h"
42 #include "auth/gensec/gensec.h"
43 #include "librpc/crypto/gse_krb5.h"
44 #include "lib/afs/afs_funcs.h"
45 #include "libsmb/samlogon_cache.h"
48 #define DBGC_CLASS DBGC_WINBIND
50 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
52 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
53 struct winbindd_response *resp,
54 struct netr_SamInfo3 *info3)
59 resp->data.auth.info3.logon_time =
60 nt_time_to_unix(info3->base.logon_time);
61 resp->data.auth.info3.logoff_time =
62 nt_time_to_unix(info3->base.logoff_time);
63 resp->data.auth.info3.kickoff_time =
64 nt_time_to_unix(info3->base.kickoff_time);
65 resp->data.auth.info3.pass_last_set_time =
66 nt_time_to_unix(info3->base.last_password_change);
67 resp->data.auth.info3.pass_can_change_time =
68 nt_time_to_unix(info3->base.allow_password_change);
69 resp->data.auth.info3.pass_must_change_time =
70 nt_time_to_unix(info3->base.force_password_change);
72 resp->data.auth.info3.logon_count = info3->base.logon_count;
73 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
75 resp->data.auth.info3.user_rid = info3->base.rid;
76 resp->data.auth.info3.group_rid = info3->base.primary_gid;
77 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
79 resp->data.auth.info3.num_groups = info3->base.groups.count;
80 resp->data.auth.info3.user_flgs = info3->base.user_flags;
82 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
83 resp->data.auth.info3.num_other_sids = info3->sidcount;
85 fstrcpy(resp->data.auth.info3.user_name,
86 info3->base.account_name.string);
87 fstrcpy(resp->data.auth.info3.full_name,
88 info3->base.full_name.string);
89 fstrcpy(resp->data.auth.info3.logon_script,
90 info3->base.logon_script.string);
91 fstrcpy(resp->data.auth.info3.profile_path,
92 info3->base.profile_path.string);
93 fstrcpy(resp->data.auth.info3.home_dir,
94 info3->base.home_directory.string);
95 fstrcpy(resp->data.auth.info3.dir_drive,
96 info3->base.home_drive.string);
98 fstrcpy(resp->data.auth.info3.logon_srv,
99 info3->base.logon_server.string);
100 fstrcpy(resp->data.auth.info3.logon_dom,
101 info3->base.logon_domain.string);
103 ex = talloc_strdup(mem_ctx, "");
104 NT_STATUS_HAVE_NO_MEMORY(ex);
106 for (i=0; i < info3->base.groups.count; i++) {
107 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
108 info3->base.groups.rids[i].rid,
109 info3->base.groups.rids[i].attributes);
110 NT_STATUS_HAVE_NO_MEMORY(ex);
113 for (i=0; i < info3->sidcount; i++) {
116 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
117 NT_STATUS_HAVE_NO_MEMORY(sid);
119 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
121 info3->sids[i].attributes);
122 NT_STATUS_HAVE_NO_MEMORY(ex);
127 resp->extra_data.data = ex;
128 resp->length += talloc_get_size(ex);
133 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
134 struct winbindd_response *resp,
135 struct netr_SamInfo3 *info3)
138 enum ndr_err_code ndr_err;
140 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
141 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
142 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
143 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
144 return ndr_map_error2ntstatus(ndr_err);
147 resp->extra_data.data = blob.data;
148 resp->length += blob.length;
153 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
154 struct winbindd_response *resp,
155 const struct netr_SamInfo3 *info3,
156 const char *name_domain,
157 const char *name_user)
159 /* We've been asked to return the unix username, per
160 'winbind use default domain' settings and the like */
162 const char *nt_username, *nt_domain;
164 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
166 /* If the server didn't give us one, just use the one
168 nt_domain = name_domain;
171 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
173 /* If the server didn't give us one, just use the one
175 nt_username = name_user;
178 fill_domain_username(resp->data.auth.unix_username,
179 nt_domain, nt_username, true);
181 DEBUG(5, ("Setting unix username to [%s]\n",
182 resp->data.auth.unix_username));
187 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
188 struct winbindd_response *resp,
189 const struct netr_SamInfo3 *info3,
190 const char *name_domain,
191 const char *name_user)
193 char *afsname = NULL;
197 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
198 if (afsname == NULL) {
199 return NT_STATUS_NO_MEMORY;
202 afsname = talloc_string_sub(mem_ctx,
203 lp_afs_username_map(),
205 afsname = talloc_string_sub(mem_ctx, afsname,
207 afsname = talloc_string_sub(mem_ctx, afsname,
211 struct dom_sid user_sid;
214 sid_compose(&user_sid, info3->base.domain_sid,
216 sid_to_fstring(sidstr, &user_sid);
217 afsname = talloc_string_sub(mem_ctx, afsname,
221 if (afsname == NULL) {
222 return NT_STATUS_NO_MEMORY;
225 if (!strlower_m(afsname)) {
226 return NT_STATUS_INVALID_PARAMETER;
229 DEBUG(10, ("Generating token for user %s\n", afsname));
231 cell = strchr(afsname, '@');
234 return NT_STATUS_NO_MEMORY;
240 token = afs_createtoken_str(afsname, cell);
244 resp->extra_data.data = talloc_strdup(mem_ctx, token);
245 if (resp->extra_data.data == NULL) {
246 return NT_STATUS_NO_MEMORY;
248 resp->length += strlen((const char *)resp->extra_data.data)+1;
253 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
254 const char *group_sid)
256 * Check whether a user belongs to a group or list of groups.
258 * @param mem_ctx talloc memory context.
259 * @param info3 user information, including group membership info.
260 * @param group_sid One or more groups , separated by commas.
262 * @return NT_STATUS_OK on success,
263 * NT_STATUS_LOGON_FAILURE if the user does not belong,
264 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
267 struct dom_sid *require_membership_of_sid;
268 uint32_t num_require_membership_of_sid;
273 struct security_token *token;
274 TALLOC_CTX *frame = talloc_stackframe();
277 /* Parse the 'required group' SID */
279 if (!group_sid || !group_sid[0]) {
280 /* NO sid supplied, all users may access */
285 token = talloc_zero(talloc_tos(), struct security_token);
287 DEBUG(0, ("talloc failed\n"));
289 return NT_STATUS_NO_MEMORY;
292 num_require_membership_of_sid = 0;
293 require_membership_of_sid = NULL;
297 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
298 if (!string_to_sid(&sid, req_sid)) {
299 DEBUG(0, ("check_info3_in_group: could not parse %s "
300 "as a SID!", req_sid));
302 return NT_STATUS_INVALID_PARAMETER;
305 status = add_sid_to_array(talloc_tos(), &sid,
306 &require_membership_of_sid,
307 &num_require_membership_of_sid);
308 if (!NT_STATUS_IS_OK(status)) {
309 DEBUG(0, ("add_sid_to_array failed\n"));
315 status = sid_array_from_info3(talloc_tos(), info3,
319 if (!NT_STATUS_IS_OK(status)) {
324 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
326 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
328 DEBUG(3, ("could not add aliases: %s\n",
334 security_token_debug(DBGC_CLASS, 10, token);
336 for (i=0; i<num_require_membership_of_sid; i++) {
337 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
338 &require_membership_of_sid[i])));
339 if (nt_token_check_sid(&require_membership_of_sid[i],
341 DEBUG(10, ("Access ok\n"));
347 /* Do not distinguish this error from a wrong username/pw */
350 return NT_STATUS_LOGON_FAILURE;
353 struct winbindd_domain *find_auth_domain(uint8_t flags,
354 const char *domain_name)
356 struct winbindd_domain *domain;
359 domain = find_domain_from_name_noinit(domain_name);
360 if (domain == NULL) {
361 DEBUG(3, ("Authentication for domain [%s] refused "
362 "as it is not a trusted domain\n",
368 if (strequal(domain_name, get_global_sam_name())) {
369 return find_domain_from_name_noinit(domain_name);
372 /* we can auth against trusted domains */
373 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
374 domain = find_domain_from_name_noinit(domain_name);
375 if (domain == NULL) {
376 DEBUG(3, ("Authentication for domain [%s] skipped "
377 "as it is not a trusted domain\n",
384 return find_our_domain();
387 static void fill_in_password_policy(struct winbindd_response *r,
388 const struct samr_DomInfo1 *p)
390 r->data.auth.policy.min_length_password =
391 p->min_password_length;
392 r->data.auth.policy.password_history =
393 p->password_history_length;
394 r->data.auth.policy.password_properties =
395 p->password_properties;
396 r->data.auth.policy.expire =
397 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
398 r->data.auth.policy.min_passwordage =
399 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
402 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
403 struct winbindd_response *response)
405 TALLOC_CTX *frame = talloc_stackframe();
407 struct samr_DomInfo1 password_policy;
409 if ( !winbindd_can_contact_domain( domain ) ) {
410 DEBUG(5,("fillup_password_policy: No inbound trust to "
411 "contact domain %s\n", domain->name));
412 status = NT_STATUS_NOT_SUPPORTED;
416 status = wb_cache_password_policy(domain, talloc_tos(),
418 if (NT_STATUS_IS_ERR(status)) {
422 fill_in_password_policy(response, &password_policy);
429 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
431 uint16_t *lockout_threshold)
433 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
434 struct samr_DomInfo12 lockout_policy;
436 *lockout_threshold = 0;
438 status = wb_cache_lockout_policy(domain, mem_ctx, &lockout_policy);
439 if (NT_STATUS_IS_ERR(status)) {
443 *lockout_threshold = lockout_policy.lockout_threshold;
448 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
450 uint32_t *password_properties)
452 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
453 struct samr_DomInfo1 password_policy;
455 *password_properties = 0;
457 status = wb_cache_password_policy(domain, mem_ctx, &password_policy);
458 if (NT_STATUS_IS_ERR(status)) {
462 *password_properties = password_policy.password_properties;
469 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
472 const char **user_ccache_file)
474 /* accept FILE and WRFILE as krb5_cc_type from the client and then
475 * build the full ccname string based on the user's uid here -
478 const char *gen_cc = NULL;
481 if (strequal(type, "FILE")) {
482 gen_cc = talloc_asprintf(
483 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
485 if (strequal(type, "WRFILE")) {
486 gen_cc = talloc_asprintf(
487 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
489 if (strequal(type, "KEYRING")) {
490 gen_cc = talloc_asprintf(
491 mem_ctx, "KEYRING:persistent:%d", uid);
494 if (strnequal(type, "FILE:/", 6) ||
495 strnequal(type, "WRFILE:/", 8) ||
496 strnequal(type, "DIR:/", 5)) {
498 /* we allow only one "%u" substitution */
502 p = strchr(type, '%');
507 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
508 char uid_str[sizeof("18446744073709551615")];
510 snprintf(uid_str, sizeof(uid_str), "%u", uid);
512 gen_cc = talloc_string_sub2(mem_ctx,
516 /* remove_unsafe_characters */
520 /* allow_trailing_dollar */
527 *user_ccache_file = gen_cc;
529 if (gen_cc == NULL) {
530 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
532 if (gen_cc == NULL) {
533 DEBUG(0,("out of memory\n"));
537 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
538 (*user_ccache_file == NULL) ? " (internal)":""));
545 uid_t get_uid_from_request(struct winbindd_request *request)
549 uid = request->data.auth.uid;
551 if (uid == (uid_t)-1) {
552 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
558 /**********************************************************************
559 Authenticate a user with a clear text password using Kerberos and fill up
561 **********************************************************************/
563 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
564 struct winbindd_domain *domain,
567 const char *krb5_cc_type,
569 struct netr_SamInfo3 **info3,
573 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
574 krb5_error_code krb5_ret;
575 const char *cc = NULL;
576 const char *principal_s = NULL;
577 const char *service = NULL;
579 fstring name_domain, name_user;
580 time_t ticket_lifetime = 0;
581 time_t renewal_until = 0;
583 time_t time_offset = 0;
584 const char *user_ccache_file;
585 struct PAC_LOGON_INFO *logon_info = NULL;
586 struct PAC_DATA *pac_data = NULL;
587 struct PAC_DATA_CTR *pac_data_ctr = NULL;
588 const char *local_service;
590 struct netr_SamInfo3 *info3_copy = NULL;
594 if (domain->alt_name == NULL) {
595 return NT_STATUS_INVALID_PARAMETER;
599 * prepare a krb5_cc_cache string for the user */
602 DEBUG(0,("no valid uid\n"));
605 cc = generate_krb5_ccache(mem_ctx,
610 return NT_STATUS_NO_MEMORY;
615 * get kerberos properties */
617 if (domain->private_data) {
618 ads = (ADS_STRUCT *)domain->private_data;
619 time_offset = ads->auth.time_offset;
624 * do kerberos auth and setup ccache as the user */
626 parse_domain_user(user, name_domain, name_user);
628 realm = talloc_strdup(mem_ctx, domain->alt_name);
630 return NT_STATUS_NO_MEMORY;
633 if (!strupper_m(realm)) {
634 return NT_STATUS_INVALID_PARAMETER;
637 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
638 if (principal_s == NULL) {
639 return NT_STATUS_NO_MEMORY;
642 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
643 if (service == NULL) {
644 return NT_STATUS_NO_MEMORY;
647 local_service = talloc_asprintf(mem_ctx, "%s$@%s",
648 lp_netbios_name(), lp_realm());
649 if (local_service == NULL) {
650 return NT_STATUS_NO_MEMORY;
654 /* if this is a user ccache, we need to act as the user to let the krb5
655 * library handle the chown, etc. */
657 /************************ ENTERING NON-ROOT **********************/
659 if (user_ccache_file != NULL) {
660 set_effective_uid(uid);
661 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
664 result = kerberos_return_pac(mem_ctx,
673 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
677 if (user_ccache_file != NULL) {
678 gain_root_privilege();
681 /************************ RETURNED TO ROOT **********************/
683 if (!NT_STATUS_IS_OK(result)) {
687 if (pac_data_ctr == NULL) {
691 pac_data = pac_data_ctr->pac_data;
692 if (pac_data == NULL) {
696 for (i=0; i < pac_data->num_buffers; i++) {
698 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
702 logon_info = pac_data->buffers[i].info->logon_info.info;
704 return NT_STATUS_INVALID_PARAMETER;
710 if (logon_info == NULL) {
711 DEBUG(10,("Missing logon_info in ticket of %s\n",
713 return NT_STATUS_INVALID_PARAMETER;
716 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
719 result = create_info3_from_pac_logon_info(mem_ctx, logon_info, &info3_copy);
720 if (!NT_STATUS_IS_OK(result)) {
724 /* if we had a user's ccache then return that string for the pam
727 if (user_ccache_file != NULL) {
729 fstrcpy(krb5ccname, user_ccache_file);
731 result = add_ccache_to_list(principal_s,
743 if (!NT_STATUS_IS_OK(result)) {
744 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
749 /* need to delete the memory cred cache, it is not used anymore */
751 krb5_ret = ads_kdestroy(cc);
753 DEBUG(3,("winbindd_raw_kerberos_login: "
754 "could not destroy krb5 credential cache: "
755 "%s\n", error_message(krb5_ret)));
764 * Do not delete an existing valid credential cache, if the user
765 * e.g. enters a wrong password
767 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
768 && user_ccache_file != NULL) {
772 /* we could have created a new credential cache with a valid tgt in it
773 * but we werent able to get or verify the service ticket for this
774 * local host and therefor didn't get the PAC, we need to remove that
775 * cache entirely now */
777 krb5_ret = ads_kdestroy(cc);
779 DEBUG(3,("winbindd_raw_kerberos_login: "
780 "could not destroy krb5 credential cache: "
781 "%s\n", error_message(krb5_ret)));
784 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
785 DEBUG(3,("winbindd_raw_kerberos_login: "
786 "could not remove ccache for user %s\n",
792 return NT_STATUS_NOT_SUPPORTED;
793 #endif /* HAVE_KRB5 */
796 /****************************************************************
797 ****************************************************************/
799 bool check_request_flags(uint32_t flags)
801 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
802 WBFLAG_PAM_INFO3_TEXT |
803 WBFLAG_PAM_INFO3_NDR;
805 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
806 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
807 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
808 !(flags & flags_edata) ) {
812 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
818 /****************************************************************
819 ****************************************************************/
821 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
822 struct winbindd_response *resp,
823 uint32_t request_flags,
824 struct netr_SamInfo3 *info3,
825 const char *name_domain,
826 const char *name_user)
830 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
831 memcpy(resp->data.auth.user_session_key,
833 sizeof(resp->data.auth.user_session_key)
837 if (request_flags & WBFLAG_PAM_LMKEY) {
838 memcpy(resp->data.auth.first_8_lm_hash,
839 info3->base.LMSessKey.key,
840 sizeof(resp->data.auth.first_8_lm_hash)
844 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
845 result = append_unix_username(mem_ctx, resp,
846 info3, name_domain, name_user);
847 if (!NT_STATUS_IS_OK(result)) {
848 DEBUG(10,("Failed to append Unix Username: %s\n",
854 /* currently, anything from here on potentially overwrites extra_data. */
856 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
857 result = append_info3_as_ndr(mem_ctx, resp, info3);
858 if (!NT_STATUS_IS_OK(result)) {
859 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
865 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
866 result = append_info3_as_txt(mem_ctx, resp, info3);
867 if (!NT_STATUS_IS_OK(result)) {
868 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
874 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
875 result = append_afs_token(mem_ctx, resp,
876 info3, name_domain, name_user);
877 if (!NT_STATUS_IS_OK(result)) {
878 DEBUG(10,("Failed to append AFS token: %s\n",
887 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
888 struct winbindd_cli_state *state,
889 struct netr_SamInfo3 **info3)
891 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
892 uint16_t max_allowed_bad_attempts;
893 fstring name_domain, name_user;
895 enum lsa_SidType type;
896 uchar new_nt_pass[NT_HASH_LEN];
897 const uint8_t *cached_nt_pass;
898 const uint8_t *cached_salt;
899 struct netr_SamInfo3 *my_info3;
900 time_t kickoff_time, must_change_time;
901 bool password_good = false;
903 struct winbindd_tdc_domain *tdc_domain = NULL;
910 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
912 /* Parse domain and username */
914 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
917 if (!lookup_cached_name(name_domain,
921 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
922 return NT_STATUS_NO_SUCH_USER;
925 if (type != SID_NAME_USER) {
926 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
927 return NT_STATUS_LOGON_FAILURE;
930 result = winbindd_get_creds(domain,
936 if (!NT_STATUS_IS_OK(result)) {
937 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
943 E_md4hash(state->request->data.auth.pass, new_nt_pass);
945 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
946 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
948 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
952 /* In this case we didn't store the nt_hash itself,
953 but the MD5 combination of salt + nt_hash. */
954 uchar salted_hash[NT_HASH_LEN];
955 E_md5hash(cached_salt, new_nt_pass, salted_hash);
957 password_good = (memcmp(cached_nt_pass, salted_hash,
960 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
961 password_good = (memcmp(cached_nt_pass, new_nt_pass,
967 /* User *DOES* know the password, update logon_time and reset
970 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
972 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
973 return NT_STATUS_ACCOUNT_LOCKED_OUT;
976 if (my_info3->base.acct_flags & ACB_DISABLED) {
977 return NT_STATUS_ACCOUNT_DISABLED;
980 if (my_info3->base.acct_flags & ACB_WSTRUST) {
981 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
984 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
985 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
988 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
989 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
992 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
993 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
994 my_info3->base.acct_flags));
995 return NT_STATUS_LOGON_FAILURE;
998 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
999 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
1000 return NT_STATUS_ACCOUNT_EXPIRED;
1003 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
1004 if (must_change_time != 0 && must_change_time < time(NULL)) {
1005 /* we allow grace logons when the password has expired */
1006 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
1007 /* return NT_STATUS_PASSWORD_EXPIRED; */
1012 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
1013 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1014 ((tdc_domain->trust_type & LSA_TRUST_TYPE_UPLEVEL) ||
1015 /* used to cope with the case winbindd starting without network. */
1016 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
1019 const char *cc = NULL;
1021 const char *principal_s = NULL;
1022 const char *service = NULL;
1023 const char *user_ccache_file;
1025 if (domain->alt_name == NULL) {
1026 return NT_STATUS_INVALID_PARAMETER;
1029 uid = get_uid_from_request(state->request);
1031 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1032 return NT_STATUS_INVALID_PARAMETER;
1035 cc = generate_krb5_ccache(state->mem_ctx,
1036 state->request->data.auth.krb5_cc_type,
1037 state->request->data.auth.uid,
1040 return NT_STATUS_NO_MEMORY;
1043 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
1044 if (realm == NULL) {
1045 return NT_STATUS_NO_MEMORY;
1048 if (!strupper_m(realm)) {
1049 return NT_STATUS_INVALID_PARAMETER;
1052 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1053 if (principal_s == NULL) {
1054 return NT_STATUS_NO_MEMORY;
1057 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1058 if (service == NULL) {
1059 return NT_STATUS_NO_MEMORY;
1062 if (user_ccache_file != NULL) {
1064 fstrcpy(state->response->data.auth.krb5ccname,
1067 result = add_ccache_to_list(principal_s,
1070 state->request->data.auth.user,
1071 state->request->data.auth.pass,
1075 time(NULL) + lp_winbind_cache_time(),
1076 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1079 if (!NT_STATUS_IS_OK(result)) {
1080 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1081 "to add ccache to list: %s\n",
1082 nt_errstr(result)));
1086 #endif /* HAVE_KRB5 */
1088 /* FIXME: we possibly should handle logon hours as well (does xp when
1089 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1091 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1092 my_info3->base.bad_password_count = 0;
1094 result = winbindd_update_creds_by_info3(domain,
1095 state->request->data.auth.user,
1096 state->request->data.auth.pass,
1098 if (!NT_STATUS_IS_OK(result)) {
1099 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1100 nt_errstr(result)));
1104 return NT_STATUS_OK;
1108 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1109 if (domain->online == false) {
1113 /* failure of this is not critical */
1114 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1115 if (!NT_STATUS_IS_OK(result)) {
1116 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1117 "Won't be able to honour account lockout policies\n"));
1120 /* increase counter */
1121 my_info3->base.bad_password_count++;
1123 if (max_allowed_bad_attempts == 0) {
1128 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1130 uint32_t password_properties;
1132 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1133 if (!NT_STATUS_IS_OK(result)) {
1134 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1137 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1138 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1139 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1144 result = winbindd_update_creds_by_info3(domain,
1145 state->request->data.auth.user,
1149 if (!NT_STATUS_IS_OK(result)) {
1150 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1151 nt_errstr(result)));
1154 return NT_STATUS_LOGON_FAILURE;
1157 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1158 struct winbindd_cli_state *state,
1159 struct netr_SamInfo3 **info3)
1161 struct winbindd_domain *contact_domain;
1162 fstring name_domain, name_user;
1165 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1167 /* Parse domain and username */
1169 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1171 /* what domain should we contact? */
1174 if (!(contact_domain = find_domain_from_name(name_domain))) {
1175 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1176 state->request->data.auth.user, name_domain, name_user, name_domain));
1177 result = NT_STATUS_NO_SUCH_USER;
1182 if (is_myname(name_domain)) {
1183 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1184 result = NT_STATUS_NO_SUCH_USER;
1188 contact_domain = find_domain_from_name(name_domain);
1189 if (contact_domain == NULL) {
1190 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1191 state->request->data.auth.user, name_domain, name_user, name_domain));
1193 result = NT_STATUS_NO_SUCH_USER;
1198 if (contact_domain->initialized &&
1199 contact_domain->active_directory) {
1203 if (!contact_domain->initialized) {
1204 init_dc_connection(contact_domain, false);
1207 if (!contact_domain->active_directory) {
1208 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1209 return NT_STATUS_INVALID_LOGON_TYPE;
1212 result = winbindd_raw_kerberos_login(
1213 state->mem_ctx, contact_domain,
1214 state->request->data.auth.user,
1215 state->request->data.auth.pass,
1216 state->request->data.auth.krb5_cc_type,
1217 get_uid_from_request(state->request),
1218 info3, state->response->data.auth.krb5ccname);
1223 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1224 uint32_t logon_parameters,
1225 const char *domain, const char *user,
1226 const DATA_BLOB *challenge,
1227 const DATA_BLOB *lm_resp,
1228 const DATA_BLOB *nt_resp,
1230 uint8_t *pauthoritative,
1231 struct netr_SamInfo3 **pinfo3)
1233 struct auth_context *auth_context;
1234 struct auth_serversupplied_info *server_info;
1235 struct auth_usersupplied_info *user_info = NULL;
1236 struct tsocket_address *local;
1237 struct netr_SamInfo3 *info3;
1241 TALLOC_CTX *frame = talloc_stackframe();
1244 * We are authoritative by default
1246 *pauthoritative = 1;
1248 rc = tsocket_address_inet_from_strings(frame,
1255 return NT_STATUS_NO_MEMORY;
1259 * TODO: We should get the service description passed in from
1260 * the winbind client, so we can have "smb2", "squid" or "samr" logged
1263 status = make_user_info(frame, &user_info, user, user, domain, domain,
1264 lp_netbios_name(), local,
1266 lm_resp, nt_resp, NULL, NULL,
1267 NULL, AUTH_PASSWORD_RESPONSE);
1268 if (!NT_STATUS_IS_OK(status)) {
1269 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1274 user_info->logon_parameters = logon_parameters;
1276 /* We don't want any more mapping of the username */
1277 user_info->mapped_state = True;
1279 /* We don't want to come back to winbindd or to do PAM account checks */
1280 user_info->flags |= USER_INFO_INFO3_AND_NO_AUTHZ;
1283 user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
1286 status = make_auth3_context_for_winbind(frame, &auth_context);
1287 if (!NT_STATUS_IS_OK(status)) {
1288 DBG_ERR("make_auth3_context_for_winbind failed: %s\n",
1294 ok = auth3_context_set_challenge(auth_context,
1295 challenge->data, "fixed");
1298 return NT_STATUS_NO_MEMORY;
1301 status = auth_check_ntlm_password(mem_ctx,
1306 if (!NT_STATUS_IS_OK(status)) {
1311 info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1312 if (info3 == NULL) {
1314 return NT_STATUS_NO_MEMORY;
1317 status = serverinfo_to_SamInfo3(server_info, info3);
1318 if (!NT_STATUS_IS_OK(status)) {
1321 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1322 nt_errstr(status)));
1327 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1328 user, nt_errstr(status)));
1333 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1334 TALLOC_CTX *mem_ctx,
1335 uint32_t logon_parameters,
1336 const char *username,
1337 const char *password,
1338 const char *domainname,
1339 const char *workstation,
1340 const uint8_t chal[8],
1341 DATA_BLOB lm_response,
1342 DATA_BLOB nt_response,
1344 uint8_t *authoritative,
1346 struct netr_SamInfo3 **info3)
1349 int netr_attempts = 0;
1354 struct rpc_pipe_client *netlogon_pipe;
1356 ZERO_STRUCTP(info3);
1359 result = cm_connect_netlogon(domain, &netlogon_pipe);
1361 if (NT_STATUS_EQUAL(result,
1362 NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
1364 * This means we don't have a trust account.
1367 result = NT_STATUS_NO_SUCH_USER;
1371 if (!NT_STATUS_IS_OK(result)) {
1372 DEBUG(3,("Could not open handle to NETLOGON pipe "
1373 "(error: %s, attempts: %d)\n",
1374 nt_errstr(result), netr_attempts));
1376 /* After the first retry always close the connection */
1377 if (netr_attempts > 0) {
1378 DEBUG(3, ("This is again a problem for this "
1379 "particular call, forcing the close "
1380 "of this connection\n"));
1381 invalidate_cm_connection(domain);
1384 /* After the second retry failover to the next DC */
1385 if (netr_attempts > 1) {
1387 * If the netlogon server is not reachable then
1388 * it is possible that the DC is rebuilding
1389 * sysvol and shutdown netlogon for that time.
1390 * We should failover to the next dc.
1392 DEBUG(3, ("This is the third problem for this "
1393 "particular call, adding DC to the "
1394 "negative cache list\n"));
1395 add_failed_connection_entry(domain->name,
1398 saf_delete(domain->name);
1401 /* Only allow 3 retries */
1402 if (netr_attempts < 3) {
1403 DEBUG(3, ("The connection to netlogon "
1404 "failed, retrying\n"));
1412 if (domain->conn.netlogon_creds == NULL) {
1413 DBG_NOTICE("No security credentials available for "
1414 "domain [%s]\n", domainname);
1415 result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1416 } else if (interactive && username != NULL && password != NULL) {
1417 result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1418 netlogon_pipe->binding_handle,
1425 NetlogonInteractiveInformation,
1430 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1431 netlogon_pipe->binding_handle,
1446 * we increment this after the "feature negotiation"
1447 * for can_do_samlogon_ex and can_do_validation6
1451 /* We have to try a second time as cm_connect_netlogon
1452 might not yet have noticed that the DC has killed
1455 if (!rpccli_is_connected(netlogon_pipe)) {
1460 /* if we get access denied, a possible cause was that we had
1461 and open connection to the DC, but someone changed our
1462 machine account password out from underneath us using 'net
1463 rpc changetrustpw' */
1465 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1466 DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1467 "ACCESS_DENIED. Maybe the DC has Restrict "
1468 "NTLM set or the trust account "
1469 "password was changed and we didn't know it. "
1470 "Killing connections to domain %s\n",
1472 invalidate_cm_connection(domain);
1476 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1478 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1479 * (no Ex). This happens against old Samba
1480 * DCs, if LogonSamLogonEx() fails with an error
1481 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1483 * The server will log something like this:
1484 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1486 * This sets the whole connection into a fault_state mode
1487 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1489 * This also happens to our retry with LogonSamLogonWithFlags()
1490 * and LogonSamLogon().
1492 * In order to recover from this situation, we need to
1493 * drop the connection.
1495 invalidate_cm_connection(domain);
1496 result = NT_STATUS_LOGON_FAILURE;
1500 } while ( (attempts < 2) && retry );
1502 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1503 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1504 "returned NT_STATUS_IO_TIMEOUT after the retry."
1505 "Killing connections to domain %s\n",
1507 invalidate_cm_connection(domain);
1512 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1513 struct winbindd_domain *domain,
1516 uint32_t request_flags,
1517 struct netr_SamInfo3 **info3)
1523 unsigned char local_nt_response[24];
1524 fstring name_domain, name_user;
1526 struct netr_SamInfo3 *my_info3 = NULL;
1527 uint8_t authoritative = 0;
1532 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1534 /* Parse domain and username */
1536 parse_domain_user(user, name_domain, name_user);
1538 /* do password magic */
1540 generate_random_buffer(chal, sizeof(chal));
1542 if (lp_client_ntlmv2_auth()) {
1543 DATA_BLOB server_chal;
1544 DATA_BLOB names_blob;
1545 server_chal = data_blob_const(chal, 8);
1547 /* note that the 'workgroup' here is for the local
1548 machine. The 'server name' must match the
1549 'workstation' passed to the actual SamLogon call.
1551 names_blob = NTLMv2_generate_names_blob(
1552 mem_ctx, lp_netbios_name(), lp_workgroup());
1554 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1558 &lm_resp, &nt_resp, NULL, NULL)) {
1559 data_blob_free(&names_blob);
1560 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1561 result = NT_STATUS_NO_MEMORY;
1564 data_blob_free(&names_blob);
1566 lm_resp = data_blob_null;
1567 SMBNTencrypt(pass, chal, local_nt_response);
1569 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1570 sizeof(local_nt_response));
1573 if (strequal(name_domain, get_global_sam_name())) {
1574 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1576 result = winbindd_dual_auth_passdb(
1577 mem_ctx, 0, name_domain, name_user,
1578 &chal_blob, &lm_resp, &nt_resp,
1579 true, /* interactive */
1584 * We need to try the remote NETLOGON server if this is
1585 * not authoritative.
1587 if (authoritative != 0) {
1592 /* check authentication loop */
1594 result = winbind_samlogon_retry_loop(domain,
1604 true, /* interactive */
1608 if (!NT_STATUS_IS_OK(result)) {
1612 /* handle the case where a NT4 DC does not fill in the acct_flags in
1613 * the samlogon reply info3. When accurate info3 is required by the
1614 * caller, we look up the account flags ourselves - gd */
1616 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1617 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1619 struct rpc_pipe_client *samr_pipe;
1620 struct policy_handle samr_domain_handle, user_pol;
1621 union samr_UserInfo *info = NULL;
1622 NTSTATUS status_tmp, result_tmp;
1623 uint32_t acct_flags;
1624 struct dcerpc_binding_handle *b;
1626 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1627 &samr_pipe, &samr_domain_handle);
1629 if (!NT_STATUS_IS_OK(status_tmp)) {
1630 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1631 nt_errstr(status_tmp)));
1635 b = samr_pipe->binding_handle;
1637 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1638 &samr_domain_handle,
1639 MAXIMUM_ALLOWED_ACCESS,
1644 if (!NT_STATUS_IS_OK(status_tmp)) {
1645 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1646 nt_errstr(status_tmp)));
1649 if (!NT_STATUS_IS_OK(result_tmp)) {
1650 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1651 nt_errstr(result_tmp)));
1655 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1661 if (!NT_STATUS_IS_OK(status_tmp)) {
1662 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1663 nt_errstr(status_tmp)));
1664 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1667 if (!NT_STATUS_IS_OK(result_tmp)) {
1668 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1669 nt_errstr(result_tmp)));
1670 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1674 acct_flags = info->info16.acct_flags;
1676 if (acct_flags == 0) {
1677 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1681 my_info3->base.acct_flags = acct_flags;
1683 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1685 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1693 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1694 struct winbindd_cli_state *state)
1696 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1697 NTSTATUS krb5_result = NT_STATUS_OK;
1698 fstring name_domain, name_user;
1700 fstring domain_user;
1701 struct netr_SamInfo3 *info3 = NULL;
1702 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1704 /* Ensure null termination */
1705 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1707 /* Ensure null termination */
1708 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1710 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1711 state->request->data.auth.user));
1713 /* Parse domain and username */
1715 name_map_status = normalize_name_unmap(state->mem_ctx,
1716 state->request->data.auth.user,
1719 /* If the name normalization didnt' actually do anything,
1720 just use the original name */
1722 if (!NT_STATUS_IS_OK(name_map_status) &&
1723 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1725 mapped_user = state->request->data.auth.user;
1728 parse_domain_user(mapped_user, name_domain, name_user);
1730 if ( mapped_user != state->request->data.auth.user ) {
1731 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1732 *lp_winbind_separator(),
1734 strlcpy( state->request->data.auth.user, domain_user,
1735 sizeof(state->request->data.auth.user));
1738 if (!domain->online) {
1739 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1740 if (domain->startup) {
1741 /* Logons are very important to users. If we're offline and
1742 we get a request within the first 30 seconds of startup,
1743 try very hard to find a DC and go online. */
1745 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1746 "request in startup mode.\n", domain->name ));
1748 winbindd_flush_negative_conn_cache(domain);
1749 result = init_dc_connection(domain, false);
1753 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1755 /* Check for Kerberos authentication */
1756 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1758 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1759 /* save for later */
1760 krb5_result = result;
1763 if (NT_STATUS_IS_OK(result)) {
1764 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1765 goto process_result;
1767 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1770 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1771 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1772 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1773 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1774 set_domain_offline( domain );
1778 /* there are quite some NT_STATUS errors where there is no
1779 * point in retrying with a samlogon, we explictly have to take
1780 * care not to increase the bad logon counter on the DC */
1782 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1783 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1784 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1785 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1786 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1787 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1788 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1789 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1790 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1791 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1795 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1796 DEBUG(3,("falling back to samlogon\n"));
1804 /* Check for Samlogon authentication */
1805 if (domain->online) {
1806 result = winbindd_dual_pam_auth_samlogon(
1807 state->mem_ctx, domain,
1808 state->request->data.auth.user,
1809 state->request->data.auth.pass,
1810 state->request->flags,
1813 if (NT_STATUS_IS_OK(result)) {
1814 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1815 /* add the Krb5 err if we have one */
1816 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1817 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1819 goto process_result;
1822 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1823 nt_errstr(result)));
1825 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1826 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1827 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1829 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1830 set_domain_offline( domain );
1834 if (domain->online) {
1835 /* We're still online - fail. */
1841 /* Check for Cached logons */
1842 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1843 lp_winbind_offline_logon()) {
1845 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1847 if (NT_STATUS_IS_OK(result)) {
1848 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1849 goto process_result;
1851 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1858 if (NT_STATUS_IS_OK(result)) {
1860 struct dom_sid user_sid;
1862 /* In all codepaths where result == NT_STATUS_OK info3 must have
1863 been initialized. */
1865 result = NT_STATUS_INTERNAL_ERROR;
1869 sid_compose(&user_sid, info3->base.domain_sid,
1872 if (info3->base.full_name.string == NULL) {
1873 struct netr_SamInfo3 *cached_info3;
1875 cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1877 if (cached_info3 != NULL &&
1878 cached_info3->base.full_name.string != NULL) {
1879 info3->base.full_name.string =
1880 talloc_strdup(info3,
1881 cached_info3->base.full_name.string);
1884 /* this might fail so we don't check the return code */
1885 wcache_query_user_fullname(domain,
1888 &info3->base.full_name.string);
1892 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1894 netsamlogon_cache_store(name_user, info3);
1896 /* save name_to_sid info as early as possible (only if
1897 this is our primary domain so we don't invalidate
1898 the cache entry by storing the seq_num for the wrong
1900 if ( domain->primary ) {
1901 cache_name2sid(domain, name_domain, name_user,
1902 SID_NAME_USER, &user_sid);
1905 /* Check if the user is in the right group */
1907 result = check_info3_in_group(
1909 state->request->data.auth.require_membership_of_sid);
1910 if (!NT_STATUS_IS_OK(result)) {
1911 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1912 state->request->data.auth.user,
1913 state->request->data.auth.require_membership_of_sid));
1917 result = append_auth_data(state->mem_ctx, state->response,
1918 state->request->flags, info3,
1919 name_domain, name_user);
1920 if (!NT_STATUS_IS_OK(result)) {
1924 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1925 && lp_winbind_offline_logon()) {
1927 result = winbindd_store_creds(domain,
1928 state->request->data.auth.user,
1929 state->request->data.auth.pass,
1933 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1934 struct winbindd_domain *our_domain = find_our_domain();
1936 /* This is not entirely correct I believe, but it is
1937 consistent. Only apply the password policy settings
1938 too warn users for our own domain. Cannot obtain these
1939 from trusted DCs all the time so don't do it at all.
1942 result = NT_STATUS_NOT_SUPPORTED;
1943 if (our_domain == domain ) {
1944 result = fillup_password_policy(
1945 our_domain, state->response);
1948 if (!NT_STATUS_IS_OK(result)
1949 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1951 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1952 domain->name, nt_errstr(result)));
1957 result = NT_STATUS_OK;
1961 /* give us a more useful (more correct?) error code */
1962 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1963 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1964 result = NT_STATUS_NO_LOGON_SERVERS;
1967 set_auth_errors(state->response, result);
1969 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1970 state->request->data.auth.user,
1971 state->response->data.auth.nt_status_string,
1972 state->response->data.auth.pam_error));
1974 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1977 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1978 TALLOC_CTX *mem_ctx,
1979 uint32_t logon_parameters,
1980 const char *name_user,
1981 const char *name_domain,
1982 const char *workstation,
1983 const uint8_t chal[8],
1984 DATA_BLOB lm_response,
1985 DATA_BLOB nt_response,
1986 uint8_t *authoritative,
1988 struct netr_SamInfo3 **info3)
1992 if (strequal(name_domain, get_global_sam_name())) {
1993 DATA_BLOB chal_blob = data_blob_const(
1996 result = winbindd_dual_auth_passdb(
1999 name_domain, name_user,
2000 &chal_blob, &lm_response, &nt_response,
2001 false, /* interactive */
2006 * We need to try the remote NETLOGON server if this is
2007 * not authoritative.
2009 if (*authoritative != 0) {
2011 goto process_result;
2015 result = winbind_samlogon_retry_loop(domain,
2019 NULL, /* password */
2021 /* Bug #3248 - found by Stefan Burkei. */
2022 workstation, /* We carefully set this above so use it... */
2026 false, /* interactive */
2030 if (!NT_STATUS_IS_OK(result)) {
2036 if (NT_STATUS_IS_OK(result)) {
2037 struct dom_sid user_sid;
2039 sid_compose(&user_sid, (*info3)->base.domain_sid,
2040 (*info3)->base.rid);
2042 if ((*info3)->base.full_name.string == NULL) {
2043 struct netr_SamInfo3 *cached_info3;
2045 cached_info3 = netsamlogon_cache_get(mem_ctx,
2047 if (cached_info3 != NULL &&
2048 cached_info3->base.full_name.string != NULL) {
2049 (*info3)->base.full_name.string =
2050 talloc_strdup(*info3,
2051 cached_info3->base.full_name.string);
2054 /* this might fail so we don't check the return code */
2055 wcache_query_user_fullname(domain,
2058 &(*info3)->base.full_name.string);
2062 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2064 netsamlogon_cache_store(name_user, *info3);
2069 /* give us a more useful (more correct?) error code */
2070 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2071 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2072 result = NT_STATUS_NO_LOGON_SERVERS;
2075 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2076 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2079 nt_errstr(result)));
2084 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2085 struct winbindd_cli_state *state)
2088 struct netr_SamInfo3 *info3 = NULL;
2089 const char *name_user = NULL;
2090 const char *name_domain = NULL;
2091 const char *workstation;
2092 uint8_t authoritative;
2095 DATA_BLOB lm_resp, nt_resp;
2097 /* This is child-only, so no check for privileged access is needed
2100 /* Ensure null termination */
2101 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2102 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2104 name_user = state->request->data.auth_crap.user;
2105 name_domain = state->request->data.auth_crap.domain;
2106 workstation = state->request->data.auth_crap.workstation;
2108 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2109 name_domain, name_user));
2111 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2112 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2113 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2114 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2115 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2116 state->request->data.auth_crap.lm_resp_len,
2117 state->request->data.auth_crap.nt_resp_len));
2118 result = NT_STATUS_INVALID_PARAMETER;
2123 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2124 state->request->data.auth_crap.lm_resp_len);
2126 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2127 nt_resp = data_blob_talloc(state->mem_ctx,
2128 state->request->extra_data.data,
2129 state->request->data.auth_crap.nt_resp_len);
2131 nt_resp = data_blob_talloc(state->mem_ctx,
2132 state->request->data.auth_crap.nt_resp,
2133 state->request->data.auth_crap.nt_resp_len);
2136 result = winbind_dual_SamLogon(domain,
2138 state->request->data.auth_crap.logon_parameters,
2141 /* Bug #3248 - found by Stefan Burkei. */
2142 workstation, /* We carefully set this above so use it... */
2143 state->request->data.auth_crap.chal,
2149 if (!NT_STATUS_IS_OK(result)) {
2150 state->response->data.auth.authoritative = authoritative;
2154 if (NT_STATUS_IS_OK(result)) {
2155 /* Check if the user is in the right group */
2157 result = check_info3_in_group(
2159 state->request->data.auth_crap.require_membership_of_sid);
2160 if (!NT_STATUS_IS_OK(result)) {
2161 DEBUG(3, ("User %s is not in the required group (%s), so "
2162 "crap authentication is rejected\n",
2163 state->request->data.auth_crap.user,
2164 state->request->data.auth_crap.require_membership_of_sid));
2168 result = append_auth_data(state->mem_ctx, state->response,
2169 state->request->flags, info3,
2170 name_domain, name_user);
2171 if (!NT_STATUS_IS_OK(result)) {
2178 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2179 result = nt_status_squash(result);
2182 set_auth_errors(state->response, result);
2184 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2187 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2188 struct winbindd_cli_state *state)
2191 char *newpass = NULL;
2192 struct policy_handle dom_pol;
2193 struct rpc_pipe_client *cli = NULL;
2194 bool got_info = false;
2195 struct samr_DomInfo1 *info = NULL;
2196 struct userPwdChangeFailureInformation *reject = NULL;
2197 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2198 fstring domain, user;
2199 struct dcerpc_binding_handle *b = NULL;
2201 ZERO_STRUCT(dom_pol);
2203 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2204 state->request->data.auth.user));
2206 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2210 /* Change password */
2212 oldpass = state->request->data.chauthtok.oldpass;
2213 newpass = state->request->data.chauthtok.newpass;
2215 /* Initialize reject reason */
2216 state->response->data.auth.reject_reason = Undefined;
2218 /* Get sam handle */
2220 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2222 if (!NT_STATUS_IS_OK(result)) {
2223 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2227 b = cli->binding_handle;
2229 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2236 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2238 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2240 fill_in_password_policy(state->response, info);
2242 state->response->data.auth.reject_reason =
2243 reject->extendedFailureReason;
2248 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2249 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2250 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2251 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2253 /* only fallback when the chgpasswd_user3 call is not supported */
2254 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2255 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2256 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2257 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2259 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2260 nt_errstr(result)));
2262 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2264 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2265 Map to the same status code as Windows 2003. */
2267 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2268 result = NT_STATUS_PASSWORD_RESTRICTION;
2274 if (NT_STATUS_IS_OK(result)
2275 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2276 && lp_winbind_offline_logon()) {
2277 result = winbindd_update_creds_by_name(contact_domain, user,
2279 /* Again, this happens when we login from gdm or xdm
2280 * and the password expires, *BUT* cached crendentials
2281 * doesn't exist. winbindd_update_creds_by_name()
2282 * returns NT_STATUS_NO_SUCH_USER.
2283 * This is not a failure.
2286 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2287 result = NT_STATUS_OK;
2290 if (!NT_STATUS_IS_OK(result)) {
2291 DEBUG(10, ("Failed to store creds: %s\n",
2292 nt_errstr(result)));
2293 goto process_result;
2297 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2299 NTSTATUS policy_ret;
2301 policy_ret = fillup_password_policy(
2302 contact_domain, state->response);
2304 /* failure of this is non critical, it will just provide no
2305 * additional information to the client why the change has
2306 * failed - Guenther */
2308 if (!NT_STATUS_IS_OK(policy_ret)) {
2309 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2310 goto process_result;
2316 if (strequal(contact_domain->name, get_global_sam_name())) {
2317 /* FIXME: internal rpc pipe does not cache handles yet */
2319 if (is_valid_policy_hnd(&dom_pol)) {
2321 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2327 set_auth_errors(state->response, result);
2329 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2330 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2333 state->response->data.auth.nt_status_string,
2334 state->response->data.auth.pam_error));
2336 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2339 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2340 struct winbindd_cli_state *state)
2342 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2344 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2345 state->request->data.logoff.user));
2347 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2348 result = NT_STATUS_OK;
2349 goto process_result;
2352 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2353 result = NT_STATUS_OK;
2354 goto process_result;
2359 if (state->request->data.logoff.uid == (uid_t)-1) {
2360 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2361 goto process_result;
2364 /* what we need here is to find the corresponding krb5 ccache name *we*
2365 * created for a given username and destroy it */
2367 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2368 result = NT_STATUS_OK;
2369 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2370 goto process_result;
2373 if (!ccache_entry_identical(state->request->data.logoff.user,
2374 state->request->data.logoff.uid,
2375 state->request->data.logoff.krb5ccname)) {
2376 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2377 goto process_result;
2380 result = remove_ccache(state->request->data.logoff.user);
2381 if (!NT_STATUS_IS_OK(result)) {
2382 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2383 nt_errstr(result)));
2384 goto process_result;
2388 * Remove any mlock'ed memory creds in the child
2389 * we might be using for krb5 ticket renewal.
2392 winbindd_delete_memory_creds(state->request->data.logoff.user);
2395 result = NT_STATUS_NOT_SUPPORTED;
2401 set_auth_errors(state->response, result);
2403 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2406 /* Change user password with auth crap*/
2408 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2411 DATA_BLOB new_nt_password;
2412 DATA_BLOB old_nt_hash_enc;
2413 DATA_BLOB new_lm_password;
2414 DATA_BLOB old_lm_hash_enc;
2415 fstring domain,user;
2416 struct policy_handle dom_pol;
2417 struct winbindd_domain *contact_domain = domainSt;
2418 struct rpc_pipe_client *cli = NULL;
2419 struct dcerpc_binding_handle *b = NULL;
2421 ZERO_STRUCT(dom_pol);
2423 /* Ensure null termination */
2424 state->request->data.chng_pswd_auth_crap.user[
2425 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2426 state->request->data.chng_pswd_auth_crap.domain[
2427 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2431 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2432 (unsigned long)state->pid,
2433 state->request->data.chng_pswd_auth_crap.domain,
2434 state->request->data.chng_pswd_auth_crap.user));
2436 if (lp_winbind_offline_logon()) {
2437 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2438 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2439 result = NT_STATUS_ACCESS_DENIED;
2443 if (*state->request->data.chng_pswd_auth_crap.domain) {
2444 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2446 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2450 DEBUG(3,("no domain specified with username (%s) - "
2452 state->request->data.chng_pswd_auth_crap.user));
2453 result = NT_STATUS_NO_SUCH_USER;
2458 if (!*domain && lp_winbind_use_default_domain()) {
2459 fstrcpy(domain,lp_workgroup());
2463 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2466 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2467 (unsigned long)state->pid, domain, user));
2469 /* Change password */
2470 new_nt_password = data_blob_const(
2471 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2472 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2474 old_nt_hash_enc = data_blob_const(
2475 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2476 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2478 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2479 new_lm_password = data_blob_const(
2480 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2481 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2483 old_lm_hash_enc = data_blob_const(
2484 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2485 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2487 new_lm_password = data_blob_null;
2488 old_lm_hash_enc = data_blob_null;
2491 /* Get sam handle */
2493 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2494 if (!NT_STATUS_IS_OK(result)) {
2495 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2499 b = cli->binding_handle;
2501 result = rpccli_samr_chng_pswd_auth_crap(
2502 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2503 new_lm_password, old_lm_hash_enc);
2507 if (strequal(contact_domain->name, get_global_sam_name())) {
2508 /* FIXME: internal rpc pipe does not cache handles yet */
2510 if (is_valid_policy_hnd(&dom_pol)) {
2512 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2518 set_auth_errors(state->response, result);
2520 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2521 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2523 state->response->data.auth.nt_status_string,
2524 state->response->data.auth.pam_error));
2526 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2530 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2531 struct PAC_LOGON_INFO **logon_info)
2533 krb5_context krbctx = NULL;
2534 krb5_error_code k5ret;
2536 krb5_kt_cursor cursor;
2537 krb5_keytab_entry entry;
2538 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2541 ZERO_STRUCT(cursor);
2543 k5ret = krb5_init_context(&krbctx);
2545 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2546 error_message(k5ret)));
2547 status = krb5_to_nt_status(k5ret);
2551 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2553 DEBUG(1, ("Failed to get keytab: %s\n",
2554 error_message(k5ret)));
2555 status = krb5_to_nt_status(k5ret);
2559 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2561 DEBUG(1, ("Failed to start seq: %s\n",
2562 error_message(k5ret)));
2563 status = krb5_to_nt_status(k5ret);
2567 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2568 while (k5ret == 0) {
2569 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2571 KRB5_KT_KEY(&entry), NULL, 0,
2573 if (NT_STATUS_IS_OK(status)) {
2576 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2577 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2580 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2582 DEBUG(1, ("Failed to end seq: %s\n",
2583 error_message(k5ret)));
2586 k5ret = krb5_kt_close(krbctx, keytab);
2588 DEBUG(1, ("Failed to close keytab: %s\n",
2589 error_message(k5ret)));
2592 krb5_free_context(krbctx);
2597 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2598 struct netr_SamInfo3 **info3)
2600 struct winbindd_request *req = state->request;
2602 struct PAC_LOGON_INFO *logon_info = NULL;
2603 struct netr_SamInfo3 *info3_copy = NULL;
2606 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2607 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2608 if (!NT_STATUS_IS_OK(result) &&
2609 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2610 DEBUG(1, ("Error during PAC signature verification: %s\n",
2611 nt_errstr(result)));
2617 * Signature verification succeeded, we can
2618 * trust the PAC and prime the netsamlogon
2619 * and name2sid caches. DO NOT DO THIS
2620 * in the signature verification failed
2623 struct winbindd_domain *domain = NULL;
2625 result = create_info3_from_pac_logon_info(state->mem_ctx,
2628 if (!NT_STATUS_IS_OK(result)) {
2631 netsamlogon_cache_store(NULL, info3_copy);
2634 * We're in the parent here, so find the child
2635 * pointer from the PAC domain name.
2637 domain = find_domain_from_name_noinit(
2638 info3_copy->base.logon_domain.string);
2639 if (domain && domain->primary ) {
2640 struct dom_sid user_sid;
2642 sid_compose(&user_sid,
2643 info3_copy->base.domain_sid,
2644 info3_copy->base.rid);
2646 cache_name2sid_trusted(domain,
2647 info3_copy->base.logon_domain.string,
2648 info3_copy->base.account_name.string,
2652 DBG_INFO("PAC for user %s\%s SID %s primed cache\n",
2653 info3_copy->base.logon_domain.string,
2654 info3_copy->base.account_name.string,
2655 sid_string_dbg(&user_sid));
2659 /* Try without signature verification */
2660 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2661 NULL, NULL, NULL, 0,
2663 if (!NT_STATUS_IS_OK(result)) {
2664 DEBUG(10, ("Could not extract PAC: %s\n",
2665 nt_errstr(result)));
2670 * Don't strictly need to copy here,
2671 * but it makes it explicit we're
2672 * returning a copy talloc'ed off
2673 * the state->mem_ctx.
2675 info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2676 &logon_info->info3);
2677 if (info3_copy == NULL) {
2678 return NT_STATUS_NO_MEMORY;
2683 *info3 = info3_copy;
2685 return NT_STATUS_OK;
2687 #else /* HAVE_KRB5 */
2688 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2689 struct netr_SamInfo3 **info3)
2691 return NT_STATUS_NO_SUCH_USER;
2693 #endif /* HAVE_KRB5 */