s3-libads: pass down local_service to kerberos_return_pac().
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob61e2cefd5224217fc7e915b6b0360fc3b2977deb
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"
45 #undef DBGC_CLASS
46 #define DBGC_CLASS DBGC_WINBIND
48 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
50 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
51 struct winbindd_response *resp,
52 struct netr_SamInfo3 *info3)
54 char *ex;
55 uint32_t i;
57 resp->data.auth.info3.logon_time =
58 nt_time_to_unix(info3->base.logon_time);
59 resp->data.auth.info3.logoff_time =
60 nt_time_to_unix(info3->base.logoff_time);
61 resp->data.auth.info3.kickoff_time =
62 nt_time_to_unix(info3->base.kickoff_time);
63 resp->data.auth.info3.pass_last_set_time =
64 nt_time_to_unix(info3->base.last_password_change);
65 resp->data.auth.info3.pass_can_change_time =
66 nt_time_to_unix(info3->base.allow_password_change);
67 resp->data.auth.info3.pass_must_change_time =
68 nt_time_to_unix(info3->base.force_password_change);
70 resp->data.auth.info3.logon_count = info3->base.logon_count;
71 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
73 resp->data.auth.info3.user_rid = info3->base.rid;
74 resp->data.auth.info3.group_rid = info3->base.primary_gid;
75 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
77 resp->data.auth.info3.num_groups = info3->base.groups.count;
78 resp->data.auth.info3.user_flgs = info3->base.user_flags;
80 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
81 resp->data.auth.info3.num_other_sids = info3->sidcount;
83 fstrcpy(resp->data.auth.info3.user_name,
84 info3->base.account_name.string);
85 fstrcpy(resp->data.auth.info3.full_name,
86 info3->base.full_name.string);
87 fstrcpy(resp->data.auth.info3.logon_script,
88 info3->base.logon_script.string);
89 fstrcpy(resp->data.auth.info3.profile_path,
90 info3->base.profile_path.string);
91 fstrcpy(resp->data.auth.info3.home_dir,
92 info3->base.home_directory.string);
93 fstrcpy(resp->data.auth.info3.dir_drive,
94 info3->base.home_drive.string);
96 fstrcpy(resp->data.auth.info3.logon_srv,
97 info3->base.logon_server.string);
98 fstrcpy(resp->data.auth.info3.logon_dom,
99 info3->base.logon_domain.string);
101 ex = talloc_strdup(mem_ctx, "");
102 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->base.groups.count; i++) {
105 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
106 info3->base.groups.rids[i].rid,
107 info3->base.groups.rids[i].attributes);
108 NT_STATUS_HAVE_NO_MEMORY(ex);
111 for (i=0; i < info3->sidcount; i++) {
112 char *sid;
114 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
115 NT_STATUS_HAVE_NO_MEMORY(sid);
117 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
118 sid,
119 info3->sids[i].attributes);
120 NT_STATUS_HAVE_NO_MEMORY(ex);
122 talloc_free(sid);
125 resp->extra_data.data = ex;
126 resp->length += talloc_get_size(ex);
128 return NT_STATUS_OK;
131 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
132 struct winbindd_response *resp,
133 struct netr_SamInfo3 *info3)
135 DATA_BLOB blob;
136 enum ndr_err_code ndr_err;
138 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
139 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
140 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
141 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
142 return ndr_map_error2ntstatus(ndr_err);
145 resp->extra_data.data = blob.data;
146 resp->length += blob.length;
148 return NT_STATUS_OK;
151 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
152 struct winbindd_response *resp,
153 const struct netr_SamInfo3 *info3,
154 const char *name_domain,
155 const char *name_user)
157 /* We've been asked to return the unix username, per
158 'winbind use default domain' settings and the like */
160 const char *nt_username, *nt_domain;
162 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
163 if (!nt_domain) {
164 /* If the server didn't give us one, just use the one
165 * we sent them */
166 nt_domain = name_domain;
169 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
170 if (!nt_username) {
171 /* If the server didn't give us one, just use the one
172 * we sent them */
173 nt_username = name_user;
176 fill_domain_username(resp->data.auth.unix_username,
177 nt_domain, nt_username, true);
179 DEBUG(5, ("Setting unix username to [%s]\n",
180 resp->data.auth.unix_username));
182 return NT_STATUS_OK;
185 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
186 struct winbindd_response *resp,
187 const struct netr_SamInfo3 *info3,
188 const char *name_domain,
189 const char *name_user)
191 char *afsname = NULL;
192 char *cell;
193 char *token;
195 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
196 if (afsname == NULL) {
197 return NT_STATUS_NO_MEMORY;
200 afsname = talloc_string_sub(mem_ctx,
201 lp_afs_username_map(),
202 "%D", name_domain);
203 afsname = talloc_string_sub(mem_ctx, afsname,
204 "%u", name_user);
205 afsname = talloc_string_sub(mem_ctx, afsname,
206 "%U", name_user);
209 struct dom_sid user_sid;
210 fstring sidstr;
212 sid_compose(&user_sid, info3->base.domain_sid,
213 info3->base.rid);
214 sid_to_fstring(sidstr, &user_sid);
215 afsname = talloc_string_sub(mem_ctx, afsname,
216 "%s", sidstr);
219 if (afsname == NULL) {
220 return NT_STATUS_NO_MEMORY;
223 if (!strlower_m(afsname)) {
224 return NT_STATUS_INVALID_PARAMETER;
227 DEBUG(10, ("Generating token for user %s\n", afsname));
229 cell = strchr(afsname, '@');
231 if (cell == NULL) {
232 return NT_STATUS_NO_MEMORY;
235 *cell = '\0';
236 cell += 1;
238 token = afs_createtoken_str(afsname, cell);
239 if (token == NULL) {
240 return NT_STATUS_OK;
242 resp->extra_data.data = talloc_strdup(mem_ctx, token);
243 if (resp->extra_data.data == NULL) {
244 return NT_STATUS_NO_MEMORY;
246 resp->length += strlen((const char *)resp->extra_data.data)+1;
248 return NT_STATUS_OK;
251 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
252 const char *group_sid)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 struct dom_sid *require_membership_of_sid;
266 uint32_t num_require_membership_of_sid;
267 char *req_sid;
268 const char *p;
269 struct dom_sid sid;
270 size_t i;
271 struct security_token *token;
272 TALLOC_CTX *frame = talloc_stackframe();
273 NTSTATUS status;
275 /* Parse the 'required group' SID */
277 if (!group_sid || !group_sid[0]) {
278 /* NO sid supplied, all users may access */
279 TALLOC_FREE(frame);
280 return NT_STATUS_OK;
283 token = talloc_zero(talloc_tos(), struct security_token);
284 if (token == NULL) {
285 DEBUG(0, ("talloc failed\n"));
286 TALLOC_FREE(frame);
287 return NT_STATUS_NO_MEMORY;
290 num_require_membership_of_sid = 0;
291 require_membership_of_sid = NULL;
293 p = group_sid;
295 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
296 if (!string_to_sid(&sid, req_sid)) {
297 DEBUG(0, ("check_info3_in_group: could not parse %s "
298 "as a SID!", req_sid));
299 TALLOC_FREE(frame);
300 return NT_STATUS_INVALID_PARAMETER;
303 status = add_sid_to_array(talloc_tos(), &sid,
304 &require_membership_of_sid,
305 &num_require_membership_of_sid);
306 if (!NT_STATUS_IS_OK(status)) {
307 DEBUG(0, ("add_sid_to_array failed\n"));
308 TALLOC_FREE(frame);
309 return status;
313 status = sid_array_from_info3(talloc_tos(), info3,
314 &token->sids,
315 &token->num_sids,
316 true);
317 if (!NT_STATUS_IS_OK(status)) {
318 TALLOC_FREE(frame);
319 return status;
322 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
323 token))
324 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
325 token))) {
326 DEBUG(3, ("could not add aliases: %s\n",
327 nt_errstr(status)));
328 TALLOC_FREE(frame);
329 return status;
332 security_token_debug(DBGC_CLASS, 10, token);
334 for (i=0; i<num_require_membership_of_sid; i++) {
335 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
336 &require_membership_of_sid[i])));
337 if (nt_token_check_sid(&require_membership_of_sid[i],
338 token)) {
339 DEBUG(10, ("Access ok\n"));
340 TALLOC_FREE(frame);
341 return NT_STATUS_OK;
345 /* Do not distinguish this error from a wrong username/pw */
347 TALLOC_FREE(frame);
348 return NT_STATUS_LOGON_FAILURE;
351 struct winbindd_domain *find_auth_domain(uint8_t flags,
352 const char *domain_name)
354 struct winbindd_domain *domain;
356 if (IS_DC) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] refused "
360 "as it is not a trusted domain\n",
361 domain_name));
363 return domain;
366 if (strequal(domain_name, get_global_sam_name())) {
367 return find_domain_from_name_noinit(domain_name);
370 /* we can auth against trusted domains */
371 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
372 domain = find_domain_from_name_noinit(domain_name);
373 if (domain == NULL) {
374 DEBUG(3, ("Authentication for domain [%s] skipped "
375 "as it is not a trusted domain\n",
376 domain_name));
377 } else {
378 return domain;
382 return find_our_domain();
385 static void fill_in_password_policy(struct winbindd_response *r,
386 const struct samr_DomInfo1 *p)
388 r->data.auth.policy.min_length_password =
389 p->min_password_length;
390 r->data.auth.policy.password_history =
391 p->password_history_length;
392 r->data.auth.policy.password_properties =
393 p->password_properties;
394 r->data.auth.policy.expire =
395 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
396 r->data.auth.policy.min_passwordage =
397 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
400 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
401 struct winbindd_response *response)
403 TALLOC_CTX *frame = talloc_stackframe();
404 struct winbindd_methods *methods;
405 NTSTATUS status;
406 struct samr_DomInfo1 password_policy;
408 if ( !winbindd_can_contact_domain( domain ) ) {
409 DEBUG(5,("fillup_password_policy: No inbound trust to "
410 "contact domain %s\n", domain->name));
411 status = NT_STATUS_NOT_SUPPORTED;
412 goto done;
415 methods = domain->methods;
417 status = methods->password_policy(domain, talloc_tos(), &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 *lockout_threshold)
433 struct winbindd_methods *methods;
434 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
435 struct samr_DomInfo12 lockout_policy;
437 *lockout_threshold = 0;
439 methods = domain->methods;
441 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
442 if (NT_STATUS_IS_ERR(status)) {
443 return status;
446 *lockout_threshold = lockout_policy.lockout_threshold;
448 return NT_STATUS_OK;
451 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
452 TALLOC_CTX *mem_ctx,
453 uint32 *password_properties)
455 struct winbindd_methods *methods;
456 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
457 struct samr_DomInfo1 password_policy;
459 *password_properties = 0;
461 methods = domain->methods;
463 status = methods->password_policy(domain, mem_ctx, &password_policy);
464 if (NT_STATUS_IS_ERR(status)) {
465 return status;
468 *password_properties = password_policy.password_properties;
470 return NT_STATUS_OK;
473 #ifdef HAVE_KRB5
475 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
476 const char *type,
477 uid_t uid,
478 const char **user_ccache_file)
480 /* accept FILE and WRFILE as krb5_cc_type from the client and then
481 * build the full ccname string based on the user's uid here -
482 * Guenther*/
484 const char *gen_cc = NULL;
486 if (uid != -1) {
487 if (strequal(type, "FILE")) {
488 gen_cc = talloc_asprintf(
489 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
491 if (strequal(type, "WRFILE")) {
492 gen_cc = talloc_asprintf(
493 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
495 if (strequal(type, "KEYRING")) {
496 gen_cc = talloc_asprintf(
497 mem_ctx, "KEYRING:persistent:%d", uid);
500 if (strnequal(type, "FILE:/", 6) ||
501 strnequal(type, "WRFILE:/", 8) ||
502 strnequal(type, "DIR:/", 5)) {
504 /* we allow only one "%u" substitution */
506 char *p;
508 p = strchr(type, '%');
509 if (p != NULL) {
511 p++;
513 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
514 gen_cc = talloc_asprintf(mem_ctx, type, uid);
520 *user_ccache_file = gen_cc;
522 if (gen_cc == NULL) {
523 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
525 if (gen_cc == NULL) {
526 DEBUG(0,("out of memory\n"));
527 return NULL;
530 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
531 (*user_ccache_file == NULL) ? " (internal)":""));
533 return gen_cc;
536 #endif
538 uid_t get_uid_from_request(struct winbindd_request *request)
540 uid_t uid;
542 uid = request->data.auth.uid;
544 if (uid < 0) {
545 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
546 return -1;
548 return uid;
551 /**********************************************************************
552 Authenticate a user with a clear text password using Kerberos and fill up
553 ccache if required
554 **********************************************************************/
556 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
557 struct winbindd_domain *domain,
558 const char *user,
559 const char *pass,
560 const char *krb5_cc_type,
561 uid_t uid,
562 struct netr_SamInfo3 **info3,
563 fstring krb5ccname)
565 #ifdef HAVE_KRB5
566 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
567 krb5_error_code krb5_ret;
568 const char *cc = NULL;
569 const char *principal_s = NULL;
570 const char *service = NULL;
571 char *realm = NULL;
572 fstring name_domain, name_user;
573 time_t ticket_lifetime = 0;
574 time_t renewal_until = 0;
575 ADS_STRUCT *ads;
576 time_t time_offset = 0;
577 const char *user_ccache_file;
578 struct PAC_LOGON_INFO *logon_info = NULL;
579 const char *local_service;
581 *info3 = NULL;
583 if (domain->alt_name == NULL) {
584 return NT_STATUS_INVALID_PARAMETER;
587 /* 1st step:
588 * prepare a krb5_cc_cache string for the user */
590 if (uid == -1) {
591 DEBUG(0,("no valid uid\n"));
594 cc = generate_krb5_ccache(mem_ctx,
595 krb5_cc_type,
596 uid,
597 &user_ccache_file);
598 if (cc == NULL) {
599 return NT_STATUS_NO_MEMORY;
603 /* 2nd step:
604 * get kerberos properties */
606 if (domain->private_data) {
607 ads = (ADS_STRUCT *)domain->private_data;
608 time_offset = ads->auth.time_offset;
612 /* 3rd step:
613 * do kerberos auth and setup ccache as the user */
615 parse_domain_user(user, name_domain, name_user);
617 realm = talloc_strdup(mem_ctx, domain->alt_name);
618 if (realm == NULL) {
619 return NT_STATUS_NO_MEMORY;
622 if (!strupper_m(realm)) {
623 return NT_STATUS_INVALID_PARAMETER;
626 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
627 if (principal_s == NULL) {
628 return NT_STATUS_NO_MEMORY;
631 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
632 if (service == NULL) {
633 return NT_STATUS_NO_MEMORY;
636 local_service = talloc_asprintf(mem_ctx, "%s$@%s",
637 lp_netbios_name(), lp_realm());
638 if (local_service == NULL) {
639 return NT_STATUS_NO_MEMORY;
643 /* if this is a user ccache, we need to act as the user to let the krb5
644 * library handle the chown, etc. */
646 /************************ ENTERING NON-ROOT **********************/
648 if (user_ccache_file != NULL) {
649 set_effective_uid(uid);
650 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
653 result = kerberos_return_pac(mem_ctx,
654 principal_s,
655 pass,
656 time_offset,
657 &ticket_lifetime,
658 &renewal_until,
660 true,
661 true,
662 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
663 NULL,
664 local_service,
665 &logon_info);
666 if (user_ccache_file != NULL) {
667 gain_root_privilege();
670 /************************ RETURNED TO ROOT **********************/
672 if (!NT_STATUS_IS_OK(result)) {
673 goto failed;
676 *info3 = &logon_info->info3;
678 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
679 principal_s));
681 /* if we had a user's ccache then return that string for the pam
682 * environment */
684 if (user_ccache_file != NULL) {
686 fstrcpy(krb5ccname, user_ccache_file);
688 result = add_ccache_to_list(principal_s,
690 service,
691 user,
692 pass,
693 realm,
694 uid,
695 time(NULL),
696 ticket_lifetime,
697 renewal_until,
698 false);
700 if (!NT_STATUS_IS_OK(result)) {
701 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
702 nt_errstr(result)));
704 } else {
706 /* need to delete the memory cred cache, it is not used anymore */
708 krb5_ret = ads_kdestroy(cc);
709 if (krb5_ret) {
710 DEBUG(3,("winbindd_raw_kerberos_login: "
711 "could not destroy krb5 credential cache: "
712 "%s\n", error_message(krb5_ret)));
717 return NT_STATUS_OK;
719 failed:
721 * Do not delete an existing valid credential cache, if the user
722 * e.g. enters a wrong password
724 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
725 && user_ccache_file != NULL) {
726 return result;
729 /* we could have created a new credential cache with a valid tgt in it
730 * but we werent able to get or verify the service ticket for this
731 * local host and therefor didn't get the PAC, we need to remove that
732 * cache entirely now */
734 krb5_ret = ads_kdestroy(cc);
735 if (krb5_ret) {
736 DEBUG(3,("winbindd_raw_kerberos_login: "
737 "could not destroy krb5 credential cache: "
738 "%s\n", error_message(krb5_ret)));
741 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
742 DEBUG(3,("winbindd_raw_kerberos_login: "
743 "could not remove ccache for user %s\n",
744 user));
747 return result;
748 #else
749 return NT_STATUS_NOT_SUPPORTED;
750 #endif /* HAVE_KRB5 */
753 /****************************************************************
754 ****************************************************************/
756 bool check_request_flags(uint32_t flags)
758 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
759 WBFLAG_PAM_INFO3_TEXT |
760 WBFLAG_PAM_INFO3_NDR;
762 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
763 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
764 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
765 !(flags & flags_edata) ) {
766 return true;
769 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
770 flags));
772 return false;
775 /****************************************************************
776 ****************************************************************/
778 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
779 struct winbindd_response *resp,
780 uint32_t request_flags,
781 struct netr_SamInfo3 *info3,
782 const char *name_domain,
783 const char *name_user)
785 NTSTATUS result;
787 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
788 memcpy(resp->data.auth.user_session_key,
789 info3->base.key.key,
790 sizeof(resp->data.auth.user_session_key)
791 /* 16 */);
794 if (request_flags & WBFLAG_PAM_LMKEY) {
795 memcpy(resp->data.auth.first_8_lm_hash,
796 info3->base.LMSessKey.key,
797 sizeof(resp->data.auth.first_8_lm_hash)
798 /* 8 */);
801 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
802 result = append_unix_username(mem_ctx, resp,
803 info3, name_domain, name_user);
804 if (!NT_STATUS_IS_OK(result)) {
805 DEBUG(10,("Failed to append Unix Username: %s\n",
806 nt_errstr(result)));
807 return result;
811 /* currently, anything from here on potentially overwrites extra_data. */
813 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
814 result = append_info3_as_ndr(mem_ctx, resp, info3);
815 if (!NT_STATUS_IS_OK(result)) {
816 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
817 nt_errstr(result)));
818 return result;
822 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
823 result = append_info3_as_txt(mem_ctx, resp, info3);
824 if (!NT_STATUS_IS_OK(result)) {
825 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
826 nt_errstr(result)));
827 return result;
831 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
832 result = append_afs_token(mem_ctx, resp,
833 info3, name_domain, name_user);
834 if (!NT_STATUS_IS_OK(result)) {
835 DEBUG(10,("Failed to append AFS token: %s\n",
836 nt_errstr(result)));
837 return result;
841 return NT_STATUS_OK;
844 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
845 struct winbindd_cli_state *state,
846 struct netr_SamInfo3 **info3)
848 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
849 uint16 max_allowed_bad_attempts;
850 fstring name_domain, name_user;
851 struct dom_sid sid;
852 enum lsa_SidType type;
853 uchar new_nt_pass[NT_HASH_LEN];
854 const uint8 *cached_nt_pass;
855 const uint8 *cached_salt;
856 struct netr_SamInfo3 *my_info3;
857 time_t kickoff_time, must_change_time;
858 bool password_good = false;
859 #ifdef HAVE_KRB5
860 struct winbindd_tdc_domain *tdc_domain = NULL;
861 #endif
863 *info3 = NULL;
865 ZERO_STRUCTP(info3);
867 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
869 /* Parse domain and username */
871 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
874 if (!lookup_cached_name(name_domain,
875 name_user,
876 &sid,
877 &type)) {
878 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
879 return NT_STATUS_NO_SUCH_USER;
882 if (type != SID_NAME_USER) {
883 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
884 return NT_STATUS_LOGON_FAILURE;
887 result = winbindd_get_creds(domain,
888 state->mem_ctx,
889 &sid,
890 &my_info3,
891 &cached_nt_pass,
892 &cached_salt);
893 if (!NT_STATUS_IS_OK(result)) {
894 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
895 return result;
898 *info3 = my_info3;
900 E_md4hash(state->request->data.auth.pass, new_nt_pass);
902 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
903 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
904 if (cached_salt) {
905 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
908 if (cached_salt) {
909 /* In this case we didn't store the nt_hash itself,
910 but the MD5 combination of salt + nt_hash. */
911 uchar salted_hash[NT_HASH_LEN];
912 E_md5hash(cached_salt, new_nt_pass, salted_hash);
914 password_good = (memcmp(cached_nt_pass, salted_hash,
915 NT_HASH_LEN) == 0);
916 } else {
917 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
918 password_good = (memcmp(cached_nt_pass, new_nt_pass,
919 NT_HASH_LEN) == 0);
922 if (password_good) {
924 /* User *DOES* know the password, update logon_time and reset
925 * bad_pw_count */
927 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
929 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
930 return NT_STATUS_ACCOUNT_LOCKED_OUT;
933 if (my_info3->base.acct_flags & ACB_DISABLED) {
934 return NT_STATUS_ACCOUNT_DISABLED;
937 if (my_info3->base.acct_flags & ACB_WSTRUST) {
938 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
941 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
942 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
945 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
946 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
949 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
950 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
951 my_info3->base.acct_flags));
952 return NT_STATUS_LOGON_FAILURE;
955 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
956 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
957 return NT_STATUS_ACCOUNT_EXPIRED;
960 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
961 if (must_change_time != 0 && must_change_time < time(NULL)) {
962 /* we allow grace logons when the password has expired */
963 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
964 /* return NT_STATUS_PASSWORD_EXPIRED; */
965 goto success;
968 #ifdef HAVE_KRB5
969 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
970 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
971 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
972 /* used to cope with the case winbindd starting without network. */
973 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
975 uid_t uid = -1;
976 const char *cc = NULL;
977 char *realm = NULL;
978 const char *principal_s = NULL;
979 const char *service = NULL;
980 const char *user_ccache_file;
982 if (domain->alt_name == NULL) {
983 return NT_STATUS_INVALID_PARAMETER;
986 uid = get_uid_from_request(state->request);
987 if (uid == -1) {
988 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
989 return NT_STATUS_INVALID_PARAMETER;
992 cc = generate_krb5_ccache(state->mem_ctx,
993 state->request->data.auth.krb5_cc_type,
994 state->request->data.auth.uid,
995 &user_ccache_file);
996 if (cc == NULL) {
997 return NT_STATUS_NO_MEMORY;
1000 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
1001 if (realm == NULL) {
1002 return NT_STATUS_NO_MEMORY;
1005 if (!strupper_m(realm)) {
1006 return NT_STATUS_INVALID_PARAMETER;
1009 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1010 if (principal_s == NULL) {
1011 return NT_STATUS_NO_MEMORY;
1014 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1015 if (service == NULL) {
1016 return NT_STATUS_NO_MEMORY;
1019 if (user_ccache_file != NULL) {
1021 fstrcpy(state->response->data.auth.krb5ccname,
1022 user_ccache_file);
1024 result = add_ccache_to_list(principal_s,
1026 service,
1027 state->request->data.auth.user,
1028 state->request->data.auth.pass,
1029 realm,
1030 uid,
1031 time(NULL),
1032 time(NULL) + lp_winbind_cache_time(),
1033 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1034 true);
1036 if (!NT_STATUS_IS_OK(result)) {
1037 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1038 "to add ccache to list: %s\n",
1039 nt_errstr(result)));
1043 #endif /* HAVE_KRB5 */
1044 success:
1045 /* FIXME: we possibly should handle logon hours as well (does xp when
1046 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1048 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1049 my_info3->base.bad_password_count = 0;
1051 result = winbindd_update_creds_by_info3(domain,
1052 state->request->data.auth.user,
1053 state->request->data.auth.pass,
1054 my_info3);
1055 if (!NT_STATUS_IS_OK(result)) {
1056 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1057 nt_errstr(result)));
1058 return result;
1061 return NT_STATUS_OK;
1065 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1066 if (domain->online == false) {
1067 goto failed;
1070 /* failure of this is not critical */
1071 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1072 if (!NT_STATUS_IS_OK(result)) {
1073 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1074 "Won't be able to honour account lockout policies\n"));
1077 /* increase counter */
1078 my_info3->base.bad_password_count++;
1080 if (max_allowed_bad_attempts == 0) {
1081 goto failed;
1084 /* lockout user */
1085 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1087 uint32 password_properties;
1089 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1090 if (!NT_STATUS_IS_OK(result)) {
1091 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1094 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1095 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1096 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1100 failed:
1101 result = winbindd_update_creds_by_info3(domain,
1102 state->request->data.auth.user,
1103 NULL,
1104 my_info3);
1106 if (!NT_STATUS_IS_OK(result)) {
1107 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1108 nt_errstr(result)));
1111 return NT_STATUS_LOGON_FAILURE;
1114 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1115 struct winbindd_cli_state *state,
1116 struct netr_SamInfo3 **info3)
1118 struct winbindd_domain *contact_domain;
1119 fstring name_domain, name_user;
1120 NTSTATUS result;
1122 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1124 /* Parse domain and username */
1126 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1128 /* what domain should we contact? */
1130 if ( IS_DC ) {
1131 if (!(contact_domain = find_domain_from_name(name_domain))) {
1132 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1133 state->request->data.auth.user, name_domain, name_user, name_domain));
1134 result = NT_STATUS_NO_SUCH_USER;
1135 goto done;
1138 } else {
1139 if (is_myname(name_domain)) {
1140 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1141 result = NT_STATUS_NO_SUCH_USER;
1142 goto done;
1145 contact_domain = find_domain_from_name(name_domain);
1146 if (contact_domain == NULL) {
1147 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1148 state->request->data.auth.user, name_domain, name_user, name_domain));
1150 result = NT_STATUS_NO_SUCH_USER;
1151 goto done;
1155 if (contact_domain->initialized &&
1156 contact_domain->active_directory) {
1157 goto try_login;
1160 if (!contact_domain->initialized) {
1161 init_dc_connection(contact_domain);
1164 if (!contact_domain->active_directory) {
1165 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1166 return NT_STATUS_INVALID_LOGON_TYPE;
1168 try_login:
1169 result = winbindd_raw_kerberos_login(
1170 state->mem_ctx, contact_domain,
1171 state->request->data.auth.user,
1172 state->request->data.auth.pass,
1173 state->request->data.auth.krb5_cc_type,
1174 get_uid_from_request(state->request),
1175 info3, state->response->data.auth.krb5ccname);
1176 done:
1177 return result;
1180 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1181 uint32_t logon_parameters,
1182 const char *domain, const char *user,
1183 const DATA_BLOB *challenge,
1184 const DATA_BLOB *lm_resp,
1185 const DATA_BLOB *nt_resp,
1186 struct netr_SamInfo3 **pinfo3)
1188 struct auth_usersupplied_info *user_info = NULL;
1189 struct tsocket_address *local;
1190 NTSTATUS status;
1191 int rc;
1193 rc = tsocket_address_inet_from_strings(mem_ctx,
1194 "ip",
1195 "127.0.0.1",
1197 &local);
1198 if (rc < 0) {
1199 return NT_STATUS_NO_MEMORY;
1201 status = make_user_info(&user_info, user, user, domain, domain,
1202 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1203 NULL, AUTH_PASSWORD_RESPONSE);
1204 if (!NT_STATUS_IS_OK(status)) {
1205 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1206 return status;
1208 user_info->logon_parameters = logon_parameters;
1210 /* We don't want any more mapping of the username */
1211 user_info->mapped_state = True;
1213 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1214 pinfo3);
1215 free_user_info(&user_info);
1216 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1217 user, nt_errstr(status)));
1218 return status;
1221 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1222 TALLOC_CTX *mem_ctx,
1223 uint32_t logon_parameters,
1224 const char *server,
1225 const char *username,
1226 const char *domainname,
1227 const char *workstation,
1228 const uint8_t chal[8],
1229 DATA_BLOB lm_response,
1230 DATA_BLOB nt_response,
1231 struct netr_SamInfo3 **info3)
1233 int attempts = 0;
1234 int netr_attempts = 0;
1235 bool retry = false;
1236 NTSTATUS result;
1238 do {
1239 struct rpc_pipe_client *netlogon_pipe;
1240 uint8_t authoritative = 0;
1241 uint32_t flags = 0;
1243 ZERO_STRUCTP(info3);
1244 retry = false;
1246 result = cm_connect_netlogon(domain, &netlogon_pipe);
1248 if (!NT_STATUS_IS_OK(result)) {
1249 DEBUG(3,("Could not open handle to NETLOGON pipe "
1250 "(error: %s, attempts: %d)\n",
1251 nt_errstr(result), netr_attempts));
1253 /* After the first retry always close the connection */
1254 if (netr_attempts > 0) {
1255 DEBUG(3, ("This is again a problem for this "
1256 "particular call, forcing the close "
1257 "of this connection\n"));
1258 invalidate_cm_connection(&domain->conn);
1261 /* After the second retry failover to the next DC */
1262 if (netr_attempts > 1) {
1264 * If the netlogon server is not reachable then
1265 * it is possible that the DC is rebuilding
1266 * sysvol and shutdown netlogon for that time.
1267 * We should failover to the next dc.
1269 DEBUG(3, ("This is the third problem for this "
1270 "particular call, adding DC to the "
1271 "negative cache list\n"));
1272 add_failed_connection_entry(domain->name,
1273 domain->dcname,
1274 result);
1275 saf_delete(domain->name);
1278 /* Only allow 3 retries */
1279 if (netr_attempts < 3) {
1280 DEBUG(3, ("The connection to netlogon "
1281 "failed, retrying\n"));
1282 netr_attempts++;
1283 retry = true;
1284 continue;
1286 return result;
1288 netr_attempts = 0;
1290 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1291 netlogon_pipe->binding_handle,
1292 mem_ctx,
1293 logon_parameters,
1294 username,
1295 domainname,
1296 workstation,
1297 chal,
1298 lm_response,
1299 nt_response,
1300 &authoritative,
1301 &flags,
1302 info3);
1305 * we increment this after the "feature negotiation"
1306 * for can_do_samlogon_ex and can_do_validation6
1308 attempts += 1;
1310 /* We have to try a second time as cm_connect_netlogon
1311 might not yet have noticed that the DC has killed
1312 our connection. */
1314 if (!rpccli_is_connected(netlogon_pipe)) {
1315 retry = true;
1316 continue;
1319 /* if we get access denied, a possible cause was that we had
1320 and open connection to the DC, but someone changed our
1321 machine account password out from underneath us using 'net
1322 rpc changetrustpw' */
1324 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1325 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1326 "ACCESS_DENIED. Maybe the trust account "
1327 "password was changed and we didn't know it. "
1328 "Killing connections to domain %s\n",
1329 domainname));
1330 invalidate_cm_connection(&domain->conn);
1331 retry = true;
1334 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1336 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1337 * (no Ex). This happens against old Samba
1338 * DCs, if LogonSamLogonEx() fails with an error
1339 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1341 * The server will log something like this:
1342 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1344 * This sets the whole connection into a fault_state mode
1345 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1347 * This also happens to our retry with LogonSamLogonWithFlags()
1348 * and LogonSamLogon().
1350 * In order to recover from this situation, we need to
1351 * drop the connection.
1353 invalidate_cm_connection(&domain->conn);
1354 result = NT_STATUS_LOGON_FAILURE;
1355 break;
1358 } while ( (attempts < 2) && retry );
1360 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1361 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1362 "returned NT_STATUS_IO_TIMEOUT after the retry."
1363 "Killing connections to domain %s\n",
1364 domainname));
1365 invalidate_cm_connection(&domain->conn);
1367 return result;
1370 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1371 struct winbindd_domain *domain,
1372 const char *user,
1373 const char *pass,
1374 uint32_t request_flags,
1375 struct netr_SamInfo3 **info3)
1378 uchar chal[8];
1379 DATA_BLOB lm_resp;
1380 DATA_BLOB nt_resp;
1381 unsigned char local_nt_response[24];
1382 fstring name_domain, name_user;
1383 NTSTATUS result;
1384 struct netr_SamInfo3 *my_info3 = NULL;
1386 *info3 = NULL;
1388 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1390 /* Parse domain and username */
1392 parse_domain_user(user, name_domain, name_user);
1394 /* do password magic */
1396 generate_random_buffer(chal, sizeof(chal));
1398 if (lp_client_ntlmv2_auth()) {
1399 DATA_BLOB server_chal;
1400 DATA_BLOB names_blob;
1401 server_chal = data_blob_const(chal, 8);
1403 /* note that the 'workgroup' here is for the local
1404 machine. The 'server name' must match the
1405 'workstation' passed to the actual SamLogon call.
1407 names_blob = NTLMv2_generate_names_blob(
1408 mem_ctx, lp_netbios_name(), lp_workgroup());
1410 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1411 pass,
1412 &server_chal,
1413 &names_blob,
1414 &lm_resp, &nt_resp, NULL, NULL)) {
1415 data_blob_free(&names_blob);
1416 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1417 result = NT_STATUS_NO_MEMORY;
1418 goto done;
1420 data_blob_free(&names_blob);
1421 } else {
1422 lm_resp = data_blob_null;
1423 SMBNTencrypt(pass, chal, local_nt_response);
1425 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1426 sizeof(local_nt_response));
1429 if (strequal(name_domain, get_global_sam_name())) {
1430 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1432 result = winbindd_dual_auth_passdb(
1433 mem_ctx, 0, name_domain, name_user,
1434 &chal_blob, &lm_resp, &nt_resp, info3);
1435 goto done;
1438 /* check authentication loop */
1440 result = winbind_samlogon_retry_loop(domain,
1441 mem_ctx,
1443 domain->dcname,
1444 name_user,
1445 name_domain,
1446 lp_netbios_name(),
1447 chal,
1448 lm_resp,
1449 nt_resp,
1450 &my_info3);
1451 if (!NT_STATUS_IS_OK(result)) {
1452 goto done;
1455 /* handle the case where a NT4 DC does not fill in the acct_flags in
1456 * the samlogon reply info3. When accurate info3 is required by the
1457 * caller, we look up the account flags ourselve - gd */
1459 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1460 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1462 struct rpc_pipe_client *samr_pipe;
1463 struct policy_handle samr_domain_handle, user_pol;
1464 union samr_UserInfo *info = NULL;
1465 NTSTATUS status_tmp, result_tmp;
1466 uint32 acct_flags;
1467 struct dcerpc_binding_handle *b;
1469 status_tmp = cm_connect_sam(domain, mem_ctx,
1470 &samr_pipe, &samr_domain_handle);
1472 if (!NT_STATUS_IS_OK(status_tmp)) {
1473 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1474 nt_errstr(status_tmp)));
1475 goto done;
1478 b = samr_pipe->binding_handle;
1480 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1481 &samr_domain_handle,
1482 MAXIMUM_ALLOWED_ACCESS,
1483 my_info3->base.rid,
1484 &user_pol,
1485 &result_tmp);
1487 if (!NT_STATUS_IS_OK(status_tmp)) {
1488 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1489 nt_errstr(status_tmp)));
1490 goto done;
1492 if (!NT_STATUS_IS_OK(result_tmp)) {
1493 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1494 nt_errstr(result_tmp)));
1495 goto done;
1498 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1499 &user_pol,
1501 &info,
1502 &result_tmp);
1504 if (!NT_STATUS_IS_OK(status_tmp)) {
1505 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1506 nt_errstr(status_tmp)));
1507 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1508 goto done;
1510 if (!NT_STATUS_IS_OK(result_tmp)) {
1511 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1512 nt_errstr(result_tmp)));
1513 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1514 goto done;
1517 acct_flags = info->info16.acct_flags;
1519 if (acct_flags == 0) {
1520 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1521 goto done;
1524 my_info3->base.acct_flags = acct_flags;
1526 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1528 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1531 *info3 = my_info3;
1532 done:
1533 return result;
1536 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1537 struct winbindd_cli_state *state)
1539 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1540 NTSTATUS krb5_result = NT_STATUS_OK;
1541 fstring name_domain, name_user;
1542 char *mapped_user;
1543 fstring domain_user;
1544 struct netr_SamInfo3 *info3 = NULL;
1545 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1547 /* Ensure null termination */
1548 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1550 /* Ensure null termination */
1551 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1553 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1554 state->request->data.auth.user));
1556 /* Parse domain and username */
1558 name_map_status = normalize_name_unmap(state->mem_ctx,
1559 state->request->data.auth.user,
1560 &mapped_user);
1562 /* If the name normalization didnt' actually do anything,
1563 just use the original name */
1565 if (!NT_STATUS_IS_OK(name_map_status) &&
1566 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1568 mapped_user = state->request->data.auth.user;
1571 parse_domain_user(mapped_user, name_domain, name_user);
1573 if ( mapped_user != state->request->data.auth.user ) {
1574 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1575 *lp_winbind_separator(),
1576 name_user );
1577 strlcpy( state->request->data.auth.user, domain_user,
1578 sizeof(state->request->data.auth.user));
1581 if (!domain->online) {
1582 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1583 if (domain->startup) {
1584 /* Logons are very important to users. If we're offline and
1585 we get a request within the first 30 seconds of startup,
1586 try very hard to find a DC and go online. */
1588 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1589 "request in startup mode.\n", domain->name ));
1591 winbindd_flush_negative_conn_cache(domain);
1592 result = init_dc_connection(domain);
1596 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1598 /* Check for Kerberos authentication */
1599 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1601 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1602 /* save for later */
1603 krb5_result = result;
1606 if (NT_STATUS_IS_OK(result)) {
1607 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1608 goto process_result;
1609 } else {
1610 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1613 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1614 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1615 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1616 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1617 set_domain_offline( domain );
1618 goto cached_logon;
1621 /* there are quite some NT_STATUS errors where there is no
1622 * point in retrying with a samlogon, we explictly have to take
1623 * care not to increase the bad logon counter on the DC */
1625 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1626 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1627 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1628 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1629 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1630 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1631 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1632 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1633 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1634 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1635 goto done;
1638 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1639 DEBUG(3,("falling back to samlogon\n"));
1640 goto sam_logon;
1641 } else {
1642 goto cached_logon;
1646 sam_logon:
1647 /* Check for Samlogon authentication */
1648 if (domain->online) {
1649 result = winbindd_dual_pam_auth_samlogon(
1650 state->mem_ctx, domain,
1651 state->request->data.auth.user,
1652 state->request->data.auth.pass,
1653 state->request->flags,
1654 &info3);
1656 if (NT_STATUS_IS_OK(result)) {
1657 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1658 /* add the Krb5 err if we have one */
1659 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1660 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1662 goto process_result;
1665 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1666 nt_errstr(result)));
1668 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1669 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1670 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1672 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1673 set_domain_offline( domain );
1674 goto cached_logon;
1677 if (domain->online) {
1678 /* We're still online - fail. */
1679 goto done;
1683 cached_logon:
1684 /* Check for Cached logons */
1685 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1686 lp_winbind_offline_logon()) {
1688 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1690 if (NT_STATUS_IS_OK(result)) {
1691 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1692 goto process_result;
1693 } else {
1694 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1695 goto done;
1699 process_result:
1701 if (NT_STATUS_IS_OK(result)) {
1703 struct dom_sid user_sid;
1705 /* In all codepaths where result == NT_STATUS_OK info3 must have
1706 been initialized. */
1707 if (!info3) {
1708 result = NT_STATUS_INTERNAL_ERROR;
1709 goto done;
1712 sid_compose(&user_sid, info3->base.domain_sid,
1713 info3->base.rid);
1715 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1716 &user_sid);
1717 netsamlogon_cache_store(name_user, info3);
1719 /* save name_to_sid info as early as possible (only if
1720 this is our primary domain so we don't invalidate
1721 the cache entry by storing the seq_num for the wrong
1722 domain). */
1723 if ( domain->primary ) {
1724 cache_name2sid(domain, name_domain, name_user,
1725 SID_NAME_USER, &user_sid);
1728 /* Check if the user is in the right group */
1730 result = check_info3_in_group(
1731 info3,
1732 state->request->data.auth.require_membership_of_sid);
1733 if (!NT_STATUS_IS_OK(result)) {
1734 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1735 state->request->data.auth.user,
1736 state->request->data.auth.require_membership_of_sid));
1737 goto done;
1740 result = append_auth_data(state->mem_ctx, state->response,
1741 state->request->flags, info3,
1742 name_domain, name_user);
1743 if (!NT_STATUS_IS_OK(result)) {
1744 goto done;
1747 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1748 && lp_winbind_offline_logon()) {
1750 result = winbindd_store_creds(domain,
1751 state->request->data.auth.user,
1752 state->request->data.auth.pass,
1753 info3);
1756 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1757 struct winbindd_domain *our_domain = find_our_domain();
1759 /* This is not entirely correct I believe, but it is
1760 consistent. Only apply the password policy settings
1761 too warn users for our own domain. Cannot obtain these
1762 from trusted DCs all the time so don't do it at all.
1763 -- jerry */
1765 result = NT_STATUS_NOT_SUPPORTED;
1766 if (our_domain == domain ) {
1767 result = fillup_password_policy(
1768 our_domain, state->response);
1771 if (!NT_STATUS_IS_OK(result)
1772 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1774 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1775 domain->name, nt_errstr(result)));
1776 goto done;
1780 result = NT_STATUS_OK;
1783 done:
1784 /* give us a more useful (more correct?) error code */
1785 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1786 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1787 result = NT_STATUS_NO_LOGON_SERVERS;
1790 set_auth_errors(state->response, result);
1792 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1793 state->request->data.auth.user,
1794 state->response->data.auth.nt_status_string,
1795 state->response->data.auth.pam_error));
1797 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1800 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1801 struct winbindd_cli_state *state)
1803 NTSTATUS result;
1804 struct netr_SamInfo3 *info3 = NULL;
1805 const char *name_user = NULL;
1806 const char *name_domain = NULL;
1807 const char *workstation;
1809 DATA_BLOB lm_resp, nt_resp;
1811 /* This is child-only, so no check for privileged access is needed
1812 anymore */
1814 /* Ensure null termination */
1815 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1816 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1818 name_user = state->request->data.auth_crap.user;
1819 name_domain = state->request->data.auth_crap.domain;
1820 workstation = state->request->data.auth_crap.workstation;
1822 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1823 name_domain, name_user));
1825 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1826 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1827 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1828 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1829 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1830 state->request->data.auth_crap.lm_resp_len,
1831 state->request->data.auth_crap.nt_resp_len));
1832 result = NT_STATUS_INVALID_PARAMETER;
1833 goto done;
1837 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1838 state->request->data.auth_crap.lm_resp_len);
1840 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1841 nt_resp = data_blob_talloc(state->mem_ctx,
1842 state->request->extra_data.data,
1843 state->request->data.auth_crap.nt_resp_len);
1844 } else {
1845 nt_resp = data_blob_talloc(state->mem_ctx,
1846 state->request->data.auth_crap.nt_resp,
1847 state->request->data.auth_crap.nt_resp_len);
1850 if (strequal(name_domain, get_global_sam_name())) {
1851 DATA_BLOB chal_blob = data_blob_const(
1852 state->request->data.auth_crap.chal,
1853 sizeof(state->request->data.auth_crap.chal));
1855 result = winbindd_dual_auth_passdb(
1856 state->mem_ctx,
1857 state->request->data.auth_crap.logon_parameters,
1858 name_domain, name_user,
1859 &chal_blob, &lm_resp, &nt_resp, &info3);
1860 goto process_result;
1863 result = winbind_samlogon_retry_loop(domain,
1864 state->mem_ctx,
1865 state->request->data.auth_crap.logon_parameters,
1866 domain->dcname,
1867 name_user,
1868 name_domain,
1869 /* Bug #3248 - found by Stefan Burkei. */
1870 workstation, /* We carefully set this above so use it... */
1871 state->request->data.auth_crap.chal,
1872 lm_resp,
1873 nt_resp,
1874 &info3);
1875 if (!NT_STATUS_IS_OK(result)) {
1876 goto done;
1879 process_result:
1881 if (NT_STATUS_IS_OK(result)) {
1882 struct dom_sid user_sid;
1884 sid_compose(&user_sid, info3->base.domain_sid,
1885 info3->base.rid);
1886 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1887 &user_sid);
1888 netsamlogon_cache_store(name_user, info3);
1890 /* Check if the user is in the right group */
1892 result = check_info3_in_group(
1893 info3,
1894 state->request->data.auth_crap.require_membership_of_sid);
1895 if (!NT_STATUS_IS_OK(result)) {
1896 DEBUG(3, ("User %s is not in the required group (%s), so "
1897 "crap authentication is rejected\n",
1898 state->request->data.auth_crap.user,
1899 state->request->data.auth_crap.require_membership_of_sid));
1900 goto done;
1903 result = append_auth_data(state->mem_ctx, state->response,
1904 state->request->flags, info3,
1905 name_domain, name_user);
1906 if (!NT_STATUS_IS_OK(result)) {
1907 goto done;
1911 done:
1913 /* give us a more useful (more correct?) error code */
1914 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1915 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1916 result = NT_STATUS_NO_LOGON_SERVERS;
1919 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1920 result = nt_status_squash(result);
1923 set_auth_errors(state->response, result);
1925 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1926 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1927 name_domain,
1928 name_user,
1929 state->response->data.auth.nt_status_string,
1930 state->response->data.auth.pam_error));
1932 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1935 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1936 struct winbindd_cli_state *state)
1938 char *oldpass;
1939 char *newpass = NULL;
1940 struct policy_handle dom_pol;
1941 struct rpc_pipe_client *cli = NULL;
1942 bool got_info = false;
1943 struct samr_DomInfo1 *info = NULL;
1944 struct userPwdChangeFailureInformation *reject = NULL;
1945 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1946 fstring domain, user;
1947 struct dcerpc_binding_handle *b = NULL;
1949 ZERO_STRUCT(dom_pol);
1951 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1952 state->request->data.auth.user));
1954 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1955 goto done;
1958 /* Change password */
1960 oldpass = state->request->data.chauthtok.oldpass;
1961 newpass = state->request->data.chauthtok.newpass;
1963 /* Initialize reject reason */
1964 state->response->data.auth.reject_reason = Undefined;
1966 /* Get sam handle */
1968 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1969 &dom_pol);
1970 if (!NT_STATUS_IS_OK(result)) {
1971 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1972 goto done;
1975 b = cli->binding_handle;
1977 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1978 user,
1979 newpass,
1980 oldpass,
1981 &info,
1982 &reject);
1984 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1986 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1988 fill_in_password_policy(state->response, info);
1990 state->response->data.auth.reject_reason =
1991 reject->extendedFailureReason;
1993 got_info = true;
1996 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1997 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1998 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1999 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2001 /* only fallback when the chgpasswd_user3 call is not supported */
2002 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2003 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2004 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2005 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2007 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2008 nt_errstr(result)));
2010 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2012 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2013 Map to the same status code as Windows 2003. */
2015 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2016 result = NT_STATUS_PASSWORD_RESTRICTION;
2020 done:
2022 if (NT_STATUS_IS_OK(result)
2023 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2024 && lp_winbind_offline_logon()) {
2025 result = winbindd_update_creds_by_name(contact_domain, user,
2026 newpass);
2027 /* Again, this happens when we login from gdm or xdm
2028 * and the password expires, *BUT* cached crendentials
2029 * doesn't exist. winbindd_update_creds_by_name()
2030 * returns NT_STATUS_NO_SUCH_USER.
2031 * This is not a failure.
2032 * --- BoYang
2033 * */
2034 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2035 result = NT_STATUS_OK;
2038 if (!NT_STATUS_IS_OK(result)) {
2039 DEBUG(10, ("Failed to store creds: %s\n",
2040 nt_errstr(result)));
2041 goto process_result;
2045 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2047 NTSTATUS policy_ret;
2049 policy_ret = fillup_password_policy(
2050 contact_domain, state->response);
2052 /* failure of this is non critical, it will just provide no
2053 * additional information to the client why the change has
2054 * failed - Guenther */
2056 if (!NT_STATUS_IS_OK(policy_ret)) {
2057 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2058 goto process_result;
2062 process_result:
2064 if (strequal(contact_domain->name, get_global_sam_name())) {
2065 /* FIXME: internal rpc pipe does not cache handles yet */
2066 if (b) {
2067 if (is_valid_policy_hnd(&dom_pol)) {
2068 NTSTATUS _result;
2069 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2071 TALLOC_FREE(cli);
2075 set_auth_errors(state->response, result);
2077 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2078 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2079 domain,
2080 user,
2081 state->response->data.auth.nt_status_string,
2082 state->response->data.auth.pam_error));
2084 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2087 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2088 struct winbindd_cli_state *state)
2090 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2092 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2093 state->request->data.logoff.user));
2095 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2096 result = NT_STATUS_OK;
2097 goto process_result;
2100 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2101 result = NT_STATUS_OK;
2102 goto process_result;
2105 #ifdef HAVE_KRB5
2107 if (state->request->data.logoff.uid < 0) {
2108 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2109 goto process_result;
2112 /* what we need here is to find the corresponding krb5 ccache name *we*
2113 * created for a given username and destroy it */
2115 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2116 result = NT_STATUS_OK;
2117 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2118 goto process_result;
2121 if (!ccache_entry_identical(state->request->data.logoff.user,
2122 state->request->data.logoff.uid,
2123 state->request->data.logoff.krb5ccname)) {
2124 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2125 goto process_result;
2128 result = remove_ccache(state->request->data.logoff.user);
2129 if (!NT_STATUS_IS_OK(result)) {
2130 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2131 nt_errstr(result)));
2132 goto process_result;
2136 * Remove any mlock'ed memory creds in the child
2137 * we might be using for krb5 ticket renewal.
2140 winbindd_delete_memory_creds(state->request->data.logoff.user);
2142 #else
2143 result = NT_STATUS_NOT_SUPPORTED;
2144 #endif
2146 process_result:
2149 set_auth_errors(state->response, result);
2151 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2154 /* Change user password with auth crap*/
2156 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2158 NTSTATUS result;
2159 DATA_BLOB new_nt_password;
2160 DATA_BLOB old_nt_hash_enc;
2161 DATA_BLOB new_lm_password;
2162 DATA_BLOB old_lm_hash_enc;
2163 fstring domain,user;
2164 struct policy_handle dom_pol;
2165 struct winbindd_domain *contact_domain = domainSt;
2166 struct rpc_pipe_client *cli = NULL;
2167 struct dcerpc_binding_handle *b = NULL;
2169 ZERO_STRUCT(dom_pol);
2171 /* Ensure null termination */
2172 state->request->data.chng_pswd_auth_crap.user[
2173 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2174 state->request->data.chng_pswd_auth_crap.domain[
2175 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2176 *domain = 0;
2177 *user = 0;
2179 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2180 (unsigned long)state->pid,
2181 state->request->data.chng_pswd_auth_crap.domain,
2182 state->request->data.chng_pswd_auth_crap.user));
2184 if (lp_winbind_offline_logon()) {
2185 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2186 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2187 result = NT_STATUS_ACCESS_DENIED;
2188 goto done;
2191 if (*state->request->data.chng_pswd_auth_crap.domain) {
2192 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2193 } else {
2194 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2195 domain, user);
2197 if(!*domain) {
2198 DEBUG(3,("no domain specified with username (%s) - "
2199 "failing auth\n",
2200 state->request->data.chng_pswd_auth_crap.user));
2201 result = NT_STATUS_NO_SUCH_USER;
2202 goto done;
2206 if (!*domain && lp_winbind_use_default_domain()) {
2207 fstrcpy(domain,lp_workgroup());
2210 if(!*user) {
2211 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2214 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2215 (unsigned long)state->pid, domain, user));
2217 /* Change password */
2218 new_nt_password = data_blob_const(
2219 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2220 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2222 old_nt_hash_enc = data_blob_const(
2223 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2224 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2226 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2227 new_lm_password = data_blob_const(
2228 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2229 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2231 old_lm_hash_enc = data_blob_const(
2232 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2233 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2234 } else {
2235 new_lm_password = data_blob_null;
2236 old_lm_hash_enc = data_blob_null;
2239 /* Get sam handle */
2241 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2242 if (!NT_STATUS_IS_OK(result)) {
2243 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2244 goto done;
2247 b = cli->binding_handle;
2249 result = rpccli_samr_chng_pswd_auth_crap(
2250 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2251 new_lm_password, old_lm_hash_enc);
2253 done:
2255 if (strequal(contact_domain->name, get_global_sam_name())) {
2256 /* FIXME: internal rpc pipe does not cache handles yet */
2257 if (b) {
2258 if (is_valid_policy_hnd(&dom_pol)) {
2259 NTSTATUS _result;
2260 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2262 TALLOC_FREE(cli);
2266 set_auth_errors(state->response, result);
2268 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2269 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2270 domain, user,
2271 state->response->data.auth.nt_status_string,
2272 state->response->data.auth.pam_error));
2274 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2277 #ifdef HAVE_KRB5
2278 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2279 struct PAC_LOGON_INFO **logon_info)
2281 krb5_context krbctx = NULL;
2282 krb5_error_code k5ret;
2283 krb5_keytab keytab;
2284 krb5_kt_cursor cursor;
2285 krb5_keytab_entry entry;
2286 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2288 ZERO_STRUCT(entry);
2289 ZERO_STRUCT(cursor);
2291 k5ret = krb5_init_context(&krbctx);
2292 if (k5ret) {
2293 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2294 error_message(k5ret)));
2295 status = krb5_to_nt_status(k5ret);
2296 goto out;
2299 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2300 if (k5ret) {
2301 DEBUG(1, ("Failed to get keytab: %s\n",
2302 error_message(k5ret)));
2303 status = krb5_to_nt_status(k5ret);
2304 goto out_free;
2307 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2308 if (k5ret) {
2309 DEBUG(1, ("Failed to start seq: %s\n",
2310 error_message(k5ret)));
2311 status = krb5_to_nt_status(k5ret);
2312 goto out_keytab;
2315 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2316 while (k5ret == 0) {
2317 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2318 krbctx, NULL,
2319 KRB5_KT_KEY(&entry), NULL, 0,
2320 logon_info);
2321 if (NT_STATUS_IS_OK(status)) {
2322 break;
2324 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2325 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2328 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2329 if (k5ret) {
2330 DEBUG(1, ("Failed to end seq: %s\n",
2331 error_message(k5ret)));
2333 out_keytab:
2334 k5ret = krb5_kt_close(krbctx, keytab);
2335 if (k5ret) {
2336 DEBUG(1, ("Failed to close keytab: %s\n",
2337 error_message(k5ret)));
2339 out_free:
2340 krb5_free_context(krbctx);
2341 out:
2342 return status;
2345 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2346 struct netr_SamInfo3 **info3)
2348 struct winbindd_request *req = state->request;
2349 DATA_BLOB pac_blob;
2350 struct PAC_LOGON_INFO *logon_info = NULL;
2351 NTSTATUS result;
2353 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2354 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2355 if (!NT_STATUS_IS_OK(result) &&
2356 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2357 DEBUG(1, ("Error during PAC signature verification: %s\n",
2358 nt_errstr(result)));
2359 return result;
2362 if (logon_info) {
2363 /* Signature verification succeeded, trust the PAC */
2364 netsamlogon_cache_store(NULL, &logon_info->info3);
2366 } else {
2367 /* Try without signature verification */
2368 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2369 NULL, NULL, NULL, 0,
2370 &logon_info);
2371 if (!NT_STATUS_IS_OK(result)) {
2372 DEBUG(10, ("Could not extract PAC: %s\n",
2373 nt_errstr(result)));
2374 return result;
2378 *info3 = &logon_info->info3;
2380 return NT_STATUS_OK;
2382 #else /* HAVE_KRB5 */
2383 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2384 struct netr_SamInfo3 **info3)
2386 return NT_STATUS_NO_SUCH_USER;
2388 #endif /* HAVE_KRB5 */