s3:smb2_server: add SMB3 encryption support
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blobd8febe6a5c4c4a861d83b39e797ca60c9be4caae
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 realm,
650 uid,
651 time(NULL),
652 ticket_lifetime,
653 renewal_until,
654 false);
656 if (!NT_STATUS_IS_OK(result)) {
657 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
658 nt_errstr(result)));
660 } else {
662 /* need to delete the memory cred cache, it is not used anymore */
664 krb5_ret = ads_kdestroy(cc);
665 if (krb5_ret) {
666 DEBUG(3,("winbindd_raw_kerberos_login: "
667 "could not destroy krb5 credential cache: "
668 "%s\n", error_message(krb5_ret)));
673 return NT_STATUS_OK;
675 failed:
677 /* we could have created a new credential cache with a valid tgt in it
678 * but we werent able to get or verify the service ticket for this
679 * local host and therefor didn't get the PAC, we need to remove that
680 * cache entirely now */
682 krb5_ret = ads_kdestroy(cc);
683 if (krb5_ret) {
684 DEBUG(3,("winbindd_raw_kerberos_login: "
685 "could not destroy krb5 credential cache: "
686 "%s\n", error_message(krb5_ret)));
689 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
690 DEBUG(3,("winbindd_raw_kerberos_login: "
691 "could not remove ccache for user %s\n",
692 user));
695 return result;
696 #else
697 return NT_STATUS_NOT_SUPPORTED;
698 #endif /* HAVE_KRB5 */
701 /****************************************************************
702 ****************************************************************/
704 bool check_request_flags(uint32_t flags)
706 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
707 WBFLAG_PAM_INFO3_TEXT |
708 WBFLAG_PAM_INFO3_NDR;
710 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
711 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
712 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
713 !(flags & flags_edata) ) {
714 return true;
717 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
718 flags));
720 return false;
723 /****************************************************************
724 ****************************************************************/
726 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
727 struct winbindd_response *resp,
728 uint32_t request_flags,
729 struct netr_SamInfo3 *info3,
730 const char *name_domain,
731 const char *name_user)
733 NTSTATUS result;
735 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
736 memcpy(resp->data.auth.user_session_key,
737 info3->base.key.key,
738 sizeof(resp->data.auth.user_session_key)
739 /* 16 */);
742 if (request_flags & WBFLAG_PAM_LMKEY) {
743 memcpy(resp->data.auth.first_8_lm_hash,
744 info3->base.LMSessKey.key,
745 sizeof(resp->data.auth.first_8_lm_hash)
746 /* 8 */);
749 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
750 result = append_unix_username(mem_ctx, resp,
751 info3, name_domain, name_user);
752 if (!NT_STATUS_IS_OK(result)) {
753 DEBUG(10,("Failed to append Unix Username: %s\n",
754 nt_errstr(result)));
755 return result;
759 /* currently, anything from here on potentially overwrites extra_data. */
761 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
762 result = append_info3_as_ndr(mem_ctx, resp, info3);
763 if (!NT_STATUS_IS_OK(result)) {
764 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
765 nt_errstr(result)));
766 return result;
770 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
771 result = append_info3_as_txt(mem_ctx, resp, info3);
772 if (!NT_STATUS_IS_OK(result)) {
773 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
774 nt_errstr(result)));
775 return result;
779 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
780 result = append_afs_token(mem_ctx, resp,
781 info3, name_domain, name_user);
782 if (!NT_STATUS_IS_OK(result)) {
783 DEBUG(10,("Failed to append AFS token: %s\n",
784 nt_errstr(result)));
785 return result;
789 return NT_STATUS_OK;
792 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
793 struct winbindd_cli_state *state,
794 struct netr_SamInfo3 **info3)
796 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
797 uint16 max_allowed_bad_attempts;
798 fstring name_domain, name_user;
799 struct dom_sid sid;
800 enum lsa_SidType type;
801 uchar new_nt_pass[NT_HASH_LEN];
802 const uint8 *cached_nt_pass;
803 const uint8 *cached_salt;
804 struct netr_SamInfo3 *my_info3;
805 time_t kickoff_time, must_change_time;
806 bool password_good = false;
807 #ifdef HAVE_KRB5
808 struct winbindd_tdc_domain *tdc_domain = NULL;
809 #endif
811 *info3 = NULL;
813 ZERO_STRUCTP(info3);
815 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
817 /* Parse domain and username */
819 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
822 if (!lookup_cached_name(name_domain,
823 name_user,
824 &sid,
825 &type)) {
826 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
827 return NT_STATUS_NO_SUCH_USER;
830 if (type != SID_NAME_USER) {
831 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
832 return NT_STATUS_LOGON_FAILURE;
835 result = winbindd_get_creds(domain,
836 state->mem_ctx,
837 &sid,
838 &my_info3,
839 &cached_nt_pass,
840 &cached_salt);
841 if (!NT_STATUS_IS_OK(result)) {
842 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
843 return result;
846 *info3 = my_info3;
848 E_md4hash(state->request->data.auth.pass, new_nt_pass);
850 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
851 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
852 if (cached_salt) {
853 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
856 if (cached_salt) {
857 /* In this case we didn't store the nt_hash itself,
858 but the MD5 combination of salt + nt_hash. */
859 uchar salted_hash[NT_HASH_LEN];
860 E_md5hash(cached_salt, new_nt_pass, salted_hash);
862 password_good = (memcmp(cached_nt_pass, salted_hash,
863 NT_HASH_LEN) == 0);
864 } else {
865 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
866 password_good = (memcmp(cached_nt_pass, new_nt_pass,
867 NT_HASH_LEN) == 0);
870 if (password_good) {
872 /* User *DOES* know the password, update logon_time and reset
873 * bad_pw_count */
875 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
877 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
878 return NT_STATUS_ACCOUNT_LOCKED_OUT;
881 if (my_info3->base.acct_flags & ACB_DISABLED) {
882 return NT_STATUS_ACCOUNT_DISABLED;
885 if (my_info3->base.acct_flags & ACB_WSTRUST) {
886 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
889 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
890 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
893 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
894 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
897 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
898 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
899 my_info3->base.acct_flags));
900 return NT_STATUS_LOGON_FAILURE;
903 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
904 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
905 return NT_STATUS_ACCOUNT_EXPIRED;
908 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
909 if (must_change_time != 0 && must_change_time < time(NULL)) {
910 /* we allow grace logons when the password has expired */
911 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
912 /* return NT_STATUS_PASSWORD_EXPIRED; */
913 goto success;
916 #ifdef HAVE_KRB5
917 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
918 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
919 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
920 /* used to cope with the case winbindd starting without network. */
921 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
923 uid_t uid = -1;
924 const char *cc = NULL;
925 char *realm = NULL;
926 const char *principal_s = NULL;
927 const char *service = NULL;
928 const char *user_ccache_file;
930 uid = get_uid_from_request(state->request);
931 if (uid == -1) {
932 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
933 return NT_STATUS_INVALID_PARAMETER;
936 cc = generate_krb5_ccache(state->mem_ctx,
937 state->request->data.auth.krb5_cc_type,
938 state->request->data.auth.uid,
939 &user_ccache_file);
940 if (cc == NULL) {
941 return NT_STATUS_NO_MEMORY;
944 realm = domain->alt_name;
945 if (!strupper_m(realm)) {
946 return NT_STATUS_INVALID_PARAMETER;
949 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
950 if (principal_s == NULL) {
951 return NT_STATUS_NO_MEMORY;
954 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
955 if (service == NULL) {
956 return NT_STATUS_NO_MEMORY;
959 if (user_ccache_file != NULL) {
961 fstrcpy(state->response->data.auth.krb5ccname,
962 user_ccache_file);
964 result = add_ccache_to_list(principal_s,
966 service,
967 state->request->data.auth.user,
968 domain->alt_name,
969 uid,
970 time(NULL),
971 time(NULL) + lp_winbind_cache_time(),
972 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
973 true);
975 if (!NT_STATUS_IS_OK(result)) {
976 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
977 "to add ccache to list: %s\n",
978 nt_errstr(result)));
982 #endif /* HAVE_KRB5 */
983 success:
984 /* FIXME: we possibly should handle logon hours as well (does xp when
985 * offline?) see auth/auth_sam.c:sam_account_ok for details */
987 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
988 my_info3->base.bad_password_count = 0;
990 result = winbindd_update_creds_by_info3(domain,
991 state->request->data.auth.user,
992 state->request->data.auth.pass,
993 my_info3);
994 if (!NT_STATUS_IS_OK(result)) {
995 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
996 nt_errstr(result)));
997 return result;
1000 return NT_STATUS_OK;
1004 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1005 if (domain->online == false) {
1006 goto failed;
1009 /* failure of this is not critical */
1010 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1011 if (!NT_STATUS_IS_OK(result)) {
1012 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1013 "Won't be able to honour account lockout policies\n"));
1016 /* increase counter */
1017 my_info3->base.bad_password_count++;
1019 if (max_allowed_bad_attempts == 0) {
1020 goto failed;
1023 /* lockout user */
1024 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1026 uint32 password_properties;
1028 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1029 if (!NT_STATUS_IS_OK(result)) {
1030 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1033 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1034 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1035 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1039 failed:
1040 result = winbindd_update_creds_by_info3(domain,
1041 state->request->data.auth.user,
1042 NULL,
1043 my_info3);
1045 if (!NT_STATUS_IS_OK(result)) {
1046 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1047 nt_errstr(result)));
1050 return NT_STATUS_LOGON_FAILURE;
1053 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1054 struct winbindd_cli_state *state,
1055 struct netr_SamInfo3 **info3)
1057 struct winbindd_domain *contact_domain;
1058 fstring name_domain, name_user;
1059 NTSTATUS result;
1061 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1063 /* Parse domain and username */
1065 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1067 /* what domain should we contact? */
1069 if ( IS_DC ) {
1070 if (!(contact_domain = find_domain_from_name(name_domain))) {
1071 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1072 state->request->data.auth.user, name_domain, name_user, name_domain));
1073 result = NT_STATUS_NO_SUCH_USER;
1074 goto done;
1077 } else {
1078 if (is_myname(name_domain)) {
1079 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1080 result = NT_STATUS_NO_SUCH_USER;
1081 goto done;
1084 contact_domain = find_domain_from_name(name_domain);
1085 if (contact_domain == NULL) {
1086 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1087 state->request->data.auth.user, name_domain, name_user, name_domain));
1089 result = NT_STATUS_NO_SUCH_USER;
1090 goto done;
1094 if (contact_domain->initialized &&
1095 contact_domain->active_directory) {
1096 goto try_login;
1099 if (!contact_domain->initialized) {
1100 init_dc_connection(contact_domain);
1103 if (!contact_domain->active_directory) {
1104 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1105 return NT_STATUS_INVALID_LOGON_TYPE;
1107 try_login:
1108 result = winbindd_raw_kerberos_login(
1109 state->mem_ctx, contact_domain,
1110 state->request->data.auth.user,
1111 state->request->data.auth.pass,
1112 state->request->data.auth.krb5_cc_type,
1113 get_uid_from_request(state->request),
1114 info3, state->response->data.auth.krb5ccname);
1115 done:
1116 return result;
1119 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1120 uint32_t logon_parameters,
1121 const char *domain, const char *user,
1122 const DATA_BLOB *challenge,
1123 const DATA_BLOB *lm_resp,
1124 const DATA_BLOB *nt_resp,
1125 struct netr_SamInfo3 **pinfo3)
1127 struct auth_usersupplied_info *user_info = NULL;
1128 struct tsocket_address *local;
1129 NTSTATUS status;
1130 int rc;
1132 rc = tsocket_address_inet_from_strings(mem_ctx,
1133 "ip",
1134 "127.0.0.1",
1136 &local);
1137 if (rc < 0) {
1138 return NT_STATUS_NO_MEMORY;
1140 status = make_user_info(&user_info, user, user, domain, domain,
1141 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1142 NULL, AUTH_PASSWORD_RESPONSE);
1143 if (!NT_STATUS_IS_OK(status)) {
1144 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1145 return status;
1147 user_info->logon_parameters = logon_parameters;
1149 /* We don't want any more mapping of the username */
1150 user_info->mapped_state = True;
1152 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1153 pinfo3);
1154 free_user_info(&user_info);
1155 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1156 user, nt_errstr(status)));
1157 return status;
1160 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1161 TALLOC_CTX *mem_ctx,
1162 uint32_t logon_parameters,
1163 const char *server,
1164 const char *username,
1165 const char *domainname,
1166 const char *workstation,
1167 const uint8_t chal[8],
1168 DATA_BLOB lm_response,
1169 DATA_BLOB nt_response,
1170 struct netr_SamInfo3 **info3)
1172 int attempts = 0;
1173 bool retry = false;
1174 NTSTATUS result;
1176 do {
1177 struct rpc_pipe_client *netlogon_pipe;
1178 const struct pipe_auth_data *auth;
1179 uint32_t neg_flags = 0;
1181 ZERO_STRUCTP(info3);
1182 retry = false;
1184 result = cm_connect_netlogon(domain, &netlogon_pipe);
1186 if (!NT_STATUS_IS_OK(result)) {
1187 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1188 nt_errstr(result)));
1189 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1190 if (attempts > 0) {
1191 DEBUG(3, ("This is the second problem for this "
1192 "particular call, forcing the close of "
1193 "this connection\n"));
1194 invalidate_cm_connection(&domain->conn);
1195 } else {
1196 DEBUG(3, ("First call to cm_connect_netlogon "
1197 "has timed out, retrying\n"));
1198 continue;
1201 return result;
1203 auth = netlogon_pipe->auth;
1204 if (netlogon_pipe->dc) {
1205 neg_flags = netlogon_pipe->dc->negotiate_flags;
1208 /* It is really important to try SamLogonEx here,
1209 * because in a clustered environment, we want to use
1210 * one machine account from multiple physical
1211 * computers.
1213 * With a normal SamLogon call, we must keep the
1214 * credentials chain updated and intact between all
1215 * users of the machine account (which would imply
1216 * cross-node communication for every NTLM logon).
1218 * (The credentials chain is not per NETLOGON pipe
1219 * connection, but globally on the server/client pair
1220 * by machine name).
1222 * When using SamLogonEx, the credentials are not
1223 * supplied, but the session key is implied by the
1224 * wrapping SamLogon context.
1226 * -- abartlet 21 April 2008
1228 * It's also important to use NetlogonValidationSamInfo4 (6),
1229 * because it relies on the rpc transport encryption
1230 * and avoids using the global netlogon schannel
1231 * session key to en/decrypt secret information
1232 * like the user_session_key for network logons.
1234 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1235 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1236 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1237 * are the indication that the server supports
1238 * NetlogonValidationSamInfo4 (6). And it must only
1239 * be used if "SealSecureChannel" is used.
1241 * -- metze 4 February 2011
1244 if (auth == NULL) {
1245 domain->can_do_validation6 = false;
1246 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1247 domain->can_do_validation6 = false;
1248 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1249 domain->can_do_validation6 = false;
1250 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1251 domain->can_do_validation6 = false;
1252 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1253 domain->can_do_validation6 = false;
1256 if (domain->can_do_samlogon_ex && domain->can_do_validation6) {
1257 result = rpccli_netlogon_sam_network_logon_ex(
1258 netlogon_pipe,
1259 mem_ctx,
1260 logon_parameters,
1261 server, /* server name */
1262 username, /* user name */
1263 domainname, /* target domain */
1264 workstation, /* workstation */
1265 chal,
1267 lm_response,
1268 nt_response,
1269 info3);
1270 } else {
1271 result = rpccli_netlogon_sam_network_logon(
1272 netlogon_pipe,
1273 mem_ctx,
1274 logon_parameters,
1275 server, /* server name */
1276 username, /* user name */
1277 domainname, /* target domain */
1278 workstation, /* workstation */
1279 chal,
1280 domain->can_do_validation6 ? 6 : 3,
1281 lm_response,
1282 nt_response,
1283 info3);
1286 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1289 * It's likely that the server also does not support
1290 * validation level 6
1292 domain->can_do_validation6 = false;
1294 if (domain->can_do_samlogon_ex) {
1295 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1296 "retrying with NetSamLogon\n"));
1297 domain->can_do_samlogon_ex = false;
1298 retry = true;
1299 continue;
1303 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1304 * (no Ex). This happens against old Samba
1305 * DCs. Drop the connection.
1307 invalidate_cm_connection(&domain->conn);
1308 result = NT_STATUS_LOGON_FAILURE;
1309 break;
1312 if (domain->can_do_validation6 &&
1313 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1314 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1315 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1316 DEBUG(3,("Got a DC that can not do validation level 6, "
1317 "retrying with level 3\n"));
1318 domain->can_do_validation6 = false;
1319 retry = true;
1320 continue;
1324 * we increment this after the "feature negotiation"
1325 * for can_do_samlogon_ex and can_do_validation6
1327 attempts += 1;
1329 /* We have to try a second time as cm_connect_netlogon
1330 might not yet have noticed that the DC has killed
1331 our connection. */
1333 if (!rpccli_is_connected(netlogon_pipe)) {
1334 retry = true;
1335 continue;
1338 /* if we get access denied, a possible cause was that we had
1339 and open connection to the DC, but someone changed our
1340 machine account password out from underneath us using 'net
1341 rpc changetrustpw' */
1343 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1344 DEBUG(3,("winbind_samlogon_retry_loop: sam_logon returned "
1345 "ACCESS_DENIED. Maybe the trust account "
1346 "password was changed and we didn't know it. "
1347 "Killing connections to domain %s\n",
1348 domainname));
1349 invalidate_cm_connection(&domain->conn);
1350 retry = true;
1353 } while ( (attempts < 2) && retry );
1355 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1356 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1357 "returned NT_STATUS_IO_TIMEOUT after the retry."
1358 "Killing connections to domain %s\n",
1359 domainname));
1360 invalidate_cm_connection(&domain->conn);
1362 return result;
1365 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1366 struct winbindd_domain *domain,
1367 const char *user,
1368 const char *pass,
1369 uint32_t request_flags,
1370 struct netr_SamInfo3 **info3)
1373 uchar chal[8];
1374 DATA_BLOB lm_resp;
1375 DATA_BLOB nt_resp;
1376 unsigned char local_nt_response[24];
1377 fstring name_domain, name_user;
1378 NTSTATUS result;
1379 struct netr_SamInfo3 *my_info3 = NULL;
1381 *info3 = NULL;
1383 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1385 /* Parse domain and username */
1387 parse_domain_user(user, name_domain, name_user);
1389 /* do password magic */
1391 generate_random_buffer(chal, sizeof(chal));
1393 if (lp_client_ntlmv2_auth()) {
1394 DATA_BLOB server_chal;
1395 DATA_BLOB names_blob;
1396 server_chal = data_blob_const(chal, 8);
1398 /* note that the 'workgroup' here is for the local
1399 machine. The 'server name' must match the
1400 'workstation' passed to the actual SamLogon call.
1402 names_blob = NTLMv2_generate_names_blob(
1403 mem_ctx, lp_netbios_name(), lp_workgroup());
1405 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1406 pass,
1407 &server_chal,
1408 &names_blob,
1409 &lm_resp, &nt_resp, NULL, NULL)) {
1410 data_blob_free(&names_blob);
1411 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1412 result = NT_STATUS_NO_MEMORY;
1413 goto done;
1415 data_blob_free(&names_blob);
1416 } else {
1417 lm_resp = data_blob_null;
1418 SMBNTencrypt(pass, chal, local_nt_response);
1420 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1421 sizeof(local_nt_response));
1424 if (strequal(name_domain, get_global_sam_name())) {
1425 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1427 result = winbindd_dual_auth_passdb(
1428 mem_ctx, 0, name_domain, name_user,
1429 &chal_blob, &lm_resp, &nt_resp, info3);
1430 goto done;
1433 /* check authentication loop */
1435 result = winbind_samlogon_retry_loop(domain,
1436 mem_ctx,
1438 domain->dcname,
1439 name_user,
1440 name_domain,
1441 lp_netbios_name(),
1442 chal,
1443 lm_resp,
1444 nt_resp,
1445 &my_info3);
1446 if (!NT_STATUS_IS_OK(result)) {
1447 goto done;
1450 /* handle the case where a NT4 DC does not fill in the acct_flags in
1451 * the samlogon reply info3. When accurate info3 is required by the
1452 * caller, we look up the account flags ourselve - gd */
1454 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1455 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1457 struct rpc_pipe_client *samr_pipe;
1458 struct policy_handle samr_domain_handle, user_pol;
1459 union samr_UserInfo *info = NULL;
1460 NTSTATUS status_tmp, result_tmp;
1461 uint32 acct_flags;
1462 struct dcerpc_binding_handle *b;
1464 status_tmp = cm_connect_sam(domain, mem_ctx,
1465 &samr_pipe, &samr_domain_handle);
1467 if (!NT_STATUS_IS_OK(status_tmp)) {
1468 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1469 nt_errstr(status_tmp)));
1470 goto done;
1473 b = samr_pipe->binding_handle;
1475 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1476 &samr_domain_handle,
1477 MAXIMUM_ALLOWED_ACCESS,
1478 my_info3->base.rid,
1479 &user_pol,
1480 &result_tmp);
1482 if (!NT_STATUS_IS_OK(status_tmp)) {
1483 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1484 nt_errstr(status_tmp)));
1485 goto done;
1487 if (!NT_STATUS_IS_OK(result_tmp)) {
1488 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1489 nt_errstr(result_tmp)));
1490 goto done;
1493 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1494 &user_pol,
1496 &info,
1497 &result_tmp);
1499 if (!NT_STATUS_IS_OK(status_tmp)) {
1500 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1501 nt_errstr(status_tmp)));
1502 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1503 goto done;
1505 if (!NT_STATUS_IS_OK(result_tmp)) {
1506 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1507 nt_errstr(result_tmp)));
1508 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1509 goto done;
1512 acct_flags = info->info16.acct_flags;
1514 if (acct_flags == 0) {
1515 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1516 goto done;
1519 my_info3->base.acct_flags = acct_flags;
1521 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1523 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1526 *info3 = my_info3;
1527 done:
1528 return result;
1531 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1532 struct winbindd_cli_state *state)
1534 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1535 NTSTATUS krb5_result = NT_STATUS_OK;
1536 fstring name_domain, name_user;
1537 char *mapped_user;
1538 fstring domain_user;
1539 struct netr_SamInfo3 *info3 = NULL;
1540 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1542 /* Ensure null termination */
1543 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1545 /* Ensure null termination */
1546 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1548 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1549 state->request->data.auth.user));
1551 /* Parse domain and username */
1553 name_map_status = normalize_name_unmap(state->mem_ctx,
1554 state->request->data.auth.user,
1555 &mapped_user);
1557 /* If the name normalization didnt' actually do anything,
1558 just use the original name */
1560 if (!NT_STATUS_IS_OK(name_map_status) &&
1561 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1563 mapped_user = state->request->data.auth.user;
1566 parse_domain_user(mapped_user, name_domain, name_user);
1568 if ( mapped_user != state->request->data.auth.user ) {
1569 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1570 *lp_winbind_separator(),
1571 name_user );
1572 strlcpy( state->request->data.auth.user, domain_user,
1573 sizeof(state->request->data.auth.user));
1576 if (!domain->online) {
1577 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1578 if (domain->startup) {
1579 /* Logons are very important to users. If we're offline and
1580 we get a request within the first 30 seconds of startup,
1581 try very hard to find a DC and go online. */
1583 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1584 "request in startup mode.\n", domain->name ));
1586 winbindd_flush_negative_conn_cache(domain);
1587 result = init_dc_connection(domain);
1591 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1593 /* Check for Kerberos authentication */
1594 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1596 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1597 /* save for later */
1598 krb5_result = result;
1601 if (NT_STATUS_IS_OK(result)) {
1602 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1603 goto process_result;
1604 } else {
1605 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1608 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1609 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1610 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1611 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1612 set_domain_offline( domain );
1613 goto cached_logon;
1616 /* there are quite some NT_STATUS errors where there is no
1617 * point in retrying with a samlogon, we explictly have to take
1618 * care not to increase the bad logon counter on the DC */
1620 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1621 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1622 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1623 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1624 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1625 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1626 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1627 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1628 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1629 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1630 goto done;
1633 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1634 DEBUG(3,("falling back to samlogon\n"));
1635 goto sam_logon;
1636 } else {
1637 goto cached_logon;
1641 sam_logon:
1642 /* Check for Samlogon authentication */
1643 if (domain->online) {
1644 result = winbindd_dual_pam_auth_samlogon(
1645 state->mem_ctx, domain,
1646 state->request->data.auth.user,
1647 state->request->data.auth.pass,
1648 state->request->flags,
1649 &info3);
1651 if (NT_STATUS_IS_OK(result)) {
1652 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1653 /* add the Krb5 err if we have one */
1654 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1655 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1657 goto process_result;
1660 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1661 nt_errstr(result)));
1663 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1664 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1665 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1667 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1668 set_domain_offline( domain );
1669 goto cached_logon;
1672 if (domain->online) {
1673 /* We're still online - fail. */
1674 goto done;
1678 cached_logon:
1679 /* Check for Cached logons */
1680 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1681 lp_winbind_offline_logon()) {
1683 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1685 if (NT_STATUS_IS_OK(result)) {
1686 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1687 goto process_result;
1688 } else {
1689 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1690 goto done;
1694 process_result:
1696 if (NT_STATUS_IS_OK(result)) {
1698 struct dom_sid user_sid;
1700 /* In all codepaths where result == NT_STATUS_OK info3 must have
1701 been initialized. */
1702 if (!info3) {
1703 result = NT_STATUS_INTERNAL_ERROR;
1704 goto done;
1707 sid_compose(&user_sid, info3->base.domain_sid,
1708 info3->base.rid);
1710 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1711 &user_sid);
1712 netsamlogon_cache_store(name_user, info3);
1714 /* save name_to_sid info as early as possible (only if
1715 this is our primary domain so we don't invalidate
1716 the cache entry by storing the seq_num for the wrong
1717 domain). */
1718 if ( domain->primary ) {
1719 cache_name2sid(domain, name_domain, name_user,
1720 SID_NAME_USER, &user_sid);
1723 /* Check if the user is in the right group */
1725 result = check_info3_in_group(
1726 info3,
1727 state->request->data.auth.require_membership_of_sid);
1728 if (!NT_STATUS_IS_OK(result)) {
1729 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1730 state->request->data.auth.user,
1731 state->request->data.auth.require_membership_of_sid));
1732 goto done;
1735 result = append_auth_data(state->mem_ctx, state->response,
1736 state->request->flags, info3,
1737 name_domain, name_user);
1738 if (!NT_STATUS_IS_OK(result)) {
1739 goto done;
1742 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1743 && lp_winbind_offline_logon()) {
1745 result = winbindd_store_creds(domain,
1746 state->request->data.auth.user,
1747 state->request->data.auth.pass,
1748 info3);
1751 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1752 struct winbindd_domain *our_domain = find_our_domain();
1754 /* This is not entirely correct I believe, but it is
1755 consistent. Only apply the password policy settings
1756 too warn users for our own domain. Cannot obtain these
1757 from trusted DCs all the time so don't do it at all.
1758 -- jerry */
1760 result = NT_STATUS_NOT_SUPPORTED;
1761 if (our_domain == domain ) {
1762 result = fillup_password_policy(
1763 our_domain, state->response);
1766 if (!NT_STATUS_IS_OK(result)
1767 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1769 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1770 domain->name, nt_errstr(result)));
1771 goto done;
1775 result = NT_STATUS_OK;
1778 done:
1779 /* give us a more useful (more correct?) error code */
1780 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1781 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1782 result = NT_STATUS_NO_LOGON_SERVERS;
1785 set_auth_errors(state->response, result);
1787 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1788 state->request->data.auth.user,
1789 state->response->data.auth.nt_status_string,
1790 state->response->data.auth.pam_error));
1792 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1795 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1796 struct winbindd_cli_state *state)
1798 NTSTATUS result;
1799 struct netr_SamInfo3 *info3 = NULL;
1800 const char *name_user = NULL;
1801 const char *name_domain = NULL;
1802 const char *workstation;
1804 DATA_BLOB lm_resp, nt_resp;
1806 /* This is child-only, so no check for privileged access is needed
1807 anymore */
1809 /* Ensure null termination */
1810 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1811 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1813 name_user = state->request->data.auth_crap.user;
1814 name_domain = state->request->data.auth_crap.domain;
1815 workstation = state->request->data.auth_crap.workstation;
1817 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1818 name_domain, name_user));
1820 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1821 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1822 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1823 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1824 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1825 state->request->data.auth_crap.lm_resp_len,
1826 state->request->data.auth_crap.nt_resp_len));
1827 result = NT_STATUS_INVALID_PARAMETER;
1828 goto done;
1832 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1833 state->request->data.auth_crap.lm_resp_len);
1835 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1836 nt_resp = data_blob_talloc(state->mem_ctx,
1837 state->request->extra_data.data,
1838 state->request->data.auth_crap.nt_resp_len);
1839 } else {
1840 nt_resp = data_blob_talloc(state->mem_ctx,
1841 state->request->data.auth_crap.nt_resp,
1842 state->request->data.auth_crap.nt_resp_len);
1845 if (strequal(name_domain, get_global_sam_name())) {
1846 DATA_BLOB chal_blob = data_blob_const(
1847 state->request->data.auth_crap.chal,
1848 sizeof(state->request->data.auth_crap.chal));
1850 result = winbindd_dual_auth_passdb(
1851 state->mem_ctx,
1852 state->request->data.auth_crap.logon_parameters,
1853 name_domain, name_user,
1854 &chal_blob, &lm_resp, &nt_resp, &info3);
1855 goto process_result;
1858 result = winbind_samlogon_retry_loop(domain,
1859 state->mem_ctx,
1860 state->request->data.auth_crap.logon_parameters,
1861 domain->dcname,
1862 name_user,
1863 name_domain,
1864 /* Bug #3248 - found by Stefan Burkei. */
1865 workstation, /* We carefully set this above so use it... */
1866 state->request->data.auth_crap.chal,
1867 lm_resp,
1868 nt_resp,
1869 &info3);
1870 if (!NT_STATUS_IS_OK(result)) {
1871 goto done;
1874 process_result:
1876 if (NT_STATUS_IS_OK(result)) {
1877 struct dom_sid user_sid;
1879 sid_compose(&user_sid, info3->base.domain_sid,
1880 info3->base.rid);
1881 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1882 &user_sid);
1883 netsamlogon_cache_store(name_user, info3);
1885 /* Check if the user is in the right group */
1887 result = check_info3_in_group(
1888 info3,
1889 state->request->data.auth_crap.require_membership_of_sid);
1890 if (!NT_STATUS_IS_OK(result)) {
1891 DEBUG(3, ("User %s is not in the required group (%s), so "
1892 "crap authentication is rejected\n",
1893 state->request->data.auth_crap.user,
1894 state->request->data.auth_crap.require_membership_of_sid));
1895 goto done;
1898 result = append_auth_data(state->mem_ctx, state->response,
1899 state->request->flags, info3,
1900 name_domain, name_user);
1901 if (!NT_STATUS_IS_OK(result)) {
1902 goto done;
1906 done:
1908 /* give us a more useful (more correct?) error code */
1909 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1910 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1911 result = NT_STATUS_NO_LOGON_SERVERS;
1914 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1915 result = nt_status_squash(result);
1918 set_auth_errors(state->response, result);
1920 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1921 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1922 name_domain,
1923 name_user,
1924 state->response->data.auth.nt_status_string,
1925 state->response->data.auth.pam_error));
1927 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1930 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1931 struct winbindd_cli_state *state)
1933 char *oldpass;
1934 char *newpass = NULL;
1935 struct policy_handle dom_pol;
1936 struct rpc_pipe_client *cli = NULL;
1937 bool got_info = false;
1938 struct samr_DomInfo1 *info = NULL;
1939 struct userPwdChangeFailureInformation *reject = NULL;
1940 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1941 fstring domain, user;
1942 struct dcerpc_binding_handle *b = NULL;
1944 ZERO_STRUCT(dom_pol);
1946 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1947 state->request->data.auth.user));
1949 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1950 goto done;
1953 /* Change password */
1955 oldpass = state->request->data.chauthtok.oldpass;
1956 newpass = state->request->data.chauthtok.newpass;
1958 /* Initialize reject reason */
1959 state->response->data.auth.reject_reason = Undefined;
1961 /* Get sam handle */
1963 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1964 &dom_pol);
1965 if (!NT_STATUS_IS_OK(result)) {
1966 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1967 goto done;
1970 b = cli->binding_handle;
1972 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1973 user,
1974 newpass,
1975 oldpass,
1976 &info,
1977 &reject);
1979 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1981 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1983 fill_in_password_policy(state->response, info);
1985 state->response->data.auth.reject_reason =
1986 reject->extendedFailureReason;
1988 got_info = true;
1991 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1992 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1993 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1994 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1996 /* only fallback when the chgpasswd_user3 call is not supported */
1997 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1998 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1999 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2000 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2002 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2003 nt_errstr(result)));
2005 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2007 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2008 Map to the same status code as Windows 2003. */
2010 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2011 result = NT_STATUS_PASSWORD_RESTRICTION;
2015 done:
2017 if (NT_STATUS_IS_OK(result)
2018 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2019 && lp_winbind_offline_logon()) {
2020 result = winbindd_update_creds_by_name(contact_domain, user,
2021 newpass);
2022 /* Again, this happens when we login from gdm or xdm
2023 * and the password expires, *BUT* cached crendentials
2024 * doesn't exist. winbindd_update_creds_by_name()
2025 * returns NT_STATUS_NO_SUCH_USER.
2026 * This is not a failure.
2027 * --- BoYang
2028 * */
2029 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2030 result = NT_STATUS_OK;
2033 if (!NT_STATUS_IS_OK(result)) {
2034 DEBUG(10, ("Failed to store creds: %s\n",
2035 nt_errstr(result)));
2036 goto process_result;
2040 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2042 NTSTATUS policy_ret;
2044 policy_ret = fillup_password_policy(
2045 contact_domain, state->response);
2047 /* failure of this is non critical, it will just provide no
2048 * additional information to the client why the change has
2049 * failed - Guenther */
2051 if (!NT_STATUS_IS_OK(policy_ret)) {
2052 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2053 goto process_result;
2057 process_result:
2059 if (strequal(contact_domain->name, get_global_sam_name())) {
2060 /* FIXME: internal rpc pipe does not cache handles yet */
2061 if (b) {
2062 if (is_valid_policy_hnd(&dom_pol)) {
2063 NTSTATUS _result;
2064 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2066 TALLOC_FREE(cli);
2070 set_auth_errors(state->response, result);
2072 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2073 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2074 domain,
2075 user,
2076 state->response->data.auth.nt_status_string,
2077 state->response->data.auth.pam_error));
2079 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2082 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2083 struct winbindd_cli_state *state)
2085 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2087 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2088 state->request->data.logoff.user));
2090 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2091 result = NT_STATUS_OK;
2092 goto process_result;
2095 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2096 result = NT_STATUS_OK;
2097 goto process_result;
2100 #ifdef HAVE_KRB5
2102 if (state->request->data.logoff.uid < 0) {
2103 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2104 goto process_result;
2107 /* what we need here is to find the corresponding krb5 ccache name *we*
2108 * created for a given username and destroy it */
2110 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2111 result = NT_STATUS_OK;
2112 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2113 goto process_result;
2116 if (!ccache_entry_identical(state->request->data.logoff.user,
2117 state->request->data.logoff.uid,
2118 state->request->data.logoff.krb5ccname)) {
2119 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2120 goto process_result;
2123 result = remove_ccache(state->request->data.logoff.user);
2124 if (!NT_STATUS_IS_OK(result)) {
2125 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2126 nt_errstr(result)));
2127 goto process_result;
2130 #else
2131 result = NT_STATUS_NOT_SUPPORTED;
2132 #endif
2134 process_result:
2137 set_auth_errors(state->response, result);
2139 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2142 /* Change user password with auth crap*/
2144 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2146 NTSTATUS result;
2147 DATA_BLOB new_nt_password;
2148 DATA_BLOB old_nt_hash_enc;
2149 DATA_BLOB new_lm_password;
2150 DATA_BLOB old_lm_hash_enc;
2151 fstring domain,user;
2152 struct policy_handle dom_pol;
2153 struct winbindd_domain *contact_domain = domainSt;
2154 struct rpc_pipe_client *cli = NULL;
2155 struct dcerpc_binding_handle *b = NULL;
2157 ZERO_STRUCT(dom_pol);
2159 /* Ensure null termination */
2160 state->request->data.chng_pswd_auth_crap.user[
2161 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2162 state->request->data.chng_pswd_auth_crap.domain[
2163 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2164 *domain = 0;
2165 *user = 0;
2167 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2168 (unsigned long)state->pid,
2169 state->request->data.chng_pswd_auth_crap.domain,
2170 state->request->data.chng_pswd_auth_crap.user));
2172 if (lp_winbind_offline_logon()) {
2173 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2174 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2175 result = NT_STATUS_ACCESS_DENIED;
2176 goto done;
2179 if (*state->request->data.chng_pswd_auth_crap.domain) {
2180 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2181 } else {
2182 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2183 domain, user);
2185 if(!*domain) {
2186 DEBUG(3,("no domain specified with username (%s) - "
2187 "failing auth\n",
2188 state->request->data.chng_pswd_auth_crap.user));
2189 result = NT_STATUS_NO_SUCH_USER;
2190 goto done;
2194 if (!*domain && lp_winbind_use_default_domain()) {
2195 fstrcpy(domain,lp_workgroup());
2198 if(!*user) {
2199 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2202 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2203 (unsigned long)state->pid, domain, user));
2205 /* Change password */
2206 new_nt_password = data_blob_const(
2207 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2208 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2210 old_nt_hash_enc = data_blob_const(
2211 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2212 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2214 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2215 new_lm_password = data_blob_const(
2216 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2217 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2219 old_lm_hash_enc = data_blob_const(
2220 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2221 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2222 } else {
2223 new_lm_password = data_blob_null;
2224 old_lm_hash_enc = data_blob_null;
2227 /* Get sam handle */
2229 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2230 if (!NT_STATUS_IS_OK(result)) {
2231 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2232 goto done;
2235 b = cli->binding_handle;
2237 result = rpccli_samr_chng_pswd_auth_crap(
2238 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2239 new_lm_password, old_lm_hash_enc);
2241 done:
2243 if (strequal(contact_domain->name, get_global_sam_name())) {
2244 /* FIXME: internal rpc pipe does not cache handles yet */
2245 if (b) {
2246 if (is_valid_policy_hnd(&dom_pol)) {
2247 NTSTATUS _result;
2248 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2250 TALLOC_FREE(cli);
2254 set_auth_errors(state->response, result);
2256 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2257 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2258 domain, user,
2259 state->response->data.auth.nt_status_string,
2260 state->response->data.auth.pam_error));
2262 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;