winbind: Pass up args from winbind_dual_SamLogon
[Samba.git] / source3 / winbindd / winbindd_pam.c
bloba8bc34eae9b8963725f12cd569b1094dfeb86a0f
1 /*
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/>.
25 #include "includes.h"
26 #include "winbindd.h"
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"
33 #include "smb_krb5.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
36 #include "ads.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
39 #include "auth.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"
47 #undef DBGC_CLASS
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)
56 char *ex;
57 uint32_t i;
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++) {
114 char *sid;
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",
120 sid,
121 info3->sids[i].attributes);
122 NT_STATUS_HAVE_NO_MEMORY(ex);
124 talloc_free(sid);
127 resp->extra_data.data = ex;
128 resp->length += talloc_get_size(ex);
130 return NT_STATUS_OK;
133 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
134 struct winbindd_response *resp,
135 struct netr_SamInfo3 *info3)
137 DATA_BLOB blob;
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;
150 return NT_STATUS_OK;
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);
165 if (!nt_domain) {
166 /* If the server didn't give us one, just use the one
167 * we sent them */
168 nt_domain = name_domain;
171 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
172 if (!nt_username) {
173 /* If the server didn't give us one, just use the one
174 * we sent them */
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));
184 return NT_STATUS_OK;
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;
194 char *cell;
195 char *token;
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(),
204 "%D", name_domain);
205 afsname = talloc_string_sub(mem_ctx, afsname,
206 "%u", name_user);
207 afsname = talloc_string_sub(mem_ctx, afsname,
208 "%U", name_user);
211 struct dom_sid user_sid;
212 fstring sidstr;
214 sid_compose(&user_sid, info3->base.domain_sid,
215 info3->base.rid);
216 sid_to_fstring(sidstr, &user_sid);
217 afsname = talloc_string_sub(mem_ctx, afsname,
218 "%s", sidstr);
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, '@');
233 if (cell == NULL) {
234 return NT_STATUS_NO_MEMORY;
237 *cell = '\0';
238 cell += 1;
240 token = afs_createtoken_str(afsname, cell);
241 if (token == NULL) {
242 return NT_STATUS_OK;
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;
250 return NT_STATUS_OK;
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;
269 char *req_sid;
270 const char *p;
271 struct dom_sid sid;
272 size_t i;
273 struct security_token *token;
274 TALLOC_CTX *frame = talloc_stackframe();
275 NTSTATUS status;
277 /* Parse the 'required group' SID */
279 if (!group_sid || !group_sid[0]) {
280 /* NO sid supplied, all users may access */
281 TALLOC_FREE(frame);
282 return NT_STATUS_OK;
285 token = talloc_zero(talloc_tos(), struct security_token);
286 if (token == NULL) {
287 DEBUG(0, ("talloc failed\n"));
288 TALLOC_FREE(frame);
289 return NT_STATUS_NO_MEMORY;
292 num_require_membership_of_sid = 0;
293 require_membership_of_sid = NULL;
295 p = group_sid;
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));
301 TALLOC_FREE(frame);
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"));
310 TALLOC_FREE(frame);
311 return status;
315 status = sid_array_from_info3(talloc_tos(), info3,
316 &token->sids,
317 &token->num_sids,
318 true);
319 if (!NT_STATUS_IS_OK(status)) {
320 TALLOC_FREE(frame);
321 return status;
324 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
325 token))
326 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
327 token))) {
328 DEBUG(3, ("could not add aliases: %s\n",
329 nt_errstr(status)));
330 TALLOC_FREE(frame);
331 return status;
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],
340 token)) {
341 DEBUG(10, ("Access ok\n"));
342 TALLOC_FREE(frame);
343 return NT_STATUS_OK;
347 /* Do not distinguish this error from a wrong username/pw */
349 TALLOC_FREE(frame);
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;
358 if (IS_DC) {
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",
363 domain_name));
365 return domain;
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",
378 domain_name));
379 } else {
380 return domain;
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();
406 NTSTATUS status;
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;
413 goto done;
416 status = wb_cache_password_policy(domain, talloc_tos(),
417 &password_policy);
418 if (NT_STATUS_IS_ERR(status)) {
419 goto done;
422 fill_in_password_policy(response, &password_policy);
424 done:
425 TALLOC_FREE(frame);
426 return NT_STATUS_OK;
429 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
430 TALLOC_CTX *mem_ctx,
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)) {
440 return status;
443 *lockout_threshold = lockout_policy.lockout_threshold;
445 return NT_STATUS_OK;
448 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
449 TALLOC_CTX *mem_ctx,
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)) {
459 return status;
462 *password_properties = password_policy.password_properties;
464 return NT_STATUS_OK;
467 #ifdef HAVE_KRB5
469 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
470 const char *type,
471 uid_t uid,
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 -
476 * Guenther*/
478 const char *gen_cc = NULL;
480 if (uid != -1) {
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 */
500 char *p;
502 p = strchr(type, '%');
503 if (p != NULL) {
505 p++;
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,
513 type,
514 "%u",
515 uid_str,
516 /* remove_unsafe_characters */
517 false,
518 /* replace_once */
519 true,
520 /* allow_trailing_dollar */
521 false);
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"));
534 return NULL;
537 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
538 (*user_ccache_file == NULL) ? " (internal)":""));
540 return gen_cc;
543 #endif
545 uid_t get_uid_from_request(struct winbindd_request *request)
547 uid_t uid;
549 uid = request->data.auth.uid;
551 if (uid == (uid_t)-1) {
552 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
553 return -1;
555 return uid;
558 /**********************************************************************
559 Authenticate a user with a clear text password using Kerberos and fill up
560 ccache if required
561 **********************************************************************/
563 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
564 struct winbindd_domain *domain,
565 const char *user,
566 const char *pass,
567 const char *krb5_cc_type,
568 uid_t uid,
569 struct netr_SamInfo3 **info3,
570 fstring krb5ccname)
572 #ifdef HAVE_KRB5
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;
578 char *realm = NULL;
579 fstring name_domain, name_user;
580 time_t ticket_lifetime = 0;
581 time_t renewal_until = 0;
582 ADS_STRUCT *ads;
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;
589 int i;
590 struct netr_SamInfo3 *info3_copy = NULL;
592 *info3 = NULL;
594 if (domain->alt_name == NULL) {
595 return NT_STATUS_INVALID_PARAMETER;
598 /* 1st step:
599 * prepare a krb5_cc_cache string for the user */
601 if (uid == -1) {
602 DEBUG(0,("no valid uid\n"));
605 cc = generate_krb5_ccache(mem_ctx,
606 krb5_cc_type,
607 uid,
608 &user_ccache_file);
609 if (cc == NULL) {
610 return NT_STATUS_NO_MEMORY;
614 /* 2nd step:
615 * get kerberos properties */
617 if (domain->private_data) {
618 ads = (ADS_STRUCT *)domain->private_data;
619 time_offset = ads->auth.time_offset;
623 /* 3rd step:
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);
629 if (realm == NULL) {
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,
665 principal_s,
666 pass,
667 time_offset,
668 &ticket_lifetime,
669 &renewal_until,
671 true,
672 true,
673 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
674 NULL,
675 local_service,
676 &pac_data_ctr);
677 if (user_ccache_file != NULL) {
678 gain_root_privilege();
681 /************************ RETURNED TO ROOT **********************/
683 if (!NT_STATUS_IS_OK(result)) {
684 goto failed;
687 if (pac_data_ctr == NULL) {
688 goto failed;
691 pac_data = pac_data_ctr->pac_data;
692 if (pac_data == NULL) {
693 goto failed;
696 for (i=0; i < pac_data->num_buffers; i++) {
698 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
699 continue;
702 logon_info = pac_data->buffers[i].info->logon_info.info;
703 if (!logon_info) {
704 return NT_STATUS_INVALID_PARAMETER;
707 break;
710 if (logon_info == NULL) {
711 DEBUG(10,("Missing logon_info in ticket of %s\n",
712 principal_s));
713 return NT_STATUS_INVALID_PARAMETER;
716 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
717 principal_s));
719 result = create_info3_from_pac_logon_info(mem_ctx, logon_info, &info3_copy);
720 if (!NT_STATUS_IS_OK(result)) {
721 goto failed;
724 /* if we had a user's ccache then return that string for the pam
725 * environment */
727 if (user_ccache_file != NULL) {
729 fstrcpy(krb5ccname, user_ccache_file);
731 result = add_ccache_to_list(principal_s,
733 service,
734 user,
735 pass,
736 realm,
737 uid,
738 time(NULL),
739 ticket_lifetime,
740 renewal_until,
741 false);
743 if (!NT_STATUS_IS_OK(result)) {
744 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
745 nt_errstr(result)));
747 } else {
749 /* need to delete the memory cred cache, it is not used anymore */
751 krb5_ret = ads_kdestroy(cc);
752 if (krb5_ret) {
753 DEBUG(3,("winbindd_raw_kerberos_login: "
754 "could not destroy krb5 credential cache: "
755 "%s\n", error_message(krb5_ret)));
759 *info3 = info3_copy;
760 return NT_STATUS_OK;
762 failed:
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) {
769 return result;
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);
778 if (krb5_ret) {
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",
787 user));
790 return result;
791 #else
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) ) {
809 return true;
812 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
813 flags));
815 return false;
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)
828 NTSTATUS result;
830 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
831 memcpy(resp->data.auth.user_session_key,
832 info3->base.key.key,
833 sizeof(resp->data.auth.user_session_key)
834 /* 16 */);
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)
841 /* 8 */);
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",
849 nt_errstr(result)));
850 return result;
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",
860 nt_errstr(result)));
861 return result;
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",
869 nt_errstr(result)));
870 return result;
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",
879 nt_errstr(result)));
880 return result;
884 return NT_STATUS_OK;
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;
894 struct dom_sid sid;
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;
902 #ifdef HAVE_KRB5
903 struct winbindd_tdc_domain *tdc_domain = NULL;
904 #endif
906 *info3 = NULL;
908 ZERO_STRUCTP(info3);
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,
918 name_user,
919 &sid,
920 &type)) {
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,
931 state->mem_ctx,
932 &sid,
933 &my_info3,
934 &cached_nt_pass,
935 &cached_salt);
936 if (!NT_STATUS_IS_OK(result)) {
937 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
938 return result;
941 *info3 = my_info3;
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);
947 if (cached_salt) {
948 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
951 if (cached_salt) {
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,
958 NT_HASH_LEN) == 0);
959 } else {
960 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
961 password_good = (memcmp(cached_nt_pass, new_nt_pass,
962 NT_HASH_LEN) == 0);
965 if (password_good) {
967 /* User *DOES* know the password, update logon_time and reset
968 * bad_pw_count */
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; */
1008 goto success;
1011 #ifdef HAVE_KRB5
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))) {
1018 uid_t uid = -1;
1019 const char *cc = NULL;
1020 char *realm = 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);
1030 if (uid == -1) {
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,
1038 &user_ccache_file);
1039 if (cc == NULL) {
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,
1065 user_ccache_file);
1067 result = add_ccache_to_list(principal_s,
1069 service,
1070 state->request->data.auth.user,
1071 state->request->data.auth.pass,
1072 realm,
1073 uid,
1074 time(NULL),
1075 time(NULL) + lp_winbind_cache_time(),
1076 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1077 true);
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 */
1087 success:
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,
1097 my_info3);
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)));
1101 return 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) {
1110 goto failed;
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) {
1124 goto failed;
1127 /* lockout user */
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;
1143 failed:
1144 result = winbindd_update_creds_by_info3(domain,
1145 state->request->data.auth.user,
1146 NULL,
1147 my_info3);
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;
1163 NTSTATUS result;
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? */
1173 if ( IS_DC ) {
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;
1178 goto done;
1181 } else {
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;
1185 goto done;
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;
1194 goto done;
1198 if (contact_domain->initialized &&
1199 contact_domain->active_directory) {
1200 goto try_login;
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;
1211 try_login:
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);
1219 done:
1220 return result;
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,
1229 bool interactive,
1230 struct netr_SamInfo3 **pinfo3)
1232 struct auth_context *auth_context;
1233 struct auth_serversupplied_info *server_info;
1234 struct auth_usersupplied_info *user_info = NULL;
1235 struct tsocket_address *local;
1236 struct netr_SamInfo3 *info3;
1237 NTSTATUS status;
1238 int rc;
1239 TALLOC_CTX *frame = talloc_stackframe();
1241 rc = tsocket_address_inet_from_strings(frame,
1242 "ip",
1243 "127.0.0.1",
1245 &local);
1246 if (rc < 0) {
1247 TALLOC_FREE(frame);
1248 return NT_STATUS_NO_MEMORY;
1250 status = make_user_info(frame, &user_info, user, user, domain, domain,
1251 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1252 NULL, AUTH_PASSWORD_RESPONSE);
1253 if (!NT_STATUS_IS_OK(status)) {
1254 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1255 TALLOC_FREE(frame);
1256 return status;
1259 user_info->logon_parameters = logon_parameters;
1261 /* We don't want any more mapping of the username */
1262 user_info->mapped_state = True;
1264 /* We don't want to come back to winbindd or to do PAM account checks */
1265 user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
1267 if (interactive) {
1268 user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
1271 status = make_auth_context_fixed(frame, &auth_context, challenge->data);
1273 if (!NT_STATUS_IS_OK(status)) {
1274 DBG_ERR("make_auth_context_fixed failed: %s\n",
1275 nt_errstr(status));
1276 TALLOC_FREE(frame);
1277 return status;
1280 status = auth_check_ntlm_password(mem_ctx,
1281 auth_context,
1282 user_info,
1283 &server_info);
1285 if (!NT_STATUS_IS_OK(status)) {
1286 TALLOC_FREE(frame);
1287 return status;
1290 info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1291 if (info3 == NULL) {
1292 TALLOC_FREE(frame);
1293 return NT_STATUS_NO_MEMORY;
1296 status = serverinfo_to_SamInfo3(server_info, info3);
1297 if (!NT_STATUS_IS_OK(status)) {
1298 TALLOC_FREE(frame);
1299 TALLOC_FREE(info3);
1300 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1301 nt_errstr(status)));
1302 return status;
1305 *pinfo3 = info3;
1306 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1307 user, nt_errstr(status)));
1308 TALLOC_FREE(frame);
1309 return status;
1312 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1313 TALLOC_CTX *mem_ctx,
1314 uint32_t logon_parameters,
1315 const char *username,
1316 const char *password,
1317 const char *domainname,
1318 const char *workstation,
1319 const uint8_t chal[8],
1320 DATA_BLOB lm_response,
1321 DATA_BLOB nt_response,
1322 bool interactive,
1323 uint8_t *authoritative,
1324 uint32_t *flags,
1325 struct netr_SamInfo3 **info3)
1327 int attempts = 0;
1328 int netr_attempts = 0;
1329 bool retry = false;
1330 NTSTATUS result;
1332 do {
1333 struct rpc_pipe_client *netlogon_pipe;
1335 ZERO_STRUCTP(info3);
1336 retry = false;
1338 result = cm_connect_netlogon(domain, &netlogon_pipe);
1340 if (!NT_STATUS_IS_OK(result)) {
1341 DEBUG(3,("Could not open handle to NETLOGON pipe "
1342 "(error: %s, attempts: %d)\n",
1343 nt_errstr(result), netr_attempts));
1345 /* After the first retry always close the connection */
1346 if (netr_attempts > 0) {
1347 DEBUG(3, ("This is again a problem for this "
1348 "particular call, forcing the close "
1349 "of this connection\n"));
1350 invalidate_cm_connection(domain);
1353 /* After the second retry failover to the next DC */
1354 if (netr_attempts > 1) {
1356 * If the netlogon server is not reachable then
1357 * it is possible that the DC is rebuilding
1358 * sysvol and shutdown netlogon for that time.
1359 * We should failover to the next dc.
1361 DEBUG(3, ("This is the third problem for this "
1362 "particular call, adding DC to the "
1363 "negative cache list\n"));
1364 add_failed_connection_entry(domain->name,
1365 domain->dcname,
1366 result);
1367 saf_delete(domain->name);
1370 /* Only allow 3 retries */
1371 if (netr_attempts < 3) {
1372 DEBUG(3, ("The connection to netlogon "
1373 "failed, retrying\n"));
1374 netr_attempts++;
1375 retry = true;
1376 continue;
1378 return result;
1380 netr_attempts = 0;
1381 if (domain->conn.netlogon_creds == NULL) {
1382 DBG_NOTICE("No security credentials available for "
1383 "domain [%s]\n", domainname);
1384 result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1385 } else if (interactive && username != NULL && password != NULL) {
1386 result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1387 netlogon_pipe->binding_handle,
1388 mem_ctx,
1389 logon_parameters,
1390 domainname,
1391 username,
1392 password,
1393 workstation,
1394 NetlogonInteractiveInformation,
1395 authoritative,
1396 flags,
1397 info3);
1398 } else {
1399 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1400 netlogon_pipe->binding_handle,
1401 mem_ctx,
1402 logon_parameters,
1403 username,
1404 domainname,
1405 workstation,
1406 chal,
1407 lm_response,
1408 nt_response,
1409 authoritative,
1410 flags,
1411 info3);
1415 * we increment this after the "feature negotiation"
1416 * for can_do_samlogon_ex and can_do_validation6
1418 attempts += 1;
1420 /* We have to try a second time as cm_connect_netlogon
1421 might not yet have noticed that the DC has killed
1422 our connection. */
1424 if (!rpccli_is_connected(netlogon_pipe)) {
1425 retry = true;
1426 continue;
1429 /* if we get access denied, a possible cause was that we had
1430 and open connection to the DC, but someone changed our
1431 machine account password out from underneath us using 'net
1432 rpc changetrustpw' */
1434 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1435 DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1436 "ACCESS_DENIED. Maybe the DC has Restrict "
1437 "NTLM set or the trust account "
1438 "password was changed and we didn't know it. "
1439 "Killing connections to domain %s\n",
1440 domainname));
1441 invalidate_cm_connection(domain);
1442 retry = true;
1445 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1447 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1448 * (no Ex). This happens against old Samba
1449 * DCs, if LogonSamLogonEx() fails with an error
1450 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1452 * The server will log something like this:
1453 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1455 * This sets the whole connection into a fault_state mode
1456 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1458 * This also happens to our retry with LogonSamLogonWithFlags()
1459 * and LogonSamLogon().
1461 * In order to recover from this situation, we need to
1462 * drop the connection.
1464 invalidate_cm_connection(domain);
1465 result = NT_STATUS_LOGON_FAILURE;
1466 break;
1469 } while ( (attempts < 2) && retry );
1471 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1472 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1473 "returned NT_STATUS_IO_TIMEOUT after the retry."
1474 "Killing connections to domain %s\n",
1475 domainname));
1476 invalidate_cm_connection(domain);
1478 return result;
1481 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1482 struct winbindd_domain *domain,
1483 const char *user,
1484 const char *pass,
1485 uint32_t request_flags,
1486 struct netr_SamInfo3 **info3)
1489 uchar chal[8];
1490 DATA_BLOB lm_resp;
1491 DATA_BLOB nt_resp;
1492 unsigned char local_nt_response[24];
1493 fstring name_domain, name_user;
1494 NTSTATUS result;
1495 struct netr_SamInfo3 *my_info3 = NULL;
1496 uint8_t authoritative = 0;
1497 uint32_t flags = 0;
1499 *info3 = NULL;
1501 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1503 /* Parse domain and username */
1505 parse_domain_user(user, name_domain, name_user);
1507 /* do password magic */
1509 generate_random_buffer(chal, sizeof(chal));
1511 if (lp_client_ntlmv2_auth()) {
1512 DATA_BLOB server_chal;
1513 DATA_BLOB names_blob;
1514 server_chal = data_blob_const(chal, 8);
1516 /* note that the 'workgroup' here is for the local
1517 machine. The 'server name' must match the
1518 'workstation' passed to the actual SamLogon call.
1520 names_blob = NTLMv2_generate_names_blob(
1521 mem_ctx, lp_netbios_name(), lp_workgroup());
1523 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1524 pass,
1525 &server_chal,
1526 &names_blob,
1527 &lm_resp, &nt_resp, NULL, NULL)) {
1528 data_blob_free(&names_blob);
1529 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1530 result = NT_STATUS_NO_MEMORY;
1531 goto done;
1533 data_blob_free(&names_blob);
1534 } else {
1535 lm_resp = data_blob_null;
1536 SMBNTencrypt(pass, chal, local_nt_response);
1538 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1539 sizeof(local_nt_response));
1542 if (strequal(name_domain, get_global_sam_name())) {
1543 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1545 result = winbindd_dual_auth_passdb(
1546 mem_ctx, 0, name_domain, name_user,
1547 &chal_blob, &lm_resp, &nt_resp,
1548 true, /* interactive */
1549 info3);
1552 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1554 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1555 goto done;
1559 /* check authentication loop */
1561 result = winbind_samlogon_retry_loop(domain,
1562 mem_ctx,
1564 name_user,
1565 pass,
1566 name_domain,
1567 lp_netbios_name(),
1568 chal,
1569 lm_resp,
1570 nt_resp,
1571 true, /* interactive */
1572 &authoritative,
1573 &flags,
1574 &my_info3);
1575 if (!NT_STATUS_IS_OK(result)) {
1576 goto done;
1579 /* handle the case where a NT4 DC does not fill in the acct_flags in
1580 * the samlogon reply info3. When accurate info3 is required by the
1581 * caller, we look up the account flags ourselve - gd */
1583 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1584 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1586 struct rpc_pipe_client *samr_pipe;
1587 struct policy_handle samr_domain_handle, user_pol;
1588 union samr_UserInfo *info = NULL;
1589 NTSTATUS status_tmp, result_tmp;
1590 uint32_t acct_flags;
1591 struct dcerpc_binding_handle *b;
1593 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1594 &samr_pipe, &samr_domain_handle);
1596 if (!NT_STATUS_IS_OK(status_tmp)) {
1597 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1598 nt_errstr(status_tmp)));
1599 goto done;
1602 b = samr_pipe->binding_handle;
1604 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1605 &samr_domain_handle,
1606 MAXIMUM_ALLOWED_ACCESS,
1607 my_info3->base.rid,
1608 &user_pol,
1609 &result_tmp);
1611 if (!NT_STATUS_IS_OK(status_tmp)) {
1612 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1613 nt_errstr(status_tmp)));
1614 goto done;
1616 if (!NT_STATUS_IS_OK(result_tmp)) {
1617 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1618 nt_errstr(result_tmp)));
1619 goto done;
1622 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1623 &user_pol,
1625 &info,
1626 &result_tmp);
1628 if (!NT_STATUS_IS_OK(status_tmp)) {
1629 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1630 nt_errstr(status_tmp)));
1631 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1632 goto done;
1634 if (!NT_STATUS_IS_OK(result_tmp)) {
1635 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1636 nt_errstr(result_tmp)));
1637 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1638 goto done;
1641 acct_flags = info->info16.acct_flags;
1643 if (acct_flags == 0) {
1644 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1645 goto done;
1648 my_info3->base.acct_flags = acct_flags;
1650 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1652 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1655 *info3 = my_info3;
1656 done:
1657 return result;
1660 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1661 struct winbindd_cli_state *state)
1663 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1664 NTSTATUS krb5_result = NT_STATUS_OK;
1665 fstring name_domain, name_user;
1666 char *mapped_user;
1667 fstring domain_user;
1668 struct netr_SamInfo3 *info3 = NULL;
1669 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1671 /* Ensure null termination */
1672 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1674 /* Ensure null termination */
1675 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1677 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1678 state->request->data.auth.user));
1680 /* Parse domain and username */
1682 name_map_status = normalize_name_unmap(state->mem_ctx,
1683 state->request->data.auth.user,
1684 &mapped_user);
1686 /* If the name normalization didnt' actually do anything,
1687 just use the original name */
1689 if (!NT_STATUS_IS_OK(name_map_status) &&
1690 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1692 mapped_user = state->request->data.auth.user;
1695 parse_domain_user(mapped_user, name_domain, name_user);
1697 if ( mapped_user != state->request->data.auth.user ) {
1698 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1699 *lp_winbind_separator(),
1700 name_user );
1701 strlcpy( state->request->data.auth.user, domain_user,
1702 sizeof(state->request->data.auth.user));
1705 if (!domain->online) {
1706 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1707 if (domain->startup) {
1708 /* Logons are very important to users. If we're offline and
1709 we get a request within the first 30 seconds of startup,
1710 try very hard to find a DC and go online. */
1712 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1713 "request in startup mode.\n", domain->name ));
1715 winbindd_flush_negative_conn_cache(domain);
1716 result = init_dc_connection(domain, false);
1720 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1722 /* Check for Kerberos authentication */
1723 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1725 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1726 /* save for later */
1727 krb5_result = result;
1730 if (NT_STATUS_IS_OK(result)) {
1731 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1732 goto process_result;
1733 } else {
1734 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1737 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1738 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1739 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1740 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1741 set_domain_offline( domain );
1742 goto cached_logon;
1745 /* there are quite some NT_STATUS errors where there is no
1746 * point in retrying with a samlogon, we explictly have to take
1747 * care not to increase the bad logon counter on the DC */
1749 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1750 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1751 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1752 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1753 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1754 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1755 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1756 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1757 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1758 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1759 goto done;
1762 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1763 DEBUG(3,("falling back to samlogon\n"));
1764 goto sam_logon;
1765 } else {
1766 goto cached_logon;
1770 sam_logon:
1771 /* Check for Samlogon authentication */
1772 if (domain->online) {
1773 result = winbindd_dual_pam_auth_samlogon(
1774 state->mem_ctx, domain,
1775 state->request->data.auth.user,
1776 state->request->data.auth.pass,
1777 state->request->flags,
1778 &info3);
1780 if (NT_STATUS_IS_OK(result)) {
1781 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1782 /* add the Krb5 err if we have one */
1783 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1784 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1786 goto process_result;
1789 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1790 nt_errstr(result)));
1792 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1793 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1794 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1796 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1797 set_domain_offline( domain );
1798 goto cached_logon;
1801 if (domain->online) {
1802 /* We're still online - fail. */
1803 goto done;
1807 cached_logon:
1808 /* Check for Cached logons */
1809 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1810 lp_winbind_offline_logon()) {
1812 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1814 if (NT_STATUS_IS_OK(result)) {
1815 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1816 goto process_result;
1817 } else {
1818 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1819 goto done;
1823 process_result:
1825 if (NT_STATUS_IS_OK(result)) {
1827 struct dom_sid user_sid;
1829 /* In all codepaths where result == NT_STATUS_OK info3 must have
1830 been initialized. */
1831 if (!info3) {
1832 result = NT_STATUS_INTERNAL_ERROR;
1833 goto done;
1836 sid_compose(&user_sid, info3->base.domain_sid,
1837 info3->base.rid);
1839 if (info3->base.full_name.string == NULL) {
1840 struct netr_SamInfo3 *cached_info3;
1842 cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1843 &user_sid);
1844 if (cached_info3 != NULL &&
1845 cached_info3->base.full_name.string != NULL) {
1846 info3->base.full_name.string =
1847 talloc_strdup(info3,
1848 cached_info3->base.full_name.string);
1849 } else {
1851 /* this might fail so we don't check the return code */
1852 wcache_query_user_fullname(domain,
1853 info3,
1854 &user_sid,
1855 &info3->base.full_name.string);
1859 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1860 &user_sid);
1861 netsamlogon_cache_store(name_user, info3);
1863 /* save name_to_sid info as early as possible (only if
1864 this is our primary domain so we don't invalidate
1865 the cache entry by storing the seq_num for the wrong
1866 domain). */
1867 if ( domain->primary ) {
1868 cache_name2sid(domain, name_domain, name_user,
1869 SID_NAME_USER, &user_sid);
1872 /* Check if the user is in the right group */
1874 result = check_info3_in_group(
1875 info3,
1876 state->request->data.auth.require_membership_of_sid);
1877 if (!NT_STATUS_IS_OK(result)) {
1878 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1879 state->request->data.auth.user,
1880 state->request->data.auth.require_membership_of_sid));
1881 goto done;
1884 result = append_auth_data(state->mem_ctx, state->response,
1885 state->request->flags, info3,
1886 name_domain, name_user);
1887 if (!NT_STATUS_IS_OK(result)) {
1888 goto done;
1891 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1892 && lp_winbind_offline_logon()) {
1894 result = winbindd_store_creds(domain,
1895 state->request->data.auth.user,
1896 state->request->data.auth.pass,
1897 info3);
1900 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1901 struct winbindd_domain *our_domain = find_our_domain();
1903 /* This is not entirely correct I believe, but it is
1904 consistent. Only apply the password policy settings
1905 too warn users for our own domain. Cannot obtain these
1906 from trusted DCs all the time so don't do it at all.
1907 -- jerry */
1909 result = NT_STATUS_NOT_SUPPORTED;
1910 if (our_domain == domain ) {
1911 result = fillup_password_policy(
1912 our_domain, state->response);
1915 if (!NT_STATUS_IS_OK(result)
1916 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1918 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1919 domain->name, nt_errstr(result)));
1920 goto done;
1924 result = NT_STATUS_OK;
1927 done:
1928 /* give us a more useful (more correct?) error code */
1929 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1930 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1931 result = NT_STATUS_NO_LOGON_SERVERS;
1934 set_auth_errors(state->response, result);
1936 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1937 state->request->data.auth.user,
1938 state->response->data.auth.nt_status_string,
1939 state->response->data.auth.pam_error));
1941 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1944 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1945 TALLOC_CTX *mem_ctx,
1946 uint32_t logon_parameters,
1947 const char *name_user,
1948 const char *name_domain,
1949 const char *workstation,
1950 const uint8_t chal[8],
1951 DATA_BLOB lm_response,
1952 DATA_BLOB nt_response,
1953 uint8_t *authoritative,
1954 uint32_t *flags,
1955 struct netr_SamInfo3 **info3)
1957 NTSTATUS result;
1959 if (strequal(name_domain, get_global_sam_name())) {
1960 DATA_BLOB chal_blob = data_blob_const(
1961 chal, 8);
1963 result = winbindd_dual_auth_passdb(
1964 mem_ctx,
1965 logon_parameters,
1966 name_domain, name_user,
1967 &chal_blob, &lm_response, &nt_response,
1968 false, /* interactive */
1969 info3);
1972 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1974 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1975 *authoritative = 1;
1976 *flags = 0;
1977 goto process_result;
1981 result = winbind_samlogon_retry_loop(domain,
1982 mem_ctx,
1983 logon_parameters,
1984 name_user,
1985 NULL, /* password */
1986 name_domain,
1987 /* Bug #3248 - found by Stefan Burkei. */
1988 workstation, /* We carefully set this above so use it... */
1989 chal,
1990 lm_response,
1991 nt_response,
1992 false, /* interactive */
1993 authoritative,
1994 flags,
1995 info3);
1996 if (!NT_STATUS_IS_OK(result)) {
1997 goto done;
2000 process_result:
2002 if (NT_STATUS_IS_OK(result)) {
2003 struct dom_sid user_sid;
2005 sid_compose(&user_sid, (*info3)->base.domain_sid,
2006 (*info3)->base.rid);
2008 if ((*info3)->base.full_name.string == NULL) {
2009 struct netr_SamInfo3 *cached_info3;
2011 cached_info3 = netsamlogon_cache_get(mem_ctx,
2012 &user_sid);
2013 if (cached_info3 != NULL &&
2014 cached_info3->base.full_name.string != NULL) {
2015 (*info3)->base.full_name.string =
2016 talloc_strdup(*info3,
2017 cached_info3->base.full_name.string);
2018 } else {
2020 /* this might fail so we don't check the return code */
2021 wcache_query_user_fullname(domain,
2022 *info3,
2023 &user_sid,
2024 &(*info3)->base.full_name.string);
2028 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2029 &user_sid);
2030 netsamlogon_cache_store(name_user, *info3);
2033 done:
2035 /* give us a more useful (more correct?) error code */
2036 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2037 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2038 result = NT_STATUS_NO_LOGON_SERVERS;
2041 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2042 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2043 name_domain,
2044 name_user,
2045 nt_errstr(result)));
2047 return result;
2050 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2051 struct winbindd_cli_state *state)
2053 NTSTATUS result;
2054 struct netr_SamInfo3 *info3 = NULL;
2055 const char *name_user = NULL;
2056 const char *name_domain = NULL;
2057 const char *workstation;
2058 uint8_t authoritative;
2059 uint32_t flags;
2061 DATA_BLOB lm_resp, nt_resp;
2063 /* This is child-only, so no check for privileged access is needed
2064 anymore */
2066 /* Ensure null termination */
2067 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2068 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2070 name_user = state->request->data.auth_crap.user;
2071 name_domain = state->request->data.auth_crap.domain;
2072 workstation = state->request->data.auth_crap.workstation;
2074 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2075 name_domain, name_user));
2077 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2078 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2079 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2080 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2081 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2082 state->request->data.auth_crap.lm_resp_len,
2083 state->request->data.auth_crap.nt_resp_len));
2084 result = NT_STATUS_INVALID_PARAMETER;
2085 goto done;
2089 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2090 state->request->data.auth_crap.lm_resp_len);
2092 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2093 nt_resp = data_blob_talloc(state->mem_ctx,
2094 state->request->extra_data.data,
2095 state->request->data.auth_crap.nt_resp_len);
2096 } else {
2097 nt_resp = data_blob_talloc(state->mem_ctx,
2098 state->request->data.auth_crap.nt_resp,
2099 state->request->data.auth_crap.nt_resp_len);
2102 result = winbind_dual_SamLogon(domain,
2103 state->mem_ctx,
2104 state->request->data.auth_crap.logon_parameters,
2105 name_user,
2106 name_domain,
2107 /* Bug #3248 - found by Stefan Burkei. */
2108 workstation, /* We carefully set this above so use it... */
2109 state->request->data.auth_crap.chal,
2110 lm_resp,
2111 nt_resp,
2112 &authoritative,
2113 &flags,
2114 &info3);
2115 if (!NT_STATUS_IS_OK(result)) {
2116 goto done;
2119 if (NT_STATUS_IS_OK(result)) {
2120 /* Check if the user is in the right group */
2122 result = check_info3_in_group(
2123 info3,
2124 state->request->data.auth_crap.require_membership_of_sid);
2125 if (!NT_STATUS_IS_OK(result)) {
2126 DEBUG(3, ("User %s is not in the required group (%s), so "
2127 "crap authentication is rejected\n",
2128 state->request->data.auth_crap.user,
2129 state->request->data.auth_crap.require_membership_of_sid));
2130 goto done;
2133 result = append_auth_data(state->mem_ctx, state->response,
2134 state->request->flags, info3,
2135 name_domain, name_user);
2136 if (!NT_STATUS_IS_OK(result)) {
2137 goto done;
2141 done:
2143 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2144 result = nt_status_squash(result);
2147 set_auth_errors(state->response, result);
2149 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2152 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2153 struct winbindd_cli_state *state)
2155 char *oldpass;
2156 char *newpass = NULL;
2157 struct policy_handle dom_pol;
2158 struct rpc_pipe_client *cli = NULL;
2159 bool got_info = false;
2160 struct samr_DomInfo1 *info = NULL;
2161 struct userPwdChangeFailureInformation *reject = NULL;
2162 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2163 fstring domain, user;
2164 struct dcerpc_binding_handle *b = NULL;
2166 ZERO_STRUCT(dom_pol);
2168 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2169 state->request->data.auth.user));
2171 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2172 goto done;
2175 /* Change password */
2177 oldpass = state->request->data.chauthtok.oldpass;
2178 newpass = state->request->data.chauthtok.newpass;
2180 /* Initialize reject reason */
2181 state->response->data.auth.reject_reason = Undefined;
2183 /* Get sam handle */
2185 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2186 &dom_pol);
2187 if (!NT_STATUS_IS_OK(result)) {
2188 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2189 goto done;
2192 b = cli->binding_handle;
2194 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2195 user,
2196 newpass,
2197 oldpass,
2198 &info,
2199 &reject);
2201 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2203 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2205 fill_in_password_policy(state->response, info);
2207 state->response->data.auth.reject_reason =
2208 reject->extendedFailureReason;
2210 got_info = true;
2213 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2214 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2215 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2216 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2218 /* only fallback when the chgpasswd_user3 call is not supported */
2219 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2220 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2221 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2222 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2224 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2225 nt_errstr(result)));
2227 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2229 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2230 Map to the same status code as Windows 2003. */
2232 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2233 result = NT_STATUS_PASSWORD_RESTRICTION;
2237 done:
2239 if (NT_STATUS_IS_OK(result)
2240 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2241 && lp_winbind_offline_logon()) {
2242 result = winbindd_update_creds_by_name(contact_domain, user,
2243 newpass);
2244 /* Again, this happens when we login from gdm or xdm
2245 * and the password expires, *BUT* cached crendentials
2246 * doesn't exist. winbindd_update_creds_by_name()
2247 * returns NT_STATUS_NO_SUCH_USER.
2248 * This is not a failure.
2249 * --- BoYang
2250 * */
2251 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2252 result = NT_STATUS_OK;
2255 if (!NT_STATUS_IS_OK(result)) {
2256 DEBUG(10, ("Failed to store creds: %s\n",
2257 nt_errstr(result)));
2258 goto process_result;
2262 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2264 NTSTATUS policy_ret;
2266 policy_ret = fillup_password_policy(
2267 contact_domain, state->response);
2269 /* failure of this is non critical, it will just provide no
2270 * additional information to the client why the change has
2271 * failed - Guenther */
2273 if (!NT_STATUS_IS_OK(policy_ret)) {
2274 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2275 goto process_result;
2279 process_result:
2281 if (strequal(contact_domain->name, get_global_sam_name())) {
2282 /* FIXME: internal rpc pipe does not cache handles yet */
2283 if (b) {
2284 if (is_valid_policy_hnd(&dom_pol)) {
2285 NTSTATUS _result;
2286 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2288 TALLOC_FREE(cli);
2292 set_auth_errors(state->response, result);
2294 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2295 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2296 domain,
2297 user,
2298 state->response->data.auth.nt_status_string,
2299 state->response->data.auth.pam_error));
2301 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2304 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2305 struct winbindd_cli_state *state)
2307 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2309 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2310 state->request->data.logoff.user));
2312 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2313 result = NT_STATUS_OK;
2314 goto process_result;
2317 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2318 result = NT_STATUS_OK;
2319 goto process_result;
2322 #ifdef HAVE_KRB5
2324 if (state->request->data.logoff.uid == (uid_t)-1) {
2325 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2326 goto process_result;
2329 /* what we need here is to find the corresponding krb5 ccache name *we*
2330 * created for a given username and destroy it */
2332 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2333 result = NT_STATUS_OK;
2334 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2335 goto process_result;
2338 if (!ccache_entry_identical(state->request->data.logoff.user,
2339 state->request->data.logoff.uid,
2340 state->request->data.logoff.krb5ccname)) {
2341 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2342 goto process_result;
2345 result = remove_ccache(state->request->data.logoff.user);
2346 if (!NT_STATUS_IS_OK(result)) {
2347 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2348 nt_errstr(result)));
2349 goto process_result;
2353 * Remove any mlock'ed memory creds in the child
2354 * we might be using for krb5 ticket renewal.
2357 winbindd_delete_memory_creds(state->request->data.logoff.user);
2359 #else
2360 result = NT_STATUS_NOT_SUPPORTED;
2361 #endif
2363 process_result:
2366 set_auth_errors(state->response, result);
2368 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2371 /* Change user password with auth crap*/
2373 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2375 NTSTATUS result;
2376 DATA_BLOB new_nt_password;
2377 DATA_BLOB old_nt_hash_enc;
2378 DATA_BLOB new_lm_password;
2379 DATA_BLOB old_lm_hash_enc;
2380 fstring domain,user;
2381 struct policy_handle dom_pol;
2382 struct winbindd_domain *contact_domain = domainSt;
2383 struct rpc_pipe_client *cli = NULL;
2384 struct dcerpc_binding_handle *b = NULL;
2386 ZERO_STRUCT(dom_pol);
2388 /* Ensure null termination */
2389 state->request->data.chng_pswd_auth_crap.user[
2390 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2391 state->request->data.chng_pswd_auth_crap.domain[
2392 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2393 *domain = 0;
2394 *user = 0;
2396 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2397 (unsigned long)state->pid,
2398 state->request->data.chng_pswd_auth_crap.domain,
2399 state->request->data.chng_pswd_auth_crap.user));
2401 if (lp_winbind_offline_logon()) {
2402 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2403 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2404 result = NT_STATUS_ACCESS_DENIED;
2405 goto done;
2408 if (*state->request->data.chng_pswd_auth_crap.domain) {
2409 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2410 } else {
2411 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2412 domain, user);
2414 if(!*domain) {
2415 DEBUG(3,("no domain specified with username (%s) - "
2416 "failing auth\n",
2417 state->request->data.chng_pswd_auth_crap.user));
2418 result = NT_STATUS_NO_SUCH_USER;
2419 goto done;
2423 if (!*domain && lp_winbind_use_default_domain()) {
2424 fstrcpy(domain,lp_workgroup());
2427 if(!*user) {
2428 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2431 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2432 (unsigned long)state->pid, domain, user));
2434 /* Change password */
2435 new_nt_password = data_blob_const(
2436 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2437 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2439 old_nt_hash_enc = data_blob_const(
2440 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2441 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2443 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2444 new_lm_password = data_blob_const(
2445 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2446 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2448 old_lm_hash_enc = data_blob_const(
2449 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2450 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2451 } else {
2452 new_lm_password = data_blob_null;
2453 old_lm_hash_enc = data_blob_null;
2456 /* Get sam handle */
2458 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2459 if (!NT_STATUS_IS_OK(result)) {
2460 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2461 goto done;
2464 b = cli->binding_handle;
2466 result = rpccli_samr_chng_pswd_auth_crap(
2467 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2468 new_lm_password, old_lm_hash_enc);
2470 done:
2472 if (strequal(contact_domain->name, get_global_sam_name())) {
2473 /* FIXME: internal rpc pipe does not cache handles yet */
2474 if (b) {
2475 if (is_valid_policy_hnd(&dom_pol)) {
2476 NTSTATUS _result;
2477 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2479 TALLOC_FREE(cli);
2483 set_auth_errors(state->response, result);
2485 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2486 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2487 domain, user,
2488 state->response->data.auth.nt_status_string,
2489 state->response->data.auth.pam_error));
2491 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2494 #ifdef HAVE_KRB5
2495 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2496 struct PAC_LOGON_INFO **logon_info)
2498 krb5_context krbctx = NULL;
2499 krb5_error_code k5ret;
2500 krb5_keytab keytab;
2501 krb5_kt_cursor cursor;
2502 krb5_keytab_entry entry;
2503 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2505 ZERO_STRUCT(entry);
2506 ZERO_STRUCT(cursor);
2508 k5ret = krb5_init_context(&krbctx);
2509 if (k5ret) {
2510 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2511 error_message(k5ret)));
2512 status = krb5_to_nt_status(k5ret);
2513 goto out;
2516 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2517 if (k5ret) {
2518 DEBUG(1, ("Failed to get keytab: %s\n",
2519 error_message(k5ret)));
2520 status = krb5_to_nt_status(k5ret);
2521 goto out_free;
2524 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2525 if (k5ret) {
2526 DEBUG(1, ("Failed to start seq: %s\n",
2527 error_message(k5ret)));
2528 status = krb5_to_nt_status(k5ret);
2529 goto out_keytab;
2532 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2533 while (k5ret == 0) {
2534 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2535 krbctx, NULL,
2536 KRB5_KT_KEY(&entry), NULL, 0,
2537 logon_info);
2538 if (NT_STATUS_IS_OK(status)) {
2539 break;
2541 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2542 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2545 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2546 if (k5ret) {
2547 DEBUG(1, ("Failed to end seq: %s\n",
2548 error_message(k5ret)));
2550 out_keytab:
2551 k5ret = krb5_kt_close(krbctx, keytab);
2552 if (k5ret) {
2553 DEBUG(1, ("Failed to close keytab: %s\n",
2554 error_message(k5ret)));
2556 out_free:
2557 krb5_free_context(krbctx);
2558 out:
2559 return status;
2562 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2563 struct netr_SamInfo3 **info3)
2565 struct winbindd_request *req = state->request;
2566 DATA_BLOB pac_blob;
2567 struct PAC_LOGON_INFO *logon_info = NULL;
2568 struct netr_SamInfo3 *info3_copy = NULL;
2569 NTSTATUS result;
2571 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2572 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2573 if (!NT_STATUS_IS_OK(result) &&
2574 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2575 DEBUG(1, ("Error during PAC signature verification: %s\n",
2576 nt_errstr(result)));
2577 return result;
2580 if (logon_info) {
2582 * Signature verification succeeded, we can
2583 * trust the PAC and prime the netsamlogon
2584 * and name2sid caches. DO NOT DO THIS
2585 * in the signature verification failed
2586 * code path.
2588 struct winbindd_domain *domain = NULL;
2590 result = create_info3_from_pac_logon_info(state->mem_ctx,
2591 logon_info,
2592 &info3_copy);
2593 if (!NT_STATUS_IS_OK(result)) {
2594 return result;
2596 netsamlogon_cache_store(NULL, info3_copy);
2599 * We're in the parent here, so find the child
2600 * pointer from the PAC domain name.
2602 domain = find_domain_from_name_noinit(
2603 info3_copy->base.logon_domain.string);
2604 if (domain && domain->primary ) {
2605 struct dom_sid user_sid;
2607 sid_compose(&user_sid,
2608 info3_copy->base.domain_sid,
2609 info3_copy->base.rid);
2611 cache_name2sid_trusted(domain,
2612 info3_copy->base.logon_domain.string,
2613 info3_copy->base.account_name.string,
2614 SID_NAME_USER,
2615 &user_sid);
2617 DBG_INFO("PAC for user %s\%s SID %s primed cache\n",
2618 info3_copy->base.logon_domain.string,
2619 info3_copy->base.account_name.string,
2620 sid_string_dbg(&user_sid));
2623 } else {
2624 /* Try without signature verification */
2625 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2626 NULL, NULL, NULL, 0,
2627 &logon_info);
2628 if (!NT_STATUS_IS_OK(result)) {
2629 DEBUG(10, ("Could not extract PAC: %s\n",
2630 nt_errstr(result)));
2631 return result;
2633 if (logon_info) {
2635 * Don't strictly need to copy here,
2636 * but it makes it explicit we're
2637 * returning a copy talloc'ed off
2638 * the state->mem_ctx.
2640 info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2641 &logon_info->info3);
2642 if (info3_copy == NULL) {
2643 return NT_STATUS_NO_MEMORY;
2648 *info3 = info3_copy;
2650 return NT_STATUS_OK;
2652 #else /* HAVE_KRB5 */
2653 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2654 struct netr_SamInfo3 **info3)
2656 return NT_STATUS_NO_SUCH_USER;
2658 #endif /* HAVE_KRB5 */