auth/credentials: Better integrate fetch of secrets.tdb and secrets.ldb records
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob6ad0baf1964258e9e1c31fff33b5fcec29c3dd7b
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"
42 #undef DBGC_CLASS
43 #define DBGC_CLASS DBGC_WINBIND
45 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
47 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
48 struct winbindd_response *resp,
49 struct netr_SamInfo3 *info3)
51 char *ex;
52 uint32_t i;
54 resp->data.auth.info3.logon_time =
55 nt_time_to_unix(info3->base.logon_time);
56 resp->data.auth.info3.logoff_time =
57 nt_time_to_unix(info3->base.logoff_time);
58 resp->data.auth.info3.kickoff_time =
59 nt_time_to_unix(info3->base.kickoff_time);
60 resp->data.auth.info3.pass_last_set_time =
61 nt_time_to_unix(info3->base.last_password_change);
62 resp->data.auth.info3.pass_can_change_time =
63 nt_time_to_unix(info3->base.allow_password_change);
64 resp->data.auth.info3.pass_must_change_time =
65 nt_time_to_unix(info3->base.force_password_change);
67 resp->data.auth.info3.logon_count = info3->base.logon_count;
68 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
70 resp->data.auth.info3.user_rid = info3->base.rid;
71 resp->data.auth.info3.group_rid = info3->base.primary_gid;
72 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
74 resp->data.auth.info3.num_groups = info3->base.groups.count;
75 resp->data.auth.info3.user_flgs = info3->base.user_flags;
77 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
78 resp->data.auth.info3.num_other_sids = info3->sidcount;
80 fstrcpy(resp->data.auth.info3.user_name,
81 info3->base.account_name.string);
82 fstrcpy(resp->data.auth.info3.full_name,
83 info3->base.full_name.string);
84 fstrcpy(resp->data.auth.info3.logon_script,
85 info3->base.logon_script.string);
86 fstrcpy(resp->data.auth.info3.profile_path,
87 info3->base.profile_path.string);
88 fstrcpy(resp->data.auth.info3.home_dir,
89 info3->base.home_directory.string);
90 fstrcpy(resp->data.auth.info3.dir_drive,
91 info3->base.home_drive.string);
93 fstrcpy(resp->data.auth.info3.logon_srv,
94 info3->base.logon_server.string);
95 fstrcpy(resp->data.auth.info3.logon_dom,
96 info3->base.logon_domain.string);
98 ex = talloc_strdup(mem_ctx, "");
99 NT_STATUS_HAVE_NO_MEMORY(ex);
101 for (i=0; i < info3->base.groups.count; i++) {
102 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
103 info3->base.groups.rids[i].rid,
104 info3->base.groups.rids[i].attributes);
105 NT_STATUS_HAVE_NO_MEMORY(ex);
108 for (i=0; i < info3->sidcount; i++) {
109 char *sid;
111 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
112 NT_STATUS_HAVE_NO_MEMORY(sid);
114 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
115 sid,
116 info3->sids[i].attributes);
117 NT_STATUS_HAVE_NO_MEMORY(ex);
119 talloc_free(sid);
122 resp->extra_data.data = ex;
123 resp->length += talloc_get_size(ex);
125 return NT_STATUS_OK;
128 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
129 struct winbindd_response *resp,
130 struct netr_SamInfo3 *info3)
132 DATA_BLOB blob;
133 enum ndr_err_code ndr_err;
135 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
136 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
137 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
138 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
139 return ndr_map_error2ntstatus(ndr_err);
142 resp->extra_data.data = blob.data;
143 resp->length += blob.length;
145 return NT_STATUS_OK;
148 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
149 struct winbindd_response *resp,
150 const struct netr_SamInfo3 *info3,
151 const char *name_domain,
152 const char *name_user)
154 /* We've been asked to return the unix username, per
155 'winbind use default domain' settings and the like */
157 const char *nt_username, *nt_domain;
159 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
160 if (!nt_domain) {
161 /* If the server didn't give us one, just use the one
162 * we sent them */
163 nt_domain = name_domain;
166 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
167 if (!nt_username) {
168 /* If the server didn't give us one, just use the one
169 * we sent them */
170 nt_username = name_user;
173 fill_domain_username(resp->data.auth.unix_username,
174 nt_domain, nt_username, true);
176 DEBUG(5, ("Setting unix username to [%s]\n",
177 resp->data.auth.unix_username));
179 return NT_STATUS_OK;
182 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
183 struct winbindd_response *resp,
184 const struct netr_SamInfo3 *info3,
185 const char *name_domain,
186 const char *name_user)
188 char *afsname = NULL;
189 char *cell;
190 char *token;
192 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
193 if (afsname == NULL) {
194 return NT_STATUS_NO_MEMORY;
197 afsname = talloc_string_sub(mem_ctx,
198 lp_afs_username_map(),
199 "%D", name_domain);
200 afsname = talloc_string_sub(mem_ctx, afsname,
201 "%u", name_user);
202 afsname = talloc_string_sub(mem_ctx, afsname,
203 "%U", name_user);
206 struct dom_sid user_sid;
207 fstring sidstr;
209 sid_compose(&user_sid, info3->base.domain_sid,
210 info3->base.rid);
211 sid_to_fstring(sidstr, &user_sid);
212 afsname = talloc_string_sub(mem_ctx, afsname,
213 "%s", sidstr);
216 if (afsname == NULL) {
217 return NT_STATUS_NO_MEMORY;
220 if (!strlower_m(afsname)) {
221 return NT_STATUS_INVALID_PARAMETER;
224 DEBUG(10, ("Generating token for user %s\n", afsname));
226 cell = strchr(afsname, '@');
228 if (cell == NULL) {
229 return NT_STATUS_NO_MEMORY;
232 *cell = '\0';
233 cell += 1;
235 token = afs_createtoken_str(afsname, cell);
236 if (token == NULL) {
237 return NT_STATUS_OK;
239 resp->extra_data.data = talloc_strdup(mem_ctx, token);
240 if (resp->extra_data.data == NULL) {
241 return NT_STATUS_NO_MEMORY;
243 resp->length += strlen((const char *)resp->extra_data.data)+1;
245 return NT_STATUS_OK;
248 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
249 const char *group_sid)
251 * Check whether a user belongs to a group or list of groups.
253 * @param mem_ctx talloc memory context.
254 * @param info3 user information, including group membership info.
255 * @param group_sid One or more groups , separated by commas.
257 * @return NT_STATUS_OK on success,
258 * NT_STATUS_LOGON_FAILURE if the user does not belong,
259 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
262 struct dom_sid *require_membership_of_sid;
263 uint32_t num_require_membership_of_sid;
264 char *req_sid;
265 const char *p;
266 struct dom_sid sid;
267 size_t i;
268 struct security_token *token;
269 TALLOC_CTX *frame = talloc_stackframe();
270 NTSTATUS status;
272 /* Parse the 'required group' SID */
274 if (!group_sid || !group_sid[0]) {
275 /* NO sid supplied, all users may access */
276 TALLOC_FREE(frame);
277 return NT_STATUS_OK;
280 token = talloc_zero(talloc_tos(), struct security_token);
281 if (token == NULL) {
282 DEBUG(0, ("talloc failed\n"));
283 TALLOC_FREE(frame);
284 return NT_STATUS_NO_MEMORY;
287 num_require_membership_of_sid = 0;
288 require_membership_of_sid = NULL;
290 p = group_sid;
292 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
293 if (!string_to_sid(&sid, req_sid)) {
294 DEBUG(0, ("check_info3_in_group: could not parse %s "
295 "as a SID!", req_sid));
296 TALLOC_FREE(frame);
297 return NT_STATUS_INVALID_PARAMETER;
300 status = add_sid_to_array(talloc_tos(), &sid,
301 &require_membership_of_sid,
302 &num_require_membership_of_sid);
303 if (!NT_STATUS_IS_OK(status)) {
304 DEBUG(0, ("add_sid_to_array failed\n"));
305 TALLOC_FREE(frame);
306 return status;
310 status = sid_array_from_info3(talloc_tos(), info3,
311 &token->sids,
312 &token->num_sids,
313 true);
314 if (!NT_STATUS_IS_OK(status)) {
315 TALLOC_FREE(frame);
316 return status;
319 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
320 token))
321 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
322 token))) {
323 DEBUG(3, ("could not add aliases: %s\n",
324 nt_errstr(status)));
325 TALLOC_FREE(frame);
326 return status;
329 security_token_debug(DBGC_CLASS, 10, token);
331 for (i=0; i<num_require_membership_of_sid; i++) {
332 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
333 &require_membership_of_sid[i])));
334 if (nt_token_check_sid(&require_membership_of_sid[i],
335 token)) {
336 DEBUG(10, ("Access ok\n"));
337 TALLOC_FREE(frame);
338 return NT_STATUS_OK;
342 /* Do not distinguish this error from a wrong username/pw */
344 TALLOC_FREE(frame);
345 return NT_STATUS_LOGON_FAILURE;
348 struct winbindd_domain *find_auth_domain(uint8_t flags,
349 const char *domain_name)
351 struct winbindd_domain *domain;
353 if (IS_DC) {
354 domain = find_domain_from_name_noinit(domain_name);
355 if (domain == NULL) {
356 DEBUG(3, ("Authentication for domain [%s] refused "
357 "as it is not a trusted domain\n",
358 domain_name));
360 return domain;
363 if (strequal(domain_name, get_global_sam_name())) {
364 return find_domain_from_name_noinit(domain_name);
367 /* we can auth against trusted domains */
368 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
369 domain = find_domain_from_name_noinit(domain_name);
370 if (domain == NULL) {
371 DEBUG(3, ("Authentication for domain [%s] skipped "
372 "as it is not a trusted domain\n",
373 domain_name));
374 } else {
375 return domain;
379 return find_our_domain();
382 static void fill_in_password_policy(struct winbindd_response *r,
383 const struct samr_DomInfo1 *p)
385 r->data.auth.policy.min_length_password =
386 p->min_password_length;
387 r->data.auth.policy.password_history =
388 p->password_history_length;
389 r->data.auth.policy.password_properties =
390 p->password_properties;
391 r->data.auth.policy.expire =
392 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
393 r->data.auth.policy.min_passwordage =
394 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
397 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
398 struct winbindd_response *response)
400 TALLOC_CTX *frame = talloc_stackframe();
401 struct winbindd_methods *methods;
402 NTSTATUS status;
403 struct samr_DomInfo1 password_policy;
405 if ( !winbindd_can_contact_domain( domain ) ) {
406 DEBUG(5,("fillup_password_policy: No inbound trust to "
407 "contact domain %s\n", domain->name));
408 status = NT_STATUS_NOT_SUPPORTED;
409 goto done;
412 methods = domain->methods;
414 status = methods->password_policy(domain, talloc_tos(), &password_policy);
415 if (NT_STATUS_IS_ERR(status)) {
416 goto done;
419 fill_in_password_policy(response, &password_policy);
421 done:
422 TALLOC_FREE(frame);
423 return NT_STATUS_OK;
426 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
427 TALLOC_CTX *mem_ctx,
428 uint16 *lockout_threshold)
430 struct winbindd_methods *methods;
431 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
432 struct samr_DomInfo12 lockout_policy;
434 *lockout_threshold = 0;
436 methods = domain->methods;
438 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
439 if (NT_STATUS_IS_ERR(status)) {
440 return status;
443 *lockout_threshold = lockout_policy.lockout_threshold;
445 return NT_STATUS_OK;
448 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
449 TALLOC_CTX *mem_ctx,
450 uint32 *password_properties)
452 struct winbindd_methods *methods;
453 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
454 struct samr_DomInfo1 password_policy;
456 *password_properties = 0;
458 methods = domain->methods;
460 status = methods->password_policy(domain, mem_ctx, &password_policy);
461 if (NT_STATUS_IS_ERR(status)) {
462 return status;
465 *password_properties = password_policy.password_properties;
467 return NT_STATUS_OK;
470 #ifdef HAVE_KRB5
472 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
473 const char *type,
474 uid_t uid,
475 const char **user_ccache_file)
477 /* accept FILE and WRFILE as krb5_cc_type from the client and then
478 * build the full ccname string based on the user's uid here -
479 * Guenther*/
481 const char *gen_cc = NULL;
483 if (uid != -1) {
484 if (strequal(type, "FILE")) {
485 gen_cc = talloc_asprintf(
486 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
488 if (strequal(type, "WRFILE")) {
489 gen_cc = talloc_asprintf(
490 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
494 *user_ccache_file = gen_cc;
496 if (gen_cc == NULL) {
497 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
499 if (gen_cc == NULL) {
500 DEBUG(0,("out of memory\n"));
501 return NULL;
504 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
505 (*user_ccache_file == NULL) ? " (internal)":""));
507 return gen_cc;
510 #endif
512 uid_t get_uid_from_request(struct winbindd_request *request)
514 uid_t uid;
516 uid = request->data.auth.uid;
518 if (uid < 0) {
519 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
520 return -1;
522 return uid;
525 /**********************************************************************
526 Authenticate a user with a clear text password using Kerberos and fill up
527 ccache if required
528 **********************************************************************/
530 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
531 struct winbindd_domain *domain,
532 const char *user,
533 const char *pass,
534 const char *krb5_cc_type,
535 uid_t uid,
536 struct netr_SamInfo3 **info3,
537 fstring krb5ccname)
539 #ifdef HAVE_KRB5
540 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
541 krb5_error_code krb5_ret;
542 const char *cc = NULL;
543 const char *principal_s = NULL;
544 const char *service = NULL;
545 char *realm = NULL;
546 fstring name_domain, name_user;
547 time_t ticket_lifetime = 0;
548 time_t renewal_until = 0;
549 ADS_STRUCT *ads;
550 time_t time_offset = 0;
551 const char *user_ccache_file;
552 struct PAC_LOGON_INFO *logon_info = NULL;
554 *info3 = NULL;
556 /* 1st step:
557 * prepare a krb5_cc_cache string for the user */
559 if (uid == -1) {
560 DEBUG(0,("no valid uid\n"));
563 cc = generate_krb5_ccache(mem_ctx,
564 krb5_cc_type,
565 uid,
566 &user_ccache_file);
567 if (cc == NULL) {
568 return NT_STATUS_NO_MEMORY;
572 /* 2nd step:
573 * get kerberos properties */
575 if (domain->private_data) {
576 ads = (ADS_STRUCT *)domain->private_data;
577 time_offset = ads->auth.time_offset;
581 /* 3rd step:
582 * do kerberos auth and setup ccache as the user */
584 parse_domain_user(user, name_domain, name_user);
586 realm = domain->alt_name;
587 if (!strupper_m(realm)) {
588 return NT_STATUS_INVALID_PARAMETER;
591 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
592 if (principal_s == NULL) {
593 return NT_STATUS_NO_MEMORY;
596 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
597 if (service == NULL) {
598 return NT_STATUS_NO_MEMORY;
601 /* if this is a user ccache, we need to act as the user to let the krb5
602 * library handle the chown, etc. */
604 /************************ ENTERING NON-ROOT **********************/
606 if (user_ccache_file != NULL) {
607 set_effective_uid(uid);
608 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
611 result = kerberos_return_pac(mem_ctx,
612 principal_s,
613 pass,
614 time_offset,
615 &ticket_lifetime,
616 &renewal_until,
618 true,
619 true,
620 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
621 NULL,
622 &logon_info);
623 if (user_ccache_file != NULL) {
624 gain_root_privilege();
627 /************************ RETURNED TO ROOT **********************/
629 if (!NT_STATUS_IS_OK(result)) {
630 goto failed;
633 *info3 = &logon_info->info3;
635 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
636 principal_s));
638 /* if we had a user's ccache then return that string for the pam
639 * environment */
641 if (user_ccache_file != NULL) {
643 fstrcpy(krb5ccname, user_ccache_file);
645 result = add_ccache_to_list(principal_s,
647 service,
648 user,
649 pass,
650 realm,
651 uid,
652 time(NULL),
653 ticket_lifetime,
654 renewal_until,
655 false);
657 if (!NT_STATUS_IS_OK(result)) {
658 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
659 nt_errstr(result)));
661 } else {
663 /* need to delete the memory cred cache, it is not used anymore */
665 krb5_ret = ads_kdestroy(cc);
666 if (krb5_ret) {
667 DEBUG(3,("winbindd_raw_kerberos_login: "
668 "could not destroy krb5 credential cache: "
669 "%s\n", error_message(krb5_ret)));
674 return NT_STATUS_OK;
676 failed:
678 /* we could have created a new credential cache with a valid tgt in it
679 * but we werent able to get or verify the service ticket for this
680 * local host and therefor didn't get the PAC, we need to remove that
681 * cache entirely now */
683 krb5_ret = ads_kdestroy(cc);
684 if (krb5_ret) {
685 DEBUG(3,("winbindd_raw_kerberos_login: "
686 "could not destroy krb5 credential cache: "
687 "%s\n", error_message(krb5_ret)));
690 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
691 DEBUG(3,("winbindd_raw_kerberos_login: "
692 "could not remove ccache for user %s\n",
693 user));
696 return result;
697 #else
698 return NT_STATUS_NOT_SUPPORTED;
699 #endif /* HAVE_KRB5 */
702 /****************************************************************
703 ****************************************************************/
705 bool check_request_flags(uint32_t flags)
707 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
708 WBFLAG_PAM_INFO3_TEXT |
709 WBFLAG_PAM_INFO3_NDR;
711 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
712 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
713 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
714 !(flags & flags_edata) ) {
715 return true;
718 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
719 flags));
721 return false;
724 /****************************************************************
725 ****************************************************************/
727 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
728 struct winbindd_response *resp,
729 uint32_t request_flags,
730 struct netr_SamInfo3 *info3,
731 const char *name_domain,
732 const char *name_user)
734 NTSTATUS result;
736 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
737 memcpy(resp->data.auth.user_session_key,
738 info3->base.key.key,
739 sizeof(resp->data.auth.user_session_key)
740 /* 16 */);
743 if (request_flags & WBFLAG_PAM_LMKEY) {
744 memcpy(resp->data.auth.first_8_lm_hash,
745 info3->base.LMSessKey.key,
746 sizeof(resp->data.auth.first_8_lm_hash)
747 /* 8 */);
750 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
751 result = append_unix_username(mem_ctx, resp,
752 info3, name_domain, name_user);
753 if (!NT_STATUS_IS_OK(result)) {
754 DEBUG(10,("Failed to append Unix Username: %s\n",
755 nt_errstr(result)));
756 return result;
760 /* currently, anything from here on potentially overwrites extra_data. */
762 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
763 result = append_info3_as_ndr(mem_ctx, resp, info3);
764 if (!NT_STATUS_IS_OK(result)) {
765 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
766 nt_errstr(result)));
767 return result;
771 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
772 result = append_info3_as_txt(mem_ctx, resp, info3);
773 if (!NT_STATUS_IS_OK(result)) {
774 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
775 nt_errstr(result)));
776 return result;
780 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
781 result = append_afs_token(mem_ctx, resp,
782 info3, name_domain, name_user);
783 if (!NT_STATUS_IS_OK(result)) {
784 DEBUG(10,("Failed to append AFS token: %s\n",
785 nt_errstr(result)));
786 return result;
790 return NT_STATUS_OK;
793 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
794 struct winbindd_cli_state *state,
795 struct netr_SamInfo3 **info3)
797 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
798 uint16 max_allowed_bad_attempts;
799 fstring name_domain, name_user;
800 struct dom_sid sid;
801 enum lsa_SidType type;
802 uchar new_nt_pass[NT_HASH_LEN];
803 const uint8 *cached_nt_pass;
804 const uint8 *cached_salt;
805 struct netr_SamInfo3 *my_info3;
806 time_t kickoff_time, must_change_time;
807 bool password_good = false;
808 #ifdef HAVE_KRB5
809 struct winbindd_tdc_domain *tdc_domain = NULL;
810 #endif
812 *info3 = NULL;
814 ZERO_STRUCTP(info3);
816 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
818 /* Parse domain and username */
820 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
823 if (!lookup_cached_name(name_domain,
824 name_user,
825 &sid,
826 &type)) {
827 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
828 return NT_STATUS_NO_SUCH_USER;
831 if (type != SID_NAME_USER) {
832 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
833 return NT_STATUS_LOGON_FAILURE;
836 result = winbindd_get_creds(domain,
837 state->mem_ctx,
838 &sid,
839 &my_info3,
840 &cached_nt_pass,
841 &cached_salt);
842 if (!NT_STATUS_IS_OK(result)) {
843 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
844 return result;
847 *info3 = my_info3;
849 E_md4hash(state->request->data.auth.pass, new_nt_pass);
851 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
852 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
853 if (cached_salt) {
854 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
857 if (cached_salt) {
858 /* In this case we didn't store the nt_hash itself,
859 but the MD5 combination of salt + nt_hash. */
860 uchar salted_hash[NT_HASH_LEN];
861 E_md5hash(cached_salt, new_nt_pass, salted_hash);
863 password_good = (memcmp(cached_nt_pass, salted_hash,
864 NT_HASH_LEN) == 0);
865 } else {
866 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
867 password_good = (memcmp(cached_nt_pass, new_nt_pass,
868 NT_HASH_LEN) == 0);
871 if (password_good) {
873 /* User *DOES* know the password, update logon_time and reset
874 * bad_pw_count */
876 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
878 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
879 return NT_STATUS_ACCOUNT_LOCKED_OUT;
882 if (my_info3->base.acct_flags & ACB_DISABLED) {
883 return NT_STATUS_ACCOUNT_DISABLED;
886 if (my_info3->base.acct_flags & ACB_WSTRUST) {
887 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
890 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
891 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
894 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
895 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
898 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
899 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
900 my_info3->base.acct_flags));
901 return NT_STATUS_LOGON_FAILURE;
904 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
905 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
906 return NT_STATUS_ACCOUNT_EXPIRED;
909 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
910 if (must_change_time != 0 && must_change_time < time(NULL)) {
911 /* we allow grace logons when the password has expired */
912 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
913 /* return NT_STATUS_PASSWORD_EXPIRED; */
914 goto success;
917 #ifdef HAVE_KRB5
918 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
919 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
920 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
921 /* used to cope with the case winbindd starting without network. */
922 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
924 uid_t uid = -1;
925 const char *cc = NULL;
926 char *realm = NULL;
927 const char *principal_s = NULL;
928 const char *service = NULL;
929 const char *user_ccache_file;
931 uid = get_uid_from_request(state->request);
932 if (uid == -1) {
933 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
934 return NT_STATUS_INVALID_PARAMETER;
937 cc = generate_krb5_ccache(state->mem_ctx,
938 state->request->data.auth.krb5_cc_type,
939 state->request->data.auth.uid,
940 &user_ccache_file);
941 if (cc == NULL) {
942 return NT_STATUS_NO_MEMORY;
945 realm = domain->alt_name;
946 if (!strupper_m(realm)) {
947 return NT_STATUS_INVALID_PARAMETER;
950 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
951 if (principal_s == NULL) {
952 return NT_STATUS_NO_MEMORY;
955 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
956 if (service == NULL) {
957 return NT_STATUS_NO_MEMORY;
960 if (user_ccache_file != NULL) {
962 fstrcpy(state->response->data.auth.krb5ccname,
963 user_ccache_file);
965 result = add_ccache_to_list(principal_s,
967 service,
968 state->request->data.auth.user,
969 state->request->data.auth.pass,
970 domain->alt_name,
971 uid,
972 time(NULL),
973 time(NULL) + lp_winbind_cache_time(),
974 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
975 true);
977 if (!NT_STATUS_IS_OK(result)) {
978 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
979 "to add ccache to list: %s\n",
980 nt_errstr(result)));
984 #endif /* HAVE_KRB5 */
985 success:
986 /* FIXME: we possibly should handle logon hours as well (does xp when
987 * offline?) see auth/auth_sam.c:sam_account_ok for details */
989 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
990 my_info3->base.bad_password_count = 0;
992 result = winbindd_update_creds_by_info3(domain,
993 state->request->data.auth.user,
994 state->request->data.auth.pass,
995 my_info3);
996 if (!NT_STATUS_IS_OK(result)) {
997 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
998 nt_errstr(result)));
999 return result;
1002 return NT_STATUS_OK;
1006 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1007 if (domain->online == false) {
1008 goto failed;
1011 /* failure of this is not critical */
1012 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1013 if (!NT_STATUS_IS_OK(result)) {
1014 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1015 "Won't be able to honour account lockout policies\n"));
1018 /* increase counter */
1019 my_info3->base.bad_password_count++;
1021 if (max_allowed_bad_attempts == 0) {
1022 goto failed;
1025 /* lockout user */
1026 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1028 uint32 password_properties;
1030 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1031 if (!NT_STATUS_IS_OK(result)) {
1032 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1035 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1036 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1037 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1041 failed:
1042 result = winbindd_update_creds_by_info3(domain,
1043 state->request->data.auth.user,
1044 NULL,
1045 my_info3);
1047 if (!NT_STATUS_IS_OK(result)) {
1048 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1049 nt_errstr(result)));
1052 return NT_STATUS_LOGON_FAILURE;
1055 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1056 struct winbindd_cli_state *state,
1057 struct netr_SamInfo3 **info3)
1059 struct winbindd_domain *contact_domain;
1060 fstring name_domain, name_user;
1061 NTSTATUS result;
1063 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1065 /* Parse domain and username */
1067 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1069 /* what domain should we contact? */
1071 if ( IS_DC ) {
1072 if (!(contact_domain = find_domain_from_name(name_domain))) {
1073 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1074 state->request->data.auth.user, name_domain, name_user, name_domain));
1075 result = NT_STATUS_NO_SUCH_USER;
1076 goto done;
1079 } else {
1080 if (is_myname(name_domain)) {
1081 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1082 result = NT_STATUS_NO_SUCH_USER;
1083 goto done;
1086 contact_domain = find_domain_from_name(name_domain);
1087 if (contact_domain == NULL) {
1088 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1089 state->request->data.auth.user, name_domain, name_user, name_domain));
1091 result = NT_STATUS_NO_SUCH_USER;
1092 goto done;
1096 if (contact_domain->initialized &&
1097 contact_domain->active_directory) {
1098 goto try_login;
1101 if (!contact_domain->initialized) {
1102 init_dc_connection(contact_domain);
1105 if (!contact_domain->active_directory) {
1106 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1107 return NT_STATUS_INVALID_LOGON_TYPE;
1109 try_login:
1110 result = winbindd_raw_kerberos_login(
1111 state->mem_ctx, contact_domain,
1112 state->request->data.auth.user,
1113 state->request->data.auth.pass,
1114 state->request->data.auth.krb5_cc_type,
1115 get_uid_from_request(state->request),
1116 info3, state->response->data.auth.krb5ccname);
1117 done:
1118 return result;
1121 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1122 uint32_t logon_parameters,
1123 const char *domain, const char *user,
1124 const DATA_BLOB *challenge,
1125 const DATA_BLOB *lm_resp,
1126 const DATA_BLOB *nt_resp,
1127 struct netr_SamInfo3 **pinfo3)
1129 struct auth_usersupplied_info *user_info = NULL;
1130 struct tsocket_address *local;
1131 NTSTATUS status;
1132 int rc;
1134 rc = tsocket_address_inet_from_strings(mem_ctx,
1135 "ip",
1136 "127.0.0.1",
1138 &local);
1139 if (rc < 0) {
1140 return NT_STATUS_NO_MEMORY;
1142 status = make_user_info(&user_info, user, user, domain, domain,
1143 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1144 NULL, AUTH_PASSWORD_RESPONSE);
1145 if (!NT_STATUS_IS_OK(status)) {
1146 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1147 return status;
1149 user_info->logon_parameters = logon_parameters;
1151 /* We don't want any more mapping of the username */
1152 user_info->mapped_state = True;
1154 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1155 pinfo3);
1156 free_user_info(&user_info);
1157 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1158 user, nt_errstr(status)));
1159 return status;
1162 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1163 TALLOC_CTX *mem_ctx,
1164 uint32_t logon_parameters,
1165 const char *server,
1166 const char *username,
1167 const char *domainname,
1168 const char *workstation,
1169 const uint8_t chal[8],
1170 DATA_BLOB lm_response,
1171 DATA_BLOB nt_response,
1172 struct netr_SamInfo3 **info3)
1174 int attempts = 0;
1175 bool retry = false;
1176 NTSTATUS result;
1178 do {
1179 struct rpc_pipe_client *netlogon_pipe;
1180 const struct pipe_auth_data *auth;
1181 uint32_t neg_flags = 0;
1183 ZERO_STRUCTP(info3);
1184 retry = false;
1186 result = cm_connect_netlogon(domain, &netlogon_pipe);
1188 if (!NT_STATUS_IS_OK(result)) {
1189 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1190 nt_errstr(result)));
1191 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1192 if (attempts > 0) {
1193 DEBUG(3, ("This is the second problem for this "
1194 "particular call, forcing the close of "
1195 "this connection\n"));
1196 invalidate_cm_connection(&domain->conn);
1197 } else {
1198 DEBUG(3, ("First call to cm_connect_netlogon "
1199 "has timed out, retrying\n"));
1200 continue;
1203 return result;
1205 auth = netlogon_pipe->auth;
1206 if (netlogon_pipe->dc) {
1207 neg_flags = netlogon_pipe->dc->negotiate_flags;
1210 /* It is really important to try SamLogonEx here,
1211 * because in a clustered environment, we want to use
1212 * one machine account from multiple physical
1213 * computers.
1215 * With a normal SamLogon call, we must keep the
1216 * credentials chain updated and intact between all
1217 * users of the machine account (which would imply
1218 * cross-node communication for every NTLM logon).
1220 * (The credentials chain is not per NETLOGON pipe
1221 * connection, but globally on the server/client pair
1222 * by machine name).
1224 * When using SamLogonEx, the credentials are not
1225 * supplied, but the session key is implied by the
1226 * wrapping SamLogon context.
1228 * -- abartlet 21 April 2008
1230 * It's also important to use NetlogonValidationSamInfo4 (6),
1231 * because it relies on the rpc transport encryption
1232 * and avoids using the global netlogon schannel
1233 * session key to en/decrypt secret information
1234 * like the user_session_key for network logons.
1236 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1237 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1238 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1239 * are the indication that the server supports
1240 * NetlogonValidationSamInfo4 (6). And it must only
1241 * be used if "SealSecureChannel" is used.
1243 * -- metze 4 February 2011
1246 if (auth == NULL) {
1247 domain->can_do_validation6 = false;
1248 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1249 domain->can_do_validation6 = false;
1250 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1251 domain->can_do_validation6 = false;
1252 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1253 domain->can_do_validation6 = false;
1254 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1255 domain->can_do_validation6 = false;
1258 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1259 result = rpccli_netlogon_sam_network_logon_ex(
1260 netlogon_pipe,
1261 mem_ctx,
1262 logon_parameters,
1263 server, /* server name */
1264 username, /* user name */
1265 domainname, /* target domain */
1266 workstation, /* workstation */
1267 chal,
1269 lm_response,
1270 nt_response,
1271 info3);
1272 } else {
1273 result = rpccli_netlogon_sam_network_logon(
1274 netlogon_pipe,
1275 mem_ctx,
1276 logon_parameters,
1277 server, /* server name */
1278 username, /* user name */
1279 domainname, /* target domain */
1280 workstation, /* workstation */
1281 chal,
1282 domain->can_do_validation6 ? 6 : 3,
1283 lm_response,
1284 nt_response,
1285 info3);
1288 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1291 * It's likely that the server also does not support
1292 * validation level 6
1294 domain->can_do_validation6 = false;
1296 if (domain->can_do_samlogon_ex) {
1297 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1298 "retrying with NetSamLogon\n"));
1299 domain->can_do_samlogon_ex = false;
1300 retry = true;
1301 continue;
1305 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1306 * (no Ex). This happens against old Samba
1307 * DCs. Drop the connection.
1309 invalidate_cm_connection(&domain->conn);
1310 result = NT_STATUS_LOGON_FAILURE;
1311 break;
1314 if (domain->can_do_validation6 &&
1315 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1316 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1317 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1318 DEBUG(3,("Got a DC that can not do validation level 6, "
1319 "retrying with level 3\n"));
1320 domain->can_do_validation6 = false;
1321 retry = true;
1322 continue;
1326 * we increment this after the "feature negotiation"
1327 * for can_do_samlogon_ex and can_do_validation6
1329 attempts += 1;
1331 /* We have to try a second time as cm_connect_netlogon
1332 might not yet have noticed that the DC has killed
1333 our connection. */
1335 if (!rpccli_is_connected(netlogon_pipe)) {
1336 retry = true;
1337 continue;
1340 /* if we get access denied, a possible cause was that we had
1341 and open connection to the DC, but someone changed our
1342 machine account password out from underneath us using 'net
1343 rpc changetrustpw' */
1345 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1346 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1347 "ACCESS_DENIED. Maybe the trust account "
1348 "password was changed and we didn't know it. "
1349 "Killing connections to domain %s\n",
1350 domainname));
1351 invalidate_cm_connection(&domain->conn);
1352 retry = true;
1355 } while ( (attempts < 2) && retry );
1357 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1358 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1359 "returned NT_STATUS_IO_TIMEOUT after the retry."
1360 "Killing connections to domain %s\n",
1361 domainname));
1362 invalidate_cm_connection(&domain->conn);
1364 return result;
1367 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1368 struct winbindd_domain *domain,
1369 const char *user,
1370 const char *pass,
1371 uint32_t request_flags,
1372 struct netr_SamInfo3 **info3)
1375 uchar chal[8];
1376 DATA_BLOB lm_resp;
1377 DATA_BLOB nt_resp;
1378 unsigned char local_nt_response[24];
1379 fstring name_domain, name_user;
1380 NTSTATUS result;
1381 struct netr_SamInfo3 *my_info3 = NULL;
1383 *info3 = NULL;
1385 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1387 /* Parse domain and username */
1389 parse_domain_user(user, name_domain, name_user);
1391 /* do password magic */
1393 generate_random_buffer(chal, sizeof(chal));
1395 if (lp_client_ntlmv2_auth()) {
1396 DATA_BLOB server_chal;
1397 DATA_BLOB names_blob;
1398 server_chal = data_blob_const(chal, 8);
1400 /* note that the 'workgroup' here is for the local
1401 machine. The 'server name' must match the
1402 'workstation' passed to the actual SamLogon call.
1404 names_blob = NTLMv2_generate_names_blob(
1405 mem_ctx, lp_netbios_name(), lp_workgroup());
1407 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1408 pass,
1409 &server_chal,
1410 &names_blob,
1411 &lm_resp, &nt_resp, NULL, NULL)) {
1412 data_blob_free(&names_blob);
1413 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1414 result = NT_STATUS_NO_MEMORY;
1415 goto done;
1417 data_blob_free(&names_blob);
1418 } else {
1419 lm_resp = data_blob_null;
1420 SMBNTencrypt(pass, chal, local_nt_response);
1422 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1423 sizeof(local_nt_response));
1426 if (strequal(name_domain, get_global_sam_name())) {
1427 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1429 result = winbindd_dual_auth_passdb(
1430 mem_ctx, 0, name_domain, name_user,
1431 &chal_blob, &lm_resp, &nt_resp, info3);
1432 goto done;
1435 /* check authentication loop */
1437 result = winbind_samlogon_retry_loop(domain,
1438 mem_ctx,
1440 domain->dcname,
1441 name_user,
1442 name_domain,
1443 lp_netbios_name(),
1444 chal,
1445 lm_resp,
1446 nt_resp,
1447 &my_info3);
1448 if (!NT_STATUS_IS_OK(result)) {
1449 goto done;
1452 /* handle the case where a NT4 DC does not fill in the acct_flags in
1453 * the samlogon reply info3. When accurate info3 is required by the
1454 * caller, we look up the account flags ourselve - gd */
1456 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1457 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1459 struct rpc_pipe_client *samr_pipe;
1460 struct policy_handle samr_domain_handle, user_pol;
1461 union samr_UserInfo *info = NULL;
1462 NTSTATUS status_tmp, result_tmp;
1463 uint32 acct_flags;
1464 struct dcerpc_binding_handle *b;
1466 status_tmp = cm_connect_sam(domain, mem_ctx,
1467 &samr_pipe, &samr_domain_handle);
1469 if (!NT_STATUS_IS_OK(status_tmp)) {
1470 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1471 nt_errstr(status_tmp)));
1472 goto done;
1475 b = samr_pipe->binding_handle;
1477 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1478 &samr_domain_handle,
1479 MAXIMUM_ALLOWED_ACCESS,
1480 my_info3->base.rid,
1481 &user_pol,
1482 &result_tmp);
1484 if (!NT_STATUS_IS_OK(status_tmp)) {
1485 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1486 nt_errstr(status_tmp)));
1487 goto done;
1489 if (!NT_STATUS_IS_OK(result_tmp)) {
1490 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1491 nt_errstr(result_tmp)));
1492 goto done;
1495 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1496 &user_pol,
1498 &info,
1499 &result_tmp);
1501 if (!NT_STATUS_IS_OK(status_tmp)) {
1502 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1503 nt_errstr(status_tmp)));
1504 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1505 goto done;
1507 if (!NT_STATUS_IS_OK(result_tmp)) {
1508 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1509 nt_errstr(result_tmp)));
1510 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1511 goto done;
1514 acct_flags = info->info16.acct_flags;
1516 if (acct_flags == 0) {
1517 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1518 goto done;
1521 my_info3->base.acct_flags = acct_flags;
1523 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1525 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1528 *info3 = my_info3;
1529 done:
1530 return result;
1533 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1534 struct winbindd_cli_state *state)
1536 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1537 NTSTATUS krb5_result = NT_STATUS_OK;
1538 fstring name_domain, name_user;
1539 char *mapped_user;
1540 fstring domain_user;
1541 struct netr_SamInfo3 *info3 = NULL;
1542 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1544 /* Ensure null termination */
1545 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1547 /* Ensure null termination */
1548 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1550 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1551 state->request->data.auth.user));
1553 /* Parse domain and username */
1555 name_map_status = normalize_name_unmap(state->mem_ctx,
1556 state->request->data.auth.user,
1557 &mapped_user);
1559 /* If the name normalization didnt' actually do anything,
1560 just use the original name */
1562 if (!NT_STATUS_IS_OK(name_map_status) &&
1563 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1565 mapped_user = state->request->data.auth.user;
1568 parse_domain_user(mapped_user, name_domain, name_user);
1570 if ( mapped_user != state->request->data.auth.user ) {
1571 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1572 *lp_winbind_separator(),
1573 name_user );
1574 strlcpy( state->request->data.auth.user, domain_user,
1575 sizeof(state->request->data.auth.user));
1578 if (!domain->online) {
1579 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1580 if (domain->startup) {
1581 /* Logons are very important to users. If we're offline and
1582 we get a request within the first 30 seconds of startup,
1583 try very hard to find a DC and go online. */
1585 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1586 "request in startup mode.\n", domain->name ));
1588 winbindd_flush_negative_conn_cache(domain);
1589 result = init_dc_connection(domain);
1593 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1595 /* Check for Kerberos authentication */
1596 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1598 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1599 /* save for later */
1600 krb5_result = result;
1603 if (NT_STATUS_IS_OK(result)) {
1604 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1605 goto process_result;
1606 } else {
1607 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1610 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1611 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1612 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1613 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1614 set_domain_offline( domain );
1615 goto cached_logon;
1618 /* there are quite some NT_STATUS errors where there is no
1619 * point in retrying with a samlogon, we explictly have to take
1620 * care not to increase the bad logon counter on the DC */
1622 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1623 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1624 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1625 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1626 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1627 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1628 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1629 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1630 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1631 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1632 goto done;
1635 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1636 DEBUG(3,("falling back to samlogon\n"));
1637 goto sam_logon;
1638 } else {
1639 goto cached_logon;
1643 sam_logon:
1644 /* Check for Samlogon authentication */
1645 if (domain->online) {
1646 result = winbindd_dual_pam_auth_samlogon(
1647 state->mem_ctx, domain,
1648 state->request->data.auth.user,
1649 state->request->data.auth.pass,
1650 state->request->flags,
1651 &info3);
1653 if (NT_STATUS_IS_OK(result)) {
1654 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1655 /* add the Krb5 err if we have one */
1656 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1657 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1659 goto process_result;
1662 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1663 nt_errstr(result)));
1665 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1666 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1667 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1669 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1670 set_domain_offline( domain );
1671 goto cached_logon;
1674 if (domain->online) {
1675 /* We're still online - fail. */
1676 goto done;
1680 cached_logon:
1681 /* Check for Cached logons */
1682 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1683 lp_winbind_offline_logon()) {
1685 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1687 if (NT_STATUS_IS_OK(result)) {
1688 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1689 goto process_result;
1690 } else {
1691 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1692 goto done;
1696 process_result:
1698 if (NT_STATUS_IS_OK(result)) {
1700 struct dom_sid user_sid;
1702 /* In all codepaths where result == NT_STATUS_OK info3 must have
1703 been initialized. */
1704 if (!info3) {
1705 result = NT_STATUS_INTERNAL_ERROR;
1706 goto done;
1709 sid_compose(&user_sid, info3->base.domain_sid,
1710 info3->base.rid);
1712 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1713 &user_sid);
1714 netsamlogon_cache_store(name_user, info3);
1716 /* save name_to_sid info as early as possible (only if
1717 this is our primary domain so we don't invalidate
1718 the cache entry by storing the seq_num for the wrong
1719 domain). */
1720 if ( domain->primary ) {
1721 cache_name2sid(domain, name_domain, name_user,
1722 SID_NAME_USER, &user_sid);
1725 /* Check if the user is in the right group */
1727 result = check_info3_in_group(
1728 info3,
1729 state->request->data.auth.require_membership_of_sid);
1730 if (!NT_STATUS_IS_OK(result)) {
1731 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1732 state->request->data.auth.user,
1733 state->request->data.auth.require_membership_of_sid));
1734 goto done;
1737 result = append_auth_data(state->mem_ctx, state->response,
1738 state->request->flags, info3,
1739 name_domain, name_user);
1740 if (!NT_STATUS_IS_OK(result)) {
1741 goto done;
1744 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1745 && lp_winbind_offline_logon()) {
1747 result = winbindd_store_creds(domain,
1748 state->request->data.auth.user,
1749 state->request->data.auth.pass,
1750 info3);
1753 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1754 struct winbindd_domain *our_domain = find_our_domain();
1756 /* This is not entirely correct I believe, but it is
1757 consistent. Only apply the password policy settings
1758 too warn users for our own domain. Cannot obtain these
1759 from trusted DCs all the time so don't do it at all.
1760 -- jerry */
1762 result = NT_STATUS_NOT_SUPPORTED;
1763 if (our_domain == domain ) {
1764 result = fillup_password_policy(
1765 our_domain, state->response);
1768 if (!NT_STATUS_IS_OK(result)
1769 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1771 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1772 domain->name, nt_errstr(result)));
1773 goto done;
1777 result = NT_STATUS_OK;
1780 done:
1781 /* give us a more useful (more correct?) error code */
1782 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1783 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1784 result = NT_STATUS_NO_LOGON_SERVERS;
1787 set_auth_errors(state->response, result);
1789 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1790 state->request->data.auth.user,
1791 state->response->data.auth.nt_status_string,
1792 state->response->data.auth.pam_error));
1794 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1797 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1798 struct winbindd_cli_state *state)
1800 NTSTATUS result;
1801 struct netr_SamInfo3 *info3 = NULL;
1802 const char *name_user = NULL;
1803 const char *name_domain = NULL;
1804 const char *workstation;
1806 DATA_BLOB lm_resp, nt_resp;
1808 /* This is child-only, so no check for privileged access is needed
1809 anymore */
1811 /* Ensure null termination */
1812 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1813 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1815 name_user = state->request->data.auth_crap.user;
1816 name_domain = state->request->data.auth_crap.domain;
1817 workstation = state->request->data.auth_crap.workstation;
1819 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1820 name_domain, name_user));
1822 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1823 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1824 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1825 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1826 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1827 state->request->data.auth_crap.lm_resp_len,
1828 state->request->data.auth_crap.nt_resp_len));
1829 result = NT_STATUS_INVALID_PARAMETER;
1830 goto done;
1834 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1835 state->request->data.auth_crap.lm_resp_len);
1837 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1838 nt_resp = data_blob_talloc(state->mem_ctx,
1839 state->request->extra_data.data,
1840 state->request->data.auth_crap.nt_resp_len);
1841 } else {
1842 nt_resp = data_blob_talloc(state->mem_ctx,
1843 state->request->data.auth_crap.nt_resp,
1844 state->request->data.auth_crap.nt_resp_len);
1847 if (strequal(name_domain, get_global_sam_name())) {
1848 DATA_BLOB chal_blob = data_blob_const(
1849 state->request->data.auth_crap.chal,
1850 sizeof(state->request->data.auth_crap.chal));
1852 result = winbindd_dual_auth_passdb(
1853 state->mem_ctx,
1854 state->request->data.auth_crap.logon_parameters,
1855 name_domain, name_user,
1856 &chal_blob, &lm_resp, &nt_resp, &info3);
1857 goto process_result;
1860 result = winbind_samlogon_retry_loop(domain,
1861 state->mem_ctx,
1862 state->request->data.auth_crap.logon_parameters,
1863 domain->dcname,
1864 name_user,
1865 name_domain,
1866 /* Bug #3248 - found by Stefan Burkei. */
1867 workstation, /* We carefully set this above so use it... */
1868 state->request->data.auth_crap.chal,
1869 lm_resp,
1870 nt_resp,
1871 &info3);
1872 if (!NT_STATUS_IS_OK(result)) {
1873 goto done;
1876 process_result:
1878 if (NT_STATUS_IS_OK(result)) {
1879 struct dom_sid user_sid;
1881 sid_compose(&user_sid, info3->base.domain_sid,
1882 info3->base.rid);
1883 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1884 &user_sid);
1885 netsamlogon_cache_store(name_user, info3);
1887 /* Check if the user is in the right group */
1889 result = check_info3_in_group(
1890 info3,
1891 state->request->data.auth_crap.require_membership_of_sid);
1892 if (!NT_STATUS_IS_OK(result)) {
1893 DEBUG(3, ("User %s is not in the required group (%s), so "
1894 "crap authentication is rejected\n",
1895 state->request->data.auth_crap.user,
1896 state->request->data.auth_crap.require_membership_of_sid));
1897 goto done;
1900 result = append_auth_data(state->mem_ctx, state->response,
1901 state->request->flags, info3,
1902 name_domain, name_user);
1903 if (!NT_STATUS_IS_OK(result)) {
1904 goto done;
1908 done:
1910 /* give us a more useful (more correct?) error code */
1911 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1912 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1913 result = NT_STATUS_NO_LOGON_SERVERS;
1916 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1917 result = nt_status_squash(result);
1920 set_auth_errors(state->response, result);
1922 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1923 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1924 name_domain,
1925 name_user,
1926 state->response->data.auth.nt_status_string,
1927 state->response->data.auth.pam_error));
1929 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1932 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1933 struct winbindd_cli_state *state)
1935 char *oldpass;
1936 char *newpass = NULL;
1937 struct policy_handle dom_pol;
1938 struct rpc_pipe_client *cli = NULL;
1939 bool got_info = false;
1940 struct samr_DomInfo1 *info = NULL;
1941 struct userPwdChangeFailureInformation *reject = NULL;
1942 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1943 fstring domain, user;
1944 struct dcerpc_binding_handle *b = NULL;
1946 ZERO_STRUCT(dom_pol);
1948 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1949 state->request->data.auth.user));
1951 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1952 goto done;
1955 /* Change password */
1957 oldpass = state->request->data.chauthtok.oldpass;
1958 newpass = state->request->data.chauthtok.newpass;
1960 /* Initialize reject reason */
1961 state->response->data.auth.reject_reason = Undefined;
1963 /* Get sam handle */
1965 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1966 &dom_pol);
1967 if (!NT_STATUS_IS_OK(result)) {
1968 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1969 goto done;
1972 b = cli->binding_handle;
1974 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1975 user,
1976 newpass,
1977 oldpass,
1978 &info,
1979 &reject);
1981 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1983 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1985 fill_in_password_policy(state->response, info);
1987 state->response->data.auth.reject_reason =
1988 reject->extendedFailureReason;
1990 got_info = true;
1993 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1994 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1995 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1996 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1998 /* only fallback when the chgpasswd_user3 call is not supported */
1999 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2000 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2001 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2002 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2004 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2005 nt_errstr(result)));
2007 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2009 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2010 Map to the same status code as Windows 2003. */
2012 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2013 result = NT_STATUS_PASSWORD_RESTRICTION;
2017 done:
2019 if (NT_STATUS_IS_OK(result)
2020 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2021 && lp_winbind_offline_logon()) {
2022 result = winbindd_update_creds_by_name(contact_domain, user,
2023 newpass);
2024 /* Again, this happens when we login from gdm or xdm
2025 * and the password expires, *BUT* cached crendentials
2026 * doesn't exist. winbindd_update_creds_by_name()
2027 * returns NT_STATUS_NO_SUCH_USER.
2028 * This is not a failure.
2029 * --- BoYang
2030 * */
2031 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2032 result = NT_STATUS_OK;
2035 if (!NT_STATUS_IS_OK(result)) {
2036 DEBUG(10, ("Failed to store creds: %s\n",
2037 nt_errstr(result)));
2038 goto process_result;
2042 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2044 NTSTATUS policy_ret;
2046 policy_ret = fillup_password_policy(
2047 contact_domain, state->response);
2049 /* failure of this is non critical, it will just provide no
2050 * additional information to the client why the change has
2051 * failed - Guenther */
2053 if (!NT_STATUS_IS_OK(policy_ret)) {
2054 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2055 goto process_result;
2059 process_result:
2061 if (strequal(contact_domain->name, get_global_sam_name())) {
2062 /* FIXME: internal rpc pipe does not cache handles yet */
2063 if (b) {
2064 if (is_valid_policy_hnd(&dom_pol)) {
2065 NTSTATUS _result;
2066 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2068 TALLOC_FREE(cli);
2072 set_auth_errors(state->response, result);
2074 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2075 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2076 domain,
2077 user,
2078 state->response->data.auth.nt_status_string,
2079 state->response->data.auth.pam_error));
2081 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2084 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2085 struct winbindd_cli_state *state)
2087 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2089 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2090 state->request->data.logoff.user));
2092 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2093 result = NT_STATUS_OK;
2094 goto process_result;
2097 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2098 result = NT_STATUS_OK;
2099 goto process_result;
2102 #ifdef HAVE_KRB5
2104 if (state->request->data.logoff.uid < 0) {
2105 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2106 goto process_result;
2109 /* what we need here is to find the corresponding krb5 ccache name *we*
2110 * created for a given username and destroy it */
2112 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2113 result = NT_STATUS_OK;
2114 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2115 goto process_result;
2118 if (!ccache_entry_identical(state->request->data.logoff.user,
2119 state->request->data.logoff.uid,
2120 state->request->data.logoff.krb5ccname)) {
2121 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2122 goto process_result;
2125 result = remove_ccache(state->request->data.logoff.user);
2126 if (!NT_STATUS_IS_OK(result)) {
2127 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2128 nt_errstr(result)));
2129 goto process_result;
2133 * Remove any mlock'ed memory creds in the child
2134 * we might be using for krb5 ticket renewal.
2137 winbindd_delete_memory_creds(state->request->data.logoff.user);
2139 #else
2140 result = NT_STATUS_NOT_SUPPORTED;
2141 #endif
2143 process_result:
2146 set_auth_errors(state->response, result);
2148 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2151 /* Change user password with auth crap*/
2153 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2155 NTSTATUS result;
2156 DATA_BLOB new_nt_password;
2157 DATA_BLOB old_nt_hash_enc;
2158 DATA_BLOB new_lm_password;
2159 DATA_BLOB old_lm_hash_enc;
2160 fstring domain,user;
2161 struct policy_handle dom_pol;
2162 struct winbindd_domain *contact_domain = domainSt;
2163 struct rpc_pipe_client *cli = NULL;
2164 struct dcerpc_binding_handle *b = NULL;
2166 ZERO_STRUCT(dom_pol);
2168 /* Ensure null termination */
2169 state->request->data.chng_pswd_auth_crap.user[
2170 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2171 state->request->data.chng_pswd_auth_crap.domain[
2172 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2173 *domain = 0;
2174 *user = 0;
2176 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2177 (unsigned long)state->pid,
2178 state->request->data.chng_pswd_auth_crap.domain,
2179 state->request->data.chng_pswd_auth_crap.user));
2181 if (lp_winbind_offline_logon()) {
2182 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2183 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2184 result = NT_STATUS_ACCESS_DENIED;
2185 goto done;
2188 if (*state->request->data.chng_pswd_auth_crap.domain) {
2189 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2190 } else {
2191 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2192 domain, user);
2194 if(!*domain) {
2195 DEBUG(3,("no domain specified with username (%s) - "
2196 "failing auth\n",
2197 state->request->data.chng_pswd_auth_crap.user));
2198 result = NT_STATUS_NO_SUCH_USER;
2199 goto done;
2203 if (!*domain && lp_winbind_use_default_domain()) {
2204 fstrcpy(domain,lp_workgroup());
2207 if(!*user) {
2208 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2211 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2212 (unsigned long)state->pid, domain, user));
2214 /* Change password */
2215 new_nt_password = data_blob_const(
2216 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2217 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2219 old_nt_hash_enc = data_blob_const(
2220 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2221 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2223 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2224 new_lm_password = data_blob_const(
2225 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2226 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2228 old_lm_hash_enc = data_blob_const(
2229 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2230 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2231 } else {
2232 new_lm_password = data_blob_null;
2233 old_lm_hash_enc = data_blob_null;
2236 /* Get sam handle */
2238 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2239 if (!NT_STATUS_IS_OK(result)) {
2240 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2241 goto done;
2244 b = cli->binding_handle;
2246 result = rpccli_samr_chng_pswd_auth_crap(
2247 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2248 new_lm_password, old_lm_hash_enc);
2250 done:
2252 if (strequal(contact_domain->name, get_global_sam_name())) {
2253 /* FIXME: internal rpc pipe does not cache handles yet */
2254 if (b) {
2255 if (is_valid_policy_hnd(&dom_pol)) {
2256 NTSTATUS _result;
2257 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2259 TALLOC_FREE(cli);
2263 set_auth_errors(state->response, result);
2265 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2266 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2267 domain, user,
2268 state->response->data.auth.nt_status_string,
2269 state->response->data.auth.pam_error));
2271 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;