s3-winbind: Do not delete an existing valid credential cache.
[Samba.git] / source3 / winbindd / winbindd_pam.c
blobaed47416ac84b366c515044e95d6833093cd6693
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);
497 *user_ccache_file = gen_cc;
499 if (gen_cc == NULL) {
500 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
502 if (gen_cc == NULL) {
503 DEBUG(0,("out of memory\n"));
504 return NULL;
507 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
508 (*user_ccache_file == NULL) ? " (internal)":""));
510 return gen_cc;
513 #endif
515 uid_t get_uid_from_request(struct winbindd_request *request)
517 uid_t uid;
519 uid = request->data.auth.uid;
521 if (uid < 0) {
522 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
523 return -1;
525 return uid;
528 /**********************************************************************
529 Authenticate a user with a clear text password using Kerberos and fill up
530 ccache if required
531 **********************************************************************/
533 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
534 struct winbindd_domain *domain,
535 const char *user,
536 const char *pass,
537 const char *krb5_cc_type,
538 uid_t uid,
539 struct netr_SamInfo3 **info3,
540 fstring krb5ccname)
542 #ifdef HAVE_KRB5
543 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
544 krb5_error_code krb5_ret;
545 const char *cc = NULL;
546 const char *principal_s = NULL;
547 const char *service = NULL;
548 char *realm = NULL;
549 fstring name_domain, name_user;
550 time_t ticket_lifetime = 0;
551 time_t renewal_until = 0;
552 ADS_STRUCT *ads;
553 time_t time_offset = 0;
554 const char *user_ccache_file;
555 struct PAC_LOGON_INFO *logon_info = NULL;
557 *info3 = NULL;
559 if (domain->alt_name == NULL) {
560 return NT_STATUS_INVALID_PARAMETER;
563 /* 1st step:
564 * prepare a krb5_cc_cache string for the user */
566 if (uid == -1) {
567 DEBUG(0,("no valid uid\n"));
570 cc = generate_krb5_ccache(mem_ctx,
571 krb5_cc_type,
572 uid,
573 &user_ccache_file);
574 if (cc == NULL) {
575 return NT_STATUS_NO_MEMORY;
579 /* 2nd step:
580 * get kerberos properties */
582 if (domain->private_data) {
583 ads = (ADS_STRUCT *)domain->private_data;
584 time_offset = ads->auth.time_offset;
588 /* 3rd step:
589 * do kerberos auth and setup ccache as the user */
591 parse_domain_user(user, name_domain, name_user);
593 realm = talloc_strdup(mem_ctx, domain->alt_name);
594 if (realm == NULL) {
595 return NT_STATUS_NO_MEMORY;
598 if (!strupper_m(realm)) {
599 return NT_STATUS_INVALID_PARAMETER;
602 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
603 if (principal_s == NULL) {
604 return NT_STATUS_NO_MEMORY;
607 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
608 if (service == NULL) {
609 return NT_STATUS_NO_MEMORY;
612 /* if this is a user ccache, we need to act as the user to let the krb5
613 * library handle the chown, etc. */
615 /************************ ENTERING NON-ROOT **********************/
617 if (user_ccache_file != NULL) {
618 set_effective_uid(uid);
619 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
622 result = kerberos_return_pac(mem_ctx,
623 principal_s,
624 pass,
625 time_offset,
626 &ticket_lifetime,
627 &renewal_until,
629 true,
630 true,
631 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
632 NULL,
633 &logon_info);
634 if (user_ccache_file != NULL) {
635 gain_root_privilege();
638 /************************ RETURNED TO ROOT **********************/
640 if (!NT_STATUS_IS_OK(result)) {
641 goto failed;
644 *info3 = &logon_info->info3;
646 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
647 principal_s));
649 /* if we had a user's ccache then return that string for the pam
650 * environment */
652 if (user_ccache_file != NULL) {
654 fstrcpy(krb5ccname, user_ccache_file);
656 result = add_ccache_to_list(principal_s,
658 service,
659 user,
660 pass,
661 realm,
662 uid,
663 time(NULL),
664 ticket_lifetime,
665 renewal_until,
666 false);
668 if (!NT_STATUS_IS_OK(result)) {
669 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
670 nt_errstr(result)));
672 } else {
674 /* need to delete the memory cred cache, it is not used anymore */
676 krb5_ret = ads_kdestroy(cc);
677 if (krb5_ret) {
678 DEBUG(3,("winbindd_raw_kerberos_login: "
679 "could not destroy krb5 credential cache: "
680 "%s\n", error_message(krb5_ret)));
685 return NT_STATUS_OK;
687 failed:
689 * Do not delete an existing valid credential cache, if the user
690 * e.g. enters a wrong password
692 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
693 && user_ccache_file != NULL) {
694 return result;
697 /* we could have created a new credential cache with a valid tgt in it
698 * but we werent able to get or verify the service ticket for this
699 * local host and therefor didn't get the PAC, we need to remove that
700 * cache entirely now */
702 krb5_ret = ads_kdestroy(cc);
703 if (krb5_ret) {
704 DEBUG(3,("winbindd_raw_kerberos_login: "
705 "could not destroy krb5 credential cache: "
706 "%s\n", error_message(krb5_ret)));
709 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
710 DEBUG(3,("winbindd_raw_kerberos_login: "
711 "could not remove ccache for user %s\n",
712 user));
715 return result;
716 #else
717 return NT_STATUS_NOT_SUPPORTED;
718 #endif /* HAVE_KRB5 */
721 /****************************************************************
722 ****************************************************************/
724 bool check_request_flags(uint32_t flags)
726 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
727 WBFLAG_PAM_INFO3_TEXT |
728 WBFLAG_PAM_INFO3_NDR;
730 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
731 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
732 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
733 !(flags & flags_edata) ) {
734 return true;
737 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
738 flags));
740 return false;
743 /****************************************************************
744 ****************************************************************/
746 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
747 struct winbindd_response *resp,
748 uint32_t request_flags,
749 struct netr_SamInfo3 *info3,
750 const char *name_domain,
751 const char *name_user)
753 NTSTATUS result;
755 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
756 memcpy(resp->data.auth.user_session_key,
757 info3->base.key.key,
758 sizeof(resp->data.auth.user_session_key)
759 /* 16 */);
762 if (request_flags & WBFLAG_PAM_LMKEY) {
763 memcpy(resp->data.auth.first_8_lm_hash,
764 info3->base.LMSessKey.key,
765 sizeof(resp->data.auth.first_8_lm_hash)
766 /* 8 */);
769 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
770 result = append_unix_username(mem_ctx, resp,
771 info3, name_domain, name_user);
772 if (!NT_STATUS_IS_OK(result)) {
773 DEBUG(10,("Failed to append Unix Username: %s\n",
774 nt_errstr(result)));
775 return result;
779 /* currently, anything from here on potentially overwrites extra_data. */
781 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
782 result = append_info3_as_ndr(mem_ctx, resp, info3);
783 if (!NT_STATUS_IS_OK(result)) {
784 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
785 nt_errstr(result)));
786 return result;
790 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
791 result = append_info3_as_txt(mem_ctx, resp, info3);
792 if (!NT_STATUS_IS_OK(result)) {
793 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
794 nt_errstr(result)));
795 return result;
799 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
800 result = append_afs_token(mem_ctx, resp,
801 info3, name_domain, name_user);
802 if (!NT_STATUS_IS_OK(result)) {
803 DEBUG(10,("Failed to append AFS token: %s\n",
804 nt_errstr(result)));
805 return result;
809 return NT_STATUS_OK;
812 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
813 struct winbindd_cli_state *state,
814 struct netr_SamInfo3 **info3)
816 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
817 uint16 max_allowed_bad_attempts;
818 fstring name_domain, name_user;
819 struct dom_sid sid;
820 enum lsa_SidType type;
821 uchar new_nt_pass[NT_HASH_LEN];
822 const uint8 *cached_nt_pass;
823 const uint8 *cached_salt;
824 struct netr_SamInfo3 *my_info3;
825 time_t kickoff_time, must_change_time;
826 bool password_good = false;
827 #ifdef HAVE_KRB5
828 struct winbindd_tdc_domain *tdc_domain = NULL;
829 #endif
831 *info3 = NULL;
833 ZERO_STRUCTP(info3);
835 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
837 /* Parse domain and username */
839 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
842 if (!lookup_cached_name(name_domain,
843 name_user,
844 &sid,
845 &type)) {
846 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
847 return NT_STATUS_NO_SUCH_USER;
850 if (type != SID_NAME_USER) {
851 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
852 return NT_STATUS_LOGON_FAILURE;
855 result = winbindd_get_creds(domain,
856 state->mem_ctx,
857 &sid,
858 &my_info3,
859 &cached_nt_pass,
860 &cached_salt);
861 if (!NT_STATUS_IS_OK(result)) {
862 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
863 return result;
866 *info3 = my_info3;
868 E_md4hash(state->request->data.auth.pass, new_nt_pass);
870 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
871 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
872 if (cached_salt) {
873 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
876 if (cached_salt) {
877 /* In this case we didn't store the nt_hash itself,
878 but the MD5 combination of salt + nt_hash. */
879 uchar salted_hash[NT_HASH_LEN];
880 E_md5hash(cached_salt, new_nt_pass, salted_hash);
882 password_good = (memcmp(cached_nt_pass, salted_hash,
883 NT_HASH_LEN) == 0);
884 } else {
885 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
886 password_good = (memcmp(cached_nt_pass, new_nt_pass,
887 NT_HASH_LEN) == 0);
890 if (password_good) {
892 /* User *DOES* know the password, update logon_time and reset
893 * bad_pw_count */
895 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
897 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
898 return NT_STATUS_ACCOUNT_LOCKED_OUT;
901 if (my_info3->base.acct_flags & ACB_DISABLED) {
902 return NT_STATUS_ACCOUNT_DISABLED;
905 if (my_info3->base.acct_flags & ACB_WSTRUST) {
906 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
909 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
910 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
913 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
914 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
917 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
918 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
919 my_info3->base.acct_flags));
920 return NT_STATUS_LOGON_FAILURE;
923 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
924 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
925 return NT_STATUS_ACCOUNT_EXPIRED;
928 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
929 if (must_change_time != 0 && must_change_time < time(NULL)) {
930 /* we allow grace logons when the password has expired */
931 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
932 /* return NT_STATUS_PASSWORD_EXPIRED; */
933 goto success;
936 #ifdef HAVE_KRB5
937 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
938 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
939 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
940 /* used to cope with the case winbindd starting without network. */
941 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
943 uid_t uid = -1;
944 const char *cc = NULL;
945 char *realm = NULL;
946 const char *principal_s = NULL;
947 const char *service = NULL;
948 const char *user_ccache_file;
950 if (domain->alt_name == NULL) {
951 return NT_STATUS_INVALID_PARAMETER;
954 uid = get_uid_from_request(state->request);
955 if (uid == -1) {
956 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
957 return NT_STATUS_INVALID_PARAMETER;
960 cc = generate_krb5_ccache(state->mem_ctx,
961 state->request->data.auth.krb5_cc_type,
962 state->request->data.auth.uid,
963 &user_ccache_file);
964 if (cc == NULL) {
965 return NT_STATUS_NO_MEMORY;
968 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
969 if (realm == NULL) {
970 return NT_STATUS_NO_MEMORY;
973 if (!strupper_m(realm)) {
974 return NT_STATUS_INVALID_PARAMETER;
977 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
978 if (principal_s == NULL) {
979 return NT_STATUS_NO_MEMORY;
982 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
983 if (service == NULL) {
984 return NT_STATUS_NO_MEMORY;
987 if (user_ccache_file != NULL) {
989 fstrcpy(state->response->data.auth.krb5ccname,
990 user_ccache_file);
992 result = add_ccache_to_list(principal_s,
994 service,
995 state->request->data.auth.user,
996 state->request->data.auth.pass,
997 realm,
998 uid,
999 time(NULL),
1000 time(NULL) + lp_winbind_cache_time(),
1001 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1002 true);
1004 if (!NT_STATUS_IS_OK(result)) {
1005 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1006 "to add ccache to list: %s\n",
1007 nt_errstr(result)));
1011 #endif /* HAVE_KRB5 */
1012 success:
1013 /* FIXME: we possibly should handle logon hours as well (does xp when
1014 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1016 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1017 my_info3->base.bad_password_count = 0;
1019 result = winbindd_update_creds_by_info3(domain,
1020 state->request->data.auth.user,
1021 state->request->data.auth.pass,
1022 my_info3);
1023 if (!NT_STATUS_IS_OK(result)) {
1024 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1025 nt_errstr(result)));
1026 return result;
1029 return NT_STATUS_OK;
1033 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1034 if (domain->online == false) {
1035 goto failed;
1038 /* failure of this is not critical */
1039 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1040 if (!NT_STATUS_IS_OK(result)) {
1041 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1042 "Won't be able to honour account lockout policies\n"));
1045 /* increase counter */
1046 my_info3->base.bad_password_count++;
1048 if (max_allowed_bad_attempts == 0) {
1049 goto failed;
1052 /* lockout user */
1053 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1055 uint32 password_properties;
1057 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1058 if (!NT_STATUS_IS_OK(result)) {
1059 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1062 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1063 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1064 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1068 failed:
1069 result = winbindd_update_creds_by_info3(domain,
1070 state->request->data.auth.user,
1071 NULL,
1072 my_info3);
1074 if (!NT_STATUS_IS_OK(result)) {
1075 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1076 nt_errstr(result)));
1079 return NT_STATUS_LOGON_FAILURE;
1082 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1083 struct winbindd_cli_state *state,
1084 struct netr_SamInfo3 **info3)
1086 struct winbindd_domain *contact_domain;
1087 fstring name_domain, name_user;
1088 NTSTATUS result;
1090 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1092 /* Parse domain and username */
1094 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1096 /* what domain should we contact? */
1098 if ( IS_DC ) {
1099 if (!(contact_domain = find_domain_from_name(name_domain))) {
1100 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1101 state->request->data.auth.user, name_domain, name_user, name_domain));
1102 result = NT_STATUS_NO_SUCH_USER;
1103 goto done;
1106 } else {
1107 if (is_myname(name_domain)) {
1108 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1109 result = NT_STATUS_NO_SUCH_USER;
1110 goto done;
1113 contact_domain = find_domain_from_name(name_domain);
1114 if (contact_domain == NULL) {
1115 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1116 state->request->data.auth.user, name_domain, name_user, name_domain));
1118 result = NT_STATUS_NO_SUCH_USER;
1119 goto done;
1123 if (contact_domain->initialized &&
1124 contact_domain->active_directory) {
1125 goto try_login;
1128 if (!contact_domain->initialized) {
1129 init_dc_connection(contact_domain);
1132 if (!contact_domain->active_directory) {
1133 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1134 return NT_STATUS_INVALID_LOGON_TYPE;
1136 try_login:
1137 result = winbindd_raw_kerberos_login(
1138 state->mem_ctx, contact_domain,
1139 state->request->data.auth.user,
1140 state->request->data.auth.pass,
1141 state->request->data.auth.krb5_cc_type,
1142 get_uid_from_request(state->request),
1143 info3, state->response->data.auth.krb5ccname);
1144 done:
1145 return result;
1148 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1149 uint32_t logon_parameters,
1150 const char *domain, const char *user,
1151 const DATA_BLOB *challenge,
1152 const DATA_BLOB *lm_resp,
1153 const DATA_BLOB *nt_resp,
1154 struct netr_SamInfo3 **pinfo3)
1156 struct auth_usersupplied_info *user_info = NULL;
1157 struct tsocket_address *local;
1158 NTSTATUS status;
1159 int rc;
1161 rc = tsocket_address_inet_from_strings(mem_ctx,
1162 "ip",
1163 "127.0.0.1",
1165 &local);
1166 if (rc < 0) {
1167 return NT_STATUS_NO_MEMORY;
1169 status = make_user_info(&user_info, user, user, domain, domain,
1170 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1171 NULL, AUTH_PASSWORD_RESPONSE);
1172 if (!NT_STATUS_IS_OK(status)) {
1173 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1174 return status;
1176 user_info->logon_parameters = logon_parameters;
1178 /* We don't want any more mapping of the username */
1179 user_info->mapped_state = True;
1181 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1182 pinfo3);
1183 free_user_info(&user_info);
1184 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1185 user, nt_errstr(status)));
1186 return status;
1189 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1190 TALLOC_CTX *mem_ctx,
1191 uint32_t logon_parameters,
1192 const char *server,
1193 const char *username,
1194 const char *domainname,
1195 const char *workstation,
1196 const uint8_t chal[8],
1197 DATA_BLOB lm_response,
1198 DATA_BLOB nt_response,
1199 struct netr_SamInfo3 **info3)
1201 int attempts = 0;
1202 int netr_attempts = 0;
1203 bool retry = false;
1204 NTSTATUS result;
1206 do {
1207 struct rpc_pipe_client *netlogon_pipe;
1208 const struct pipe_auth_data *auth;
1209 uint32_t neg_flags = 0;
1211 ZERO_STRUCTP(info3);
1212 retry = false;
1214 result = cm_connect_netlogon(domain, &netlogon_pipe);
1216 if (!NT_STATUS_IS_OK(result)) {
1217 DEBUG(3,("Could not open handle to NETLOGON pipe "
1218 "(error: %s, attempts: %d)\n",
1219 nt_errstr(result), netr_attempts));
1221 /* After the first retry always close the connection */
1222 if (netr_attempts > 0) {
1223 DEBUG(3, ("This is again a problem for this "
1224 "particular call, forcing the close "
1225 "of this connection\n"));
1226 invalidate_cm_connection(&domain->conn);
1229 /* After the second retry failover to the next DC */
1230 if (netr_attempts > 1) {
1232 * If the netlogon server is not reachable then
1233 * it is possible that the DC is rebuilding
1234 * sysvol and shutdown netlogon for that time.
1235 * We should failover to the next dc.
1237 DEBUG(3, ("This is the third problem for this "
1238 "particular call, adding DC to the "
1239 "negative cache list\n"));
1240 add_failed_connection_entry(domain->name,
1241 domain->dcname,
1242 result);
1243 saf_delete(domain->name);
1246 /* Only allow 3 retries */
1247 if (netr_attempts < 3) {
1248 DEBUG(3, ("The connection to netlogon "
1249 "failed, retrying\n"));
1250 netr_attempts++;
1251 retry = true;
1252 continue;
1254 return result;
1256 netr_attempts = 0;
1258 auth = netlogon_pipe->auth;
1259 if (netlogon_pipe->dc) {
1260 neg_flags = netlogon_pipe->dc->negotiate_flags;
1263 /* It is really important to try SamLogonEx here,
1264 * because in a clustered environment, we want to use
1265 * one machine account from multiple physical
1266 * computers.
1268 * With a normal SamLogon call, we must keep the
1269 * credentials chain updated and intact between all
1270 * users of the machine account (which would imply
1271 * cross-node communication for every NTLM logon).
1273 * (The credentials chain is not per NETLOGON pipe
1274 * connection, but globally on the server/client pair
1275 * by machine name).
1277 * When using SamLogonEx, the credentials are not
1278 * supplied, but the session key is implied by the
1279 * wrapping SamLogon context.
1281 * -- abartlet 21 April 2008
1283 * It's also important to use NetlogonValidationSamInfo4 (6),
1284 * because it relies on the rpc transport encryption
1285 * and avoids using the global netlogon schannel
1286 * session key to en/decrypt secret information
1287 * like the user_session_key for network logons.
1289 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1290 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1291 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1292 * are the indication that the server supports
1293 * NetlogonValidationSamInfo4 (6). And it must only
1294 * be used if "SealSecureChannel" is used.
1296 * -- metze 4 February 2011
1299 if (auth == NULL) {
1300 domain->can_do_validation6 = false;
1301 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1302 domain->can_do_validation6 = false;
1303 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1304 domain->can_do_validation6 = false;
1305 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1306 domain->can_do_validation6 = false;
1307 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1308 domain->can_do_validation6 = false;
1311 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1312 result = rpccli_netlogon_sam_network_logon_ex(
1313 netlogon_pipe,
1314 mem_ctx,
1315 logon_parameters,
1316 server, /* server name */
1317 username, /* user name */
1318 domainname, /* target domain */
1319 workstation, /* workstation */
1320 chal,
1322 lm_response,
1323 nt_response,
1324 info3);
1325 } else {
1326 result = rpccli_netlogon_sam_network_logon(
1327 netlogon_pipe,
1328 mem_ctx,
1329 logon_parameters,
1330 server, /* server name */
1331 username, /* user name */
1332 domainname, /* target domain */
1333 workstation, /* workstation */
1334 chal,
1335 domain->can_do_validation6 ? 6 : 3,
1336 lm_response,
1337 nt_response,
1338 info3);
1341 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1344 * It's likely that the server also does not support
1345 * validation level 6
1347 domain->can_do_validation6 = false;
1349 if (domain->can_do_samlogon_ex) {
1350 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1351 "retrying with NetSamLogon\n"));
1352 domain->can_do_samlogon_ex = false;
1353 retry = true;
1354 continue;
1358 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1359 * (no Ex). This happens against old Samba
1360 * DCs. Drop the connection.
1362 invalidate_cm_connection(&domain->conn);
1363 result = NT_STATUS_LOGON_FAILURE;
1364 break;
1367 if (domain->can_do_validation6 &&
1368 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1369 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1370 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1371 DEBUG(3,("Got a DC that can not do validation level 6, "
1372 "retrying with level 3\n"));
1373 domain->can_do_validation6 = false;
1374 retry = true;
1375 continue;
1379 * we increment this after the "feature negotiation"
1380 * for can_do_samlogon_ex and can_do_validation6
1382 attempts += 1;
1384 /* We have to try a second time as cm_connect_netlogon
1385 might not yet have noticed that the DC has killed
1386 our connection. */
1388 if (!rpccli_is_connected(netlogon_pipe)) {
1389 retry = true;
1390 continue;
1393 /* if we get access denied, a possible cause was that we had
1394 and open connection to the DC, but someone changed our
1395 machine account password out from underneath us using 'net
1396 rpc changetrustpw' */
1398 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1399 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1400 "ACCESS_DENIED. Maybe the trust account "
1401 "password was changed and we didn't know it. "
1402 "Killing connections to domain %s\n",
1403 domainname));
1404 invalidate_cm_connection(&domain->conn);
1405 retry = true;
1408 } while ( (attempts < 2) && retry );
1410 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1411 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1412 "returned NT_STATUS_IO_TIMEOUT after the retry."
1413 "Killing connections to domain %s\n",
1414 domainname));
1415 invalidate_cm_connection(&domain->conn);
1417 return result;
1420 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1421 struct winbindd_domain *domain,
1422 const char *user,
1423 const char *pass,
1424 uint32_t request_flags,
1425 struct netr_SamInfo3 **info3)
1428 uchar chal[8];
1429 DATA_BLOB lm_resp;
1430 DATA_BLOB nt_resp;
1431 unsigned char local_nt_response[24];
1432 fstring name_domain, name_user;
1433 NTSTATUS result;
1434 struct netr_SamInfo3 *my_info3 = NULL;
1436 *info3 = NULL;
1438 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1440 /* Parse domain and username */
1442 parse_domain_user(user, name_domain, name_user);
1444 /* do password magic */
1446 generate_random_buffer(chal, sizeof(chal));
1448 if (lp_client_ntlmv2_auth()) {
1449 DATA_BLOB server_chal;
1450 DATA_BLOB names_blob;
1451 server_chal = data_blob_const(chal, 8);
1453 /* note that the 'workgroup' here is for the local
1454 machine. The 'server name' must match the
1455 'workstation' passed to the actual SamLogon call.
1457 names_blob = NTLMv2_generate_names_blob(
1458 mem_ctx, lp_netbios_name(), lp_workgroup());
1460 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1461 pass,
1462 &server_chal,
1463 &names_blob,
1464 &lm_resp, &nt_resp, NULL, NULL)) {
1465 data_blob_free(&names_blob);
1466 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1467 result = NT_STATUS_NO_MEMORY;
1468 goto done;
1470 data_blob_free(&names_blob);
1471 } else {
1472 lm_resp = data_blob_null;
1473 SMBNTencrypt(pass, chal, local_nt_response);
1475 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1476 sizeof(local_nt_response));
1479 if (strequal(name_domain, get_global_sam_name())) {
1480 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1482 result = winbindd_dual_auth_passdb(
1483 mem_ctx, 0, name_domain, name_user,
1484 &chal_blob, &lm_resp, &nt_resp, info3);
1485 goto done;
1488 /* check authentication loop */
1490 result = winbind_samlogon_retry_loop(domain,
1491 mem_ctx,
1493 domain->dcname,
1494 name_user,
1495 name_domain,
1496 lp_netbios_name(),
1497 chal,
1498 lm_resp,
1499 nt_resp,
1500 &my_info3);
1501 if (!NT_STATUS_IS_OK(result)) {
1502 goto done;
1505 /* handle the case where a NT4 DC does not fill in the acct_flags in
1506 * the samlogon reply info3. When accurate info3 is required by the
1507 * caller, we look up the account flags ourselve - gd */
1509 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1510 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1512 struct rpc_pipe_client *samr_pipe;
1513 struct policy_handle samr_domain_handle, user_pol;
1514 union samr_UserInfo *info = NULL;
1515 NTSTATUS status_tmp, result_tmp;
1516 uint32 acct_flags;
1517 struct dcerpc_binding_handle *b;
1519 status_tmp = cm_connect_sam(domain, mem_ctx,
1520 &samr_pipe, &samr_domain_handle);
1522 if (!NT_STATUS_IS_OK(status_tmp)) {
1523 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1524 nt_errstr(status_tmp)));
1525 goto done;
1528 b = samr_pipe->binding_handle;
1530 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1531 &samr_domain_handle,
1532 MAXIMUM_ALLOWED_ACCESS,
1533 my_info3->base.rid,
1534 &user_pol,
1535 &result_tmp);
1537 if (!NT_STATUS_IS_OK(status_tmp)) {
1538 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1539 nt_errstr(status_tmp)));
1540 goto done;
1542 if (!NT_STATUS_IS_OK(result_tmp)) {
1543 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1544 nt_errstr(result_tmp)));
1545 goto done;
1548 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1549 &user_pol,
1551 &info,
1552 &result_tmp);
1554 if (!NT_STATUS_IS_OK(status_tmp)) {
1555 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1556 nt_errstr(status_tmp)));
1557 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1558 goto done;
1560 if (!NT_STATUS_IS_OK(result_tmp)) {
1561 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1562 nt_errstr(result_tmp)));
1563 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1564 goto done;
1567 acct_flags = info->info16.acct_flags;
1569 if (acct_flags == 0) {
1570 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1571 goto done;
1574 my_info3->base.acct_flags = acct_flags;
1576 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1578 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1581 *info3 = my_info3;
1582 done:
1583 return result;
1586 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1587 struct winbindd_cli_state *state)
1589 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1590 NTSTATUS krb5_result = NT_STATUS_OK;
1591 fstring name_domain, name_user;
1592 char *mapped_user;
1593 fstring domain_user;
1594 struct netr_SamInfo3 *info3 = NULL;
1595 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1597 /* Ensure null termination */
1598 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1600 /* Ensure null termination */
1601 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1603 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1604 state->request->data.auth.user));
1606 /* Parse domain and username */
1608 name_map_status = normalize_name_unmap(state->mem_ctx,
1609 state->request->data.auth.user,
1610 &mapped_user);
1612 /* If the name normalization didnt' actually do anything,
1613 just use the original name */
1615 if (!NT_STATUS_IS_OK(name_map_status) &&
1616 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1618 mapped_user = state->request->data.auth.user;
1621 parse_domain_user(mapped_user, name_domain, name_user);
1623 if ( mapped_user != state->request->data.auth.user ) {
1624 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1625 *lp_winbind_separator(),
1626 name_user );
1627 strlcpy( state->request->data.auth.user, domain_user,
1628 sizeof(state->request->data.auth.user));
1631 if (!domain->online) {
1632 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1633 if (domain->startup) {
1634 /* Logons are very important to users. If we're offline and
1635 we get a request within the first 30 seconds of startup,
1636 try very hard to find a DC and go online. */
1638 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1639 "request in startup mode.\n", domain->name ));
1641 winbindd_flush_negative_conn_cache(domain);
1642 result = init_dc_connection(domain);
1646 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1648 /* Check for Kerberos authentication */
1649 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1651 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1652 /* save for later */
1653 krb5_result = result;
1656 if (NT_STATUS_IS_OK(result)) {
1657 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1658 goto process_result;
1659 } else {
1660 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1663 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1664 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1665 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1666 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1667 set_domain_offline( domain );
1668 goto cached_logon;
1671 /* there are quite some NT_STATUS errors where there is no
1672 * point in retrying with a samlogon, we explictly have to take
1673 * care not to increase the bad logon counter on the DC */
1675 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1676 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1677 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1678 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1679 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1680 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1681 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1682 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1683 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1684 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1685 goto done;
1688 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1689 DEBUG(3,("falling back to samlogon\n"));
1690 goto sam_logon;
1691 } else {
1692 goto cached_logon;
1696 sam_logon:
1697 /* Check for Samlogon authentication */
1698 if (domain->online) {
1699 result = winbindd_dual_pam_auth_samlogon(
1700 state->mem_ctx, domain,
1701 state->request->data.auth.user,
1702 state->request->data.auth.pass,
1703 state->request->flags,
1704 &info3);
1706 if (NT_STATUS_IS_OK(result)) {
1707 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1708 /* add the Krb5 err if we have one */
1709 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1710 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1712 goto process_result;
1715 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1716 nt_errstr(result)));
1718 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1719 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1720 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1722 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1723 set_domain_offline( domain );
1724 goto cached_logon;
1727 if (domain->online) {
1728 /* We're still online - fail. */
1729 goto done;
1733 cached_logon:
1734 /* Check for Cached logons */
1735 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1736 lp_winbind_offline_logon()) {
1738 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1740 if (NT_STATUS_IS_OK(result)) {
1741 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1742 goto process_result;
1743 } else {
1744 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1745 goto done;
1749 process_result:
1751 if (NT_STATUS_IS_OK(result)) {
1753 struct dom_sid user_sid;
1755 /* In all codepaths where result == NT_STATUS_OK info3 must have
1756 been initialized. */
1757 if (!info3) {
1758 result = NT_STATUS_INTERNAL_ERROR;
1759 goto done;
1762 sid_compose(&user_sid, info3->base.domain_sid,
1763 info3->base.rid);
1765 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1766 &user_sid);
1767 netsamlogon_cache_store(name_user, info3);
1769 /* save name_to_sid info as early as possible (only if
1770 this is our primary domain so we don't invalidate
1771 the cache entry by storing the seq_num for the wrong
1772 domain). */
1773 if ( domain->primary ) {
1774 cache_name2sid(domain, name_domain, name_user,
1775 SID_NAME_USER, &user_sid);
1778 /* Check if the user is in the right group */
1780 result = check_info3_in_group(
1781 info3,
1782 state->request->data.auth.require_membership_of_sid);
1783 if (!NT_STATUS_IS_OK(result)) {
1784 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1785 state->request->data.auth.user,
1786 state->request->data.auth.require_membership_of_sid));
1787 goto done;
1790 result = append_auth_data(state->mem_ctx, state->response,
1791 state->request->flags, info3,
1792 name_domain, name_user);
1793 if (!NT_STATUS_IS_OK(result)) {
1794 goto done;
1797 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1798 && lp_winbind_offline_logon()) {
1800 result = winbindd_store_creds(domain,
1801 state->request->data.auth.user,
1802 state->request->data.auth.pass,
1803 info3);
1806 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1807 struct winbindd_domain *our_domain = find_our_domain();
1809 /* This is not entirely correct I believe, but it is
1810 consistent. Only apply the password policy settings
1811 too warn users for our own domain. Cannot obtain these
1812 from trusted DCs all the time so don't do it at all.
1813 -- jerry */
1815 result = NT_STATUS_NOT_SUPPORTED;
1816 if (our_domain == domain ) {
1817 result = fillup_password_policy(
1818 our_domain, state->response);
1821 if (!NT_STATUS_IS_OK(result)
1822 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1824 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1825 domain->name, nt_errstr(result)));
1826 goto done;
1830 result = NT_STATUS_OK;
1833 done:
1834 /* give us a more useful (more correct?) error code */
1835 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1836 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1837 result = NT_STATUS_NO_LOGON_SERVERS;
1840 set_auth_errors(state->response, result);
1842 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1843 state->request->data.auth.user,
1844 state->response->data.auth.nt_status_string,
1845 state->response->data.auth.pam_error));
1847 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1850 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1851 struct winbindd_cli_state *state)
1853 NTSTATUS result;
1854 struct netr_SamInfo3 *info3 = NULL;
1855 const char *name_user = NULL;
1856 const char *name_domain = NULL;
1857 const char *workstation;
1859 DATA_BLOB lm_resp, nt_resp;
1861 /* This is child-only, so no check for privileged access is needed
1862 anymore */
1864 /* Ensure null termination */
1865 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1866 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1868 name_user = state->request->data.auth_crap.user;
1869 name_domain = state->request->data.auth_crap.domain;
1870 workstation = state->request->data.auth_crap.workstation;
1872 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1873 name_domain, name_user));
1875 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1876 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1877 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1878 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1879 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1880 state->request->data.auth_crap.lm_resp_len,
1881 state->request->data.auth_crap.nt_resp_len));
1882 result = NT_STATUS_INVALID_PARAMETER;
1883 goto done;
1887 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1888 state->request->data.auth_crap.lm_resp_len);
1890 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1891 nt_resp = data_blob_talloc(state->mem_ctx,
1892 state->request->extra_data.data,
1893 state->request->data.auth_crap.nt_resp_len);
1894 } else {
1895 nt_resp = data_blob_talloc(state->mem_ctx,
1896 state->request->data.auth_crap.nt_resp,
1897 state->request->data.auth_crap.nt_resp_len);
1900 if (strequal(name_domain, get_global_sam_name())) {
1901 DATA_BLOB chal_blob = data_blob_const(
1902 state->request->data.auth_crap.chal,
1903 sizeof(state->request->data.auth_crap.chal));
1905 result = winbindd_dual_auth_passdb(
1906 state->mem_ctx,
1907 state->request->data.auth_crap.logon_parameters,
1908 name_domain, name_user,
1909 &chal_blob, &lm_resp, &nt_resp, &info3);
1910 goto process_result;
1913 result = winbind_samlogon_retry_loop(domain,
1914 state->mem_ctx,
1915 state->request->data.auth_crap.logon_parameters,
1916 domain->dcname,
1917 name_user,
1918 name_domain,
1919 /* Bug #3248 - found by Stefan Burkei. */
1920 workstation, /* We carefully set this above so use it... */
1921 state->request->data.auth_crap.chal,
1922 lm_resp,
1923 nt_resp,
1924 &info3);
1925 if (!NT_STATUS_IS_OK(result)) {
1926 goto done;
1929 process_result:
1931 if (NT_STATUS_IS_OK(result)) {
1932 struct dom_sid user_sid;
1934 sid_compose(&user_sid, info3->base.domain_sid,
1935 info3->base.rid);
1936 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1937 &user_sid);
1938 netsamlogon_cache_store(name_user, info3);
1940 /* Check if the user is in the right group */
1942 result = check_info3_in_group(
1943 info3,
1944 state->request->data.auth_crap.require_membership_of_sid);
1945 if (!NT_STATUS_IS_OK(result)) {
1946 DEBUG(3, ("User %s is not in the required group (%s), so "
1947 "crap authentication is rejected\n",
1948 state->request->data.auth_crap.user,
1949 state->request->data.auth_crap.require_membership_of_sid));
1950 goto done;
1953 result = append_auth_data(state->mem_ctx, state->response,
1954 state->request->flags, info3,
1955 name_domain, name_user);
1956 if (!NT_STATUS_IS_OK(result)) {
1957 goto done;
1961 done:
1963 /* give us a more useful (more correct?) error code */
1964 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1965 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1966 result = NT_STATUS_NO_LOGON_SERVERS;
1969 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1970 result = nt_status_squash(result);
1973 set_auth_errors(state->response, result);
1975 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1976 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1977 name_domain,
1978 name_user,
1979 state->response->data.auth.nt_status_string,
1980 state->response->data.auth.pam_error));
1982 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1985 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1986 struct winbindd_cli_state *state)
1988 char *oldpass;
1989 char *newpass = NULL;
1990 struct policy_handle dom_pol;
1991 struct rpc_pipe_client *cli = NULL;
1992 bool got_info = false;
1993 struct samr_DomInfo1 *info = NULL;
1994 struct userPwdChangeFailureInformation *reject = NULL;
1995 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1996 fstring domain, user;
1997 struct dcerpc_binding_handle *b = NULL;
1999 ZERO_STRUCT(dom_pol);
2001 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2002 state->request->data.auth.user));
2004 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2005 goto done;
2008 /* Change password */
2010 oldpass = state->request->data.chauthtok.oldpass;
2011 newpass = state->request->data.chauthtok.newpass;
2013 /* Initialize reject reason */
2014 state->response->data.auth.reject_reason = Undefined;
2016 /* Get sam handle */
2018 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2019 &dom_pol);
2020 if (!NT_STATUS_IS_OK(result)) {
2021 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2022 goto done;
2025 b = cli->binding_handle;
2027 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2028 user,
2029 newpass,
2030 oldpass,
2031 &info,
2032 &reject);
2034 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2036 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2038 fill_in_password_policy(state->response, info);
2040 state->response->data.auth.reject_reason =
2041 reject->extendedFailureReason;
2043 got_info = true;
2046 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2047 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2048 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2049 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2051 /* only fallback when the chgpasswd_user3 call is not supported */
2052 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2053 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2054 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2055 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2057 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2058 nt_errstr(result)));
2060 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2062 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2063 Map to the same status code as Windows 2003. */
2065 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2066 result = NT_STATUS_PASSWORD_RESTRICTION;
2070 done:
2072 if (NT_STATUS_IS_OK(result)
2073 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2074 && lp_winbind_offline_logon()) {
2075 result = winbindd_update_creds_by_name(contact_domain, user,
2076 newpass);
2077 /* Again, this happens when we login from gdm or xdm
2078 * and the password expires, *BUT* cached crendentials
2079 * doesn't exist. winbindd_update_creds_by_name()
2080 * returns NT_STATUS_NO_SUCH_USER.
2081 * This is not a failure.
2082 * --- BoYang
2083 * */
2084 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2085 result = NT_STATUS_OK;
2088 if (!NT_STATUS_IS_OK(result)) {
2089 DEBUG(10, ("Failed to store creds: %s\n",
2090 nt_errstr(result)));
2091 goto process_result;
2095 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2097 NTSTATUS policy_ret;
2099 policy_ret = fillup_password_policy(
2100 contact_domain, state->response);
2102 /* failure of this is non critical, it will just provide no
2103 * additional information to the client why the change has
2104 * failed - Guenther */
2106 if (!NT_STATUS_IS_OK(policy_ret)) {
2107 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2108 goto process_result;
2112 process_result:
2114 if (strequal(contact_domain->name, get_global_sam_name())) {
2115 /* FIXME: internal rpc pipe does not cache handles yet */
2116 if (b) {
2117 if (is_valid_policy_hnd(&dom_pol)) {
2118 NTSTATUS _result;
2119 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2121 TALLOC_FREE(cli);
2125 set_auth_errors(state->response, result);
2127 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2128 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2129 domain,
2130 user,
2131 state->response->data.auth.nt_status_string,
2132 state->response->data.auth.pam_error));
2134 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2137 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2138 struct winbindd_cli_state *state)
2140 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2142 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2143 state->request->data.logoff.user));
2145 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2146 result = NT_STATUS_OK;
2147 goto process_result;
2150 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2151 result = NT_STATUS_OK;
2152 goto process_result;
2155 #ifdef HAVE_KRB5
2157 if (state->request->data.logoff.uid < 0) {
2158 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2159 goto process_result;
2162 /* what we need here is to find the corresponding krb5 ccache name *we*
2163 * created for a given username and destroy it */
2165 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2166 result = NT_STATUS_OK;
2167 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2168 goto process_result;
2171 if (!ccache_entry_identical(state->request->data.logoff.user,
2172 state->request->data.logoff.uid,
2173 state->request->data.logoff.krb5ccname)) {
2174 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2175 goto process_result;
2178 result = remove_ccache(state->request->data.logoff.user);
2179 if (!NT_STATUS_IS_OK(result)) {
2180 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2181 nt_errstr(result)));
2182 goto process_result;
2186 * Remove any mlock'ed memory creds in the child
2187 * we might be using for krb5 ticket renewal.
2190 winbindd_delete_memory_creds(state->request->data.logoff.user);
2192 #else
2193 result = NT_STATUS_NOT_SUPPORTED;
2194 #endif
2196 process_result:
2199 set_auth_errors(state->response, result);
2201 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2204 /* Change user password with auth crap*/
2206 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2208 NTSTATUS result;
2209 DATA_BLOB new_nt_password;
2210 DATA_BLOB old_nt_hash_enc;
2211 DATA_BLOB new_lm_password;
2212 DATA_BLOB old_lm_hash_enc;
2213 fstring domain,user;
2214 struct policy_handle dom_pol;
2215 struct winbindd_domain *contact_domain = domainSt;
2216 struct rpc_pipe_client *cli = NULL;
2217 struct dcerpc_binding_handle *b = NULL;
2219 ZERO_STRUCT(dom_pol);
2221 /* Ensure null termination */
2222 state->request->data.chng_pswd_auth_crap.user[
2223 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2224 state->request->data.chng_pswd_auth_crap.domain[
2225 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2226 *domain = 0;
2227 *user = 0;
2229 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2230 (unsigned long)state->pid,
2231 state->request->data.chng_pswd_auth_crap.domain,
2232 state->request->data.chng_pswd_auth_crap.user));
2234 if (lp_winbind_offline_logon()) {
2235 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2236 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2237 result = NT_STATUS_ACCESS_DENIED;
2238 goto done;
2241 if (*state->request->data.chng_pswd_auth_crap.domain) {
2242 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2243 } else {
2244 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2245 domain, user);
2247 if(!*domain) {
2248 DEBUG(3,("no domain specified with username (%s) - "
2249 "failing auth\n",
2250 state->request->data.chng_pswd_auth_crap.user));
2251 result = NT_STATUS_NO_SUCH_USER;
2252 goto done;
2256 if (!*domain && lp_winbind_use_default_domain()) {
2257 fstrcpy(domain,lp_workgroup());
2260 if(!*user) {
2261 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2264 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2265 (unsigned long)state->pid, domain, user));
2267 /* Change password */
2268 new_nt_password = data_blob_const(
2269 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2270 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2272 old_nt_hash_enc = data_blob_const(
2273 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2274 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2276 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2277 new_lm_password = data_blob_const(
2278 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2279 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2281 old_lm_hash_enc = data_blob_const(
2282 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2283 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2284 } else {
2285 new_lm_password = data_blob_null;
2286 old_lm_hash_enc = data_blob_null;
2289 /* Get sam handle */
2291 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2292 if (!NT_STATUS_IS_OK(result)) {
2293 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2294 goto done;
2297 b = cli->binding_handle;
2299 result = rpccli_samr_chng_pswd_auth_crap(
2300 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2301 new_lm_password, old_lm_hash_enc);
2303 done:
2305 if (strequal(contact_domain->name, get_global_sam_name())) {
2306 /* FIXME: internal rpc pipe does not cache handles yet */
2307 if (b) {
2308 if (is_valid_policy_hnd(&dom_pol)) {
2309 NTSTATUS _result;
2310 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2312 TALLOC_FREE(cli);
2316 set_auth_errors(state->response, result);
2318 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2319 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2320 domain, user,
2321 state->response->data.auth.nt_status_string,
2322 state->response->data.auth.pam_error));
2324 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2327 #ifdef HAVE_KRB5
2328 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2329 struct PAC_LOGON_INFO **logon_info)
2331 krb5_context krbctx = NULL;
2332 krb5_error_code k5ret;
2333 krb5_keytab keytab;
2334 krb5_kt_cursor cursor;
2335 krb5_keytab_entry entry;
2336 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2338 ZERO_STRUCT(entry);
2339 ZERO_STRUCT(cursor);
2341 k5ret = krb5_init_context(&krbctx);
2342 if (k5ret) {
2343 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2344 error_message(k5ret)));
2345 status = krb5_to_nt_status(k5ret);
2346 goto out;
2349 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2350 if (k5ret) {
2351 DEBUG(1, ("Failed to get keytab: %s\n",
2352 error_message(k5ret)));
2353 status = krb5_to_nt_status(k5ret);
2354 goto out_free;
2357 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2358 if (k5ret) {
2359 DEBUG(1, ("Failed to start seq: %s\n",
2360 error_message(k5ret)));
2361 status = krb5_to_nt_status(k5ret);
2362 goto out_keytab;
2365 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2366 while (k5ret == 0) {
2367 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2368 krbctx, NULL,
2369 KRB5_KT_KEY(&entry), NULL, 0,
2370 logon_info);
2371 if (NT_STATUS_IS_OK(status)) {
2372 break;
2374 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2375 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2378 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2379 if (k5ret) {
2380 DEBUG(1, ("Failed to end seq: %s\n",
2381 error_message(k5ret)));
2383 out_keytab:
2384 k5ret = krb5_kt_close(krbctx, keytab);
2385 if (k5ret) {
2386 DEBUG(1, ("Failed to close keytab: %s\n",
2387 error_message(k5ret)));
2389 out_free:
2390 krb5_free_context(krbctx);
2391 out:
2392 return status;
2395 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2396 struct netr_SamInfo3 **info3)
2398 struct winbindd_request *req = state->request;
2399 DATA_BLOB pac_blob;
2400 struct PAC_LOGON_INFO *logon_info = NULL;
2401 NTSTATUS result;
2403 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2404 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2405 if (!NT_STATUS_IS_OK(result) &&
2406 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2407 DEBUG(1, ("Error during PAC signature verification: %s\n",
2408 nt_errstr(result)));
2409 return result;
2412 if (logon_info) {
2413 /* Signature verification succeeded, trust the PAC */
2414 netsamlogon_cache_store(NULL, &logon_info->info3);
2416 } else {
2417 /* Try without signature verification */
2418 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2419 NULL, NULL, NULL, 0,
2420 &logon_info);
2421 if (!NT_STATUS_IS_OK(result)) {
2422 DEBUG(10, ("Could not extract PAC: %s\n",
2423 nt_errstr(result)));
2424 return result;
2428 *info3 = &logon_info->info3;
2430 return NT_STATUS_OK;
2432 #else /* HAVE_KRB5 */
2433 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2434 struct netr_SamInfo3 **info3)
2436 return NT_STATUS_NO_SUCH_USER;
2438 #endif /* HAVE_KRB5 */