Fix the wcache_invalidate_samlogon calls.
[Samba/ekacnet.git] / source3 / winbindd / winbindd_pam.c
blobd4a2e3ed7942f2a524856efebafad15d69fd9069
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 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
32 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
33 struct winbindd_cli_state *state,
34 struct netr_SamInfo3 *info3)
36 char *ex;
37 size_t size;
38 uint32_t i;
40 state->response.data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response.data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response.data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response.data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response.data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response.data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response.data.auth.info3.logon_count = info3->base.logon_count;
54 state->response.data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response.data.auth.info3.user_rid = info3->base.rid;
57 state->response.data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response.data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response.data.auth.info3.num_groups = info3->base.groups.count;
61 state->response.data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response.data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response.data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response.data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response.data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response.data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response.data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response.data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response.data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response.data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response.data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
95 char *sid;
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
101 sid,
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 talloc_free(sid);
108 size = talloc_get_size(ex);
110 SAFE_FREE(state->response.extra_data.data);
111 state->response.extra_data.data = SMB_MALLOC(size);
112 if (!state->response.extra_data.data) {
113 return NT_STATUS_NO_MEMORY;
115 memcpy(state->response.extra_data.data, ex, size);
116 talloc_free(ex);
118 state->response.length += size;
120 return NT_STATUS_OK;
123 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
124 struct winbindd_cli_state *state,
125 struct netr_SamInfo3 *info3)
127 DATA_BLOB blob;
128 enum ndr_err_code ndr_err;
130 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
131 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
132 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
134 return ndr_map_error2ntstatus(ndr_err);
137 SAFE_FREE(state->response.extra_data.data);
138 state->response.extra_data.data = SMB_MALLOC(blob.length);
139 if (!state->response.extra_data.data) {
140 data_blob_free(&blob);
141 return NT_STATUS_NO_MEMORY;
144 memset(state->response.extra_data.data, '\0', blob.length);
145 memcpy(state->response.extra_data.data, blob.data, blob.length);
146 state->response.length += blob.length;
148 data_blob_free(&blob);
150 return NT_STATUS_OK;
153 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
154 struct winbindd_cli_state *state,
155 const struct netr_SamInfo3 *info3,
156 const char *name_domain,
157 const char *name_user)
159 /* We've been asked to return the unix username, per
160 'winbind use default domain' settings and the like */
162 const char *nt_username, *nt_domain;
164 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
165 if (!nt_domain) {
166 /* If the server didn't give us one, just use the one
167 * we sent them */
168 nt_domain = name_domain;
171 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
172 if (!nt_username) {
173 /* If the server didn't give us one, just use the one
174 * we sent them */
175 nt_username = name_user;
178 fill_domain_username(state->response.data.auth.unix_username,
179 nt_domain, nt_username, true);
181 DEBUG(5,("Setting unix username to [%s]\n",
182 state->response.data.auth.unix_username));
184 return NT_STATUS_OK;
187 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
188 struct winbindd_cli_state *state,
189 const struct netr_SamInfo3 *info3,
190 const char *name_domain,
191 const char *name_user)
193 char *afsname = NULL;
194 char *cell;
196 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197 if (afsname == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 afsname = talloc_string_sub(mem_ctx,
202 lp_afs_username_map(),
203 "%D", name_domain);
204 afsname = talloc_string_sub(mem_ctx, afsname,
205 "%u", name_user);
206 afsname = talloc_string_sub(mem_ctx, afsname,
207 "%U", name_user);
210 DOM_SID user_sid;
211 fstring sidstr;
213 sid_copy(&user_sid, info3->base.domain_sid);
214 sid_append_rid(&user_sid, info3->base.rid);
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
217 "%s", sidstr);
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 strlower_m(afsname);
226 DEBUG(10, ("Generating token for user %s\n", afsname));
228 cell = strchr(afsname, '@');
230 if (cell == NULL) {
231 return NT_STATUS_NO_MEMORY;
234 *cell = '\0';
235 cell += 1;
237 /* Append an AFS token string */
238 SAFE_FREE(state->response.extra_data.data);
239 state->response.extra_data.data =
240 afs_createtoken_str(afsname, cell);
242 if (state->response.extra_data.data != NULL) {
243 state->response.length +=
244 strlen((const char *)state->response.extra_data.data)+1;
247 return NT_STATUS_OK;
250 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
251 struct netr_SamInfo3 *info3,
252 const char *group_sid)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 DOM_SID *require_membership_of_sid;
266 size_t num_require_membership_of_sid;
267 char *req_sid;
268 const char *p;
269 DOM_SID sid;
270 size_t i;
271 struct nt_user_token *token;
272 TALLOC_CTX *frame = NULL;
273 NTSTATUS status;
275 /* Parse the 'required group' SID */
277 if (!group_sid || !group_sid[0]) {
278 /* NO sid supplied, all users may access */
279 return NT_STATUS_OK;
282 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
283 DEBUG(0, ("talloc failed\n"));
284 return NT_STATUS_NO_MEMORY;
287 num_require_membership_of_sid = 0;
288 require_membership_of_sid = NULL;
290 p = group_sid;
292 frame = talloc_stackframe();
293 while (next_token_talloc(frame, &p, &req_sid, ",")) {
294 if (!string_to_sid(&sid, req_sid)) {
295 DEBUG(0, ("check_info3_in_group: could not parse %s "
296 "as a SID!", req_sid));
297 TALLOC_FREE(frame);
298 return NT_STATUS_INVALID_PARAMETER;
301 status = add_sid_to_array(mem_ctx, &sid,
302 &require_membership_of_sid,
303 &num_require_membership_of_sid);
304 if (!NT_STATUS_IS_OK(status)) {
305 DEBUG(0, ("add_sid_to_array failed\n"));
306 TALLOC_FREE(frame);
307 return status;
311 TALLOC_FREE(frame);
313 status = sid_array_from_info3(mem_ctx, info3,
314 &token->user_sids,
315 &token->num_sids,
316 true, false);
317 if (!NT_STATUS_IS_OK(status)) {
318 return status;
321 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
322 token))
323 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
324 token))) {
325 DEBUG(3, ("could not add aliases: %s\n",
326 nt_errstr(status)));
327 return status;
330 debug_nt_user_token(DBGC_CLASS, 10, token);
332 for (i=0; i<num_require_membership_of_sid; i++) {
333 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
334 &require_membership_of_sid[i])));
335 if (nt_token_check_sid(&require_membership_of_sid[i],
336 token)) {
337 DEBUG(10, ("Access ok\n"));
338 return NT_STATUS_OK;
342 /* Do not distinguish this error from a wrong username/pw */
344 return NT_STATUS_LOGON_FAILURE;
347 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
348 const char *domain_name)
350 struct winbindd_domain *domain;
352 if (IS_DC) {
353 domain = find_domain_from_name_noinit(domain_name);
354 if (domain == NULL) {
355 DEBUG(3, ("Authentication for domain [%s] refused "
356 "as it is not a trusted domain\n",
357 domain_name));
359 return domain;
362 if (is_myname(domain_name)) {
363 DEBUG(3, ("Authentication for domain %s (local domain "
364 "to this server) not supported at this "
365 "stage\n", domain_name));
366 return NULL;
369 /* we can auth against trusted domains */
370 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
371 domain = find_domain_from_name_noinit(domain_name);
372 if (domain == NULL) {
373 DEBUG(3, ("Authentication for domain [%s] skipped "
374 "as it is not a trusted domain\n",
375 domain_name));
376 } else {
377 return domain;
381 return find_our_domain();
384 static void fill_in_password_policy(struct winbindd_response *r,
385 const struct samr_DomInfo1 *p)
387 r->data.auth.policy.min_length_password =
388 p->min_password_length;
389 r->data.auth.policy.password_history =
390 p->password_history_length;
391 r->data.auth.policy.password_properties =
392 p->password_properties;
393 r->data.auth.policy.expire =
394 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
395 r->data.auth.policy.min_passwordage =
396 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
399 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
400 struct winbindd_cli_state *state)
402 struct winbindd_methods *methods;
403 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
404 struct samr_DomInfo1 password_policy;
406 if ( !winbindd_can_contact_domain( domain ) ) {
407 DEBUG(5,("fillup_password_policy: No inbound trust to "
408 "contact domain %s\n", domain->name));
409 return NT_STATUS_NOT_SUPPORTED;
412 methods = domain->methods;
414 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
415 if (NT_STATUS_IS_ERR(status)) {
416 return status;
419 fill_in_password_policy(&state->response, &password_policy);
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 bool *internal_ccache)
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 *internal_ccache = true;
483 if (uid == -1) {
484 goto memory_ccache;
487 if (!type || type[0] == '\0') {
488 goto memory_ccache;
491 if (strequal(type, "FILE")) {
492 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
493 } else if (strequal(type, "WRFILE")) {
494 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
495 } else {
496 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
497 goto memory_ccache;
500 *internal_ccache = false;
501 goto done;
503 memory_ccache:
504 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
506 done:
507 if (gen_cc == NULL) {
508 DEBUG(0,("out of memory\n"));
509 return NULL;
512 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
514 return gen_cc;
517 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
519 const char *type = state->request.data.auth.krb5_cc_type;
521 state->response.data.auth.krb5ccname[0] = '\0';
523 if (type[0] == '\0') {
524 return;
527 if (!strequal(type, "FILE") &&
528 !strequal(type, "WRFILE")) {
529 DEBUG(10,("won't return krbccname for a %s type ccache\n",
530 type));
531 return;
534 fstrcpy(state->response.data.auth.krb5ccname, cc);
537 #endif
539 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
541 uid_t uid = -1;
543 uid = state->request.data.auth.uid;
545 if (uid < 0) {
546 DEBUG(1,("invalid uid: '%d'\n", uid));
547 return -1;
549 return uid;
552 /**********************************************************************
553 Authenticate a user with a clear text password using Kerberos and fill up
554 ccache if required
555 **********************************************************************/
557 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
558 struct winbindd_cli_state *state,
559 struct netr_SamInfo3 **info3)
561 #ifdef HAVE_KRB5
562 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
563 krb5_error_code krb5_ret;
564 const char *cc = NULL;
565 const char *principal_s = NULL;
566 const char *service = NULL;
567 char *realm = NULL;
568 fstring name_domain, name_user;
569 time_t ticket_lifetime = 0;
570 time_t renewal_until = 0;
571 uid_t uid = -1;
572 ADS_STRUCT *ads;
573 time_t time_offset = 0;
574 bool internal_ccache = true;
576 ZERO_STRUCTP(info3);
578 *info3 = NULL;
580 /* 1st step:
581 * prepare a krb5_cc_cache string for the user */
583 uid = get_uid_from_state(state);
584 if (uid == -1) {
585 DEBUG(0,("no valid uid\n"));
588 cc = generate_krb5_ccache(state->mem_ctx,
589 state->request.data.auth.krb5_cc_type,
590 state->request.data.auth.uid,
591 &internal_ccache);
592 if (cc == NULL) {
593 return NT_STATUS_NO_MEMORY;
597 /* 2nd step:
598 * get kerberos properties */
600 if (domain->private_data) {
601 ads = (ADS_STRUCT *)domain->private_data;
602 time_offset = ads->auth.time_offset;
606 /* 3rd step:
607 * do kerberos auth and setup ccache as the user */
609 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
611 realm = domain->alt_name;
612 strupper_m(realm);
614 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
615 if (principal_s == NULL) {
616 return NT_STATUS_NO_MEMORY;
619 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
620 if (service == NULL) {
621 return NT_STATUS_NO_MEMORY;
624 /* if this is a user ccache, we need to act as the user to let the krb5
625 * library handle the chown, etc. */
627 /************************ ENTERING NON-ROOT **********************/
629 if (!internal_ccache) {
630 set_effective_uid(uid);
631 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
634 result = kerberos_return_info3_from_pac(state->mem_ctx,
635 principal_s,
636 state->request.data.auth.pass,
637 time_offset,
638 &ticket_lifetime,
639 &renewal_until,
641 true,
642 true,
643 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
644 info3);
645 if (!internal_ccache) {
646 gain_root_privilege();
649 /************************ RETURNED TO ROOT **********************/
651 if (!NT_STATUS_IS_OK(result)) {
652 goto failed;
655 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
656 principal_s));
658 /* if we had a user's ccache then return that string for the pam
659 * environment */
661 if (!internal_ccache) {
663 setup_return_cc_name(state, cc);
665 result = add_ccache_to_list(principal_s,
667 service,
668 state->request.data.auth.user,
669 realm,
670 uid,
671 time(NULL),
672 ticket_lifetime,
673 renewal_until,
674 false);
676 if (!NT_STATUS_IS_OK(result)) {
677 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
678 nt_errstr(result)));
680 } else {
682 /* need to delete the memory cred cache, it is not used anymore */
684 krb5_ret = ads_kdestroy(cc);
685 if (krb5_ret) {
686 DEBUG(3,("winbindd_raw_kerberos_login: "
687 "could not destroy krb5 credential cache: "
688 "%s\n", error_message(krb5_ret)));
693 return NT_STATUS_OK;
695 failed:
697 /* we could have created a new credential cache with a valid tgt in it
698 * but we werent able to get or verify the service ticket for this
699 * local host and therefor didn't get the PAC, we need to remove that
700 * cache entirely now */
702 krb5_ret = ads_kdestroy(cc);
703 if (krb5_ret) {
704 DEBUG(3,("winbindd_raw_kerberos_login: "
705 "could not destroy krb5 credential cache: "
706 "%s\n", error_message(krb5_ret)));
709 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
710 DEBUG(3,("winbindd_raw_kerberos_login: "
711 "could not remove ccache for user %s\n",
712 state->request.data.auth.user));
715 return result;
716 #else
717 return NT_STATUS_NOT_SUPPORTED;
718 #endif /* HAVE_KRB5 */
721 /****************************************************************
722 ****************************************************************/
724 static bool check_request_flags(uint32_t flags)
726 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
727 WBFLAG_PAM_INFO3_TEXT |
728 WBFLAG_PAM_INFO3_NDR;
730 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
731 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
732 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
733 !(flags & flags_edata) ) {
734 return true;
737 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
739 return false;
742 /****************************************************************
743 ****************************************************************/
745 static NTSTATUS append_data(struct winbindd_cli_state *state,
746 struct netr_SamInfo3 *info3,
747 const char *name_domain,
748 const char *name_user)
750 NTSTATUS result;
751 uint32_t flags = state->request.flags;
753 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
754 memcpy(state->response.data.auth.user_session_key,
755 info3->base.key.key,
756 sizeof(state->response.data.auth.user_session_key)
757 /* 16 */);
760 if (flags & WBFLAG_PAM_LMKEY) {
761 memcpy(state->response.data.auth.first_8_lm_hash,
762 info3->base.LMSessKey.key,
763 sizeof(state->response.data.auth.first_8_lm_hash)
764 /* 8 */);
767 if (flags & WBFLAG_PAM_INFO3_TEXT) {
768 result = append_info3_as_txt(state->mem_ctx, state, info3);
769 if (!NT_STATUS_IS_OK(result)) {
770 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
771 nt_errstr(result)));
772 return result;
776 /* currently, anything from here on potentially overwrites extra_data. */
778 if (flags & WBFLAG_PAM_INFO3_NDR) {
779 result = append_info3_as_ndr(state->mem_ctx, state, info3);
780 if (!NT_STATUS_IS_OK(result)) {
781 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
782 nt_errstr(result)));
783 return result;
787 if (flags & WBFLAG_PAM_UNIX_NAME) {
788 result = append_unix_username(state->mem_ctx, state, info3,
789 name_domain, name_user);
790 if (!NT_STATUS_IS_OK(result)) {
791 DEBUG(10,("Failed to append Unix Username: %s\n",
792 nt_errstr(result)));
793 return result;
797 if (flags & WBFLAG_PAM_AFS_TOKEN) {
798 result = append_afs_token(state->mem_ctx, state, info3,
799 name_domain, name_user);
800 if (!NT_STATUS_IS_OK(result)) {
801 DEBUG(10,("Failed to append AFS token: %s\n",
802 nt_errstr(result)));
803 return result;
807 return NT_STATUS_OK;
810 void winbindd_pam_auth(struct winbindd_cli_state *state)
812 struct winbindd_domain *domain;
813 fstring name_domain, name_user;
814 NTSTATUS result;
816 /* Ensure null termination */
817 state->request.data.auth.user
818 [sizeof(state->request.data.auth.user)-1]='\0';
820 /* Ensure null termination */
821 state->request.data.auth.pass
822 [sizeof(state->request.data.auth.pass)-1]='\0';
824 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
825 state->request.data.auth.user));
827 if (!check_request_flags(state->request.flags)) {
828 result = NT_STATUS_INVALID_PARAMETER_MIX;
829 goto done;
832 /* Parse domain and username */
834 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
836 if (!canonicalize_username(state->request.data.auth.user,
837 name_domain, name_user)) {
838 result = NT_STATUS_NO_SUCH_USER;
839 goto done;
842 domain = find_auth_domain(state, name_domain);
844 if (domain == NULL) {
845 result = NT_STATUS_NO_SUCH_USER;
846 goto done;
849 sendto_domain(state, domain);
850 return;
851 done:
852 set_auth_errors(&state->response, result);
853 DEBUG(5, ("Plain text authentication for %s returned %s "
854 "(PAM: %d)\n",
855 state->request.data.auth.user,
856 state->response.data.auth.nt_status_string,
857 state->response.data.auth.pam_error));
858 request_error(state);
861 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
862 struct winbindd_cli_state *state,
863 struct netr_SamInfo3 **info3)
865 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
866 uint16 max_allowed_bad_attempts;
867 fstring name_domain, name_user;
868 DOM_SID sid;
869 enum lsa_SidType type;
870 uchar new_nt_pass[NT_HASH_LEN];
871 const uint8 *cached_nt_pass;
872 const uint8 *cached_salt;
873 struct netr_SamInfo3 *my_info3;
874 time_t kickoff_time, must_change_time;
875 bool password_good = false;
876 #ifdef HAVE_KRB5
877 struct winbindd_tdc_domain *tdc_domain = NULL;
878 #endif
880 *info3 = NULL;
882 ZERO_STRUCTP(info3);
884 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
886 /* Parse domain and username */
888 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
891 if (!lookup_cached_name(state->mem_ctx,
892 name_domain,
893 name_user,
894 &sid,
895 &type)) {
896 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
897 return NT_STATUS_NO_SUCH_USER;
900 if (type != SID_NAME_USER) {
901 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
902 return NT_STATUS_LOGON_FAILURE;
905 result = winbindd_get_creds(domain,
906 state->mem_ctx,
907 &sid,
908 &my_info3,
909 &cached_nt_pass,
910 &cached_salt);
911 if (!NT_STATUS_IS_OK(result)) {
912 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
913 return result;
916 *info3 = my_info3;
918 E_md4hash(state->request.data.auth.pass, new_nt_pass);
920 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
921 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
922 if (cached_salt) {
923 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
926 if (cached_salt) {
927 /* In this case we didn't store the nt_hash itself,
928 but the MD5 combination of salt + nt_hash. */
929 uchar salted_hash[NT_HASH_LEN];
930 E_md5hash(cached_salt, new_nt_pass, salted_hash);
932 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
933 true : false;
934 } else {
935 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
936 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
937 true : false;
940 if (password_good) {
942 /* User *DOES* know the password, update logon_time and reset
943 * bad_pw_count */
945 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
947 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
948 return NT_STATUS_ACCOUNT_LOCKED_OUT;
951 if (my_info3->base.acct_flags & ACB_DISABLED) {
952 return NT_STATUS_ACCOUNT_DISABLED;
955 if (my_info3->base.acct_flags & ACB_WSTRUST) {
956 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
959 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
960 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
963 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
964 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
967 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
968 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
969 my_info3->base.acct_flags));
970 return NT_STATUS_LOGON_FAILURE;
973 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
974 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
975 return NT_STATUS_ACCOUNT_EXPIRED;
978 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
979 if (must_change_time != 0 && must_change_time < time(NULL)) {
980 /* we allow grace logons when the password has expired */
981 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
982 /* return NT_STATUS_PASSWORD_EXPIRED; */
983 goto success;
986 #ifdef HAVE_KRB5
987 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
988 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
989 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
991 uid_t uid = -1;
992 const char *cc = NULL;
993 char *realm = NULL;
994 const char *principal_s = NULL;
995 const char *service = NULL;
996 bool internal_ccache = false;
998 uid = get_uid_from_state(state);
999 if (uid == -1) {
1000 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1001 return NT_STATUS_INVALID_PARAMETER;
1004 cc = generate_krb5_ccache(state->mem_ctx,
1005 state->request.data.auth.krb5_cc_type,
1006 state->request.data.auth.uid,
1007 &internal_ccache);
1008 if (cc == NULL) {
1009 return NT_STATUS_NO_MEMORY;
1012 realm = domain->alt_name;
1013 strupper_m(realm);
1015 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1016 if (principal_s == NULL) {
1017 return NT_STATUS_NO_MEMORY;
1020 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1021 if (service == NULL) {
1022 return NT_STATUS_NO_MEMORY;
1025 if (!internal_ccache) {
1027 setup_return_cc_name(state, cc);
1029 result = add_ccache_to_list(principal_s,
1031 service,
1032 state->request.data.auth.user,
1033 domain->alt_name,
1034 uid,
1035 time(NULL),
1036 time(NULL) + lp_winbind_cache_time(),
1037 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1038 true);
1040 if (!NT_STATUS_IS_OK(result)) {
1041 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1042 "to add ccache to list: %s\n",
1043 nt_errstr(result)));
1047 #endif /* HAVE_KRB5 */
1048 success:
1049 /* FIXME: we possibly should handle logon hours as well (does xp when
1050 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1052 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1053 my_info3->base.bad_password_count = 0;
1055 result = winbindd_update_creds_by_info3(domain,
1056 state->mem_ctx,
1057 state->request.data.auth.user,
1058 state->request.data.auth.pass,
1059 my_info3);
1060 if (!NT_STATUS_IS_OK(result)) {
1061 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1062 nt_errstr(result)));
1063 return result;
1066 return NT_STATUS_OK;
1070 /* User does *NOT* know the correct password, modify info3 accordingly */
1072 /* failure of this is not critical */
1073 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1074 if (!NT_STATUS_IS_OK(result)) {
1075 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1076 "Won't be able to honour account lockout policies\n"));
1079 /* increase counter */
1080 my_info3->base.bad_password_count++;
1082 if (max_allowed_bad_attempts == 0) {
1083 goto failed;
1086 /* lockout user */
1087 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1089 uint32 password_properties;
1091 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1092 if (!NT_STATUS_IS_OK(result)) {
1093 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1096 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1097 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1098 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1102 failed:
1103 result = winbindd_update_creds_by_info3(domain,
1104 state->mem_ctx,
1105 state->request.data.auth.user,
1106 NULL,
1107 my_info3);
1109 if (!NT_STATUS_IS_OK(result)) {
1110 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1111 nt_errstr(result)));
1114 return NT_STATUS_LOGON_FAILURE;
1117 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1118 struct winbindd_cli_state *state,
1119 struct netr_SamInfo3 **info3)
1121 struct winbindd_domain *contact_domain;
1122 fstring name_domain, name_user;
1123 NTSTATUS result;
1125 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1127 /* Parse domain and username */
1129 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1131 /* what domain should we contact? */
1133 if ( IS_DC ) {
1134 if (!(contact_domain = find_domain_from_name(name_domain))) {
1135 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1136 state->request.data.auth.user, name_domain, name_user, name_domain));
1137 result = NT_STATUS_NO_SUCH_USER;
1138 goto done;
1141 } else {
1142 if (is_myname(name_domain)) {
1143 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1144 result = NT_STATUS_NO_SUCH_USER;
1145 goto done;
1148 contact_domain = find_domain_from_name(name_domain);
1149 if (contact_domain == NULL) {
1150 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1151 state->request.data.auth.user, name_domain, name_user, name_domain));
1153 contact_domain = find_our_domain();
1157 if (contact_domain->initialized &&
1158 contact_domain->active_directory) {
1159 goto try_login;
1162 if (!contact_domain->initialized) {
1163 init_dc_connection(contact_domain);
1166 if (!contact_domain->active_directory) {
1167 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1168 return NT_STATUS_INVALID_LOGON_TYPE;
1170 try_login:
1171 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1172 done:
1173 return result;
1176 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1177 TALLOC_CTX *mem_ctx,
1178 uint32 logon_parameters,
1179 const char *server,
1180 const char *username,
1181 const char *domain,
1182 const char *workstation,
1183 const uint8 chal[8],
1184 DATA_BLOB lm_response,
1185 DATA_BLOB nt_response,
1186 struct netr_SamInfo3 **info3);
1188 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1189 struct winbindd_cli_state *state,
1190 struct netr_SamInfo3 **info3)
1193 struct rpc_pipe_client *netlogon_pipe;
1194 uchar chal[8];
1195 DATA_BLOB lm_resp;
1196 DATA_BLOB nt_resp;
1197 int attempts = 0;
1198 unsigned char local_lm_response[24];
1199 unsigned char local_nt_response[24];
1200 struct winbindd_domain *contact_domain;
1201 fstring name_domain, name_user;
1202 bool retry;
1203 NTSTATUS result;
1204 struct netr_SamInfo3 *my_info3 = NULL;
1206 *info3 = NULL;
1208 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1210 /* Parse domain and username */
1212 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1214 /* do password magic */
1217 generate_random_buffer(chal, 8);
1218 if (lp_client_ntlmv2_auth()) {
1219 DATA_BLOB server_chal;
1220 DATA_BLOB names_blob;
1221 DATA_BLOB nt_response;
1222 DATA_BLOB lm_response;
1223 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1225 /* note that the 'workgroup' here is a best guess - we don't know
1226 the server's domain at this point. The 'server name' is also
1227 dodgy...
1229 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1231 if (!SMBNTLMv2encrypt(name_user, name_domain,
1232 state->request.data.auth.pass,
1233 &server_chal,
1234 &names_blob,
1235 &lm_response, &nt_response, NULL)) {
1236 data_blob_free(&names_blob);
1237 data_blob_free(&server_chal);
1238 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1239 result = NT_STATUS_NO_MEMORY;
1240 goto done;
1242 data_blob_free(&names_blob);
1243 data_blob_free(&server_chal);
1244 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1245 lm_response.length);
1246 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1247 nt_response.length);
1248 data_blob_free(&lm_response);
1249 data_blob_free(&nt_response);
1251 } else {
1252 if (lp_client_lanman_auth()
1253 && SMBencrypt(state->request.data.auth.pass,
1254 chal,
1255 local_lm_response)) {
1256 lm_resp = data_blob_talloc(state->mem_ctx,
1257 local_lm_response,
1258 sizeof(local_lm_response));
1259 } else {
1260 lm_resp = data_blob_null;
1262 SMBNTencrypt(state->request.data.auth.pass,
1263 chal,
1264 local_nt_response);
1266 nt_resp = data_blob_talloc(state->mem_ctx,
1267 local_nt_response,
1268 sizeof(local_nt_response));
1271 /* what domain should we contact? */
1273 if ( IS_DC ) {
1274 if (!(contact_domain = find_domain_from_name(name_domain))) {
1275 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1276 state->request.data.auth.user, name_domain, name_user, name_domain));
1277 result = NT_STATUS_NO_SUCH_USER;
1278 goto done;
1281 } else {
1282 if (is_myname(name_domain)) {
1283 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1284 result = NT_STATUS_NO_SUCH_USER;
1285 goto done;
1288 contact_domain = find_our_domain();
1291 /* check authentication loop */
1293 do {
1294 netlogon_fn_t logon_fn;
1296 ZERO_STRUCTP(my_info3);
1297 retry = false;
1299 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1301 if (!NT_STATUS_IS_OK(result)) {
1302 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1303 goto done;
1306 /* It is really important to try SamLogonEx here,
1307 * because in a clustered environment, we want to use
1308 * one machine account from multiple physical
1309 * computers.
1311 * With a normal SamLogon call, we must keep the
1312 * credentials chain updated and intact between all
1313 * users of the machine account (which would imply
1314 * cross-node communication for every NTLM logon).
1316 * (The credentials chain is not per NETLOGON pipe
1317 * connection, but globally on the server/client pair
1318 * by machine name).
1320 * When using SamLogonEx, the credentials are not
1321 * supplied, but the session key is implied by the
1322 * wrapping SamLogon context.
1324 * -- abartlet 21 April 2008
1327 logon_fn = contact_domain->can_do_samlogon_ex
1328 ? rpccli_netlogon_sam_network_logon_ex
1329 : rpccli_netlogon_sam_network_logon;
1331 result = logon_fn(netlogon_pipe,
1332 state->mem_ctx,
1334 contact_domain->dcname, /* server name */
1335 name_user, /* user name */
1336 name_domain, /* target domain */
1337 global_myname(), /* workstation */
1338 chal,
1339 lm_resp,
1340 nt_resp,
1341 &my_info3);
1342 attempts += 1;
1344 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1345 && contact_domain->can_do_samlogon_ex) {
1346 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1347 "retrying with NetSamLogon\n"));
1348 contact_domain->can_do_samlogon_ex = false;
1349 retry = true;
1350 continue;
1353 /* We have to try a second time as cm_connect_netlogon
1354 might not yet have noticed that the DC has killed
1355 our connection. */
1357 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1358 retry = true;
1359 continue;
1362 /* if we get access denied, a possible cause was that we had
1363 and open connection to the DC, but someone changed our
1364 machine account password out from underneath us using 'net
1365 rpc changetrustpw' */
1367 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1368 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1369 "ACCESS_DENIED. Maybe the trust account "
1370 "password was changed and we didn't know it. "
1371 "Killing connections to domain %s\n",
1372 name_domain));
1373 invalidate_cm_connection(&contact_domain->conn);
1374 retry = true;
1377 } while ( (attempts < 2) && retry );
1379 /* handle the case where a NT4 DC does not fill in the acct_flags in
1380 * the samlogon reply info3. When accurate info3 is required by the
1381 * caller, we look up the account flags ourselve - gd */
1383 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1384 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1386 struct rpc_pipe_client *samr_pipe;
1387 POLICY_HND samr_domain_handle, user_pol;
1388 union samr_UserInfo *info = NULL;
1389 NTSTATUS status_tmp;
1390 uint32 acct_flags;
1392 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1393 &samr_pipe, &samr_domain_handle);
1395 if (!NT_STATUS_IS_OK(status_tmp)) {
1396 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1397 nt_errstr(status_tmp)));
1398 goto done;
1401 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1402 &samr_domain_handle,
1403 MAXIMUM_ALLOWED_ACCESS,
1404 my_info3->base.rid,
1405 &user_pol);
1407 if (!NT_STATUS_IS_OK(status_tmp)) {
1408 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1409 nt_errstr(status_tmp)));
1410 goto done;
1413 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1414 &user_pol,
1416 &info);
1418 if (!NT_STATUS_IS_OK(status_tmp)) {
1419 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1420 nt_errstr(status_tmp)));
1421 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1422 goto done;
1425 acct_flags = info->info16.acct_flags;
1427 if (acct_flags == 0) {
1428 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1429 goto done;
1432 my_info3->base.acct_flags = acct_flags;
1434 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1436 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1439 *info3 = my_info3;
1440 done:
1441 return result;
1444 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1445 struct winbindd_cli_state *state)
1447 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1448 NTSTATUS krb5_result = NT_STATUS_OK;
1449 fstring name_domain, name_user;
1450 struct netr_SamInfo3 *info3 = NULL;
1452 /* Ensure null termination */
1453 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1455 /* Ensure null termination */
1456 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1458 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1459 state->request.data.auth.user));
1461 if (!check_request_flags(state->request.flags)) {
1462 result = NT_STATUS_INVALID_PARAMETER_MIX;
1463 goto done;
1466 /* Parse domain and username */
1468 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1470 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1472 if (domain->online == false) {
1473 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1474 if (domain->startup) {
1475 /* Logons are very important to users. If we're offline and
1476 we get a request within the first 30 seconds of startup,
1477 try very hard to find a DC and go online. */
1479 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1480 "request in startup mode.\n", domain->name ));
1482 winbindd_flush_negative_conn_cache(domain);
1483 result = init_dc_connection(domain);
1487 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1489 /* Check for Kerberos authentication */
1490 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1492 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1493 /* save for later */
1494 krb5_result = result;
1497 if (NT_STATUS_IS_OK(result)) {
1498 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1499 goto process_result;
1500 } else {
1501 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1504 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1505 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1506 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1507 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1508 set_domain_offline( domain );
1509 goto cached_logon;
1512 /* there are quite some NT_STATUS errors where there is no
1513 * point in retrying with a samlogon, we explictly have to take
1514 * care not to increase the bad logon counter on the DC */
1516 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1517 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1518 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1519 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1520 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1526 goto process_result;
1529 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1530 DEBUG(3,("falling back to samlogon\n"));
1531 goto sam_logon;
1532 } else {
1533 goto cached_logon;
1537 sam_logon:
1538 /* Check for Samlogon authentication */
1539 if (domain->online) {
1540 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1542 if (NT_STATUS_IS_OK(result)) {
1543 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1544 /* add the Krb5 err if we have one */
1545 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1546 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1548 goto process_result;
1551 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1552 nt_errstr(result)));
1554 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1558 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1559 set_domain_offline( domain );
1560 goto cached_logon;
1563 if (domain->online) {
1564 /* We're still online - fail. */
1565 goto done;
1569 cached_logon:
1570 /* Check for Cached logons */
1571 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1572 lp_winbind_offline_logon()) {
1574 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1576 if (NT_STATUS_IS_OK(result)) {
1577 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1578 goto process_result;
1579 } else {
1580 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1581 goto done;
1585 process_result:
1587 if (NT_STATUS_IS_OK(result)) {
1589 DOM_SID user_sid;
1591 /* In all codepaths where result == NT_STATUS_OK info3 must have
1592 been initialized. */
1593 if (!info3) {
1594 result = NT_STATUS_INTERNAL_ERROR;
1595 goto done;
1598 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1599 netsamlogon_cache_store(name_user, info3);
1601 /* save name_to_sid info as early as possible (only if
1602 this is our primary domain so we don't invalidate
1603 the cache entry by storing the seq_num for the wrong
1604 domain). */
1605 if ( domain->primary ) {
1606 sid_compose(&user_sid, info3->base.domain_sid,
1607 info3->base.rid);
1608 cache_name2sid(domain, name_domain, name_user,
1609 SID_NAME_USER, &user_sid);
1612 /* Check if the user is in the right group */
1614 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1615 state->request.data.auth.require_membership_of_sid))) {
1616 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1617 state->request.data.auth.user,
1618 state->request.data.auth.require_membership_of_sid));
1619 goto done;
1622 result = append_data(state, info3, name_domain, name_user);
1623 if (!NT_STATUS_IS_OK(result)) {
1624 goto done;
1627 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1629 /* Store in-memory creds for single-signon using ntlm_auth. */
1630 result = winbindd_add_memory_creds(state->request.data.auth.user,
1631 get_uid_from_state(state),
1632 state->request.data.auth.pass);
1634 if (!NT_STATUS_IS_OK(result)) {
1635 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1636 goto done;
1639 if (lp_winbind_offline_logon()) {
1640 result = winbindd_store_creds(domain,
1641 state->mem_ctx,
1642 state->request.data.auth.user,
1643 state->request.data.auth.pass,
1644 info3, NULL);
1645 if (!NT_STATUS_IS_OK(result)) {
1647 /* Release refcount. */
1648 winbindd_delete_memory_creds(state->request.data.auth.user);
1650 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1651 goto done;
1657 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1658 struct winbindd_domain *our_domain = find_our_domain();
1660 /* This is not entirely correct I believe, but it is
1661 consistent. Only apply the password policy settings
1662 too warn users for our own domain. Cannot obtain these
1663 from trusted DCs all the time so don't do it at all.
1664 -- jerry */
1666 result = NT_STATUS_NOT_SUPPORTED;
1667 if (our_domain == domain ) {
1668 result = fillup_password_policy(our_domain, state);
1671 if (!NT_STATUS_IS_OK(result)
1672 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1674 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1675 domain->name, nt_errstr(result)));
1676 goto done;
1680 result = NT_STATUS_OK;
1683 done:
1684 /* give us a more useful (more correct?) error code */
1685 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1686 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1687 result = NT_STATUS_NO_LOGON_SERVERS;
1690 set_auth_errors(&state->response, result);
1692 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1693 state->request.data.auth.user,
1694 state->response.data.auth.nt_status_string,
1695 state->response.data.auth.pam_error));
1697 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1701 /**********************************************************************
1702 Challenge Response Authentication Protocol
1703 **********************************************************************/
1705 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1707 struct winbindd_domain *domain = NULL;
1708 const char *domain_name = NULL;
1709 NTSTATUS result;
1711 if (!check_request_flags(state->request.flags)) {
1712 result = NT_STATUS_INVALID_PARAMETER_MIX;
1713 goto done;
1716 if (!state->privileged) {
1717 char *error_string = NULL;
1718 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1719 "denied. !\n"));
1720 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1721 "on %s are set correctly.\n",
1722 get_winbind_priv_pipe_dir()));
1723 /* send a better message than ACCESS_DENIED */
1724 error_string = talloc_asprintf(state->mem_ctx,
1725 "winbind client not authorized "
1726 "to use winbindd_pam_auth_crap."
1727 " Ensure permissions on %s "
1728 "are set correctly.",
1729 get_winbind_priv_pipe_dir());
1730 fstrcpy(state->response.data.auth.error_string, error_string);
1731 result = NT_STATUS_ACCESS_DENIED;
1732 goto done;
1735 /* Ensure null termination */
1736 state->request.data.auth_crap.user
1737 [sizeof(state->request.data.auth_crap.user)-1]=0;
1738 state->request.data.auth_crap.domain
1739 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1741 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1742 (unsigned long)state->pid,
1743 state->request.data.auth_crap.domain,
1744 state->request.data.auth_crap.user));
1746 if (*state->request.data.auth_crap.domain != '\0') {
1747 domain_name = state->request.data.auth_crap.domain;
1748 } else if (lp_winbind_use_default_domain()) {
1749 domain_name = lp_workgroup();
1752 if (domain_name != NULL)
1753 domain = find_auth_domain(state, domain_name);
1755 if (domain != NULL) {
1756 sendto_domain(state, domain);
1757 return;
1760 result = NT_STATUS_NO_SUCH_USER;
1762 done:
1763 set_auth_errors(&state->response, result);
1764 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1765 state->request.data.auth_crap.domain,
1766 state->request.data.auth_crap.user,
1767 state->response.data.auth.nt_status_string,
1768 state->response.data.auth.pam_error));
1769 request_error(state);
1770 return;
1774 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1775 struct winbindd_cli_state *state)
1777 NTSTATUS result;
1778 struct netr_SamInfo3 *info3 = NULL;
1779 struct rpc_pipe_client *netlogon_pipe;
1780 const char *name_user = NULL;
1781 const char *name_domain = NULL;
1782 const char *workstation;
1783 struct winbindd_domain *contact_domain;
1784 int attempts = 0;
1785 bool retry;
1787 DATA_BLOB lm_resp, nt_resp;
1789 /* This is child-only, so no check for privileged access is needed
1790 anymore */
1792 /* Ensure null termination */
1793 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1794 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1796 if (!check_request_flags(state->request.flags)) {
1797 result = NT_STATUS_INVALID_PARAMETER_MIX;
1798 goto done;
1801 name_user = state->request.data.auth_crap.user;
1803 if (*state->request.data.auth_crap.domain) {
1804 name_domain = state->request.data.auth_crap.domain;
1805 } else if (lp_winbind_use_default_domain()) {
1806 name_domain = lp_workgroup();
1807 } else {
1808 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1809 name_user));
1810 result = NT_STATUS_NO_SUCH_USER;
1811 goto done;
1814 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1815 name_domain, name_user));
1817 if (*state->request.data.auth_crap.workstation) {
1818 workstation = state->request.data.auth_crap.workstation;
1819 } else {
1820 workstation = global_myname();
1823 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1824 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1825 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1826 state->request.data.auth_crap.lm_resp_len,
1827 state->request.data.auth_crap.nt_resp_len));
1828 result = NT_STATUS_INVALID_PARAMETER;
1829 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);
1834 nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1835 state->request.data.auth_crap.nt_resp_len);
1837 /* what domain should we contact? */
1839 if ( IS_DC ) {
1840 if (!(contact_domain = find_domain_from_name(name_domain))) {
1841 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1842 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1843 result = NT_STATUS_NO_SUCH_USER;
1844 goto done;
1846 } else {
1847 if (is_myname(name_domain)) {
1848 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1849 result = NT_STATUS_NO_SUCH_USER;
1850 goto done;
1852 contact_domain = find_our_domain();
1855 do {
1856 netlogon_fn_t logon_fn;
1858 retry = false;
1860 netlogon_pipe = NULL;
1861 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1863 if (!NT_STATUS_IS_OK(result)) {
1864 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1865 nt_errstr(result)));
1866 goto done;
1869 logon_fn = contact_domain->can_do_samlogon_ex
1870 ? rpccli_netlogon_sam_network_logon_ex
1871 : rpccli_netlogon_sam_network_logon;
1873 result = logon_fn(netlogon_pipe,
1874 state->mem_ctx,
1875 state->request.data.auth_crap.logon_parameters,
1876 contact_domain->dcname,
1877 name_user,
1878 name_domain,
1879 /* Bug #3248 - found by Stefan Burkei. */
1880 workstation, /* We carefully set this above so use it... */
1881 state->request.data.auth_crap.chal,
1882 lm_resp,
1883 nt_resp,
1884 &info3);
1886 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1887 && contact_domain->can_do_samlogon_ex) {
1888 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1889 "retrying with NetSamLogon\n"));
1890 contact_domain->can_do_samlogon_ex = false;
1891 retry = true;
1892 continue;
1895 attempts += 1;
1897 /* We have to try a second time as cm_connect_netlogon
1898 might not yet have noticed that the DC has killed
1899 our connection. */
1901 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1902 retry = true;
1903 continue;
1906 /* if we get access denied, a possible cause was that we had and open
1907 connection to the DC, but someone changed our machine account password
1908 out from underneath us using 'net rpc changetrustpw' */
1910 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1911 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1912 "ACCESS_DENIED. Maybe the trust account "
1913 "password was changed and we didn't know it. "
1914 "Killing connections to domain %s\n",
1915 name_domain));
1916 invalidate_cm_connection(&contact_domain->conn);
1917 retry = true;
1920 } while ( (attempts < 2) && retry );
1922 if (NT_STATUS_IS_OK(result)) {
1924 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1925 netsamlogon_cache_store(name_user, info3);
1927 /* Check if the user is in the right group */
1929 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1930 state->request.data.auth_crap.require_membership_of_sid))) {
1931 DEBUG(3, ("User %s is not in the required group (%s), so "
1932 "crap authentication is rejected\n",
1933 state->request.data.auth_crap.user,
1934 state->request.data.auth_crap.require_membership_of_sid));
1935 goto done;
1938 result = append_data(state, info3, name_domain, name_user);
1939 if (!NT_STATUS_IS_OK(result)) {
1940 goto done;
1944 done:
1946 /* give us a more useful (more correct?) error code */
1947 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1948 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1949 result = NT_STATUS_NO_LOGON_SERVERS;
1952 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1953 result = nt_status_squash(result);
1956 set_auth_errors(&state->response, result);
1958 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1959 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1960 name_domain,
1961 name_user,
1962 state->response.data.auth.nt_status_string,
1963 state->response.data.auth.pam_error));
1965 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1968 /* Change a user password */
1970 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1972 fstring domain, user;
1973 struct winbindd_domain *contact_domain;
1975 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1976 state->request.data.chauthtok.user));
1978 /* Setup crap */
1980 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1982 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
1983 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1984 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
1985 "(PAM: %d)\n",
1986 state->request.data.auth.user,
1987 state->response.data.auth.nt_status_string,
1988 state->response.data.auth.pam_error));
1989 request_error(state);
1990 return;
1993 contact_domain = find_domain_from_name(domain);
1994 if (!contact_domain) {
1995 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1996 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
1997 state->request.data.chauthtok.user, domain, user, domain));
1998 request_error(state);
1999 return;
2002 sendto_domain(state, contact_domain);
2005 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2006 struct winbindd_cli_state *state)
2008 char *oldpass;
2009 char *newpass = NULL;
2010 POLICY_HND dom_pol;
2011 struct rpc_pipe_client *cli;
2012 bool got_info = false;
2013 struct samr_DomInfo1 *info = NULL;
2014 struct samr_ChangeReject *reject = NULL;
2015 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2016 fstring domain, user;
2018 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2019 state->request.data.auth.user));
2021 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2022 goto done;
2025 /* Change password */
2027 oldpass = state->request.data.chauthtok.oldpass;
2028 newpass = state->request.data.chauthtok.newpass;
2030 /* Initialize reject reason */
2031 state->response.data.auth.reject_reason = Undefined;
2033 /* Get sam handle */
2035 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2036 &dom_pol);
2037 if (!NT_STATUS_IS_OK(result)) {
2038 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2039 goto done;
2042 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2043 user,
2044 newpass,
2045 oldpass,
2046 &info,
2047 &reject);
2049 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2051 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2053 fill_in_password_policy(&state->response, info);
2055 state->response.data.auth.reject_reason =
2056 reject->reason;
2058 got_info = true;
2061 /* only fallback when the chgpasswd_user3 call is not supported */
2062 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2063 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2064 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2066 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2067 nt_errstr(result)));
2069 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2071 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2072 Map to the same status code as Windows 2003. */
2074 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2075 result = NT_STATUS_PASSWORD_RESTRICTION;
2079 done:
2081 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2083 /* Update the single sign-on memory creds. */
2084 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2085 newpass);
2087 /* When we login from gdm or xdm and password expires,
2088 * we change password, but there are no memory crendentials
2089 * So, winbindd_replace_memory_creds() returns
2090 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2091 * --- BoYang
2092 * */
2093 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2094 result = NT_STATUS_OK;
2097 if (!NT_STATUS_IS_OK(result)) {
2098 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2099 goto process_result;
2102 if (lp_winbind_offline_logon()) {
2103 result = winbindd_update_creds_by_name(contact_domain,
2104 state->mem_ctx, user,
2105 newpass);
2106 /* Again, this happens when we login from gdm or xdm
2107 * and the password expires, *BUT* cached crendentials
2108 * doesn't exist. winbindd_update_creds_by_name()
2109 * returns NT_STATUS_NO_SUCH_USER.
2110 * This is not a failure.
2111 * --- BoYang
2112 * */
2113 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2114 result = NT_STATUS_OK;
2117 if (!NT_STATUS_IS_OK(result)) {
2118 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2119 goto process_result;
2124 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2126 NTSTATUS policy_ret;
2128 policy_ret = fillup_password_policy(contact_domain, state);
2130 /* failure of this is non critical, it will just provide no
2131 * additional information to the client why the change has
2132 * failed - Guenther */
2134 if (!NT_STATUS_IS_OK(policy_ret)) {
2135 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2136 goto process_result;
2140 process_result:
2142 set_auth_errors(&state->response, result);
2144 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2145 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2146 domain,
2147 user,
2148 state->response.data.auth.nt_status_string,
2149 state->response.data.auth.pam_error));
2151 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2154 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2156 struct winbindd_domain *domain;
2157 fstring name_domain, user;
2158 uid_t caller_uid = (uid_t)-1;
2159 uid_t request_uid = state->request.data.logoff.uid;
2161 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2162 state->request.data.logoff.user));
2164 /* Ensure null termination */
2165 state->request.data.logoff.user
2166 [sizeof(state->request.data.logoff.user)-1]='\0';
2168 state->request.data.logoff.krb5ccname
2169 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2171 if (request_uid == (gid_t)-1) {
2172 goto failed;
2175 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2176 goto failed;
2179 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2180 goto failed;
2183 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2184 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2185 strerror(errno)));
2186 goto failed;
2189 switch (caller_uid) {
2190 case -1:
2191 goto failed;
2192 case 0:
2193 /* root must be able to logoff any user - gd */
2194 state->request.data.logoff.uid = request_uid;
2195 break;
2196 default:
2197 if (caller_uid != request_uid) {
2198 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2199 goto failed;
2201 state->request.data.logoff.uid = caller_uid;
2202 break;
2205 sendto_domain(state, domain);
2206 return;
2208 failed:
2209 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2210 DEBUG(5, ("Pam Logoff for %s returned %s "
2211 "(PAM: %d)\n",
2212 state->request.data.logoff.user,
2213 state->response.data.auth.nt_status_string,
2214 state->response.data.auth.pam_error));
2215 request_error(state);
2216 return;
2219 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2220 struct winbindd_cli_state *state)
2222 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2224 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2225 state->request.data.logoff.user));
2227 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2228 result = NT_STATUS_OK;
2229 goto process_result;
2232 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2233 result = NT_STATUS_OK;
2234 goto process_result;
2237 #ifdef HAVE_KRB5
2239 if (state->request.data.logoff.uid < 0) {
2240 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2241 goto process_result;
2244 /* what we need here is to find the corresponding krb5 ccache name *we*
2245 * created for a given username and destroy it */
2247 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2248 result = NT_STATUS_OK;
2249 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2250 goto process_result;
2253 if (!ccache_entry_identical(state->request.data.logoff.user,
2254 state->request.data.logoff.uid,
2255 state->request.data.logoff.krb5ccname)) {
2256 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2257 goto process_result;
2260 result = remove_ccache(state->request.data.logoff.user);
2261 if (!NT_STATUS_IS_OK(result)) {
2262 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2263 nt_errstr(result)));
2264 goto process_result;
2267 #else
2268 result = NT_STATUS_NOT_SUPPORTED;
2269 #endif
2271 process_result:
2273 winbindd_delete_memory_creds(state->request.data.logoff.user);
2275 set_auth_errors(&state->response, result);
2277 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2280 /* Change user password with auth crap*/
2282 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2284 struct winbindd_domain *domain = NULL;
2285 const char *domain_name = NULL;
2287 /* Ensure null termination */
2288 state->request.data.chng_pswd_auth_crap.user[
2289 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2290 state->request.data.chng_pswd_auth_crap.domain[
2291 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2293 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2294 (unsigned long)state->pid,
2295 state->request.data.chng_pswd_auth_crap.domain,
2296 state->request.data.chng_pswd_auth_crap.user));
2298 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2299 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2300 } else if (lp_winbind_use_default_domain()) {
2301 domain_name = lp_workgroup();
2304 if (domain_name != NULL)
2305 domain = find_domain_from_name(domain_name);
2307 if (domain != NULL) {
2308 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2309 "%s\n", (unsigned long)state->pid,domain->name));
2310 sendto_domain(state, domain);
2311 return;
2314 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2315 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2316 state->request.data.chng_pswd_auth_crap.domain,
2317 state->request.data.chng_pswd_auth_crap.user,
2318 state->response.data.auth.nt_status_string,
2319 state->response.data.auth.pam_error));
2320 request_error(state);
2321 return;
2324 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2326 NTSTATUS result;
2327 DATA_BLOB new_nt_password;
2328 DATA_BLOB old_nt_hash_enc;
2329 DATA_BLOB new_lm_password;
2330 DATA_BLOB old_lm_hash_enc;
2331 fstring domain,user;
2332 POLICY_HND dom_pol;
2333 struct winbindd_domain *contact_domain = domainSt;
2334 struct rpc_pipe_client *cli;
2336 /* Ensure null termination */
2337 state->request.data.chng_pswd_auth_crap.user[
2338 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2339 state->request.data.chng_pswd_auth_crap.domain[
2340 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2341 *domain = 0;
2342 *user = 0;
2344 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2345 (unsigned long)state->pid,
2346 state->request.data.chng_pswd_auth_crap.domain,
2347 state->request.data.chng_pswd_auth_crap.user));
2349 if (lp_winbind_offline_logon()) {
2350 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2351 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2352 result = NT_STATUS_ACCESS_DENIED;
2353 goto done;
2356 if (*state->request.data.chng_pswd_auth_crap.domain) {
2357 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2358 } else {
2359 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2360 domain, user);
2362 if(!*domain) {
2363 DEBUG(3,("no domain specified with username (%s) - "
2364 "failing auth\n",
2365 state->request.data.chng_pswd_auth_crap.user));
2366 result = NT_STATUS_NO_SUCH_USER;
2367 goto done;
2371 if (!*domain && lp_winbind_use_default_domain()) {
2372 fstrcpy(domain,(char *)lp_workgroup());
2375 if(!*user) {
2376 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2379 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2380 (unsigned long)state->pid, domain, user));
2382 /* Change password */
2383 new_nt_password = data_blob_talloc(
2384 state->mem_ctx,
2385 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2386 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2388 old_nt_hash_enc = data_blob_talloc(
2389 state->mem_ctx,
2390 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2391 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2393 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2394 new_lm_password = data_blob_talloc(
2395 state->mem_ctx,
2396 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2397 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2399 old_lm_hash_enc = data_blob_talloc(
2400 state->mem_ctx,
2401 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2402 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2403 } else {
2404 new_lm_password.length = 0;
2405 old_lm_hash_enc.length = 0;
2408 /* Get sam handle */
2410 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2411 if (!NT_STATUS_IS_OK(result)) {
2412 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2413 goto done;
2416 result = rpccli_samr_chng_pswd_auth_crap(
2417 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2418 new_lm_password, old_lm_hash_enc);
2420 done:
2422 set_auth_errors(&state->response, result);
2424 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2425 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2426 domain, user,
2427 state->response.data.auth.nt_status_string,
2428 state->response.data.auth.pam_error));
2430 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;