debug: Set close-on-exec for the main log file FD
[Samba.git] / source3 / winbindd / winbindd_pam.c
blobc356686488a83c9f46bd5df2629f8cce66f22396
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;
580 *info3 = NULL;
582 if (domain->alt_name == NULL) {
583 return NT_STATUS_INVALID_PARAMETER;
586 /* 1st step:
587 * prepare a krb5_cc_cache string for the user */
589 if (uid == -1) {
590 DEBUG(0,("no valid uid\n"));
593 cc = generate_krb5_ccache(mem_ctx,
594 krb5_cc_type,
595 uid,
596 &user_ccache_file);
597 if (cc == NULL) {
598 return NT_STATUS_NO_MEMORY;
602 /* 2nd step:
603 * get kerberos properties */
605 if (domain->private_data) {
606 ads = (ADS_STRUCT *)domain->private_data;
607 time_offset = ads->auth.time_offset;
611 /* 3rd step:
612 * do kerberos auth and setup ccache as the user */
614 parse_domain_user(user, name_domain, name_user);
616 realm = talloc_strdup(mem_ctx, domain->alt_name);
617 if (realm == NULL) {
618 return NT_STATUS_NO_MEMORY;
621 if (!strupper_m(realm)) {
622 return NT_STATUS_INVALID_PARAMETER;
625 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
626 if (principal_s == NULL) {
627 return NT_STATUS_NO_MEMORY;
630 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
631 if (service == NULL) {
632 return NT_STATUS_NO_MEMORY;
635 /* if this is a user ccache, we need to act as the user to let the krb5
636 * library handle the chown, etc. */
638 /************************ ENTERING NON-ROOT **********************/
640 if (user_ccache_file != NULL) {
641 set_effective_uid(uid);
642 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
645 result = kerberos_return_pac(mem_ctx,
646 principal_s,
647 pass,
648 time_offset,
649 &ticket_lifetime,
650 &renewal_until,
652 true,
653 true,
654 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
655 NULL,
656 &logon_info);
657 if (user_ccache_file != NULL) {
658 gain_root_privilege();
661 /************************ RETURNED TO ROOT **********************/
663 if (!NT_STATUS_IS_OK(result)) {
664 goto failed;
667 *info3 = &logon_info->info3;
669 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
670 principal_s));
672 /* if we had a user's ccache then return that string for the pam
673 * environment */
675 if (user_ccache_file != NULL) {
677 fstrcpy(krb5ccname, user_ccache_file);
679 result = add_ccache_to_list(principal_s,
681 service,
682 user,
683 pass,
684 realm,
685 uid,
686 time(NULL),
687 ticket_lifetime,
688 renewal_until,
689 false);
691 if (!NT_STATUS_IS_OK(result)) {
692 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
693 nt_errstr(result)));
695 } else {
697 /* need to delete the memory cred cache, it is not used anymore */
699 krb5_ret = ads_kdestroy(cc);
700 if (krb5_ret) {
701 DEBUG(3,("winbindd_raw_kerberos_login: "
702 "could not destroy krb5 credential cache: "
703 "%s\n", error_message(krb5_ret)));
708 return NT_STATUS_OK;
710 failed:
712 * Do not delete an existing valid credential cache, if the user
713 * e.g. enters a wrong password
715 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
716 && user_ccache_file != NULL) {
717 return result;
720 /* we could have created a new credential cache with a valid tgt in it
721 * but we werent able to get or verify the service ticket for this
722 * local host and therefor didn't get the PAC, we need to remove that
723 * cache entirely now */
725 krb5_ret = ads_kdestroy(cc);
726 if (krb5_ret) {
727 DEBUG(3,("winbindd_raw_kerberos_login: "
728 "could not destroy krb5 credential cache: "
729 "%s\n", error_message(krb5_ret)));
732 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
733 DEBUG(3,("winbindd_raw_kerberos_login: "
734 "could not remove ccache for user %s\n",
735 user));
738 return result;
739 #else
740 return NT_STATUS_NOT_SUPPORTED;
741 #endif /* HAVE_KRB5 */
744 /****************************************************************
745 ****************************************************************/
747 bool check_request_flags(uint32_t flags)
749 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
750 WBFLAG_PAM_INFO3_TEXT |
751 WBFLAG_PAM_INFO3_NDR;
753 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
754 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
755 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
756 !(flags & flags_edata) ) {
757 return true;
760 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
761 flags));
763 return false;
766 /****************************************************************
767 ****************************************************************/
769 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
770 struct winbindd_response *resp,
771 uint32_t request_flags,
772 struct netr_SamInfo3 *info3,
773 const char *name_domain,
774 const char *name_user)
776 NTSTATUS result;
778 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
779 memcpy(resp->data.auth.user_session_key,
780 info3->base.key.key,
781 sizeof(resp->data.auth.user_session_key)
782 /* 16 */);
785 if (request_flags & WBFLAG_PAM_LMKEY) {
786 memcpy(resp->data.auth.first_8_lm_hash,
787 info3->base.LMSessKey.key,
788 sizeof(resp->data.auth.first_8_lm_hash)
789 /* 8 */);
792 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
793 result = append_unix_username(mem_ctx, resp,
794 info3, name_domain, name_user);
795 if (!NT_STATUS_IS_OK(result)) {
796 DEBUG(10,("Failed to append Unix Username: %s\n",
797 nt_errstr(result)));
798 return result;
802 /* currently, anything from here on potentially overwrites extra_data. */
804 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
805 result = append_info3_as_ndr(mem_ctx, resp, info3);
806 if (!NT_STATUS_IS_OK(result)) {
807 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
808 nt_errstr(result)));
809 return result;
813 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
814 result = append_info3_as_txt(mem_ctx, resp, info3);
815 if (!NT_STATUS_IS_OK(result)) {
816 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
817 nt_errstr(result)));
818 return result;
822 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
823 result = append_afs_token(mem_ctx, resp,
824 info3, name_domain, name_user);
825 if (!NT_STATUS_IS_OK(result)) {
826 DEBUG(10,("Failed to append AFS token: %s\n",
827 nt_errstr(result)));
828 return result;
832 return NT_STATUS_OK;
835 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
836 struct winbindd_cli_state *state,
837 struct netr_SamInfo3 **info3)
839 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
840 uint16 max_allowed_bad_attempts;
841 fstring name_domain, name_user;
842 struct dom_sid sid;
843 enum lsa_SidType type;
844 uchar new_nt_pass[NT_HASH_LEN];
845 const uint8 *cached_nt_pass;
846 const uint8 *cached_salt;
847 struct netr_SamInfo3 *my_info3;
848 time_t kickoff_time, must_change_time;
849 bool password_good = false;
850 #ifdef HAVE_KRB5
851 struct winbindd_tdc_domain *tdc_domain = NULL;
852 #endif
854 *info3 = NULL;
856 ZERO_STRUCTP(info3);
858 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
860 /* Parse domain and username */
862 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
865 if (!lookup_cached_name(name_domain,
866 name_user,
867 &sid,
868 &type)) {
869 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
870 return NT_STATUS_NO_SUCH_USER;
873 if (type != SID_NAME_USER) {
874 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
875 return NT_STATUS_LOGON_FAILURE;
878 result = winbindd_get_creds(domain,
879 state->mem_ctx,
880 &sid,
881 &my_info3,
882 &cached_nt_pass,
883 &cached_salt);
884 if (!NT_STATUS_IS_OK(result)) {
885 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
886 return result;
889 *info3 = my_info3;
891 E_md4hash(state->request->data.auth.pass, new_nt_pass);
893 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
894 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
895 if (cached_salt) {
896 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
899 if (cached_salt) {
900 /* In this case we didn't store the nt_hash itself,
901 but the MD5 combination of salt + nt_hash. */
902 uchar salted_hash[NT_HASH_LEN];
903 E_md5hash(cached_salt, new_nt_pass, salted_hash);
905 password_good = (memcmp(cached_nt_pass, salted_hash,
906 NT_HASH_LEN) == 0);
907 } else {
908 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
909 password_good = (memcmp(cached_nt_pass, new_nt_pass,
910 NT_HASH_LEN) == 0);
913 if (password_good) {
915 /* User *DOES* know the password, update logon_time and reset
916 * bad_pw_count */
918 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
920 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
921 return NT_STATUS_ACCOUNT_LOCKED_OUT;
924 if (my_info3->base.acct_flags & ACB_DISABLED) {
925 return NT_STATUS_ACCOUNT_DISABLED;
928 if (my_info3->base.acct_flags & ACB_WSTRUST) {
929 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
932 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
933 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
936 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
937 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
940 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
941 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
942 my_info3->base.acct_flags));
943 return NT_STATUS_LOGON_FAILURE;
946 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
947 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
948 return NT_STATUS_ACCOUNT_EXPIRED;
951 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
952 if (must_change_time != 0 && must_change_time < time(NULL)) {
953 /* we allow grace logons when the password has expired */
954 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
955 /* return NT_STATUS_PASSWORD_EXPIRED; */
956 goto success;
959 #ifdef HAVE_KRB5
960 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
961 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
962 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
963 /* used to cope with the case winbindd starting without network. */
964 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
966 uid_t uid = -1;
967 const char *cc = NULL;
968 char *realm = NULL;
969 const char *principal_s = NULL;
970 const char *service = NULL;
971 const char *user_ccache_file;
973 if (domain->alt_name == NULL) {
974 return NT_STATUS_INVALID_PARAMETER;
977 uid = get_uid_from_request(state->request);
978 if (uid == -1) {
979 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
980 return NT_STATUS_INVALID_PARAMETER;
983 cc = generate_krb5_ccache(state->mem_ctx,
984 state->request->data.auth.krb5_cc_type,
985 state->request->data.auth.uid,
986 &user_ccache_file);
987 if (cc == NULL) {
988 return NT_STATUS_NO_MEMORY;
991 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
992 if (realm == NULL) {
993 return NT_STATUS_NO_MEMORY;
996 if (!strupper_m(realm)) {
997 return NT_STATUS_INVALID_PARAMETER;
1000 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1001 if (principal_s == NULL) {
1002 return NT_STATUS_NO_MEMORY;
1005 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1006 if (service == NULL) {
1007 return NT_STATUS_NO_MEMORY;
1010 if (user_ccache_file != NULL) {
1012 fstrcpy(state->response->data.auth.krb5ccname,
1013 user_ccache_file);
1015 result = add_ccache_to_list(principal_s,
1017 service,
1018 state->request->data.auth.user,
1019 state->request->data.auth.pass,
1020 realm,
1021 uid,
1022 time(NULL),
1023 time(NULL) + lp_winbind_cache_time(),
1024 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1025 true);
1027 if (!NT_STATUS_IS_OK(result)) {
1028 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1029 "to add ccache to list: %s\n",
1030 nt_errstr(result)));
1034 #endif /* HAVE_KRB5 */
1035 success:
1036 /* FIXME: we possibly should handle logon hours as well (does xp when
1037 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1039 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1040 my_info3->base.bad_password_count = 0;
1042 result = winbindd_update_creds_by_info3(domain,
1043 state->request->data.auth.user,
1044 state->request->data.auth.pass,
1045 my_info3);
1046 if (!NT_STATUS_IS_OK(result)) {
1047 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1048 nt_errstr(result)));
1049 return result;
1052 return NT_STATUS_OK;
1056 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1057 if (domain->online == false) {
1058 goto failed;
1061 /* failure of this is not critical */
1062 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1063 if (!NT_STATUS_IS_OK(result)) {
1064 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1065 "Won't be able to honour account lockout policies\n"));
1068 /* increase counter */
1069 my_info3->base.bad_password_count++;
1071 if (max_allowed_bad_attempts == 0) {
1072 goto failed;
1075 /* lockout user */
1076 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1078 uint32 password_properties;
1080 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1081 if (!NT_STATUS_IS_OK(result)) {
1082 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1085 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1086 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1087 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1091 failed:
1092 result = winbindd_update_creds_by_info3(domain,
1093 state->request->data.auth.user,
1094 NULL,
1095 my_info3);
1097 if (!NT_STATUS_IS_OK(result)) {
1098 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1099 nt_errstr(result)));
1102 return NT_STATUS_LOGON_FAILURE;
1105 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1106 struct winbindd_cli_state *state,
1107 struct netr_SamInfo3 **info3)
1109 struct winbindd_domain *contact_domain;
1110 fstring name_domain, name_user;
1111 NTSTATUS result;
1113 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1115 /* Parse domain and username */
1117 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1119 /* what domain should we contact? */
1121 if ( IS_DC ) {
1122 if (!(contact_domain = find_domain_from_name(name_domain))) {
1123 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1124 state->request->data.auth.user, name_domain, name_user, name_domain));
1125 result = NT_STATUS_NO_SUCH_USER;
1126 goto done;
1129 } else {
1130 if (is_myname(name_domain)) {
1131 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1132 result = NT_STATUS_NO_SUCH_USER;
1133 goto done;
1136 contact_domain = find_domain_from_name(name_domain);
1137 if (contact_domain == NULL) {
1138 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1139 state->request->data.auth.user, name_domain, name_user, name_domain));
1141 result = NT_STATUS_NO_SUCH_USER;
1142 goto done;
1146 if (contact_domain->initialized &&
1147 contact_domain->active_directory) {
1148 goto try_login;
1151 if (!contact_domain->initialized) {
1152 init_dc_connection(contact_domain);
1155 if (!contact_domain->active_directory) {
1156 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1157 return NT_STATUS_INVALID_LOGON_TYPE;
1159 try_login:
1160 result = winbindd_raw_kerberos_login(
1161 state->mem_ctx, contact_domain,
1162 state->request->data.auth.user,
1163 state->request->data.auth.pass,
1164 state->request->data.auth.krb5_cc_type,
1165 get_uid_from_request(state->request),
1166 info3, state->response->data.auth.krb5ccname);
1167 done:
1168 return result;
1171 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1172 uint32_t logon_parameters,
1173 const char *domain, const char *user,
1174 const DATA_BLOB *challenge,
1175 const DATA_BLOB *lm_resp,
1176 const DATA_BLOB *nt_resp,
1177 struct netr_SamInfo3 **pinfo3)
1179 struct auth_usersupplied_info *user_info = NULL;
1180 struct tsocket_address *local;
1181 NTSTATUS status;
1182 int rc;
1184 rc = tsocket_address_inet_from_strings(mem_ctx,
1185 "ip",
1186 "127.0.0.1",
1188 &local);
1189 if (rc < 0) {
1190 return NT_STATUS_NO_MEMORY;
1192 status = make_user_info(&user_info, user, user, domain, domain,
1193 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1194 NULL, AUTH_PASSWORD_RESPONSE);
1195 if (!NT_STATUS_IS_OK(status)) {
1196 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1197 return status;
1199 user_info->logon_parameters = logon_parameters;
1201 /* We don't want any more mapping of the username */
1202 user_info->mapped_state = True;
1204 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1205 pinfo3);
1206 free_user_info(&user_info);
1207 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1208 user, nt_errstr(status)));
1209 return status;
1212 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1213 TALLOC_CTX *mem_ctx,
1214 uint32_t logon_parameters,
1215 const char *server,
1216 const char *username,
1217 const char *domainname,
1218 const char *workstation,
1219 const uint8_t chal[8],
1220 DATA_BLOB lm_response,
1221 DATA_BLOB nt_response,
1222 struct netr_SamInfo3 **info3)
1224 int attempts = 0;
1225 int netr_attempts = 0;
1226 bool retry = false;
1227 NTSTATUS result;
1229 do {
1230 struct rpc_pipe_client *netlogon_pipe;
1231 const struct pipe_auth_data *auth;
1232 uint32_t neg_flags = 0;
1234 ZERO_STRUCTP(info3);
1235 retry = false;
1237 result = cm_connect_netlogon(domain, &netlogon_pipe);
1239 if (!NT_STATUS_IS_OK(result)) {
1240 DEBUG(3,("Could not open handle to NETLOGON pipe "
1241 "(error: %s, attempts: %d)\n",
1242 nt_errstr(result), netr_attempts));
1244 /* After the first retry always close the connection */
1245 if (netr_attempts > 0) {
1246 DEBUG(3, ("This is again a problem for this "
1247 "particular call, forcing the close "
1248 "of this connection\n"));
1249 invalidate_cm_connection(&domain->conn);
1252 /* After the second retry failover to the next DC */
1253 if (netr_attempts > 1) {
1255 * If the netlogon server is not reachable then
1256 * it is possible that the DC is rebuilding
1257 * sysvol and shutdown netlogon for that time.
1258 * We should failover to the next dc.
1260 DEBUG(3, ("This is the third problem for this "
1261 "particular call, adding DC to the "
1262 "negative cache list\n"));
1263 add_failed_connection_entry(domain->name,
1264 domain->dcname,
1265 result);
1266 saf_delete(domain->name);
1269 /* Only allow 3 retries */
1270 if (netr_attempts < 3) {
1271 DEBUG(3, ("The connection to netlogon "
1272 "failed, retrying\n"));
1273 netr_attempts++;
1274 retry = true;
1275 continue;
1277 return result;
1279 netr_attempts = 0;
1281 auth = netlogon_pipe->auth;
1282 if (netlogon_pipe->dc) {
1283 neg_flags = netlogon_pipe->dc->negotiate_flags;
1286 /* It is really important to try SamLogonEx here,
1287 * because in a clustered environment, we want to use
1288 * one machine account from multiple physical
1289 * computers.
1291 * With a normal SamLogon call, we must keep the
1292 * credentials chain updated and intact between all
1293 * users of the machine account (which would imply
1294 * cross-node communication for every NTLM logon).
1296 * (The credentials chain is not per NETLOGON pipe
1297 * connection, but globally on the server/client pair
1298 * by machine name).
1300 * When using SamLogonEx, the credentials are not
1301 * supplied, but the session key is implied by the
1302 * wrapping SamLogon context.
1304 * -- abartlet 21 April 2008
1306 * It's also important to use NetlogonValidationSamInfo4 (6),
1307 * because it relies on the rpc transport encryption
1308 * and avoids using the global netlogon schannel
1309 * session key to en/decrypt secret information
1310 * like the user_session_key for network logons.
1312 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1313 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1314 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1315 * are the indication that the server supports
1316 * NetlogonValidationSamInfo4 (6). And it must only
1317 * be used if "SealSecureChannel" is used.
1319 * -- metze 4 February 2011
1322 if (auth == NULL) {
1323 domain->can_do_validation6 = false;
1324 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1325 domain->can_do_validation6 = false;
1326 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1327 domain->can_do_validation6 = false;
1328 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1329 domain->can_do_validation6 = false;
1330 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1331 domain->can_do_validation6 = false;
1334 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1335 result = rpccli_netlogon_sam_network_logon_ex(
1336 netlogon_pipe,
1337 mem_ctx,
1338 logon_parameters,
1339 server, /* server name */
1340 username, /* user name */
1341 domainname, /* target domain */
1342 workstation, /* workstation */
1343 chal,
1345 lm_response,
1346 nt_response,
1347 info3);
1348 } else {
1349 result = rpccli_netlogon_sam_network_logon(
1350 netlogon_pipe,
1351 mem_ctx,
1352 logon_parameters,
1353 server, /* server name */
1354 username, /* user name */
1355 domainname, /* target domain */
1356 workstation, /* workstation */
1357 chal,
1358 domain->can_do_validation6 ? 6 : 3,
1359 lm_response,
1360 nt_response,
1361 info3);
1364 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1367 * It's likely that the server also does not support
1368 * validation level 6
1370 domain->can_do_validation6 = false;
1372 if (domain->can_do_samlogon_ex) {
1373 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1374 "retrying with NetSamLogon\n"));
1375 domain->can_do_samlogon_ex = false;
1376 retry = true;
1377 continue;
1381 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1382 * (no Ex). This happens against old Samba
1383 * DCs. Drop the connection.
1385 invalidate_cm_connection(&domain->conn);
1386 result = NT_STATUS_LOGON_FAILURE;
1387 break;
1390 if (domain->can_do_validation6 &&
1391 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1392 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1393 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1394 DEBUG(3,("Got a DC that can not do validation level 6, "
1395 "retrying with level 3\n"));
1396 domain->can_do_validation6 = false;
1397 retry = true;
1398 continue;
1402 * we increment this after the "feature negotiation"
1403 * for can_do_samlogon_ex and can_do_validation6
1405 attempts += 1;
1407 /* We have to try a second time as cm_connect_netlogon
1408 might not yet have noticed that the DC has killed
1409 our connection. */
1411 if (!rpccli_is_connected(netlogon_pipe)) {
1412 retry = true;
1413 continue;
1416 /* if we get access denied, a possible cause was that we had
1417 and open connection to the DC, but someone changed our
1418 machine account password out from underneath us using 'net
1419 rpc changetrustpw' */
1421 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1422 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1423 "ACCESS_DENIED. Maybe the trust account "
1424 "password was changed and we didn't know it. "
1425 "Killing connections to domain %s\n",
1426 domainname));
1427 invalidate_cm_connection(&domain->conn);
1428 retry = true;
1431 } while ( (attempts < 2) && retry );
1433 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1434 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1435 "returned NT_STATUS_IO_TIMEOUT after the retry."
1436 "Killing connections to domain %s\n",
1437 domainname));
1438 invalidate_cm_connection(&domain->conn);
1440 return result;
1443 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1444 struct winbindd_domain *domain,
1445 const char *user,
1446 const char *pass,
1447 uint32_t request_flags,
1448 struct netr_SamInfo3 **info3)
1451 uchar chal[8];
1452 DATA_BLOB lm_resp;
1453 DATA_BLOB nt_resp;
1454 unsigned char local_nt_response[24];
1455 fstring name_domain, name_user;
1456 NTSTATUS result;
1457 struct netr_SamInfo3 *my_info3 = NULL;
1459 *info3 = NULL;
1461 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1463 /* Parse domain and username */
1465 parse_domain_user(user, name_domain, name_user);
1467 /* do password magic */
1469 generate_random_buffer(chal, sizeof(chal));
1471 if (lp_client_ntlmv2_auth()) {
1472 DATA_BLOB server_chal;
1473 DATA_BLOB names_blob;
1474 server_chal = data_blob_const(chal, 8);
1476 /* note that the 'workgroup' here is for the local
1477 machine. The 'server name' must match the
1478 'workstation' passed to the actual SamLogon call.
1480 names_blob = NTLMv2_generate_names_blob(
1481 mem_ctx, lp_netbios_name(), lp_workgroup());
1483 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1484 pass,
1485 &server_chal,
1486 &names_blob,
1487 &lm_resp, &nt_resp, NULL, NULL)) {
1488 data_blob_free(&names_blob);
1489 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1490 result = NT_STATUS_NO_MEMORY;
1491 goto done;
1493 data_blob_free(&names_blob);
1494 } else {
1495 lm_resp = data_blob_null;
1496 SMBNTencrypt(pass, chal, local_nt_response);
1498 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1499 sizeof(local_nt_response));
1502 if (strequal(name_domain, get_global_sam_name())) {
1503 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1505 result = winbindd_dual_auth_passdb(
1506 mem_ctx, 0, name_domain, name_user,
1507 &chal_blob, &lm_resp, &nt_resp, info3);
1508 goto done;
1511 /* check authentication loop */
1513 result = winbind_samlogon_retry_loop(domain,
1514 mem_ctx,
1516 domain->dcname,
1517 name_user,
1518 name_domain,
1519 lp_netbios_name(),
1520 chal,
1521 lm_resp,
1522 nt_resp,
1523 &my_info3);
1524 if (!NT_STATUS_IS_OK(result)) {
1525 goto done;
1528 /* handle the case where a NT4 DC does not fill in the acct_flags in
1529 * the samlogon reply info3. When accurate info3 is required by the
1530 * caller, we look up the account flags ourselve - gd */
1532 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1533 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1535 struct rpc_pipe_client *samr_pipe;
1536 struct policy_handle samr_domain_handle, user_pol;
1537 union samr_UserInfo *info = NULL;
1538 NTSTATUS status_tmp, result_tmp;
1539 uint32 acct_flags;
1540 struct dcerpc_binding_handle *b;
1542 status_tmp = cm_connect_sam(domain, mem_ctx,
1543 &samr_pipe, &samr_domain_handle);
1545 if (!NT_STATUS_IS_OK(status_tmp)) {
1546 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1547 nt_errstr(status_tmp)));
1548 goto done;
1551 b = samr_pipe->binding_handle;
1553 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1554 &samr_domain_handle,
1555 MAXIMUM_ALLOWED_ACCESS,
1556 my_info3->base.rid,
1557 &user_pol,
1558 &result_tmp);
1560 if (!NT_STATUS_IS_OK(status_tmp)) {
1561 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1562 nt_errstr(status_tmp)));
1563 goto done;
1565 if (!NT_STATUS_IS_OK(result_tmp)) {
1566 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1567 nt_errstr(result_tmp)));
1568 goto done;
1571 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1572 &user_pol,
1574 &info,
1575 &result_tmp);
1577 if (!NT_STATUS_IS_OK(status_tmp)) {
1578 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1579 nt_errstr(status_tmp)));
1580 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1581 goto done;
1583 if (!NT_STATUS_IS_OK(result_tmp)) {
1584 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1585 nt_errstr(result_tmp)));
1586 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1587 goto done;
1590 acct_flags = info->info16.acct_flags;
1592 if (acct_flags == 0) {
1593 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1594 goto done;
1597 my_info3->base.acct_flags = acct_flags;
1599 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1601 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1604 *info3 = my_info3;
1605 done:
1606 return result;
1609 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1610 struct winbindd_cli_state *state)
1612 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1613 NTSTATUS krb5_result = NT_STATUS_OK;
1614 fstring name_domain, name_user;
1615 char *mapped_user;
1616 fstring domain_user;
1617 struct netr_SamInfo3 *info3 = NULL;
1618 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1620 /* Ensure null termination */
1621 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1623 /* Ensure null termination */
1624 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1626 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1627 state->request->data.auth.user));
1629 /* Parse domain and username */
1631 name_map_status = normalize_name_unmap(state->mem_ctx,
1632 state->request->data.auth.user,
1633 &mapped_user);
1635 /* If the name normalization didnt' actually do anything,
1636 just use the original name */
1638 if (!NT_STATUS_IS_OK(name_map_status) &&
1639 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1641 mapped_user = state->request->data.auth.user;
1644 parse_domain_user(mapped_user, name_domain, name_user);
1646 if ( mapped_user != state->request->data.auth.user ) {
1647 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1648 *lp_winbind_separator(),
1649 name_user );
1650 strlcpy( state->request->data.auth.user, domain_user,
1651 sizeof(state->request->data.auth.user));
1654 if (!domain->online) {
1655 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1656 if (domain->startup) {
1657 /* Logons are very important to users. If we're offline and
1658 we get a request within the first 30 seconds of startup,
1659 try very hard to find a DC and go online. */
1661 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1662 "request in startup mode.\n", domain->name ));
1664 winbindd_flush_negative_conn_cache(domain);
1665 result = init_dc_connection(domain);
1669 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1671 /* Check for Kerberos authentication */
1672 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1674 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1675 /* save for later */
1676 krb5_result = result;
1679 if (NT_STATUS_IS_OK(result)) {
1680 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1681 goto process_result;
1682 } else {
1683 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1686 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1687 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1688 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1689 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1690 set_domain_offline( domain );
1691 goto cached_logon;
1694 /* there are quite some NT_STATUS errors where there is no
1695 * point in retrying with a samlogon, we explictly have to take
1696 * care not to increase the bad logon counter on the DC */
1698 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1699 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1700 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1701 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1702 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1703 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1704 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1705 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1706 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1707 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1708 goto done;
1711 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1712 DEBUG(3,("falling back to samlogon\n"));
1713 goto sam_logon;
1714 } else {
1715 goto cached_logon;
1719 sam_logon:
1720 /* Check for Samlogon authentication */
1721 if (domain->online) {
1722 result = winbindd_dual_pam_auth_samlogon(
1723 state->mem_ctx, domain,
1724 state->request->data.auth.user,
1725 state->request->data.auth.pass,
1726 state->request->flags,
1727 &info3);
1729 if (NT_STATUS_IS_OK(result)) {
1730 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1731 /* add the Krb5 err if we have one */
1732 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1733 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1735 goto process_result;
1738 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1739 nt_errstr(result)));
1741 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1742 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1743 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1745 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1746 set_domain_offline( domain );
1747 goto cached_logon;
1750 if (domain->online) {
1751 /* We're still online - fail. */
1752 goto done;
1756 cached_logon:
1757 /* Check for Cached logons */
1758 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1759 lp_winbind_offline_logon()) {
1761 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1763 if (NT_STATUS_IS_OK(result)) {
1764 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1765 goto process_result;
1766 } else {
1767 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1768 goto done;
1772 process_result:
1774 if (NT_STATUS_IS_OK(result)) {
1776 struct dom_sid user_sid;
1778 /* In all codepaths where result == NT_STATUS_OK info3 must have
1779 been initialized. */
1780 if (!info3) {
1781 result = NT_STATUS_INTERNAL_ERROR;
1782 goto done;
1785 sid_compose(&user_sid, info3->base.domain_sid,
1786 info3->base.rid);
1788 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1789 &user_sid);
1790 netsamlogon_cache_store(name_user, info3);
1792 /* save name_to_sid info as early as possible (only if
1793 this is our primary domain so we don't invalidate
1794 the cache entry by storing the seq_num for the wrong
1795 domain). */
1796 if ( domain->primary ) {
1797 cache_name2sid(domain, name_domain, name_user,
1798 SID_NAME_USER, &user_sid);
1801 /* Check if the user is in the right group */
1803 result = check_info3_in_group(
1804 info3,
1805 state->request->data.auth.require_membership_of_sid);
1806 if (!NT_STATUS_IS_OK(result)) {
1807 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1808 state->request->data.auth.user,
1809 state->request->data.auth.require_membership_of_sid));
1810 goto done;
1813 result = append_auth_data(state->mem_ctx, state->response,
1814 state->request->flags, info3,
1815 name_domain, name_user);
1816 if (!NT_STATUS_IS_OK(result)) {
1817 goto done;
1820 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1821 && lp_winbind_offline_logon()) {
1823 result = winbindd_store_creds(domain,
1824 state->request->data.auth.user,
1825 state->request->data.auth.pass,
1826 info3);
1829 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1830 struct winbindd_domain *our_domain = find_our_domain();
1832 /* This is not entirely correct I believe, but it is
1833 consistent. Only apply the password policy settings
1834 too warn users for our own domain. Cannot obtain these
1835 from trusted DCs all the time so don't do it at all.
1836 -- jerry */
1838 result = NT_STATUS_NOT_SUPPORTED;
1839 if (our_domain == domain ) {
1840 result = fillup_password_policy(
1841 our_domain, state->response);
1844 if (!NT_STATUS_IS_OK(result)
1845 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1847 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1848 domain->name, nt_errstr(result)));
1849 goto done;
1853 result = NT_STATUS_OK;
1856 done:
1857 /* give us a more useful (more correct?) error code */
1858 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1859 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1860 result = NT_STATUS_NO_LOGON_SERVERS;
1863 set_auth_errors(state->response, result);
1865 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1866 state->request->data.auth.user,
1867 state->response->data.auth.nt_status_string,
1868 state->response->data.auth.pam_error));
1870 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1873 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1874 struct winbindd_cli_state *state)
1876 NTSTATUS result;
1877 struct netr_SamInfo3 *info3 = NULL;
1878 const char *name_user = NULL;
1879 const char *name_domain = NULL;
1880 const char *workstation;
1882 DATA_BLOB lm_resp, nt_resp;
1884 /* This is child-only, so no check for privileged access is needed
1885 anymore */
1887 /* Ensure null termination */
1888 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1889 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1891 name_user = state->request->data.auth_crap.user;
1892 name_domain = state->request->data.auth_crap.domain;
1893 workstation = state->request->data.auth_crap.workstation;
1895 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1896 name_domain, name_user));
1898 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1899 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1900 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1901 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1902 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1903 state->request->data.auth_crap.lm_resp_len,
1904 state->request->data.auth_crap.nt_resp_len));
1905 result = NT_STATUS_INVALID_PARAMETER;
1906 goto done;
1910 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1911 state->request->data.auth_crap.lm_resp_len);
1913 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1914 nt_resp = data_blob_talloc(state->mem_ctx,
1915 state->request->extra_data.data,
1916 state->request->data.auth_crap.nt_resp_len);
1917 } else {
1918 nt_resp = data_blob_talloc(state->mem_ctx,
1919 state->request->data.auth_crap.nt_resp,
1920 state->request->data.auth_crap.nt_resp_len);
1923 if (strequal(name_domain, get_global_sam_name())) {
1924 DATA_BLOB chal_blob = data_blob_const(
1925 state->request->data.auth_crap.chal,
1926 sizeof(state->request->data.auth_crap.chal));
1928 result = winbindd_dual_auth_passdb(
1929 state->mem_ctx,
1930 state->request->data.auth_crap.logon_parameters,
1931 name_domain, name_user,
1932 &chal_blob, &lm_resp, &nt_resp, &info3);
1933 goto process_result;
1936 result = winbind_samlogon_retry_loop(domain,
1937 state->mem_ctx,
1938 state->request->data.auth_crap.logon_parameters,
1939 domain->dcname,
1940 name_user,
1941 name_domain,
1942 /* Bug #3248 - found by Stefan Burkei. */
1943 workstation, /* We carefully set this above so use it... */
1944 state->request->data.auth_crap.chal,
1945 lm_resp,
1946 nt_resp,
1947 &info3);
1948 if (!NT_STATUS_IS_OK(result)) {
1949 goto done;
1952 process_result:
1954 if (NT_STATUS_IS_OK(result)) {
1955 struct dom_sid user_sid;
1957 sid_compose(&user_sid, info3->base.domain_sid,
1958 info3->base.rid);
1959 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1960 &user_sid);
1961 netsamlogon_cache_store(name_user, info3);
1963 /* Check if the user is in the right group */
1965 result = check_info3_in_group(
1966 info3,
1967 state->request->data.auth_crap.require_membership_of_sid);
1968 if (!NT_STATUS_IS_OK(result)) {
1969 DEBUG(3, ("User %s is not in the required group (%s), so "
1970 "crap authentication is rejected\n",
1971 state->request->data.auth_crap.user,
1972 state->request->data.auth_crap.require_membership_of_sid));
1973 goto done;
1976 result = append_auth_data(state->mem_ctx, state->response,
1977 state->request->flags, info3,
1978 name_domain, name_user);
1979 if (!NT_STATUS_IS_OK(result)) {
1980 goto done;
1984 done:
1986 /* give us a more useful (more correct?) error code */
1987 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1988 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1989 result = NT_STATUS_NO_LOGON_SERVERS;
1992 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1993 result = nt_status_squash(result);
1996 set_auth_errors(state->response, result);
1998 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1999 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2000 name_domain,
2001 name_user,
2002 state->response->data.auth.nt_status_string,
2003 state->response->data.auth.pam_error));
2005 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2008 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2009 struct winbindd_cli_state *state)
2011 char *oldpass;
2012 char *newpass = NULL;
2013 struct policy_handle dom_pol;
2014 struct rpc_pipe_client *cli = NULL;
2015 bool got_info = false;
2016 struct samr_DomInfo1 *info = NULL;
2017 struct userPwdChangeFailureInformation *reject = NULL;
2018 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2019 fstring domain, user;
2020 struct dcerpc_binding_handle *b = NULL;
2022 ZERO_STRUCT(dom_pol);
2024 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2025 state->request->data.auth.user));
2027 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2028 goto done;
2031 /* Change password */
2033 oldpass = state->request->data.chauthtok.oldpass;
2034 newpass = state->request->data.chauthtok.newpass;
2036 /* Initialize reject reason */
2037 state->response->data.auth.reject_reason = Undefined;
2039 /* Get sam handle */
2041 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2042 &dom_pol);
2043 if (!NT_STATUS_IS_OK(result)) {
2044 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2045 goto done;
2048 b = cli->binding_handle;
2050 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2051 user,
2052 newpass,
2053 oldpass,
2054 &info,
2055 &reject);
2057 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2059 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2061 fill_in_password_policy(state->response, info);
2063 state->response->data.auth.reject_reason =
2064 reject->extendedFailureReason;
2066 got_info = true;
2069 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2070 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2071 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2072 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2074 /* only fallback when the chgpasswd_user3 call is not supported */
2075 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2076 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2077 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2078 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2080 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2081 nt_errstr(result)));
2083 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2085 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2086 Map to the same status code as Windows 2003. */
2088 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2089 result = NT_STATUS_PASSWORD_RESTRICTION;
2093 done:
2095 if (NT_STATUS_IS_OK(result)
2096 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2097 && lp_winbind_offline_logon()) {
2098 result = winbindd_update_creds_by_name(contact_domain, user,
2099 newpass);
2100 /* Again, this happens when we login from gdm or xdm
2101 * and the password expires, *BUT* cached crendentials
2102 * doesn't exist. winbindd_update_creds_by_name()
2103 * returns NT_STATUS_NO_SUCH_USER.
2104 * This is not a failure.
2105 * --- BoYang
2106 * */
2107 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2108 result = NT_STATUS_OK;
2111 if (!NT_STATUS_IS_OK(result)) {
2112 DEBUG(10, ("Failed to store creds: %s\n",
2113 nt_errstr(result)));
2114 goto process_result;
2118 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2120 NTSTATUS policy_ret;
2122 policy_ret = fillup_password_policy(
2123 contact_domain, state->response);
2125 /* failure of this is non critical, it will just provide no
2126 * additional information to the client why the change has
2127 * failed - Guenther */
2129 if (!NT_STATUS_IS_OK(policy_ret)) {
2130 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2131 goto process_result;
2135 process_result:
2137 if (strequal(contact_domain->name, get_global_sam_name())) {
2138 /* FIXME: internal rpc pipe does not cache handles yet */
2139 if (b) {
2140 if (is_valid_policy_hnd(&dom_pol)) {
2141 NTSTATUS _result;
2142 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2144 TALLOC_FREE(cli);
2148 set_auth_errors(state->response, result);
2150 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2151 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2152 domain,
2153 user,
2154 state->response->data.auth.nt_status_string,
2155 state->response->data.auth.pam_error));
2157 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2160 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2161 struct winbindd_cli_state *state)
2163 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2165 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2166 state->request->data.logoff.user));
2168 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2169 result = NT_STATUS_OK;
2170 goto process_result;
2173 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2174 result = NT_STATUS_OK;
2175 goto process_result;
2178 #ifdef HAVE_KRB5
2180 if (state->request->data.logoff.uid < 0) {
2181 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2182 goto process_result;
2185 /* what we need here is to find the corresponding krb5 ccache name *we*
2186 * created for a given username and destroy it */
2188 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2189 result = NT_STATUS_OK;
2190 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2191 goto process_result;
2194 if (!ccache_entry_identical(state->request->data.logoff.user,
2195 state->request->data.logoff.uid,
2196 state->request->data.logoff.krb5ccname)) {
2197 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2198 goto process_result;
2201 result = remove_ccache(state->request->data.logoff.user);
2202 if (!NT_STATUS_IS_OK(result)) {
2203 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2204 nt_errstr(result)));
2205 goto process_result;
2209 * Remove any mlock'ed memory creds in the child
2210 * we might be using for krb5 ticket renewal.
2213 winbindd_delete_memory_creds(state->request->data.logoff.user);
2215 #else
2216 result = NT_STATUS_NOT_SUPPORTED;
2217 #endif
2219 process_result:
2222 set_auth_errors(state->response, result);
2224 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2227 /* Change user password with auth crap*/
2229 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2231 NTSTATUS result;
2232 DATA_BLOB new_nt_password;
2233 DATA_BLOB old_nt_hash_enc;
2234 DATA_BLOB new_lm_password;
2235 DATA_BLOB old_lm_hash_enc;
2236 fstring domain,user;
2237 struct policy_handle dom_pol;
2238 struct winbindd_domain *contact_domain = domainSt;
2239 struct rpc_pipe_client *cli = NULL;
2240 struct dcerpc_binding_handle *b = NULL;
2242 ZERO_STRUCT(dom_pol);
2244 /* Ensure null termination */
2245 state->request->data.chng_pswd_auth_crap.user[
2246 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2247 state->request->data.chng_pswd_auth_crap.domain[
2248 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2249 *domain = 0;
2250 *user = 0;
2252 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2253 (unsigned long)state->pid,
2254 state->request->data.chng_pswd_auth_crap.domain,
2255 state->request->data.chng_pswd_auth_crap.user));
2257 if (lp_winbind_offline_logon()) {
2258 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2259 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2260 result = NT_STATUS_ACCESS_DENIED;
2261 goto done;
2264 if (*state->request->data.chng_pswd_auth_crap.domain) {
2265 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2266 } else {
2267 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2268 domain, user);
2270 if(!*domain) {
2271 DEBUG(3,("no domain specified with username (%s) - "
2272 "failing auth\n",
2273 state->request->data.chng_pswd_auth_crap.user));
2274 result = NT_STATUS_NO_SUCH_USER;
2275 goto done;
2279 if (!*domain && lp_winbind_use_default_domain()) {
2280 fstrcpy(domain,lp_workgroup());
2283 if(!*user) {
2284 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2287 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2288 (unsigned long)state->pid, domain, user));
2290 /* Change password */
2291 new_nt_password = data_blob_const(
2292 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2293 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2295 old_nt_hash_enc = data_blob_const(
2296 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2297 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2299 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2300 new_lm_password = data_blob_const(
2301 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2302 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2304 old_lm_hash_enc = data_blob_const(
2305 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2306 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2307 } else {
2308 new_lm_password = data_blob_null;
2309 old_lm_hash_enc = data_blob_null;
2312 /* Get sam handle */
2314 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2315 if (!NT_STATUS_IS_OK(result)) {
2316 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2317 goto done;
2320 b = cli->binding_handle;
2322 result = rpccli_samr_chng_pswd_auth_crap(
2323 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2324 new_lm_password, old_lm_hash_enc);
2326 done:
2328 if (strequal(contact_domain->name, get_global_sam_name())) {
2329 /* FIXME: internal rpc pipe does not cache handles yet */
2330 if (b) {
2331 if (is_valid_policy_hnd(&dom_pol)) {
2332 NTSTATUS _result;
2333 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2335 TALLOC_FREE(cli);
2339 set_auth_errors(state->response, result);
2341 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2342 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2343 domain, user,
2344 state->response->data.auth.nt_status_string,
2345 state->response->data.auth.pam_error));
2347 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2350 #ifdef HAVE_KRB5
2351 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2352 struct PAC_LOGON_INFO **logon_info)
2354 krb5_context krbctx = NULL;
2355 krb5_error_code k5ret;
2356 krb5_keytab keytab;
2357 krb5_kt_cursor cursor;
2358 krb5_keytab_entry entry;
2359 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2361 ZERO_STRUCT(entry);
2362 ZERO_STRUCT(cursor);
2364 k5ret = krb5_init_context(&krbctx);
2365 if (k5ret) {
2366 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2367 error_message(k5ret)));
2368 status = krb5_to_nt_status(k5ret);
2369 goto out;
2372 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2373 if (k5ret) {
2374 DEBUG(1, ("Failed to get keytab: %s\n",
2375 error_message(k5ret)));
2376 status = krb5_to_nt_status(k5ret);
2377 goto out_free;
2380 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2381 if (k5ret) {
2382 DEBUG(1, ("Failed to start seq: %s\n",
2383 error_message(k5ret)));
2384 status = krb5_to_nt_status(k5ret);
2385 goto out_keytab;
2388 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2389 while (k5ret == 0) {
2390 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2391 krbctx, NULL,
2392 KRB5_KT_KEY(&entry), NULL, 0,
2393 logon_info);
2394 if (NT_STATUS_IS_OK(status)) {
2395 break;
2397 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2398 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2401 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2402 if (k5ret) {
2403 DEBUG(1, ("Failed to end seq: %s\n",
2404 error_message(k5ret)));
2406 out_keytab:
2407 k5ret = krb5_kt_close(krbctx, keytab);
2408 if (k5ret) {
2409 DEBUG(1, ("Failed to close keytab: %s\n",
2410 error_message(k5ret)));
2412 out_free:
2413 krb5_free_context(krbctx);
2414 out:
2415 return status;
2418 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2419 struct netr_SamInfo3 **info3)
2421 struct winbindd_request *req = state->request;
2422 DATA_BLOB pac_blob;
2423 struct PAC_LOGON_INFO *logon_info = NULL;
2424 NTSTATUS result;
2426 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2427 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2428 if (!NT_STATUS_IS_OK(result) &&
2429 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2430 DEBUG(1, ("Error during PAC signature verification: %s\n",
2431 nt_errstr(result)));
2432 return result;
2435 if (logon_info) {
2436 /* Signature verification succeeded, trust the PAC */
2437 netsamlogon_cache_store(NULL, &logon_info->info3);
2439 } else {
2440 /* Try without signature verification */
2441 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2442 NULL, NULL, NULL, 0,
2443 &logon_info);
2444 if (!NT_STATUS_IS_OK(result)) {
2445 DEBUG(10, ("Could not extract PAC: %s\n",
2446 nt_errstr(result)));
2447 return result;
2451 *info3 = &logon_info->info3;
2453 return NT_STATUS_OK;
2455 #else /* HAVE_KRB5 */
2456 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2457 struct netr_SamInfo3 **info3)
2459 return NT_STATUS_NO_SUCH_USER;
2461 #endif /* HAVE_KRB5 */