s3:torture:delete: untangle function call from result check
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
bloba64cc5692c504f0c4aef480316b3333886489255
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 strlower_m(afsname);
222 DEBUG(10, ("Generating token for user %s\n", afsname));
224 cell = strchr(afsname, '@');
226 if (cell == NULL) {
227 return NT_STATUS_NO_MEMORY;
230 *cell = '\0';
231 cell += 1;
233 token = afs_createtoken_str(afsname, cell);
234 if (token == NULL) {
235 return NT_STATUS_OK;
237 resp->extra_data.data = talloc_strdup(mem_ctx, token);
238 if (resp->extra_data.data == NULL) {
239 return NT_STATUS_NO_MEMORY;
241 resp->length += strlen((const char *)resp->extra_data.data)+1;
243 return NT_STATUS_OK;
246 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
247 const char *group_sid)
249 * Check whether a user belongs to a group or list of groups.
251 * @param mem_ctx talloc memory context.
252 * @param info3 user information, including group membership info.
253 * @param group_sid One or more groups , separated by commas.
255 * @return NT_STATUS_OK on success,
256 * NT_STATUS_LOGON_FAILURE if the user does not belong,
257 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
260 struct dom_sid *require_membership_of_sid;
261 uint32_t num_require_membership_of_sid;
262 char *req_sid;
263 const char *p;
264 struct dom_sid sid;
265 size_t i;
266 struct security_token *token;
267 TALLOC_CTX *frame = talloc_stackframe();
268 NTSTATUS status;
270 /* Parse the 'required group' SID */
272 if (!group_sid || !group_sid[0]) {
273 /* NO sid supplied, all users may access */
274 TALLOC_FREE(frame);
275 return NT_STATUS_OK;
278 token = talloc_zero(talloc_tos(), struct security_token);
279 if (token == NULL) {
280 DEBUG(0, ("talloc failed\n"));
281 TALLOC_FREE(frame);
282 return NT_STATUS_NO_MEMORY;
285 num_require_membership_of_sid = 0;
286 require_membership_of_sid = NULL;
288 p = group_sid;
290 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
291 if (!string_to_sid(&sid, req_sid)) {
292 DEBUG(0, ("check_info3_in_group: could not parse %s "
293 "as a SID!", req_sid));
294 TALLOC_FREE(frame);
295 return NT_STATUS_INVALID_PARAMETER;
298 status = add_sid_to_array(talloc_tos(), &sid,
299 &require_membership_of_sid,
300 &num_require_membership_of_sid);
301 if (!NT_STATUS_IS_OK(status)) {
302 DEBUG(0, ("add_sid_to_array failed\n"));
303 TALLOC_FREE(frame);
304 return status;
308 status = sid_array_from_info3(talloc_tos(), info3,
309 &token->sids,
310 &token->num_sids,
311 true);
312 if (!NT_STATUS_IS_OK(status)) {
313 TALLOC_FREE(frame);
314 return status;
317 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
318 token))
319 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
320 token))) {
321 DEBUG(3, ("could not add aliases: %s\n",
322 nt_errstr(status)));
323 TALLOC_FREE(frame);
324 return status;
327 security_token_debug(DBGC_CLASS, 10, token);
329 for (i=0; i<num_require_membership_of_sid; i++) {
330 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
331 &require_membership_of_sid[i])));
332 if (nt_token_check_sid(&require_membership_of_sid[i],
333 token)) {
334 DEBUG(10, ("Access ok\n"));
335 TALLOC_FREE(frame);
336 return NT_STATUS_OK;
340 /* Do not distinguish this error from a wrong username/pw */
342 TALLOC_FREE(frame);
343 return NT_STATUS_LOGON_FAILURE;
346 struct winbindd_domain *find_auth_domain(uint8_t flags,
347 const char *domain_name)
349 struct winbindd_domain *domain;
351 if (IS_DC) {
352 domain = find_domain_from_name_noinit(domain_name);
353 if (domain == NULL) {
354 DEBUG(3, ("Authentication for domain [%s] refused "
355 "as it is not a trusted domain\n",
356 domain_name));
358 return domain;
361 if (strequal(domain_name, get_global_sam_name())) {
362 return find_domain_from_name_noinit(domain_name);
365 /* we can auth against trusted domains */
366 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
367 domain = find_domain_from_name_noinit(domain_name);
368 if (domain == NULL) {
369 DEBUG(3, ("Authentication for domain [%s] skipped "
370 "as it is not a trusted domain\n",
371 domain_name));
372 } else {
373 return domain;
377 return find_our_domain();
380 static void fill_in_password_policy(struct winbindd_response *r,
381 const struct samr_DomInfo1 *p)
383 r->data.auth.policy.min_length_password =
384 p->min_password_length;
385 r->data.auth.policy.password_history =
386 p->password_history_length;
387 r->data.auth.policy.password_properties =
388 p->password_properties;
389 r->data.auth.policy.expire =
390 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
391 r->data.auth.policy.min_passwordage =
392 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
395 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
396 struct winbindd_response *response)
398 TALLOC_CTX *frame = talloc_stackframe();
399 struct winbindd_methods *methods;
400 NTSTATUS status;
401 struct samr_DomInfo1 password_policy;
403 if ( !winbindd_can_contact_domain( domain ) ) {
404 DEBUG(5,("fillup_password_policy: No inbound trust to "
405 "contact domain %s\n", domain->name));
406 status = NT_STATUS_NOT_SUPPORTED;
407 goto done;
410 methods = domain->methods;
412 status = methods->password_policy(domain, talloc_tos(), &password_policy);
413 if (NT_STATUS_IS_ERR(status)) {
414 goto done;
417 fill_in_password_policy(response, &password_policy);
419 done:
420 TALLOC_FREE(frame);
421 return NT_STATUS_OK;
424 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
425 TALLOC_CTX *mem_ctx,
426 uint16 *lockout_threshold)
428 struct winbindd_methods *methods;
429 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
430 struct samr_DomInfo12 lockout_policy;
432 *lockout_threshold = 0;
434 methods = domain->methods;
436 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
437 if (NT_STATUS_IS_ERR(status)) {
438 return status;
441 *lockout_threshold = lockout_policy.lockout_threshold;
443 return NT_STATUS_OK;
446 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
447 TALLOC_CTX *mem_ctx,
448 uint32 *password_properties)
450 struct winbindd_methods *methods;
451 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
452 struct samr_DomInfo1 password_policy;
454 *password_properties = 0;
456 methods = domain->methods;
458 status = methods->password_policy(domain, mem_ctx, &password_policy);
459 if (NT_STATUS_IS_ERR(status)) {
460 return status;
463 *password_properties = password_policy.password_properties;
465 return NT_STATUS_OK;
468 #ifdef HAVE_KRB5
470 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
471 const char *type,
472 uid_t uid,
473 const char **user_ccache_file)
475 /* accept FILE and WRFILE as krb5_cc_type from the client and then
476 * build the full ccname string based on the user's uid here -
477 * Guenther*/
479 const char *gen_cc = NULL;
481 if (uid != -1) {
482 if (strequal(type, "FILE")) {
483 gen_cc = talloc_asprintf(
484 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
486 if (strequal(type, "WRFILE")) {
487 gen_cc = talloc_asprintf(
488 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
492 *user_ccache_file = gen_cc;
494 if (gen_cc == NULL) {
495 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
497 if (gen_cc == NULL) {
498 DEBUG(0,("out of memory\n"));
499 return NULL;
502 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
503 (*user_ccache_file == NULL) ? " (internal)":""));
505 return gen_cc;
508 #endif
510 uid_t get_uid_from_request(struct winbindd_request *request)
512 uid_t uid;
514 uid = request->data.auth.uid;
516 if (uid < 0) {
517 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
518 return -1;
520 return uid;
523 /**********************************************************************
524 Authenticate a user with a clear text password using Kerberos and fill up
525 ccache if required
526 **********************************************************************/
528 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
529 struct winbindd_domain *domain,
530 const char *user,
531 const char *pass,
532 const char *krb5_cc_type,
533 uid_t uid,
534 struct netr_SamInfo3 **info3,
535 fstring krb5ccname)
537 #ifdef HAVE_KRB5
538 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
539 krb5_error_code krb5_ret;
540 const char *cc = NULL;
541 const char *principal_s = NULL;
542 const char *service = NULL;
543 char *realm = NULL;
544 fstring name_domain, name_user;
545 time_t ticket_lifetime = 0;
546 time_t renewal_until = 0;
547 ADS_STRUCT *ads;
548 time_t time_offset = 0;
549 const char *user_ccache_file;
550 struct PAC_LOGON_INFO *logon_info = NULL;
552 *info3 = NULL;
554 /* 1st step:
555 * prepare a krb5_cc_cache string for the user */
557 if (uid == -1) {
558 DEBUG(0,("no valid uid\n"));
561 cc = generate_krb5_ccache(mem_ctx,
562 krb5_cc_type,
563 uid,
564 &user_ccache_file);
565 if (cc == NULL) {
566 return NT_STATUS_NO_MEMORY;
570 /* 2nd step:
571 * get kerberos properties */
573 if (domain->private_data) {
574 ads = (ADS_STRUCT *)domain->private_data;
575 time_offset = ads->auth.time_offset;
579 /* 3rd step:
580 * do kerberos auth and setup ccache as the user */
582 parse_domain_user(user, name_domain, name_user);
584 realm = domain->alt_name;
585 strupper_m(realm);
587 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
588 if (principal_s == NULL) {
589 return NT_STATUS_NO_MEMORY;
592 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
593 if (service == NULL) {
594 return NT_STATUS_NO_MEMORY;
597 /* if this is a user ccache, we need to act as the user to let the krb5
598 * library handle the chown, etc. */
600 /************************ ENTERING NON-ROOT **********************/
602 if (user_ccache_file != NULL) {
603 set_effective_uid(uid);
604 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
607 result = kerberos_return_pac(mem_ctx,
608 principal_s,
609 pass,
610 time_offset,
611 &ticket_lifetime,
612 &renewal_until,
614 true,
615 true,
616 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
617 NULL,
618 &logon_info);
619 if (user_ccache_file != NULL) {
620 gain_root_privilege();
623 /************************ RETURNED TO ROOT **********************/
625 if (!NT_STATUS_IS_OK(result)) {
626 goto failed;
629 *info3 = &logon_info->info3;
631 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
632 principal_s));
634 /* if we had a user's ccache then return that string for the pam
635 * environment */
637 if (user_ccache_file != NULL) {
639 fstrcpy(krb5ccname, user_ccache_file);
641 result = add_ccache_to_list(principal_s,
643 service,
644 user,
645 realm,
646 uid,
647 time(NULL),
648 ticket_lifetime,
649 renewal_until,
650 false);
652 if (!NT_STATUS_IS_OK(result)) {
653 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
654 nt_errstr(result)));
656 } else {
658 /* need to delete the memory cred cache, it is not used anymore */
660 krb5_ret = ads_kdestroy(cc);
661 if (krb5_ret) {
662 DEBUG(3,("winbindd_raw_kerberos_login: "
663 "could not destroy krb5 credential cache: "
664 "%s\n", error_message(krb5_ret)));
669 return NT_STATUS_OK;
671 failed:
673 /* we could have created a new credential cache with a valid tgt in it
674 * but we werent able to get or verify the service ticket for this
675 * local host and therefor didn't get the PAC, we need to remove that
676 * cache entirely now */
678 krb5_ret = ads_kdestroy(cc);
679 if (krb5_ret) {
680 DEBUG(3,("winbindd_raw_kerberos_login: "
681 "could not destroy krb5 credential cache: "
682 "%s\n", error_message(krb5_ret)));
685 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
686 DEBUG(3,("winbindd_raw_kerberos_login: "
687 "could not remove ccache for user %s\n",
688 user));
691 return result;
692 #else
693 return NT_STATUS_NOT_SUPPORTED;
694 #endif /* HAVE_KRB5 */
697 /****************************************************************
698 ****************************************************************/
700 bool check_request_flags(uint32_t flags)
702 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
703 WBFLAG_PAM_INFO3_TEXT |
704 WBFLAG_PAM_INFO3_NDR;
706 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
707 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
708 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
709 !(flags & flags_edata) ) {
710 return true;
713 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
714 flags));
716 return false;
719 /****************************************************************
720 ****************************************************************/
722 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
723 struct winbindd_response *resp,
724 uint32_t request_flags,
725 struct netr_SamInfo3 *info3,
726 const char *name_domain,
727 const char *name_user)
729 NTSTATUS result;
731 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
732 memcpy(resp->data.auth.user_session_key,
733 info3->base.key.key,
734 sizeof(resp->data.auth.user_session_key)
735 /* 16 */);
738 if (request_flags & WBFLAG_PAM_LMKEY) {
739 memcpy(resp->data.auth.first_8_lm_hash,
740 info3->base.LMSessKey.key,
741 sizeof(resp->data.auth.first_8_lm_hash)
742 /* 8 */);
745 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
746 result = append_unix_username(mem_ctx, resp,
747 info3, name_domain, name_user);
748 if (!NT_STATUS_IS_OK(result)) {
749 DEBUG(10,("Failed to append Unix Username: %s\n",
750 nt_errstr(result)));
751 return result;
755 /* currently, anything from here on potentially overwrites extra_data. */
757 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
758 result = append_info3_as_ndr(mem_ctx, resp, info3);
759 if (!NT_STATUS_IS_OK(result)) {
760 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
761 nt_errstr(result)));
762 return result;
766 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
767 result = append_info3_as_txt(mem_ctx, resp, info3);
768 if (!NT_STATUS_IS_OK(result)) {
769 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
770 nt_errstr(result)));
771 return result;
775 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
776 result = append_afs_token(mem_ctx, resp,
777 info3, name_domain, name_user);
778 if (!NT_STATUS_IS_OK(result)) {
779 DEBUG(10,("Failed to append AFS token: %s\n",
780 nt_errstr(result)));
781 return result;
785 return NT_STATUS_OK;
788 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
789 struct winbindd_cli_state *state,
790 struct netr_SamInfo3 **info3)
792 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
793 uint16 max_allowed_bad_attempts;
794 fstring name_domain, name_user;
795 struct dom_sid sid;
796 enum lsa_SidType type;
797 uchar new_nt_pass[NT_HASH_LEN];
798 const uint8 *cached_nt_pass;
799 const uint8 *cached_salt;
800 struct netr_SamInfo3 *my_info3;
801 time_t kickoff_time, must_change_time;
802 bool password_good = false;
803 #ifdef HAVE_KRB5
804 struct winbindd_tdc_domain *tdc_domain = NULL;
805 #endif
807 *info3 = NULL;
809 ZERO_STRUCTP(info3);
811 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
813 /* Parse domain and username */
815 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
818 if (!lookup_cached_name(name_domain,
819 name_user,
820 &sid,
821 &type)) {
822 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
823 return NT_STATUS_NO_SUCH_USER;
826 if (type != SID_NAME_USER) {
827 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
828 return NT_STATUS_LOGON_FAILURE;
831 result = winbindd_get_creds(domain,
832 state->mem_ctx,
833 &sid,
834 &my_info3,
835 &cached_nt_pass,
836 &cached_salt);
837 if (!NT_STATUS_IS_OK(result)) {
838 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
839 return result;
842 *info3 = my_info3;
844 E_md4hash(state->request->data.auth.pass, new_nt_pass);
846 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
847 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
848 if (cached_salt) {
849 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
852 if (cached_salt) {
853 /* In this case we didn't store the nt_hash itself,
854 but the MD5 combination of salt + nt_hash. */
855 uchar salted_hash[NT_HASH_LEN];
856 E_md5hash(cached_salt, new_nt_pass, salted_hash);
858 password_good = (memcmp(cached_nt_pass, salted_hash,
859 NT_HASH_LEN) == 0);
860 } else {
861 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
862 password_good = (memcmp(cached_nt_pass, new_nt_pass,
863 NT_HASH_LEN) == 0);
866 if (password_good) {
868 /* User *DOES* know the password, update logon_time and reset
869 * bad_pw_count */
871 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
873 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
874 return NT_STATUS_ACCOUNT_LOCKED_OUT;
877 if (my_info3->base.acct_flags & ACB_DISABLED) {
878 return NT_STATUS_ACCOUNT_DISABLED;
881 if (my_info3->base.acct_flags & ACB_WSTRUST) {
882 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
885 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
886 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
889 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
890 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
893 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
894 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
895 my_info3->base.acct_flags));
896 return NT_STATUS_LOGON_FAILURE;
899 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
900 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
901 return NT_STATUS_ACCOUNT_EXPIRED;
904 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
905 if (must_change_time != 0 && must_change_time < time(NULL)) {
906 /* we allow grace logons when the password has expired */
907 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
908 /* return NT_STATUS_PASSWORD_EXPIRED; */
909 goto success;
912 #ifdef HAVE_KRB5
913 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
914 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
915 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
916 /* used to cope with the case winbindd starting without network. */
917 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
919 uid_t uid = -1;
920 const char *cc = NULL;
921 char *realm = NULL;
922 const char *principal_s = NULL;
923 const char *service = NULL;
924 const char *user_ccache_file;
926 uid = get_uid_from_request(state->request);
927 if (uid == -1) {
928 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
929 return NT_STATUS_INVALID_PARAMETER;
932 cc = generate_krb5_ccache(state->mem_ctx,
933 state->request->data.auth.krb5_cc_type,
934 state->request->data.auth.uid,
935 &user_ccache_file);
936 if (cc == NULL) {
937 return NT_STATUS_NO_MEMORY;
940 realm = domain->alt_name;
941 strupper_m(realm);
943 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
944 if (principal_s == NULL) {
945 return NT_STATUS_NO_MEMORY;
948 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
949 if (service == NULL) {
950 return NT_STATUS_NO_MEMORY;
953 if (user_ccache_file != NULL) {
955 fstrcpy(state->response->data.auth.krb5ccname,
956 user_ccache_file);
958 result = add_ccache_to_list(principal_s,
960 service,
961 state->request->data.auth.user,
962 domain->alt_name,
963 uid,
964 time(NULL),
965 time(NULL) + lp_winbind_cache_time(),
966 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
967 true);
969 if (!NT_STATUS_IS_OK(result)) {
970 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
971 "to add ccache to list: %s\n",
972 nt_errstr(result)));
976 #endif /* HAVE_KRB5 */
977 success:
978 /* FIXME: we possibly should handle logon hours as well (does xp when
979 * offline?) see auth/auth_sam.c:sam_account_ok for details */
981 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
982 my_info3->base.bad_password_count = 0;
984 result = winbindd_update_creds_by_info3(domain,
985 state->request->data.auth.user,
986 state->request->data.auth.pass,
987 my_info3);
988 if (!NT_STATUS_IS_OK(result)) {
989 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
990 nt_errstr(result)));
991 return result;
994 return NT_STATUS_OK;
998 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
999 if (domain->online == false) {
1000 goto failed;
1003 /* failure of this is not critical */
1004 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1005 if (!NT_STATUS_IS_OK(result)) {
1006 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1007 "Won't be able to honour account lockout policies\n"));
1010 /* increase counter */
1011 my_info3->base.bad_password_count++;
1013 if (max_allowed_bad_attempts == 0) {
1014 goto failed;
1017 /* lockout user */
1018 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1020 uint32 password_properties;
1022 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1023 if (!NT_STATUS_IS_OK(result)) {
1024 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1027 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1028 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1029 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1033 failed:
1034 result = winbindd_update_creds_by_info3(domain,
1035 state->request->data.auth.user,
1036 NULL,
1037 my_info3);
1039 if (!NT_STATUS_IS_OK(result)) {
1040 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1041 nt_errstr(result)));
1044 return NT_STATUS_LOGON_FAILURE;
1047 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1048 struct winbindd_cli_state *state,
1049 struct netr_SamInfo3 **info3)
1051 struct winbindd_domain *contact_domain;
1052 fstring name_domain, name_user;
1053 NTSTATUS result;
1055 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1057 /* Parse domain and username */
1059 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1061 /* what domain should we contact? */
1063 if ( IS_DC ) {
1064 if (!(contact_domain = find_domain_from_name(name_domain))) {
1065 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1066 state->request->data.auth.user, name_domain, name_user, name_domain));
1067 result = NT_STATUS_NO_SUCH_USER;
1068 goto done;
1071 } else {
1072 if (is_myname(name_domain)) {
1073 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1074 result = NT_STATUS_NO_SUCH_USER;
1075 goto done;
1078 contact_domain = find_domain_from_name(name_domain);
1079 if (contact_domain == NULL) {
1080 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1081 state->request->data.auth.user, name_domain, name_user, name_domain));
1083 result = NT_STATUS_NO_SUCH_USER;
1084 goto done;
1088 if (contact_domain->initialized &&
1089 contact_domain->active_directory) {
1090 goto try_login;
1093 if (!contact_domain->initialized) {
1094 init_dc_connection(contact_domain);
1097 if (!contact_domain->active_directory) {
1098 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1099 return NT_STATUS_INVALID_LOGON_TYPE;
1101 try_login:
1102 result = winbindd_raw_kerberos_login(
1103 state->mem_ctx, contact_domain,
1104 state->request->data.auth.user,
1105 state->request->data.auth.pass,
1106 state->request->data.auth.krb5_cc_type,
1107 get_uid_from_request(state->request),
1108 info3, state->response->data.auth.krb5ccname);
1109 done:
1110 return result;
1113 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1114 uint32_t logon_parameters,
1115 const char *domain, const char *user,
1116 const DATA_BLOB *challenge,
1117 const DATA_BLOB *lm_resp,
1118 const DATA_BLOB *nt_resp,
1119 struct netr_SamInfo3 **pinfo3)
1121 struct auth_usersupplied_info *user_info = NULL;
1122 struct tsocket_address *local;
1123 NTSTATUS status;
1124 int rc;
1126 rc = tsocket_address_inet_from_strings(mem_ctx,
1127 "ip",
1128 "127.0.0.1",
1130 &local);
1131 if (rc < 0) {
1132 return NT_STATUS_NO_MEMORY;
1134 status = make_user_info(&user_info, user, user, domain, domain,
1135 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1136 NULL, AUTH_PASSWORD_RESPONSE);
1137 if (!NT_STATUS_IS_OK(status)) {
1138 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1139 return status;
1141 user_info->logon_parameters = logon_parameters;
1143 /* We don't want any more mapping of the username */
1144 user_info->mapped_state = True;
1146 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1147 pinfo3);
1148 free_user_info(&user_info);
1149 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1150 user, nt_errstr(status)));
1151 return status;
1154 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1155 TALLOC_CTX *mem_ctx,
1156 uint32_t logon_parameters,
1157 const char *server,
1158 const char *username,
1159 const char *domainname,
1160 const char *workstation,
1161 const uint8_t chal[8],
1162 DATA_BLOB lm_response,
1163 DATA_BLOB nt_response,
1164 struct netr_SamInfo3 **info3)
1166 int attempts = 0;
1167 bool retry = false;
1168 NTSTATUS result;
1170 do {
1171 struct rpc_pipe_client *netlogon_pipe;
1172 const struct pipe_auth_data *auth;
1173 uint32_t neg_flags = 0;
1175 ZERO_STRUCTP(info3);
1176 retry = false;
1178 result = cm_connect_netlogon(domain, &netlogon_pipe);
1180 if (!NT_STATUS_IS_OK(result)) {
1181 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1182 nt_errstr(result)));
1183 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1184 if (attempts > 0) {
1185 DEBUG(3, ("This is the second problem for this "
1186 "particular call, forcing the close of "
1187 "this connection\n"));
1188 invalidate_cm_connection(&domain->conn);
1189 } else {
1190 DEBUG(3, ("First call to cm_connect_netlogon "
1191 "has timed out, retrying\n"));
1192 continue;
1195 return result;
1197 auth = netlogon_pipe->auth;
1198 if (netlogon_pipe->dc) {
1199 neg_flags = netlogon_pipe->dc->negotiate_flags;
1202 /* It is really important to try SamLogonEx here,
1203 * because in a clustered environment, we want to use
1204 * one machine account from multiple physical
1205 * computers.
1207 * With a normal SamLogon call, we must keep the
1208 * credentials chain updated and intact between all
1209 * users of the machine account (which would imply
1210 * cross-node communication for every NTLM logon).
1212 * (The credentials chain is not per NETLOGON pipe
1213 * connection, but globally on the server/client pair
1214 * by machine name).
1216 * When using SamLogonEx, the credentials are not
1217 * supplied, but the session key is implied by the
1218 * wrapping SamLogon context.
1220 * -- abartlet 21 April 2008
1222 * It's also important to use NetlogonValidationSamInfo4 (6),
1223 * because it relies on the rpc transport encryption
1224 * and avoids using the global netlogon schannel
1225 * session key to en/decrypt secret information
1226 * like the user_session_key for network logons.
1228 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1229 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1230 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1231 * are the indication that the server supports
1232 * NetlogonValidationSamInfo4 (6). And it must only
1233 * be used if "SealSecureChannel" is used.
1235 * -- metze 4 February 2011
1238 if (auth == NULL) {
1239 domain->can_do_validation6 = false;
1240 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1241 domain->can_do_validation6 = false;
1242 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1243 domain->can_do_validation6 = false;
1244 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1245 domain->can_do_validation6 = false;
1246 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1247 domain->can_do_validation6 = false;
1250 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1251 result = rpccli_netlogon_sam_network_logon_ex(
1252 netlogon_pipe,
1253 mem_ctx,
1254 logon_parameters,
1255 server, /* server name */
1256 username, /* user name */
1257 domainname, /* target domain */
1258 workstation, /* workstation */
1259 chal,
1261 lm_response,
1262 nt_response,
1263 info3);
1264 } else {
1265 result = rpccli_netlogon_sam_network_logon(
1266 netlogon_pipe,
1267 mem_ctx,
1268 logon_parameters,
1269 server, /* server name */
1270 username, /* user name */
1271 domainname, /* target domain */
1272 workstation, /* workstation */
1273 chal,
1274 domain->can_do_validation6 ? 6 : 3,
1275 lm_response,
1276 nt_response,
1277 info3);
1280 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1283 * It's likely that the server also does not support
1284 * validation level 6
1286 domain->can_do_validation6 = false;
1288 if (domain->can_do_samlogon_ex) {
1289 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1290 "retrying with NetSamLogon\n"));
1291 domain->can_do_samlogon_ex = false;
1292 retry = true;
1293 continue;
1297 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1298 * (no Ex). This happens against old Samba
1299 * DCs. Drop the connection.
1301 invalidate_cm_connection(&domain->conn);
1302 result = NT_STATUS_LOGON_FAILURE;
1303 break;
1306 if (domain->can_do_validation6 &&
1307 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1308 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1309 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1310 DEBUG(3,("Got a DC that can not do validation level 6, "
1311 "retrying with level 3\n"));
1312 domain->can_do_validation6 = false;
1313 retry = true;
1314 continue;
1318 * we increment this after the "feature negotiation"
1319 * for can_do_samlogon_ex and can_do_validation6
1321 attempts += 1;
1323 /* We have to try a second time as cm_connect_netlogon
1324 might not yet have noticed that the DC has killed
1325 our connection. */
1327 if (!rpccli_is_connected(netlogon_pipe)) {
1328 retry = true;
1329 continue;
1332 /* if we get access denied, a possible cause was that we had
1333 and open connection to the DC, but someone changed our
1334 machine account password out from underneath us using 'net
1335 rpc changetrustpw' */
1337 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1338 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1339 "ACCESS_DENIED. Maybe the trust account "
1340 "password was changed and we didn't know it. "
1341 "Killing connections to domain %s\n",
1342 domainname));
1343 invalidate_cm_connection(&domain->conn);
1344 retry = true;
1347 } while ( (attempts < 2) && retry );
1349 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1350 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1351 "returned NT_STATUS_IO_TIMEOUT after the retry."
1352 "Killing connections to domain %s\n",
1353 domainname));
1354 invalidate_cm_connection(&domain->conn);
1356 return result;
1359 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1360 struct winbindd_domain *domain,
1361 const char *user,
1362 const char *pass,
1363 uint32_t request_flags,
1364 struct netr_SamInfo3 **info3)
1367 uchar chal[8];
1368 DATA_BLOB lm_resp;
1369 DATA_BLOB nt_resp;
1370 unsigned char local_nt_response[24];
1371 fstring name_domain, name_user;
1372 NTSTATUS result;
1373 struct netr_SamInfo3 *my_info3 = NULL;
1375 *info3 = NULL;
1377 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1379 /* Parse domain and username */
1381 parse_domain_user(user, name_domain, name_user);
1383 /* do password magic */
1385 generate_random_buffer(chal, sizeof(chal));
1387 if (lp_client_ntlmv2_auth()) {
1388 DATA_BLOB server_chal;
1389 DATA_BLOB names_blob;
1390 server_chal = data_blob_const(chal, 8);
1392 /* note that the 'workgroup' here is for the local
1393 machine. The 'server name' must match the
1394 'workstation' passed to the actual SamLogon call.
1396 names_blob = NTLMv2_generate_names_blob(
1397 mem_ctx, lp_netbios_name(), lp_workgroup());
1399 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1400 pass,
1401 &server_chal,
1402 &names_blob,
1403 &lm_resp, &nt_resp, NULL, NULL)) {
1404 data_blob_free(&names_blob);
1405 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1406 result = NT_STATUS_NO_MEMORY;
1407 goto done;
1409 data_blob_free(&names_blob);
1410 } else {
1411 lm_resp = data_blob_null;
1412 SMBNTencrypt(pass, chal, local_nt_response);
1414 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1415 sizeof(local_nt_response));
1418 if (strequal(name_domain, get_global_sam_name())) {
1419 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1421 result = winbindd_dual_auth_passdb(
1422 mem_ctx, 0, name_domain, name_user,
1423 &chal_blob, &lm_resp, &nt_resp, info3);
1424 goto done;
1427 /* check authentication loop */
1429 result = winbind_samlogon_retry_loop(domain,
1430 mem_ctx,
1432 domain->dcname,
1433 name_user,
1434 name_domain,
1435 lp_netbios_name(),
1436 chal,
1437 lm_resp,
1438 nt_resp,
1439 &my_info3);
1440 if (!NT_STATUS_IS_OK(result)) {
1441 goto done;
1444 /* handle the case where a NT4 DC does not fill in the acct_flags in
1445 * the samlogon reply info3. When accurate info3 is required by the
1446 * caller, we look up the account flags ourselve - gd */
1448 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1449 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1451 struct rpc_pipe_client *samr_pipe;
1452 struct policy_handle samr_domain_handle, user_pol;
1453 union samr_UserInfo *info = NULL;
1454 NTSTATUS status_tmp, result_tmp;
1455 uint32 acct_flags;
1456 struct dcerpc_binding_handle *b;
1458 status_tmp = cm_connect_sam(domain, mem_ctx,
1459 &samr_pipe, &samr_domain_handle);
1461 if (!NT_STATUS_IS_OK(status_tmp)) {
1462 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1463 nt_errstr(status_tmp)));
1464 goto done;
1467 b = samr_pipe->binding_handle;
1469 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1470 &samr_domain_handle,
1471 MAXIMUM_ALLOWED_ACCESS,
1472 my_info3->base.rid,
1473 &user_pol,
1474 &result_tmp);
1476 if (!NT_STATUS_IS_OK(status_tmp)) {
1477 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1478 nt_errstr(status_tmp)));
1479 goto done;
1481 if (!NT_STATUS_IS_OK(result_tmp)) {
1482 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1483 nt_errstr(result_tmp)));
1484 goto done;
1487 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1488 &user_pol,
1490 &info,
1491 &result_tmp);
1493 if (!NT_STATUS_IS_OK(status_tmp)) {
1494 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1495 nt_errstr(status_tmp)));
1496 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1497 goto done;
1499 if (!NT_STATUS_IS_OK(result_tmp)) {
1500 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1501 nt_errstr(result_tmp)));
1502 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1503 goto done;
1506 acct_flags = info->info16.acct_flags;
1508 if (acct_flags == 0) {
1509 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1510 goto done;
1513 my_info3->base.acct_flags = acct_flags;
1515 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1517 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1520 *info3 = my_info3;
1521 done:
1522 return result;
1525 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1526 struct winbindd_cli_state *state)
1528 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1529 NTSTATUS krb5_result = NT_STATUS_OK;
1530 fstring name_domain, name_user;
1531 char *mapped_user;
1532 fstring domain_user;
1533 struct netr_SamInfo3 *info3 = NULL;
1534 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1536 /* Ensure null termination */
1537 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1539 /* Ensure null termination */
1540 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1542 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1543 state->request->data.auth.user));
1545 /* Parse domain and username */
1547 name_map_status = normalize_name_unmap(state->mem_ctx,
1548 state->request->data.auth.user,
1549 &mapped_user);
1551 /* If the name normalization didnt' actually do anything,
1552 just use the original name */
1554 if (!NT_STATUS_IS_OK(name_map_status) &&
1555 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1557 mapped_user = state->request->data.auth.user;
1560 parse_domain_user(mapped_user, name_domain, name_user);
1562 if ( mapped_user != state->request->data.auth.user ) {
1563 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1564 *lp_winbind_separator(),
1565 name_user );
1566 strlcpy( state->request->data.auth.user, domain_user,
1567 sizeof(state->request->data.auth.user));
1570 if (!domain->online) {
1571 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1572 if (domain->startup) {
1573 /* Logons are very important to users. If we're offline and
1574 we get a request within the first 30 seconds of startup,
1575 try very hard to find a DC and go online. */
1577 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1578 "request in startup mode.\n", domain->name ));
1580 winbindd_flush_negative_conn_cache(domain);
1581 result = init_dc_connection(domain);
1585 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1587 /* Check for Kerberos authentication */
1588 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1590 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1591 /* save for later */
1592 krb5_result = result;
1595 if (NT_STATUS_IS_OK(result)) {
1596 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1597 goto process_result;
1598 } else {
1599 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1602 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1603 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1604 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1605 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1606 set_domain_offline( domain );
1607 goto cached_logon;
1610 /* there are quite some NT_STATUS errors where there is no
1611 * point in retrying with a samlogon, we explictly have to take
1612 * care not to increase the bad logon counter on the DC */
1614 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1615 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1616 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1617 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1618 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1619 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1620 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1621 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1622 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1623 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1624 goto done;
1627 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1628 DEBUG(3,("falling back to samlogon\n"));
1629 goto sam_logon;
1630 } else {
1631 goto cached_logon;
1635 sam_logon:
1636 /* Check for Samlogon authentication */
1637 if (domain->online) {
1638 result = winbindd_dual_pam_auth_samlogon(
1639 state->mem_ctx, domain,
1640 state->request->data.auth.user,
1641 state->request->data.auth.pass,
1642 state->request->flags,
1643 &info3);
1645 if (NT_STATUS_IS_OK(result)) {
1646 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1647 /* add the Krb5 err if we have one */
1648 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1649 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1651 goto process_result;
1654 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1655 nt_errstr(result)));
1657 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1658 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1659 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1661 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1662 set_domain_offline( domain );
1663 goto cached_logon;
1666 if (domain->online) {
1667 /* We're still online - fail. */
1668 goto done;
1672 cached_logon:
1673 /* Check for Cached logons */
1674 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1675 lp_winbind_offline_logon()) {
1677 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1679 if (NT_STATUS_IS_OK(result)) {
1680 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1681 goto process_result;
1682 } else {
1683 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1684 goto done;
1688 process_result:
1690 if (NT_STATUS_IS_OK(result)) {
1692 struct dom_sid user_sid;
1694 /* In all codepaths where result == NT_STATUS_OK info3 must have
1695 been initialized. */
1696 if (!info3) {
1697 result = NT_STATUS_INTERNAL_ERROR;
1698 goto done;
1701 sid_compose(&user_sid, info3->base.domain_sid,
1702 info3->base.rid);
1704 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1705 &user_sid);
1706 netsamlogon_cache_store(name_user, info3);
1708 /* save name_to_sid info as early as possible (only if
1709 this is our primary domain so we don't invalidate
1710 the cache entry by storing the seq_num for the wrong
1711 domain). */
1712 if ( domain->primary ) {
1713 cache_name2sid(domain, name_domain, name_user,
1714 SID_NAME_USER, &user_sid);
1717 /* Check if the user is in the right group */
1719 result = check_info3_in_group(
1720 info3,
1721 state->request->data.auth.require_membership_of_sid);
1722 if (!NT_STATUS_IS_OK(result)) {
1723 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1724 state->request->data.auth.user,
1725 state->request->data.auth.require_membership_of_sid));
1726 goto done;
1729 result = append_auth_data(state->mem_ctx, state->response,
1730 state->request->flags, info3,
1731 name_domain, name_user);
1732 if (!NT_STATUS_IS_OK(result)) {
1733 goto done;
1736 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1737 && lp_winbind_offline_logon()) {
1739 result = winbindd_store_creds(domain,
1740 state->request->data.auth.user,
1741 state->request->data.auth.pass,
1742 info3);
1745 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1746 struct winbindd_domain *our_domain = find_our_domain();
1748 /* This is not entirely correct I believe, but it is
1749 consistent. Only apply the password policy settings
1750 too warn users for our own domain. Cannot obtain these
1751 from trusted DCs all the time so don't do it at all.
1752 -- jerry */
1754 result = NT_STATUS_NOT_SUPPORTED;
1755 if (our_domain == domain ) {
1756 result = fillup_password_policy(
1757 our_domain, state->response);
1760 if (!NT_STATUS_IS_OK(result)
1761 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1763 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1764 domain->name, nt_errstr(result)));
1765 goto done;
1769 result = NT_STATUS_OK;
1772 done:
1773 /* give us a more useful (more correct?) error code */
1774 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1775 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1776 result = NT_STATUS_NO_LOGON_SERVERS;
1779 set_auth_errors(state->response, result);
1781 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1782 state->request->data.auth.user,
1783 state->response->data.auth.nt_status_string,
1784 state->response->data.auth.pam_error));
1786 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1789 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1790 struct winbindd_cli_state *state)
1792 NTSTATUS result;
1793 struct netr_SamInfo3 *info3 = NULL;
1794 const char *name_user = NULL;
1795 const char *name_domain = NULL;
1796 const char *workstation;
1798 DATA_BLOB lm_resp, nt_resp;
1800 /* This is child-only, so no check for privileged access is needed
1801 anymore */
1803 /* Ensure null termination */
1804 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1805 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1807 name_user = state->request->data.auth_crap.user;
1808 name_domain = state->request->data.auth_crap.domain;
1809 workstation = state->request->data.auth_crap.workstation;
1811 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1812 name_domain, name_user));
1814 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1815 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1816 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1817 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1818 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1819 state->request->data.auth_crap.lm_resp_len,
1820 state->request->data.auth_crap.nt_resp_len));
1821 result = NT_STATUS_INVALID_PARAMETER;
1822 goto done;
1826 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1827 state->request->data.auth_crap.lm_resp_len);
1829 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1830 nt_resp = data_blob_talloc(state->mem_ctx,
1831 state->request->extra_data.data,
1832 state->request->data.auth_crap.nt_resp_len);
1833 } else {
1834 nt_resp = data_blob_talloc(state->mem_ctx,
1835 state->request->data.auth_crap.nt_resp,
1836 state->request->data.auth_crap.nt_resp_len);
1839 if (strequal(name_domain, get_global_sam_name())) {
1840 DATA_BLOB chal_blob = data_blob_const(
1841 state->request->data.auth_crap.chal,
1842 sizeof(state->request->data.auth_crap.chal));
1844 result = winbindd_dual_auth_passdb(
1845 state->mem_ctx,
1846 state->request->data.auth_crap.logon_parameters,
1847 name_domain, name_user,
1848 &chal_blob, &lm_resp, &nt_resp, &info3);
1849 goto process_result;
1852 result = winbind_samlogon_retry_loop(domain,
1853 state->mem_ctx,
1854 state->request->data.auth_crap.logon_parameters,
1855 domain->dcname,
1856 name_user,
1857 name_domain,
1858 /* Bug #3248 - found by Stefan Burkei. */
1859 workstation, /* We carefully set this above so use it... */
1860 state->request->data.auth_crap.chal,
1861 lm_resp,
1862 nt_resp,
1863 &info3);
1864 if (!NT_STATUS_IS_OK(result)) {
1865 goto done;
1868 process_result:
1870 if (NT_STATUS_IS_OK(result)) {
1871 struct dom_sid user_sid;
1873 sid_compose(&user_sid, info3->base.domain_sid,
1874 info3->base.rid);
1875 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1876 &user_sid);
1877 netsamlogon_cache_store(name_user, info3);
1879 /* Check if the user is in the right group */
1881 result = check_info3_in_group(
1882 info3,
1883 state->request->data.auth_crap.require_membership_of_sid);
1884 if (!NT_STATUS_IS_OK(result)) {
1885 DEBUG(3, ("User %s is not in the required group (%s), so "
1886 "crap authentication is rejected\n",
1887 state->request->data.auth_crap.user,
1888 state->request->data.auth_crap.require_membership_of_sid));
1889 goto done;
1892 result = append_auth_data(state->mem_ctx, state->response,
1893 state->request->flags, info3,
1894 name_domain, name_user);
1895 if (!NT_STATUS_IS_OK(result)) {
1896 goto done;
1900 done:
1902 /* give us a more useful (more correct?) error code */
1903 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1904 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1905 result = NT_STATUS_NO_LOGON_SERVERS;
1908 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1909 result = nt_status_squash(result);
1912 set_auth_errors(state->response, result);
1914 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1915 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1916 name_domain,
1917 name_user,
1918 state->response->data.auth.nt_status_string,
1919 state->response->data.auth.pam_error));
1921 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1924 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1925 struct winbindd_cli_state *state)
1927 char *oldpass;
1928 char *newpass = NULL;
1929 struct policy_handle dom_pol;
1930 struct rpc_pipe_client *cli = NULL;
1931 bool got_info = false;
1932 struct samr_DomInfo1 *info = NULL;
1933 struct userPwdChangeFailureInformation *reject = NULL;
1934 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1935 fstring domain, user;
1936 struct dcerpc_binding_handle *b = NULL;
1938 ZERO_STRUCT(dom_pol);
1940 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1941 state->request->data.auth.user));
1943 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1944 goto done;
1947 /* Change password */
1949 oldpass = state->request->data.chauthtok.oldpass;
1950 newpass = state->request->data.chauthtok.newpass;
1952 /* Initialize reject reason */
1953 state->response->data.auth.reject_reason = Undefined;
1955 /* Get sam handle */
1957 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1958 &dom_pol);
1959 if (!NT_STATUS_IS_OK(result)) {
1960 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1961 goto done;
1964 b = cli->binding_handle;
1966 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1967 user,
1968 newpass,
1969 oldpass,
1970 &info,
1971 &reject);
1973 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1975 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1977 fill_in_password_policy(state->response, info);
1979 state->response->data.auth.reject_reason =
1980 reject->extendedFailureReason;
1982 got_info = true;
1985 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1986 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1987 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1988 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1990 /* only fallback when the chgpasswd_user3 call is not supported */
1991 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1992 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1993 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1994 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1996 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1997 nt_errstr(result)));
1999 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2001 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2002 Map to the same status code as Windows 2003. */
2004 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2005 result = NT_STATUS_PASSWORD_RESTRICTION;
2009 done:
2011 if (NT_STATUS_IS_OK(result)
2012 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2013 && lp_winbind_offline_logon()) {
2014 result = winbindd_update_creds_by_name(contact_domain, user,
2015 newpass);
2016 /* Again, this happens when we login from gdm or xdm
2017 * and the password expires, *BUT* cached crendentials
2018 * doesn't exist. winbindd_update_creds_by_name()
2019 * returns NT_STATUS_NO_SUCH_USER.
2020 * This is not a failure.
2021 * --- BoYang
2022 * */
2023 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2024 result = NT_STATUS_OK;
2027 if (!NT_STATUS_IS_OK(result)) {
2028 DEBUG(10, ("Failed to store creds: %s\n",
2029 nt_errstr(result)));
2030 goto process_result;
2034 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2036 NTSTATUS policy_ret;
2038 policy_ret = fillup_password_policy(
2039 contact_domain, state->response);
2041 /* failure of this is non critical, it will just provide no
2042 * additional information to the client why the change has
2043 * failed - Guenther */
2045 if (!NT_STATUS_IS_OK(policy_ret)) {
2046 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2047 goto process_result;
2051 process_result:
2053 if (strequal(contact_domain->name, get_global_sam_name())) {
2054 /* FIXME: internal rpc pipe does not cache handles yet */
2055 if (b) {
2056 if (is_valid_policy_hnd(&dom_pol)) {
2057 NTSTATUS _result;
2058 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2060 TALLOC_FREE(cli);
2064 set_auth_errors(state->response, result);
2066 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2067 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2068 domain,
2069 user,
2070 state->response->data.auth.nt_status_string,
2071 state->response->data.auth.pam_error));
2073 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2076 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2077 struct winbindd_cli_state *state)
2079 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2081 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2082 state->request->data.logoff.user));
2084 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2085 result = NT_STATUS_OK;
2086 goto process_result;
2089 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2090 result = NT_STATUS_OK;
2091 goto process_result;
2094 #ifdef HAVE_KRB5
2096 if (state->request->data.logoff.uid < 0) {
2097 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2098 goto process_result;
2101 /* what we need here is to find the corresponding krb5 ccache name *we*
2102 * created for a given username and destroy it */
2104 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2105 result = NT_STATUS_OK;
2106 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2107 goto process_result;
2110 if (!ccache_entry_identical(state->request->data.logoff.user,
2111 state->request->data.logoff.uid,
2112 state->request->data.logoff.krb5ccname)) {
2113 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2114 goto process_result;
2117 result = remove_ccache(state->request->data.logoff.user);
2118 if (!NT_STATUS_IS_OK(result)) {
2119 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2120 nt_errstr(result)));
2121 goto process_result;
2124 #else
2125 result = NT_STATUS_NOT_SUPPORTED;
2126 #endif
2128 process_result:
2131 set_auth_errors(state->response, result);
2133 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2136 /* Change user password with auth crap*/
2138 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2140 NTSTATUS result;
2141 DATA_BLOB new_nt_password;
2142 DATA_BLOB old_nt_hash_enc;
2143 DATA_BLOB new_lm_password;
2144 DATA_BLOB old_lm_hash_enc;
2145 fstring domain,user;
2146 struct policy_handle dom_pol;
2147 struct winbindd_domain *contact_domain = domainSt;
2148 struct rpc_pipe_client *cli = NULL;
2149 struct dcerpc_binding_handle *b = NULL;
2151 ZERO_STRUCT(dom_pol);
2153 /* Ensure null termination */
2154 state->request->data.chng_pswd_auth_crap.user[
2155 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2156 state->request->data.chng_pswd_auth_crap.domain[
2157 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2158 *domain = 0;
2159 *user = 0;
2161 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2162 (unsigned long)state->pid,
2163 state->request->data.chng_pswd_auth_crap.domain,
2164 state->request->data.chng_pswd_auth_crap.user));
2166 if (lp_winbind_offline_logon()) {
2167 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2168 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2169 result = NT_STATUS_ACCESS_DENIED;
2170 goto done;
2173 if (*state->request->data.chng_pswd_auth_crap.domain) {
2174 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2175 } else {
2176 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2177 domain, user);
2179 if(!*domain) {
2180 DEBUG(3,("no domain specified with username (%s) - "
2181 "failing auth\n",
2182 state->request->data.chng_pswd_auth_crap.user));
2183 result = NT_STATUS_NO_SUCH_USER;
2184 goto done;
2188 if (!*domain && lp_winbind_use_default_domain()) {
2189 fstrcpy(domain,lp_workgroup());
2192 if(!*user) {
2193 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2196 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2197 (unsigned long)state->pid, domain, user));
2199 /* Change password */
2200 new_nt_password = data_blob_const(
2201 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2202 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2204 old_nt_hash_enc = data_blob_const(
2205 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2206 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2208 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2209 new_lm_password = data_blob_const(
2210 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2211 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2213 old_lm_hash_enc = data_blob_const(
2214 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2215 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2216 } else {
2217 new_lm_password = data_blob_null;
2218 old_lm_hash_enc = data_blob_null;
2221 /* Get sam handle */
2223 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2224 if (!NT_STATUS_IS_OK(result)) {
2225 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2226 goto done;
2229 b = cli->binding_handle;
2231 result = rpccli_samr_chng_pswd_auth_crap(
2232 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2233 new_lm_password, old_lm_hash_enc);
2235 done:
2237 if (strequal(contact_domain->name, get_global_sam_name())) {
2238 /* FIXME: internal rpc pipe does not cache handles yet */
2239 if (b) {
2240 if (is_valid_policy_hnd(&dom_pol)) {
2241 NTSTATUS _result;
2242 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2244 TALLOC_FREE(cli);
2248 set_auth_errors(state->response, result);
2250 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2251 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2252 domain, user,
2253 state->response->data.auth.nt_status_string,
2254 state->response->data.auth.pam_error));
2256 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;