s3:winbind: Fix bug 6793 -- segfault in winbindd_pam_auth
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob1064ec57d72a8ac3c9d02dd2fe3184d1119cc344
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, NULL, 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: '%u'\n", (unsigned int)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, mapped_user;
814 char *mapped = NULL;
815 NTSTATUS result;
816 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
818 /* Ensure null termination */
819 state->request.data.auth.user
820 [sizeof(state->request.data.auth.user)-1]='\0';
822 /* Ensure null termination */
823 state->request.data.auth.pass
824 [sizeof(state->request.data.auth.pass)-1]='\0';
826 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
827 state->request.data.auth.user));
829 if (!check_request_flags(state->request.flags)) {
830 result = NT_STATUS_INVALID_PARAMETER_MIX;
831 goto done;
834 /* Parse domain and username */
836 name_map_status = normalize_name_unmap(state->mem_ctx,
837 state->request.data.auth.user,
838 &mapped);
840 /* If the name normalization didnt' actually do anything,
841 just use the original name */
843 if (NT_STATUS_IS_OK(name_map_status)
844 ||NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
845 fstrcpy(mapped_user, mapped);
848 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
849 result = NT_STATUS_NO_SUCH_USER;
850 goto done;
853 domain = find_auth_domain(state, name_domain);
855 if (domain == NULL) {
856 result = NT_STATUS_NO_SUCH_USER;
857 goto done;
860 sendto_domain(state, domain);
861 return;
862 done:
863 set_auth_errors(&state->response, result);
864 DEBUG(5, ("Plain text authentication for %s returned %s "
865 "(PAM: %d)\n",
866 state->request.data.auth.user,
867 state->response.data.auth.nt_status_string,
868 state->response.data.auth.pam_error));
869 request_error(state);
872 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
873 struct winbindd_cli_state *state,
874 struct netr_SamInfo3 **info3)
876 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
877 uint16 max_allowed_bad_attempts;
878 fstring name_domain, name_user;
879 DOM_SID sid;
880 enum lsa_SidType type;
881 uchar new_nt_pass[NT_HASH_LEN];
882 const uint8 *cached_nt_pass;
883 const uint8 *cached_salt;
884 struct netr_SamInfo3 *my_info3;
885 time_t kickoff_time, must_change_time;
886 bool password_good = false;
887 #ifdef HAVE_KRB5
888 struct winbindd_tdc_domain *tdc_domain = NULL;
889 #endif
891 *info3 = NULL;
893 ZERO_STRUCTP(info3);
895 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
897 /* Parse domain and username */
899 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
902 if (!lookup_cached_name(state->mem_ctx,
903 name_domain,
904 name_user,
905 &sid,
906 &type)) {
907 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
908 return NT_STATUS_NO_SUCH_USER;
911 if (type != SID_NAME_USER) {
912 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
913 return NT_STATUS_LOGON_FAILURE;
916 result = winbindd_get_creds(domain,
917 state->mem_ctx,
918 &sid,
919 &my_info3,
920 &cached_nt_pass,
921 &cached_salt);
922 if (!NT_STATUS_IS_OK(result)) {
923 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
924 return result;
927 *info3 = my_info3;
929 E_md4hash(state->request.data.auth.pass, new_nt_pass);
931 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
932 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
933 if (cached_salt) {
934 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
937 if (cached_salt) {
938 /* In this case we didn't store the nt_hash itself,
939 but the MD5 combination of salt + nt_hash. */
940 uchar salted_hash[NT_HASH_LEN];
941 E_md5hash(cached_salt, new_nt_pass, salted_hash);
943 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
944 true : false;
945 } else {
946 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
947 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
948 true : false;
951 if (password_good) {
953 /* User *DOES* know the password, update logon_time and reset
954 * bad_pw_count */
956 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
958 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
959 return NT_STATUS_ACCOUNT_LOCKED_OUT;
962 if (my_info3->base.acct_flags & ACB_DISABLED) {
963 return NT_STATUS_ACCOUNT_DISABLED;
966 if (my_info3->base.acct_flags & ACB_WSTRUST) {
967 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
970 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
971 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
974 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
975 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
978 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
979 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
980 my_info3->base.acct_flags));
981 return NT_STATUS_LOGON_FAILURE;
984 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
985 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
986 return NT_STATUS_ACCOUNT_EXPIRED;
989 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
990 if (must_change_time != 0 && must_change_time < time(NULL)) {
991 /* we allow grace logons when the password has expired */
992 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
993 /* return NT_STATUS_PASSWORD_EXPIRED; */
994 goto success;
997 #ifdef HAVE_KRB5
998 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
999 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1000 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
1002 uid_t uid = -1;
1003 const char *cc = NULL;
1004 char *realm = NULL;
1005 const char *principal_s = NULL;
1006 const char *service = NULL;
1007 bool internal_ccache = false;
1009 uid = get_uid_from_state(state);
1010 if (uid == -1) {
1011 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1012 return NT_STATUS_INVALID_PARAMETER;
1015 cc = generate_krb5_ccache(state->mem_ctx,
1016 state->request.data.auth.krb5_cc_type,
1017 state->request.data.auth.uid,
1018 &internal_ccache);
1019 if (cc == NULL) {
1020 return NT_STATUS_NO_MEMORY;
1023 realm = domain->alt_name;
1024 strupper_m(realm);
1026 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1027 if (principal_s == NULL) {
1028 return NT_STATUS_NO_MEMORY;
1031 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1032 if (service == NULL) {
1033 return NT_STATUS_NO_MEMORY;
1036 if (!internal_ccache) {
1038 setup_return_cc_name(state, cc);
1040 result = add_ccache_to_list(principal_s,
1042 service,
1043 state->request.data.auth.user,
1044 domain->alt_name,
1045 uid,
1046 time(NULL),
1047 time(NULL) + lp_winbind_cache_time(),
1048 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1049 true);
1051 if (!NT_STATUS_IS_OK(result)) {
1052 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1053 "to add ccache to list: %s\n",
1054 nt_errstr(result)));
1058 #endif /* HAVE_KRB5 */
1059 success:
1060 /* FIXME: we possibly should handle logon hours as well (does xp when
1061 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1063 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1064 my_info3->base.bad_password_count = 0;
1066 result = winbindd_update_creds_by_info3(domain,
1067 state->mem_ctx,
1068 state->request.data.auth.user,
1069 state->request.data.auth.pass,
1070 my_info3);
1071 if (!NT_STATUS_IS_OK(result)) {
1072 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1073 nt_errstr(result)));
1074 return result;
1077 return NT_STATUS_OK;
1081 /* User does *NOT* know the correct password, modify info3 accordingly */
1083 /* failure of this is not critical */
1084 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1085 if (!NT_STATUS_IS_OK(result)) {
1086 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1087 "Won't be able to honour account lockout policies\n"));
1090 /* increase counter */
1091 my_info3->base.bad_password_count++;
1093 if (max_allowed_bad_attempts == 0) {
1094 goto failed;
1097 /* lockout user */
1098 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1100 uint32 password_properties;
1102 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1103 if (!NT_STATUS_IS_OK(result)) {
1104 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1107 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1108 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1109 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1113 failed:
1114 result = winbindd_update_creds_by_info3(domain,
1115 state->mem_ctx,
1116 state->request.data.auth.user,
1117 NULL,
1118 my_info3);
1120 if (!NT_STATUS_IS_OK(result)) {
1121 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1122 nt_errstr(result)));
1125 return NT_STATUS_LOGON_FAILURE;
1128 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1129 struct winbindd_cli_state *state,
1130 struct netr_SamInfo3 **info3)
1132 struct winbindd_domain *contact_domain;
1133 fstring name_domain, name_user;
1134 NTSTATUS result;
1136 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1138 /* Parse domain and username */
1140 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1142 /* what domain should we contact? */
1144 if ( IS_DC ) {
1145 if (!(contact_domain = find_domain_from_name(name_domain))) {
1146 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1147 state->request.data.auth.user, name_domain, name_user, name_domain));
1148 result = NT_STATUS_NO_SUCH_USER;
1149 goto done;
1152 } else {
1153 if (is_myname(name_domain)) {
1154 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1155 result = NT_STATUS_NO_SUCH_USER;
1156 goto done;
1159 contact_domain = find_domain_from_name(name_domain);
1160 if (contact_domain == NULL) {
1161 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1162 state->request.data.auth.user, name_domain, name_user, name_domain));
1164 contact_domain = find_our_domain();
1168 if (contact_domain->initialized &&
1169 contact_domain->active_directory) {
1170 goto try_login;
1173 if (!contact_domain->initialized) {
1174 init_dc_connection(contact_domain);
1177 if (!contact_domain->active_directory) {
1178 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1179 return NT_STATUS_INVALID_LOGON_TYPE;
1181 try_login:
1182 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1183 done:
1184 return result;
1187 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1188 TALLOC_CTX *mem_ctx,
1189 uint32 logon_parameters,
1190 const char *server,
1191 const char *username,
1192 const char *domain,
1193 const char *workstation,
1194 const uint8 chal[8],
1195 DATA_BLOB lm_response,
1196 DATA_BLOB nt_response,
1197 struct netr_SamInfo3 **info3);
1199 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1200 struct winbindd_cli_state *state,
1201 struct netr_SamInfo3 **info3)
1204 struct rpc_pipe_client *netlogon_pipe;
1205 uchar chal[8];
1206 DATA_BLOB lm_resp;
1207 DATA_BLOB nt_resp;
1208 int attempts = 0;
1209 unsigned char local_lm_response[24];
1210 unsigned char local_nt_response[24];
1211 struct winbindd_domain *contact_domain;
1212 fstring name_domain, name_user;
1213 bool retry;
1214 NTSTATUS result;
1215 struct netr_SamInfo3 *my_info3 = NULL;
1217 *info3 = NULL;
1219 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1221 /* Parse domain and username */
1223 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1225 /* do password magic */
1228 generate_random_buffer(chal, 8);
1229 if (lp_client_ntlmv2_auth()) {
1230 DATA_BLOB server_chal;
1231 DATA_BLOB names_blob;
1232 DATA_BLOB nt_response;
1233 DATA_BLOB lm_response;
1234 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1236 /* note that the 'workgroup' here is a best guess - we don't know
1237 the server's domain at this point. The 'server name' is also
1238 dodgy...
1240 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1242 if (!SMBNTLMv2encrypt(name_user, name_domain,
1243 state->request.data.auth.pass,
1244 &server_chal,
1245 &names_blob,
1246 &lm_response, &nt_response, NULL)) {
1247 data_blob_free(&names_blob);
1248 data_blob_free(&server_chal);
1249 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1250 result = NT_STATUS_NO_MEMORY;
1251 goto done;
1253 data_blob_free(&names_blob);
1254 data_blob_free(&server_chal);
1255 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1256 lm_response.length);
1257 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1258 nt_response.length);
1259 data_blob_free(&lm_response);
1260 data_blob_free(&nt_response);
1262 } else {
1263 if (lp_client_lanman_auth()
1264 && SMBencrypt(state->request.data.auth.pass,
1265 chal,
1266 local_lm_response)) {
1267 lm_resp = data_blob_talloc(state->mem_ctx,
1268 local_lm_response,
1269 sizeof(local_lm_response));
1270 } else {
1271 lm_resp = data_blob_null;
1273 SMBNTencrypt(state->request.data.auth.pass,
1274 chal,
1275 local_nt_response);
1277 nt_resp = data_blob_talloc(state->mem_ctx,
1278 local_nt_response,
1279 sizeof(local_nt_response));
1282 /* what domain should we contact? */
1284 if ( IS_DC ) {
1285 if (!(contact_domain = find_domain_from_name(name_domain))) {
1286 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1287 state->request.data.auth.user, name_domain, name_user, name_domain));
1288 result = NT_STATUS_NO_SUCH_USER;
1289 goto done;
1292 } else {
1293 if (is_myname(name_domain)) {
1294 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1295 result = NT_STATUS_NO_SUCH_USER;
1296 goto done;
1299 contact_domain = find_our_domain();
1302 /* check authentication loop */
1304 do {
1305 netlogon_fn_t logon_fn;
1307 ZERO_STRUCTP(my_info3);
1308 retry = false;
1310 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1312 if (!NT_STATUS_IS_OK(result)) {
1313 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1314 goto done;
1317 /* It is really important to try SamLogonEx here,
1318 * because in a clustered environment, we want to use
1319 * one machine account from multiple physical
1320 * computers.
1322 * With a normal SamLogon call, we must keep the
1323 * credentials chain updated and intact between all
1324 * users of the machine account (which would imply
1325 * cross-node communication for every NTLM logon).
1327 * (The credentials chain is not per NETLOGON pipe
1328 * connection, but globally on the server/client pair
1329 * by machine name).
1331 * When using SamLogonEx, the credentials are not
1332 * supplied, but the session key is implied by the
1333 * wrapping SamLogon context.
1335 * -- abartlet 21 April 2008
1338 logon_fn = contact_domain->can_do_samlogon_ex
1339 ? rpccli_netlogon_sam_network_logon_ex
1340 : rpccli_netlogon_sam_network_logon;
1342 result = logon_fn(netlogon_pipe,
1343 state->mem_ctx,
1345 contact_domain->dcname, /* server name */
1346 name_user, /* user name */
1347 name_domain, /* target domain */
1348 global_myname(), /* workstation */
1349 chal,
1350 lm_resp,
1351 nt_resp,
1352 &my_info3);
1353 attempts += 1;
1355 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1356 && contact_domain->can_do_samlogon_ex) {
1357 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1358 "retrying with NetSamLogon\n"));
1359 contact_domain->can_do_samlogon_ex = false;
1360 retry = true;
1361 continue;
1364 /* We have to try a second time as cm_connect_netlogon
1365 might not yet have noticed that the DC has killed
1366 our connection. */
1368 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1369 retry = true;
1370 continue;
1373 /* if we get access denied, a possible cause was that we had
1374 and open connection to the DC, but someone changed our
1375 machine account password out from underneath us using 'net
1376 rpc changetrustpw' */
1378 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1379 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1380 "ACCESS_DENIED. Maybe the trust account "
1381 "password was changed and we didn't know it. "
1382 "Killing connections to domain %s\n",
1383 name_domain));
1384 invalidate_cm_connection(&contact_domain->conn);
1385 retry = true;
1388 } while ( (attempts < 2) && retry );
1390 /* handle the case where a NT4 DC does not fill in the acct_flags in
1391 * the samlogon reply info3. When accurate info3 is required by the
1392 * caller, we look up the account flags ourselve - gd */
1394 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1395 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1397 struct rpc_pipe_client *samr_pipe;
1398 struct policy_handle samr_domain_handle, user_pol;
1399 union samr_UserInfo *info = NULL;
1400 NTSTATUS status_tmp;
1401 uint32 acct_flags;
1403 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1404 &samr_pipe, &samr_domain_handle);
1406 if (!NT_STATUS_IS_OK(status_tmp)) {
1407 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1408 nt_errstr(status_tmp)));
1409 goto done;
1412 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1413 &samr_domain_handle,
1414 MAXIMUM_ALLOWED_ACCESS,
1415 my_info3->base.rid,
1416 &user_pol);
1418 if (!NT_STATUS_IS_OK(status_tmp)) {
1419 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1420 nt_errstr(status_tmp)));
1421 goto done;
1424 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1425 &user_pol,
1427 &info);
1429 if (!NT_STATUS_IS_OK(status_tmp)) {
1430 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1431 nt_errstr(status_tmp)));
1432 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1433 goto done;
1436 acct_flags = info->info16.acct_flags;
1438 if (acct_flags == 0) {
1439 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1440 goto done;
1443 my_info3->base.acct_flags = acct_flags;
1445 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1447 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1450 *info3 = my_info3;
1451 done:
1452 return result;
1455 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1456 struct winbindd_cli_state *state)
1458 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1459 NTSTATUS krb5_result = NT_STATUS_OK;
1460 fstring name_domain, name_user;
1461 char *mapped_user;
1462 fstring domain_user;
1463 struct netr_SamInfo3 *info3 = NULL;
1464 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1466 /* Ensure null termination */
1467 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1469 /* Ensure null termination */
1470 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1472 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1473 state->request.data.auth.user));
1475 if (!check_request_flags(state->request.flags)) {
1476 result = NT_STATUS_INVALID_PARAMETER_MIX;
1477 goto done;
1480 /* Parse domain and username */
1482 name_map_status = normalize_name_unmap(state->mem_ctx,
1483 state->request.data.auth.user,
1484 &mapped_user);
1486 /* If the name normalization didnt' actually do anything,
1487 just use the original name */
1489 if (!NT_STATUS_IS_OK(name_map_status) &&
1490 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1492 mapped_user = state->request.data.auth.user;
1495 parse_domain_user(mapped_user, name_domain, name_user);
1497 if ( mapped_user != state->request.data.auth.user ) {
1498 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1499 safe_strcpy( state->request.data.auth.user, domain_user,
1500 sizeof(state->request.data.auth.user)-1 );
1503 if (domain->online == false) {
1504 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1505 if (domain->startup) {
1506 /* Logons are very important to users. If we're offline and
1507 we get a request within the first 30 seconds of startup,
1508 try very hard to find a DC and go online. */
1510 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1511 "request in startup mode.\n", domain->name ));
1513 winbindd_flush_negative_conn_cache(domain);
1514 result = init_dc_connection(domain);
1518 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1520 /* Check for Kerberos authentication */
1521 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1523 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1524 /* save for later */
1525 krb5_result = result;
1528 if (NT_STATUS_IS_OK(result)) {
1529 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1530 goto process_result;
1531 } else {
1532 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1535 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1536 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1538 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1539 set_domain_offline( domain );
1540 goto cached_logon;
1543 /* there are quite some NT_STATUS errors where there is no
1544 * point in retrying with a samlogon, we explictly have to take
1545 * care not to increase the bad logon counter on the DC */
1547 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1548 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1549 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1550 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1551 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1553 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1554 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1557 goto process_result;
1560 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1561 DEBUG(3,("falling back to samlogon\n"));
1562 goto sam_logon;
1563 } else {
1564 goto cached_logon;
1568 sam_logon:
1569 /* Check for Samlogon authentication */
1570 if (domain->online) {
1571 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1573 if (NT_STATUS_IS_OK(result)) {
1574 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1575 /* add the Krb5 err if we have one */
1576 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1577 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1579 goto process_result;
1582 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1583 nt_errstr(result)));
1585 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1586 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1587 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1589 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1590 set_domain_offline( domain );
1591 goto cached_logon;
1594 if (domain->online) {
1595 /* We're still online - fail. */
1596 goto done;
1600 cached_logon:
1601 /* Check for Cached logons */
1602 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1603 lp_winbind_offline_logon()) {
1605 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1607 if (NT_STATUS_IS_OK(result)) {
1608 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1609 goto process_result;
1610 } else {
1611 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1612 goto done;
1616 process_result:
1618 if (NT_STATUS_IS_OK(result)) {
1620 DOM_SID user_sid;
1622 /* In all codepaths where result == NT_STATUS_OK info3 must have
1623 been initialized. */
1624 if (!info3) {
1625 result = NT_STATUS_INTERNAL_ERROR;
1626 goto done;
1629 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1630 netsamlogon_cache_store(name_user, info3);
1632 /* save name_to_sid info as early as possible (only if
1633 this is our primary domain so we don't invalidate
1634 the cache entry by storing the seq_num for the wrong
1635 domain). */
1636 if ( domain->primary ) {
1637 sid_compose(&user_sid, info3->base.domain_sid,
1638 info3->base.rid);
1639 cache_name2sid(domain, name_domain, name_user,
1640 SID_NAME_USER, &user_sid);
1643 /* Check if the user is in the right group */
1645 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1646 state->request.data.auth.require_membership_of_sid))) {
1647 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1648 state->request.data.auth.user,
1649 state->request.data.auth.require_membership_of_sid));
1650 goto done;
1653 result = append_data(state, info3, name_domain, name_user);
1654 if (!NT_STATUS_IS_OK(result)) {
1655 goto done;
1658 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1660 /* Store in-memory creds for single-signon using ntlm_auth. */
1661 result = winbindd_add_memory_creds(state->request.data.auth.user,
1662 get_uid_from_state(state),
1663 state->request.data.auth.pass);
1665 if (!NT_STATUS_IS_OK(result)) {
1666 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1667 goto done;
1670 if (lp_winbind_offline_logon()) {
1671 result = winbindd_store_creds(domain,
1672 state->mem_ctx,
1673 state->request.data.auth.user,
1674 state->request.data.auth.pass,
1675 info3, NULL);
1676 if (!NT_STATUS_IS_OK(result)) {
1678 /* Release refcount. */
1679 winbindd_delete_memory_creds(state->request.data.auth.user);
1681 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1682 goto done;
1688 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1689 struct winbindd_domain *our_domain = find_our_domain();
1691 /* This is not entirely correct I believe, but it is
1692 consistent. Only apply the password policy settings
1693 too warn users for our own domain. Cannot obtain these
1694 from trusted DCs all the time so don't do it at all.
1695 -- jerry */
1697 result = NT_STATUS_NOT_SUPPORTED;
1698 if (our_domain == domain ) {
1699 result = fillup_password_policy(our_domain, state);
1702 if (!NT_STATUS_IS_OK(result)
1703 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1705 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1706 domain->name, nt_errstr(result)));
1707 goto done;
1711 result = NT_STATUS_OK;
1714 done:
1715 /* give us a more useful (more correct?) error code */
1716 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1717 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1718 result = NT_STATUS_NO_LOGON_SERVERS;
1721 set_auth_errors(&state->response, result);
1723 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1724 state->request.data.auth.user,
1725 state->response.data.auth.nt_status_string,
1726 state->response.data.auth.pam_error));
1728 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1732 /**********************************************************************
1733 Challenge Response Authentication Protocol
1734 **********************************************************************/
1736 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1738 struct winbindd_domain *domain = NULL;
1739 const char *domain_name = NULL;
1740 NTSTATUS result;
1742 if (!check_request_flags(state->request.flags)) {
1743 result = NT_STATUS_INVALID_PARAMETER_MIX;
1744 goto done;
1747 if (!state->privileged) {
1748 char *error_string = NULL;
1749 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1750 "denied. !\n"));
1751 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1752 "on %s are set correctly.\n",
1753 get_winbind_priv_pipe_dir()));
1754 /* send a better message than ACCESS_DENIED */
1755 error_string = talloc_asprintf(state->mem_ctx,
1756 "winbind client not authorized "
1757 "to use winbindd_pam_auth_crap."
1758 " Ensure permissions on %s "
1759 "are set correctly.",
1760 get_winbind_priv_pipe_dir());
1761 fstrcpy(state->response.data.auth.error_string, error_string);
1762 result = NT_STATUS_ACCESS_DENIED;
1763 goto done;
1766 /* Ensure null termination */
1767 state->request.data.auth_crap.user
1768 [sizeof(state->request.data.auth_crap.user)-1]=0;
1769 state->request.data.auth_crap.domain
1770 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1772 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1773 (unsigned long)state->pid,
1774 state->request.data.auth_crap.domain,
1775 state->request.data.auth_crap.user));
1777 if (*state->request.data.auth_crap.domain != '\0') {
1778 domain_name = state->request.data.auth_crap.domain;
1779 } else if (lp_winbind_use_default_domain()) {
1780 domain_name = lp_workgroup();
1783 if (domain_name != NULL)
1784 domain = find_auth_domain(state, domain_name);
1786 if (domain != NULL) {
1787 sendto_domain(state, domain);
1788 return;
1791 result = NT_STATUS_NO_SUCH_USER;
1793 done:
1794 set_auth_errors(&state->response, result);
1795 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1796 state->request.data.auth_crap.domain,
1797 state->request.data.auth_crap.user,
1798 state->response.data.auth.nt_status_string,
1799 state->response.data.auth.pam_error));
1800 request_error(state);
1801 return;
1805 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1806 struct winbindd_cli_state *state)
1808 NTSTATUS result;
1809 struct netr_SamInfo3 *info3 = NULL;
1810 struct rpc_pipe_client *netlogon_pipe;
1811 const char *name_user = NULL;
1812 const char *name_domain = NULL;
1813 const char *workstation;
1814 struct winbindd_domain *contact_domain;
1815 int attempts = 0;
1816 bool retry;
1818 DATA_BLOB lm_resp, nt_resp;
1820 /* This is child-only, so no check for privileged access is needed
1821 anymore */
1823 /* Ensure null termination */
1824 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1825 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1827 if (!check_request_flags(state->request.flags)) {
1828 result = NT_STATUS_INVALID_PARAMETER_MIX;
1829 goto done;
1832 name_user = state->request.data.auth_crap.user;
1834 if (*state->request.data.auth_crap.domain) {
1835 name_domain = state->request.data.auth_crap.domain;
1836 } else if (lp_winbind_use_default_domain()) {
1837 name_domain = lp_workgroup();
1838 } else {
1839 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1840 name_user));
1841 result = NT_STATUS_NO_SUCH_USER;
1842 goto done;
1845 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1846 name_domain, name_user));
1848 if (*state->request.data.auth_crap.workstation) {
1849 workstation = state->request.data.auth_crap.workstation;
1850 } else {
1851 workstation = global_myname();
1854 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1855 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1856 if (!(state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1857 state->request.extra_len != state->request.data.auth_crap.nt_resp_len) {
1858 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1859 state->request.data.auth_crap.lm_resp_len,
1860 state->request.data.auth_crap.nt_resp_len));
1861 result = NT_STATUS_INVALID_PARAMETER;
1862 goto done;
1866 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1867 state->request.data.auth_crap.lm_resp_len);
1869 if (state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) {
1870 nt_resp = data_blob_talloc(state->mem_ctx,
1871 state->request.extra_data.data,
1872 state->request.data.auth_crap.nt_resp_len);
1873 } else {
1874 nt_resp = data_blob_talloc(state->mem_ctx,
1875 state->request.data.auth_crap.nt_resp,
1876 state->request.data.auth_crap.nt_resp_len);
1879 /* what domain should we contact? */
1881 if ( IS_DC ) {
1882 if (!(contact_domain = find_domain_from_name(name_domain))) {
1883 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1884 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1885 result = NT_STATUS_NO_SUCH_USER;
1886 goto done;
1888 } else {
1889 if (is_myname(name_domain)) {
1890 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1891 result = NT_STATUS_NO_SUCH_USER;
1892 goto done;
1894 contact_domain = find_our_domain();
1897 do {
1898 netlogon_fn_t logon_fn;
1900 retry = false;
1902 netlogon_pipe = NULL;
1903 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1905 if (!NT_STATUS_IS_OK(result)) {
1906 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1907 nt_errstr(result)));
1908 goto done;
1911 logon_fn = contact_domain->can_do_samlogon_ex
1912 ? rpccli_netlogon_sam_network_logon_ex
1913 : rpccli_netlogon_sam_network_logon;
1915 result = logon_fn(netlogon_pipe,
1916 state->mem_ctx,
1917 state->request.data.auth_crap.logon_parameters,
1918 contact_domain->dcname,
1919 name_user,
1920 name_domain,
1921 /* Bug #3248 - found by Stefan Burkei. */
1922 workstation, /* We carefully set this above so use it... */
1923 state->request.data.auth_crap.chal,
1924 lm_resp,
1925 nt_resp,
1926 &info3);
1928 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1929 && contact_domain->can_do_samlogon_ex) {
1930 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1931 "retrying with NetSamLogon\n"));
1932 contact_domain->can_do_samlogon_ex = false;
1933 retry = true;
1934 continue;
1937 attempts += 1;
1939 /* We have to try a second time as cm_connect_netlogon
1940 might not yet have noticed that the DC has killed
1941 our connection. */
1943 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1944 retry = true;
1945 continue;
1948 /* if we get access denied, a possible cause was that we had and open
1949 connection to the DC, but someone changed our machine account password
1950 out from underneath us using 'net rpc changetrustpw' */
1952 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1953 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1954 "ACCESS_DENIED. Maybe the trust account "
1955 "password was changed and we didn't know it. "
1956 "Killing connections to domain %s\n",
1957 name_domain));
1958 invalidate_cm_connection(&contact_domain->conn);
1959 retry = true;
1962 } while ( (attempts < 2) && retry );
1964 if (NT_STATUS_IS_OK(result)) {
1966 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1967 netsamlogon_cache_store(name_user, info3);
1969 /* Check if the user is in the right group */
1971 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1972 state->request.data.auth_crap.require_membership_of_sid))) {
1973 DEBUG(3, ("User %s is not in the required group (%s), so "
1974 "crap authentication is rejected\n",
1975 state->request.data.auth_crap.user,
1976 state->request.data.auth_crap.require_membership_of_sid));
1977 goto done;
1980 result = append_data(state, info3, name_domain, name_user);
1981 if (!NT_STATUS_IS_OK(result)) {
1982 goto done;
1986 done:
1988 /* give us a more useful (more correct?) error code */
1989 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1990 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1991 result = NT_STATUS_NO_LOGON_SERVERS;
1994 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1995 result = nt_status_squash(result);
1998 set_auth_errors(&state->response, result);
2000 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2001 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2002 name_domain,
2003 name_user,
2004 state->response.data.auth.nt_status_string,
2005 state->response.data.auth.pam_error));
2007 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2010 /* Change a user password */
2012 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2014 fstring domain, user;
2015 char *mapped_user;
2016 struct winbindd_domain *contact_domain;
2017 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2019 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2020 state->request.data.chauthtok.user));
2022 /* Setup crap */
2024 nt_status = normalize_name_unmap(state->mem_ctx,
2025 state->request.data.chauthtok.user,
2026 &mapped_user);
2028 /* Update the chauthtok name if we did any mapping */
2030 if (NT_STATUS_IS_OK(nt_status) ||
2031 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2033 fstrcpy(state->request.data.chauthtok.user, mapped_user);
2036 /* Must pass in state->...chauthtok.user because
2037 canonicalize_username() assumes an fstring(). Since
2038 we have already copied it (if necessary), this is ok. */
2040 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2041 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2042 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2043 "(PAM: %d)\n",
2044 state->request.data.auth.user,
2045 state->response.data.auth.nt_status_string,
2046 state->response.data.auth.pam_error));
2047 request_error(state);
2048 return;
2051 contact_domain = find_domain_from_name(domain);
2052 if (!contact_domain) {
2053 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2054 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2055 state->request.data.chauthtok.user, domain, user, domain));
2056 request_error(state);
2057 return;
2060 sendto_domain(state, contact_domain);
2063 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2064 struct winbindd_cli_state *state)
2066 char *oldpass;
2067 char *newpass = NULL;
2068 struct policy_handle dom_pol;
2069 struct rpc_pipe_client *cli;
2070 bool got_info = false;
2071 struct samr_DomInfo1 *info = NULL;
2072 struct samr_ChangeReject *reject = NULL;
2073 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2074 fstring domain, user;
2076 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2077 state->request.data.auth.user));
2079 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2080 goto done;
2083 /* Change password */
2085 oldpass = state->request.data.chauthtok.oldpass;
2086 newpass = state->request.data.chauthtok.newpass;
2088 /* Initialize reject reason */
2089 state->response.data.auth.reject_reason = Undefined;
2091 /* Get sam handle */
2093 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2094 &dom_pol);
2095 if (!NT_STATUS_IS_OK(result)) {
2096 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2097 goto done;
2100 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2101 user,
2102 newpass,
2103 oldpass,
2104 &info,
2105 &reject);
2107 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2109 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2111 fill_in_password_policy(&state->response, info);
2113 state->response.data.auth.reject_reason =
2114 reject->reason;
2116 got_info = true;
2119 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2120 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2121 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2122 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2124 /* only fallback when the chgpasswd_user3 call is not supported */
2125 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2126 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2127 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2128 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2130 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2131 nt_errstr(result)));
2133 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2135 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2136 Map to the same status code as Windows 2003. */
2138 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2139 result = NT_STATUS_PASSWORD_RESTRICTION;
2143 done:
2145 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2147 /* Update the single sign-on memory creds. */
2148 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2149 newpass);
2151 /* When we login from gdm or xdm and password expires,
2152 * we change password, but there are no memory crendentials
2153 * So, winbindd_replace_memory_creds() returns
2154 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2155 * --- BoYang
2156 * */
2157 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2158 result = NT_STATUS_OK;
2161 if (!NT_STATUS_IS_OK(result)) {
2162 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2163 goto process_result;
2166 if (lp_winbind_offline_logon()) {
2167 result = winbindd_update_creds_by_name(contact_domain,
2168 state->mem_ctx, user,
2169 newpass);
2170 /* Again, this happens when we login from gdm or xdm
2171 * and the password expires, *BUT* cached crendentials
2172 * doesn't exist. winbindd_update_creds_by_name()
2173 * returns NT_STATUS_NO_SUCH_USER.
2174 * This is not a failure.
2175 * --- BoYang
2176 * */
2177 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2178 result = NT_STATUS_OK;
2181 if (!NT_STATUS_IS_OK(result)) {
2182 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2183 goto process_result;
2188 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2190 NTSTATUS policy_ret;
2192 policy_ret = fillup_password_policy(contact_domain, state);
2194 /* failure of this is non critical, it will just provide no
2195 * additional information to the client why the change has
2196 * failed - Guenther */
2198 if (!NT_STATUS_IS_OK(policy_ret)) {
2199 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2200 goto process_result;
2204 process_result:
2206 set_auth_errors(&state->response, result);
2208 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2209 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2210 domain,
2211 user,
2212 state->response.data.auth.nt_status_string,
2213 state->response.data.auth.pam_error));
2215 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2218 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2220 struct winbindd_domain *domain;
2221 fstring name_domain, user;
2222 uid_t caller_uid = (uid_t)-1;
2223 uid_t request_uid = state->request.data.logoff.uid;
2225 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2226 state->request.data.logoff.user));
2228 /* Ensure null termination */
2229 state->request.data.logoff.user
2230 [sizeof(state->request.data.logoff.user)-1]='\0';
2232 state->request.data.logoff.krb5ccname
2233 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2235 if (request_uid == (gid_t)-1) {
2236 goto failed;
2239 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2240 goto failed;
2243 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2244 goto failed;
2247 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2248 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2249 strerror(errno)));
2250 goto failed;
2253 switch (caller_uid) {
2254 case -1:
2255 goto failed;
2256 case 0:
2257 /* root must be able to logoff any user - gd */
2258 state->request.data.logoff.uid = request_uid;
2259 break;
2260 default:
2261 if (caller_uid != request_uid) {
2262 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2263 goto failed;
2265 state->request.data.logoff.uid = caller_uid;
2266 break;
2269 sendto_domain(state, domain);
2270 return;
2272 failed:
2273 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2274 DEBUG(5, ("Pam Logoff for %s returned %s "
2275 "(PAM: %d)\n",
2276 state->request.data.logoff.user,
2277 state->response.data.auth.nt_status_string,
2278 state->response.data.auth.pam_error));
2279 request_error(state);
2280 return;
2283 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2284 struct winbindd_cli_state *state)
2286 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2288 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2289 state->request.data.logoff.user));
2291 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2292 result = NT_STATUS_OK;
2293 goto process_result;
2296 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2297 result = NT_STATUS_OK;
2298 goto process_result;
2301 #ifdef HAVE_KRB5
2303 if (state->request.data.logoff.uid < 0) {
2304 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2305 goto process_result;
2308 /* what we need here is to find the corresponding krb5 ccache name *we*
2309 * created for a given username and destroy it */
2311 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2312 result = NT_STATUS_OK;
2313 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2314 goto process_result;
2317 if (!ccache_entry_identical(state->request.data.logoff.user,
2318 state->request.data.logoff.uid,
2319 state->request.data.logoff.krb5ccname)) {
2320 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2321 goto process_result;
2324 result = remove_ccache(state->request.data.logoff.user);
2325 if (!NT_STATUS_IS_OK(result)) {
2326 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2327 nt_errstr(result)));
2328 goto process_result;
2331 #else
2332 result = NT_STATUS_NOT_SUPPORTED;
2333 #endif
2335 process_result:
2337 winbindd_delete_memory_creds(state->request.data.logoff.user);
2339 set_auth_errors(&state->response, result);
2341 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2344 /* Change user password with auth crap*/
2346 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2348 struct winbindd_domain *domain = NULL;
2349 const char *domain_name = NULL;
2351 /* Ensure null termination */
2352 state->request.data.chng_pswd_auth_crap.user[
2353 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2354 state->request.data.chng_pswd_auth_crap.domain[
2355 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2357 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2358 (unsigned long)state->pid,
2359 state->request.data.chng_pswd_auth_crap.domain,
2360 state->request.data.chng_pswd_auth_crap.user));
2362 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2363 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2364 } else if (lp_winbind_use_default_domain()) {
2365 domain_name = lp_workgroup();
2368 if (domain_name != NULL)
2369 domain = find_domain_from_name(domain_name);
2371 if (domain != NULL) {
2372 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2373 "%s\n", (unsigned long)state->pid,domain->name));
2374 sendto_domain(state, domain);
2375 return;
2378 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2379 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2380 state->request.data.chng_pswd_auth_crap.domain,
2381 state->request.data.chng_pswd_auth_crap.user,
2382 state->response.data.auth.nt_status_string,
2383 state->response.data.auth.pam_error));
2384 request_error(state);
2385 return;
2388 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2390 NTSTATUS result;
2391 DATA_BLOB new_nt_password;
2392 DATA_BLOB old_nt_hash_enc;
2393 DATA_BLOB new_lm_password;
2394 DATA_BLOB old_lm_hash_enc;
2395 fstring domain,user;
2396 struct policy_handle dom_pol;
2397 struct winbindd_domain *contact_domain = domainSt;
2398 struct rpc_pipe_client *cli;
2400 /* Ensure null termination */
2401 state->request.data.chng_pswd_auth_crap.user[
2402 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2403 state->request.data.chng_pswd_auth_crap.domain[
2404 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2405 *domain = 0;
2406 *user = 0;
2408 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2409 (unsigned long)state->pid,
2410 state->request.data.chng_pswd_auth_crap.domain,
2411 state->request.data.chng_pswd_auth_crap.user));
2413 if (lp_winbind_offline_logon()) {
2414 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2415 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2416 result = NT_STATUS_ACCESS_DENIED;
2417 goto done;
2420 if (*state->request.data.chng_pswd_auth_crap.domain) {
2421 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2422 } else {
2423 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2424 domain, user);
2426 if(!*domain) {
2427 DEBUG(3,("no domain specified with username (%s) - "
2428 "failing auth\n",
2429 state->request.data.chng_pswd_auth_crap.user));
2430 result = NT_STATUS_NO_SUCH_USER;
2431 goto done;
2435 if (!*domain && lp_winbind_use_default_domain()) {
2436 fstrcpy(domain,(char *)lp_workgroup());
2439 if(!*user) {
2440 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2443 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2444 (unsigned long)state->pid, domain, user));
2446 /* Change password */
2447 new_nt_password = data_blob_talloc(
2448 state->mem_ctx,
2449 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2450 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2452 old_nt_hash_enc = data_blob_talloc(
2453 state->mem_ctx,
2454 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2455 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2457 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2458 new_lm_password = data_blob_talloc(
2459 state->mem_ctx,
2460 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2461 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2463 old_lm_hash_enc = data_blob_talloc(
2464 state->mem_ctx,
2465 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2466 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2467 } else {
2468 new_lm_password.length = 0;
2469 old_lm_hash_enc.length = 0;
2472 /* Get sam handle */
2474 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2475 if (!NT_STATUS_IS_OK(result)) {
2476 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2477 goto done;
2480 result = rpccli_samr_chng_pswd_auth_crap(
2481 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2482 new_lm_password, old_lm_hash_enc);
2484 done:
2486 set_auth_errors(&state->response, result);
2488 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2489 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2490 domain, user,
2491 state->response.data.auth.nt_status_string,
2492 state->response.data.auth.pam_error));
2494 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;