Convert response.extra_data.data from malloc to talloc
[Samba.git] / source3 / winbindd / winbindd_pam.c
bloba85cf0e6fcb0c4abc26439689f372985eb97db7a
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
33 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
34 struct winbindd_cli_state *state,
35 struct netr_SamInfo3 *info3)
37 char *ex;
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(state->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 state->response.extra_data.data = ex;
109 state->response.length += talloc_get_size(ex);
111 return NT_STATUS_OK;
114 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
115 struct winbindd_cli_state *state,
116 struct netr_SamInfo3 *info3)
118 DATA_BLOB blob;
119 enum ndr_err_code ndr_err;
121 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
122 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
125 return ndr_map_error2ntstatus(ndr_err);
128 state->response.extra_data.data = blob.data;
129 state->response.length += blob.length;
131 return NT_STATUS_OK;
134 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
135 struct winbindd_cli_state *state,
136 const struct netr_SamInfo3 *info3,
137 const char *name_domain,
138 const char *name_user)
140 /* We've been asked to return the unix username, per
141 'winbind use default domain' settings and the like */
143 const char *nt_username, *nt_domain;
145 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
146 if (!nt_domain) {
147 /* If the server didn't give us one, just use the one
148 * we sent them */
149 nt_domain = name_domain;
152 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
153 if (!nt_username) {
154 /* If the server didn't give us one, just use the one
155 * we sent them */
156 nt_username = name_user;
159 fill_domain_username(state->response.data.auth.unix_username,
160 nt_domain, nt_username, true);
162 DEBUG(5,("Setting unix username to [%s]\n",
163 state->response.data.auth.unix_username));
165 return NT_STATUS_OK;
168 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
169 struct winbindd_cli_state *state,
170 const struct netr_SamInfo3 *info3,
171 const char *name_domain,
172 const char *name_user)
174 char *afsname = NULL;
175 char *cell;
176 char *token;
178 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
179 if (afsname == NULL) {
180 return NT_STATUS_NO_MEMORY;
183 afsname = talloc_string_sub(mem_ctx,
184 lp_afs_username_map(),
185 "%D", name_domain);
186 afsname = talloc_string_sub(mem_ctx, afsname,
187 "%u", name_user);
188 afsname = talloc_string_sub(mem_ctx, afsname,
189 "%U", name_user);
192 DOM_SID user_sid;
193 fstring sidstr;
195 sid_copy(&user_sid, info3->base.domain_sid);
196 sid_append_rid(&user_sid, info3->base.rid);
197 sid_to_fstring(sidstr, &user_sid);
198 afsname = talloc_string_sub(mem_ctx, afsname,
199 "%s", sidstr);
202 if (afsname == NULL) {
203 return NT_STATUS_NO_MEMORY;
206 strlower_m(afsname);
208 DEBUG(10, ("Generating token for user %s\n", afsname));
210 cell = strchr(afsname, '@');
212 if (cell == NULL) {
213 return NT_STATUS_NO_MEMORY;
216 *cell = '\0';
217 cell += 1;
219 token = afs_createtoken_str(afsname, cell);
220 if (token == NULL) {
221 return NT_STATUS_OK;
223 state->response.extra_data.data = talloc_strdup(state->mem_ctx, token);
224 if (state->response.extra_data.data == NULL) {
225 return NT_STATUS_NO_MEMORY;
227 state->response.length +=
228 strlen((const char *)state->response.extra_data.data)+1;
230 return NT_STATUS_OK;
233 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
234 struct netr_SamInfo3 *info3,
235 const char *group_sid)
237 * Check whether a user belongs to a group or list of groups.
239 * @param mem_ctx talloc memory context.
240 * @param info3 user information, including group membership info.
241 * @param group_sid One or more groups , separated by commas.
243 * @return NT_STATUS_OK on success,
244 * NT_STATUS_LOGON_FAILURE if the user does not belong,
245 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
248 DOM_SID *require_membership_of_sid;
249 size_t num_require_membership_of_sid;
250 char *req_sid;
251 const char *p;
252 DOM_SID sid;
253 size_t i;
254 struct nt_user_token *token;
255 TALLOC_CTX *frame = NULL;
256 NTSTATUS status;
258 /* Parse the 'required group' SID */
260 if (!group_sid || !group_sid[0]) {
261 /* NO sid supplied, all users may access */
262 return NT_STATUS_OK;
265 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
266 DEBUG(0, ("talloc failed\n"));
267 return NT_STATUS_NO_MEMORY;
270 num_require_membership_of_sid = 0;
271 require_membership_of_sid = NULL;
273 p = group_sid;
275 frame = talloc_stackframe();
276 while (next_token_talloc(frame, &p, &req_sid, ",")) {
277 if (!string_to_sid(&sid, req_sid)) {
278 DEBUG(0, ("check_info3_in_group: could not parse %s "
279 "as a SID!", req_sid));
280 TALLOC_FREE(frame);
281 return NT_STATUS_INVALID_PARAMETER;
284 status = add_sid_to_array(mem_ctx, &sid,
285 &require_membership_of_sid,
286 &num_require_membership_of_sid);
287 if (!NT_STATUS_IS_OK(status)) {
288 DEBUG(0, ("add_sid_to_array failed\n"));
289 TALLOC_FREE(frame);
290 return status;
294 TALLOC_FREE(frame);
296 status = sid_array_from_info3(mem_ctx, info3,
297 &token->user_sids,
298 &token->num_sids,
299 true, false);
300 if (!NT_STATUS_IS_OK(status)) {
301 return status;
304 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
305 token))
306 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
307 token))) {
308 DEBUG(3, ("could not add aliases: %s\n",
309 nt_errstr(status)));
310 return status;
313 debug_nt_user_token(DBGC_CLASS, 10, token);
315 for (i=0; i<num_require_membership_of_sid; i++) {
316 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
317 &require_membership_of_sid[i])));
318 if (nt_token_check_sid(&require_membership_of_sid[i],
319 token)) {
320 DEBUG(10, ("Access ok\n"));
321 return NT_STATUS_OK;
325 /* Do not distinguish this error from a wrong username/pw */
327 return NT_STATUS_LOGON_FAILURE;
330 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
331 const char *domain_name)
333 struct winbindd_domain *domain;
335 if (IS_DC) {
336 domain = find_domain_from_name_noinit(domain_name);
337 if (domain == NULL) {
338 DEBUG(3, ("Authentication for domain [%s] refused "
339 "as it is not a trusted domain\n",
340 domain_name));
342 return domain;
345 if (is_myname(domain_name)) {
346 DEBUG(3, ("Authentication for domain %s (local domain "
347 "to this server) not supported at this "
348 "stage\n", domain_name));
349 return NULL;
352 /* we can auth against trusted domains */
353 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
354 domain = find_domain_from_name_noinit(domain_name);
355 if (domain == NULL) {
356 DEBUG(3, ("Authentication for domain [%s] skipped "
357 "as it is not a trusted domain\n",
358 domain_name));
359 } else {
360 return domain;
364 return find_our_domain();
367 static void fill_in_password_policy(struct winbindd_response *r,
368 const struct samr_DomInfo1 *p)
370 r->data.auth.policy.min_length_password =
371 p->min_password_length;
372 r->data.auth.policy.password_history =
373 p->password_history_length;
374 r->data.auth.policy.password_properties =
375 p->password_properties;
376 r->data.auth.policy.expire =
377 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
378 r->data.auth.policy.min_passwordage =
379 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
382 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
383 struct winbindd_cli_state *state)
385 struct winbindd_methods *methods;
386 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
387 struct samr_DomInfo1 password_policy;
389 if ( !winbindd_can_contact_domain( domain ) ) {
390 DEBUG(5,("fillup_password_policy: No inbound trust to "
391 "contact domain %s\n", domain->name));
392 return NT_STATUS_NOT_SUPPORTED;
395 methods = domain->methods;
397 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
398 if (NT_STATUS_IS_ERR(status)) {
399 return status;
402 fill_in_password_policy(&state->response, &password_policy);
404 return NT_STATUS_OK;
407 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
408 TALLOC_CTX *mem_ctx,
409 uint16 *lockout_threshold)
411 struct winbindd_methods *methods;
412 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
413 struct samr_DomInfo12 lockout_policy;
415 *lockout_threshold = 0;
417 methods = domain->methods;
419 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
420 if (NT_STATUS_IS_ERR(status)) {
421 return status;
424 *lockout_threshold = lockout_policy.lockout_threshold;
426 return NT_STATUS_OK;
429 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
430 TALLOC_CTX *mem_ctx,
431 uint32 *password_properties)
433 struct winbindd_methods *methods;
434 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
435 struct samr_DomInfo1 password_policy;
437 *password_properties = 0;
439 methods = domain->methods;
441 status = methods->password_policy(domain, mem_ctx, &password_policy);
442 if (NT_STATUS_IS_ERR(status)) {
443 return status;
446 *password_properties = password_policy.password_properties;
448 return NT_STATUS_OK;
451 #ifdef HAVE_KRB5
453 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
454 const char *type,
455 uid_t uid,
456 bool *internal_ccache)
458 /* accept FILE and WRFILE as krb5_cc_type from the client and then
459 * build the full ccname string based on the user's uid here -
460 * Guenther*/
462 const char *gen_cc = NULL;
464 *internal_ccache = true;
466 if (uid == -1) {
467 goto memory_ccache;
470 if (!type || type[0] == '\0') {
471 goto memory_ccache;
474 if (strequal(type, "FILE")) {
475 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
476 } else if (strequal(type, "WRFILE")) {
477 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
478 } else {
479 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
480 goto memory_ccache;
483 *internal_ccache = false;
484 goto done;
486 memory_ccache:
487 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
489 done:
490 if (gen_cc == NULL) {
491 DEBUG(0,("out of memory\n"));
492 return NULL;
495 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
497 return gen_cc;
500 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
502 const char *type = state->request.data.auth.krb5_cc_type;
504 state->response.data.auth.krb5ccname[0] = '\0';
506 if (type[0] == '\0') {
507 return;
510 if (!strequal(type, "FILE") &&
511 !strequal(type, "WRFILE")) {
512 DEBUG(10,("won't return krbccname for a %s type ccache\n",
513 type));
514 return;
517 fstrcpy(state->response.data.auth.krb5ccname, cc);
520 #endif
522 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
524 uid_t uid = -1;
526 uid = state->request.data.auth.uid;
528 if (uid < 0) {
529 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
530 return -1;
532 return uid;
535 /**********************************************************************
536 Authenticate a user with a clear text password using Kerberos and fill up
537 ccache if required
538 **********************************************************************/
540 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
541 struct winbindd_cli_state *state,
542 struct netr_SamInfo3 **info3)
544 #ifdef HAVE_KRB5
545 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
546 krb5_error_code krb5_ret;
547 const char *cc = NULL;
548 const char *principal_s = NULL;
549 const char *service = NULL;
550 char *realm = NULL;
551 fstring name_domain, name_user;
552 time_t ticket_lifetime = 0;
553 time_t renewal_until = 0;
554 uid_t uid = -1;
555 ADS_STRUCT *ads;
556 time_t time_offset = 0;
557 bool internal_ccache = true;
559 ZERO_STRUCTP(info3);
561 *info3 = NULL;
563 /* 1st step:
564 * prepare a krb5_cc_cache string for the user */
566 uid = get_uid_from_state(state);
567 if (uid == -1) {
568 DEBUG(0,("no valid uid\n"));
571 cc = generate_krb5_ccache(state->mem_ctx,
572 state->request.data.auth.krb5_cc_type,
573 state->request.data.auth.uid,
574 &internal_ccache);
575 if (cc == NULL) {
576 return NT_STATUS_NO_MEMORY;
580 /* 2nd step:
581 * get kerberos properties */
583 if (domain->private_data) {
584 ads = (ADS_STRUCT *)domain->private_data;
585 time_offset = ads->auth.time_offset;
589 /* 3rd step:
590 * do kerberos auth and setup ccache as the user */
592 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
594 realm = domain->alt_name;
595 strupper_m(realm);
597 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
598 if (principal_s == NULL) {
599 return NT_STATUS_NO_MEMORY;
602 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
603 if (service == NULL) {
604 return NT_STATUS_NO_MEMORY;
607 /* if this is a user ccache, we need to act as the user to let the krb5
608 * library handle the chown, etc. */
610 /************************ ENTERING NON-ROOT **********************/
612 if (!internal_ccache) {
613 set_effective_uid(uid);
614 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
617 result = kerberos_return_info3_from_pac(state->mem_ctx,
618 principal_s,
619 state->request.data.auth.pass,
620 time_offset,
621 &ticket_lifetime,
622 &renewal_until,
624 true,
625 true,
626 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
627 info3);
628 if (!internal_ccache) {
629 gain_root_privilege();
632 /************************ RETURNED TO ROOT **********************/
634 if (!NT_STATUS_IS_OK(result)) {
635 goto failed;
638 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
639 principal_s));
641 /* if we had a user's ccache then return that string for the pam
642 * environment */
644 if (!internal_ccache) {
646 setup_return_cc_name(state, cc);
648 result = add_ccache_to_list(principal_s,
650 service,
651 state->request.data.auth.user,
652 realm,
653 uid,
654 time(NULL),
655 ticket_lifetime,
656 renewal_until,
657 false);
659 if (!NT_STATUS_IS_OK(result)) {
660 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
661 nt_errstr(result)));
663 } else {
665 /* need to delete the memory cred cache, it is not used anymore */
667 krb5_ret = ads_kdestroy(cc);
668 if (krb5_ret) {
669 DEBUG(3,("winbindd_raw_kerberos_login: "
670 "could not destroy krb5 credential cache: "
671 "%s\n", error_message(krb5_ret)));
676 return NT_STATUS_OK;
678 failed:
680 /* we could have created a new credential cache with a valid tgt in it
681 * but we werent able to get or verify the service ticket for this
682 * local host and therefor didn't get the PAC, we need to remove that
683 * cache entirely now */
685 krb5_ret = ads_kdestroy(cc);
686 if (krb5_ret) {
687 DEBUG(3,("winbindd_raw_kerberos_login: "
688 "could not destroy krb5 credential cache: "
689 "%s\n", error_message(krb5_ret)));
692 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
693 DEBUG(3,("winbindd_raw_kerberos_login: "
694 "could not remove ccache for user %s\n",
695 state->request.data.auth.user));
698 return result;
699 #else
700 return NT_STATUS_NOT_SUPPORTED;
701 #endif /* HAVE_KRB5 */
704 /****************************************************************
705 ****************************************************************/
707 static bool check_request_flags(uint32_t flags)
709 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
710 WBFLAG_PAM_INFO3_TEXT |
711 WBFLAG_PAM_INFO3_NDR;
713 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
714 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
715 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
716 !(flags & flags_edata) ) {
717 return true;
720 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
722 return false;
725 /****************************************************************
726 ****************************************************************/
728 static NTSTATUS append_data(struct winbindd_cli_state *state,
729 struct netr_SamInfo3 *info3,
730 const char *name_domain,
731 const char *name_user)
733 NTSTATUS result;
734 uint32_t flags = state->request.flags;
736 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
737 memcpy(state->response.data.auth.user_session_key,
738 info3->base.key.key,
739 sizeof(state->response.data.auth.user_session_key)
740 /* 16 */);
743 if (flags & WBFLAG_PAM_LMKEY) {
744 memcpy(state->response.data.auth.first_8_lm_hash,
745 info3->base.LMSessKey.key,
746 sizeof(state->response.data.auth.first_8_lm_hash)
747 /* 8 */);
750 if (flags & WBFLAG_PAM_INFO3_TEXT) {
751 result = append_info3_as_txt(state->mem_ctx, state, info3);
752 if (!NT_STATUS_IS_OK(result)) {
753 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
754 nt_errstr(result)));
755 return result;
759 /* currently, anything from here on potentially overwrites extra_data. */
761 if (flags & WBFLAG_PAM_INFO3_NDR) {
762 result = append_info3_as_ndr(state->mem_ctx, state, info3);
763 if (!NT_STATUS_IS_OK(result)) {
764 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
765 nt_errstr(result)));
766 return result;
770 if (flags & WBFLAG_PAM_UNIX_NAME) {
771 result = append_unix_username(state->mem_ctx, state, info3,
772 name_domain, name_user);
773 if (!NT_STATUS_IS_OK(result)) {
774 DEBUG(10,("Failed to append Unix Username: %s\n",
775 nt_errstr(result)));
776 return result;
780 if (flags & WBFLAG_PAM_AFS_TOKEN) {
781 result = append_afs_token(state->mem_ctx, state, info3,
782 name_domain, name_user);
783 if (!NT_STATUS_IS_OK(result)) {
784 DEBUG(10,("Failed to append AFS token: %s\n",
785 nt_errstr(result)));
786 return result;
790 return NT_STATUS_OK;
793 void winbindd_pam_auth(struct winbindd_cli_state *state)
795 struct winbindd_domain *domain;
796 fstring name_domain, name_user;
797 char *mapped_user = NULL;
798 NTSTATUS result;
799 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
801 /* Ensure null termination */
802 state->request.data.auth.user
803 [sizeof(state->request.data.auth.user)-1]='\0';
805 /* Ensure null termination */
806 state->request.data.auth.pass
807 [sizeof(state->request.data.auth.pass)-1]='\0';
809 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
810 state->request.data.auth.user));
812 if (!check_request_flags(state->request.flags)) {
813 result = NT_STATUS_INVALID_PARAMETER_MIX;
814 goto done;
817 /* Parse domain and username */
819 name_map_status = normalize_name_unmap(state->mem_ctx,
820 state->request.data.auth.user,
821 &mapped_user);
823 /* If the name normalization didnt' actually do anything,
824 just use the original name */
826 if (!NT_STATUS_IS_OK(name_map_status) &&
827 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
829 mapped_user = state->request.data.auth.user;
832 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
833 result = NT_STATUS_NO_SUCH_USER;
834 goto done;
837 domain = find_auth_domain(state, name_domain);
839 if (domain == NULL) {
840 result = NT_STATUS_NO_SUCH_USER;
841 goto done;
844 sendto_domain(state, domain);
845 return;
846 done:
847 set_auth_errors(&state->response, result);
848 DEBUG(5, ("Plain text authentication for %s returned %s "
849 "(PAM: %d)\n",
850 state->request.data.auth.user,
851 state->response.data.auth.nt_status_string,
852 state->response.data.auth.pam_error));
853 request_error(state);
856 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
857 struct winbindd_cli_state *state,
858 struct netr_SamInfo3 **info3)
860 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
861 uint16 max_allowed_bad_attempts;
862 fstring name_domain, name_user;
863 DOM_SID sid;
864 enum lsa_SidType type;
865 uchar new_nt_pass[NT_HASH_LEN];
866 const uint8 *cached_nt_pass;
867 const uint8 *cached_salt;
868 struct netr_SamInfo3 *my_info3;
869 time_t kickoff_time, must_change_time;
870 bool password_good = false;
871 #ifdef HAVE_KRB5
872 struct winbindd_tdc_domain *tdc_domain = NULL;
873 #endif
875 *info3 = NULL;
877 ZERO_STRUCTP(info3);
879 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
881 /* Parse domain and username */
883 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
886 if (!lookup_cached_name(state->mem_ctx,
887 name_domain,
888 name_user,
889 &sid,
890 &type)) {
891 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
892 return NT_STATUS_NO_SUCH_USER;
895 if (type != SID_NAME_USER) {
896 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
897 return NT_STATUS_LOGON_FAILURE;
900 result = winbindd_get_creds(domain,
901 state->mem_ctx,
902 &sid,
903 &my_info3,
904 &cached_nt_pass,
905 &cached_salt);
906 if (!NT_STATUS_IS_OK(result)) {
907 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
908 return result;
911 *info3 = my_info3;
913 E_md4hash(state->request.data.auth.pass, new_nt_pass);
915 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
916 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
917 if (cached_salt) {
918 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
921 if (cached_salt) {
922 /* In this case we didn't store the nt_hash itself,
923 but the MD5 combination of salt + nt_hash. */
924 uchar salted_hash[NT_HASH_LEN];
925 E_md5hash(cached_salt, new_nt_pass, salted_hash);
927 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
928 true : false;
929 } else {
930 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
931 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
932 true : false;
935 if (password_good) {
937 /* User *DOES* know the password, update logon_time and reset
938 * bad_pw_count */
940 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
942 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
943 return NT_STATUS_ACCOUNT_LOCKED_OUT;
946 if (my_info3->base.acct_flags & ACB_DISABLED) {
947 return NT_STATUS_ACCOUNT_DISABLED;
950 if (my_info3->base.acct_flags & ACB_WSTRUST) {
951 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
954 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
955 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
958 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
959 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
962 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
963 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
964 my_info3->base.acct_flags));
965 return NT_STATUS_LOGON_FAILURE;
968 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
969 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
970 return NT_STATUS_ACCOUNT_EXPIRED;
973 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
974 if (must_change_time != 0 && must_change_time < time(NULL)) {
975 /* we allow grace logons when the password has expired */
976 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
977 /* return NT_STATUS_PASSWORD_EXPIRED; */
978 goto success;
981 #ifdef HAVE_KRB5
982 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
983 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
984 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
986 uid_t uid = -1;
987 const char *cc = NULL;
988 char *realm = NULL;
989 const char *principal_s = NULL;
990 const char *service = NULL;
991 bool internal_ccache = false;
993 uid = get_uid_from_state(state);
994 if (uid == -1) {
995 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
996 return NT_STATUS_INVALID_PARAMETER;
999 cc = generate_krb5_ccache(state->mem_ctx,
1000 state->request.data.auth.krb5_cc_type,
1001 state->request.data.auth.uid,
1002 &internal_ccache);
1003 if (cc == NULL) {
1004 return NT_STATUS_NO_MEMORY;
1007 realm = domain->alt_name;
1008 strupper_m(realm);
1010 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1011 if (principal_s == NULL) {
1012 return NT_STATUS_NO_MEMORY;
1015 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1016 if (service == NULL) {
1017 return NT_STATUS_NO_MEMORY;
1020 if (!internal_ccache) {
1022 setup_return_cc_name(state, cc);
1024 result = add_ccache_to_list(principal_s,
1026 service,
1027 state->request.data.auth.user,
1028 domain->alt_name,
1029 uid,
1030 time(NULL),
1031 time(NULL) + lp_winbind_cache_time(),
1032 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1033 true);
1035 if (!NT_STATUS_IS_OK(result)) {
1036 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1037 "to add ccache to list: %s\n",
1038 nt_errstr(result)));
1042 #endif /* HAVE_KRB5 */
1043 success:
1044 /* FIXME: we possibly should handle logon hours as well (does xp when
1045 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1047 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1048 my_info3->base.bad_password_count = 0;
1050 result = winbindd_update_creds_by_info3(domain,
1051 state->mem_ctx,
1052 state->request.data.auth.user,
1053 state->request.data.auth.pass,
1054 my_info3);
1055 if (!NT_STATUS_IS_OK(result)) {
1056 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1057 nt_errstr(result)));
1058 return result;
1061 return NT_STATUS_OK;
1065 /* User does *NOT* know the correct password, modify info3 accordingly */
1067 /* failure of this is not critical */
1068 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1069 if (!NT_STATUS_IS_OK(result)) {
1070 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1071 "Won't be able to honour account lockout policies\n"));
1074 /* increase counter */
1075 my_info3->base.bad_password_count++;
1077 if (max_allowed_bad_attempts == 0) {
1078 goto failed;
1081 /* lockout user */
1082 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1084 uint32 password_properties;
1086 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1087 if (!NT_STATUS_IS_OK(result)) {
1088 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1091 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1092 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1093 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1097 failed:
1098 result = winbindd_update_creds_by_info3(domain,
1099 state->mem_ctx,
1100 state->request.data.auth.user,
1101 NULL,
1102 my_info3);
1104 if (!NT_STATUS_IS_OK(result)) {
1105 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1106 nt_errstr(result)));
1109 return NT_STATUS_LOGON_FAILURE;
1112 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1113 struct winbindd_cli_state *state,
1114 struct netr_SamInfo3 **info3)
1116 struct winbindd_domain *contact_domain;
1117 fstring name_domain, name_user;
1118 NTSTATUS result;
1120 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1122 /* Parse domain and username */
1124 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1126 /* what domain should we contact? */
1128 if ( IS_DC ) {
1129 if (!(contact_domain = find_domain_from_name(name_domain))) {
1130 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1131 state->request.data.auth.user, name_domain, name_user, name_domain));
1132 result = NT_STATUS_NO_SUCH_USER;
1133 goto done;
1136 } else {
1137 if (is_myname(name_domain)) {
1138 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1139 result = NT_STATUS_NO_SUCH_USER;
1140 goto done;
1143 contact_domain = find_domain_from_name(name_domain);
1144 if (contact_domain == NULL) {
1145 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1146 state->request.data.auth.user, name_domain, name_user, name_domain));
1148 contact_domain = find_our_domain();
1152 if (contact_domain->initialized &&
1153 contact_domain->active_directory) {
1154 goto try_login;
1157 if (!contact_domain->initialized) {
1158 init_dc_connection(contact_domain);
1161 if (!contact_domain->active_directory) {
1162 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1163 return NT_STATUS_INVALID_LOGON_TYPE;
1165 try_login:
1166 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1167 done:
1168 return result;
1171 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1172 TALLOC_CTX *mem_ctx,
1173 uint32 logon_parameters,
1174 const char *server,
1175 const char *username,
1176 const char *domain,
1177 const char *workstation,
1178 const uint8 chal[8],
1179 DATA_BLOB lm_response,
1180 DATA_BLOB nt_response,
1181 struct netr_SamInfo3 **info3);
1183 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1184 struct winbindd_cli_state *state,
1185 struct netr_SamInfo3 **info3)
1188 struct rpc_pipe_client *netlogon_pipe;
1189 uchar chal[8];
1190 DATA_BLOB lm_resp;
1191 DATA_BLOB nt_resp;
1192 int attempts = 0;
1193 unsigned char local_lm_response[24];
1194 unsigned char local_nt_response[24];
1195 struct winbindd_domain *contact_domain;
1196 fstring name_domain, name_user;
1197 bool retry;
1198 NTSTATUS result;
1199 struct netr_SamInfo3 *my_info3 = NULL;
1201 *info3 = NULL;
1203 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1205 /* Parse domain and username */
1207 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1209 /* do password magic */
1212 generate_random_buffer(chal, 8);
1213 if (lp_client_ntlmv2_auth()) {
1214 DATA_BLOB server_chal;
1215 DATA_BLOB names_blob;
1216 DATA_BLOB nt_response;
1217 DATA_BLOB lm_response;
1218 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1220 /* note that the 'workgroup' here is a best guess - we don't know
1221 the server's domain at this point. The 'server name' is also
1222 dodgy...
1224 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1226 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1227 state->request.data.auth.pass,
1228 &server_chal,
1229 &names_blob,
1230 &lm_response, &nt_response, NULL, NULL)) {
1231 data_blob_free(&names_blob);
1232 data_blob_free(&server_chal);
1233 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1234 result = NT_STATUS_NO_MEMORY;
1235 goto done;
1237 data_blob_free(&names_blob);
1238 data_blob_free(&server_chal);
1239 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1240 lm_response.length);
1241 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1242 nt_response.length);
1243 data_blob_free(&lm_response);
1244 data_blob_free(&nt_response);
1246 } else {
1247 if (lp_client_lanman_auth()
1248 && SMBencrypt(state->request.data.auth.pass,
1249 chal,
1250 local_lm_response)) {
1251 lm_resp = data_blob_talloc(state->mem_ctx,
1252 local_lm_response,
1253 sizeof(local_lm_response));
1254 } else {
1255 lm_resp = data_blob_null;
1257 SMBNTencrypt(state->request.data.auth.pass,
1258 chal,
1259 local_nt_response);
1261 nt_resp = data_blob_talloc(state->mem_ctx,
1262 local_nt_response,
1263 sizeof(local_nt_response));
1266 /* what domain should we contact? */
1268 if ( IS_DC ) {
1269 if (!(contact_domain = find_domain_from_name(name_domain))) {
1270 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1271 state->request.data.auth.user, name_domain, name_user, name_domain));
1272 result = NT_STATUS_NO_SUCH_USER;
1273 goto done;
1276 } else {
1277 if (is_myname(name_domain)) {
1278 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1279 result = NT_STATUS_NO_SUCH_USER;
1280 goto done;
1283 contact_domain = find_our_domain();
1286 /* check authentication loop */
1288 do {
1289 netlogon_fn_t logon_fn;
1291 ZERO_STRUCTP(my_info3);
1292 retry = false;
1294 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1296 if (!NT_STATUS_IS_OK(result)) {
1297 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1298 goto done;
1301 /* It is really important to try SamLogonEx here,
1302 * because in a clustered environment, we want to use
1303 * one machine account from multiple physical
1304 * computers.
1306 * With a normal SamLogon call, we must keep the
1307 * credentials chain updated and intact between all
1308 * users of the machine account (which would imply
1309 * cross-node communication for every NTLM logon).
1311 * (The credentials chain is not per NETLOGON pipe
1312 * connection, but globally on the server/client pair
1313 * by machine name).
1315 * When using SamLogonEx, the credentials are not
1316 * supplied, but the session key is implied by the
1317 * wrapping SamLogon context.
1319 * -- abartlet 21 April 2008
1322 logon_fn = contact_domain->can_do_samlogon_ex
1323 ? rpccli_netlogon_sam_network_logon_ex
1324 : rpccli_netlogon_sam_network_logon;
1326 result = logon_fn(netlogon_pipe,
1327 state->mem_ctx,
1329 contact_domain->dcname, /* server name */
1330 name_user, /* user name */
1331 name_domain, /* target domain */
1332 global_myname(), /* workstation */
1333 chal,
1334 lm_resp,
1335 nt_resp,
1336 &my_info3);
1337 attempts += 1;
1339 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1340 && contact_domain->can_do_samlogon_ex) {
1341 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1342 "retrying with NetSamLogon\n"));
1343 contact_domain->can_do_samlogon_ex = false;
1344 retry = true;
1345 continue;
1348 /* We have to try a second time as cm_connect_netlogon
1349 might not yet have noticed that the DC has killed
1350 our connection. */
1352 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1353 retry = true;
1354 continue;
1357 /* if we get access denied, a possible cause was that we had
1358 and open connection to the DC, but someone changed our
1359 machine account password out from underneath us using 'net
1360 rpc changetrustpw' */
1362 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1363 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1364 "ACCESS_DENIED. Maybe the trust account "
1365 "password was changed and we didn't know it. "
1366 "Killing connections to domain %s\n",
1367 name_domain));
1368 invalidate_cm_connection(&contact_domain->conn);
1369 retry = true;
1372 } while ( (attempts < 2) && retry );
1374 /* handle the case where a NT4 DC does not fill in the acct_flags in
1375 * the samlogon reply info3. When accurate info3 is required by the
1376 * caller, we look up the account flags ourselve - gd */
1378 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1379 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1381 struct rpc_pipe_client *samr_pipe;
1382 struct policy_handle samr_domain_handle, user_pol;
1383 union samr_UserInfo *info = NULL;
1384 NTSTATUS status_tmp;
1385 uint32 acct_flags;
1387 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1388 &samr_pipe, &samr_domain_handle);
1390 if (!NT_STATUS_IS_OK(status_tmp)) {
1391 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1392 nt_errstr(status_tmp)));
1393 goto done;
1396 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1397 &samr_domain_handle,
1398 MAXIMUM_ALLOWED_ACCESS,
1399 my_info3->base.rid,
1400 &user_pol);
1402 if (!NT_STATUS_IS_OK(status_tmp)) {
1403 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1404 nt_errstr(status_tmp)));
1405 goto done;
1408 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1409 &user_pol,
1411 &info);
1413 if (!NT_STATUS_IS_OK(status_tmp)) {
1414 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1415 nt_errstr(status_tmp)));
1416 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1417 goto done;
1420 acct_flags = info->info16.acct_flags;
1422 if (acct_flags == 0) {
1423 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1424 goto done;
1427 my_info3->base.acct_flags = acct_flags;
1429 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1431 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1434 *info3 = my_info3;
1435 done:
1436 return result;
1439 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1440 struct winbindd_cli_state *state)
1442 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1443 NTSTATUS krb5_result = NT_STATUS_OK;
1444 fstring name_domain, name_user;
1445 char *mapped_user;
1446 fstring domain_user;
1447 struct netr_SamInfo3 *info3 = NULL;
1448 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1450 /* Ensure null termination */
1451 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1453 /* Ensure null termination */
1454 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1456 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1457 state->request.data.auth.user));
1459 if (!check_request_flags(state->request.flags)) {
1460 result = NT_STATUS_INVALID_PARAMETER_MIX;
1461 goto done;
1464 /* Parse domain and username */
1466 name_map_status = normalize_name_unmap(state->mem_ctx,
1467 state->request.data.auth.user,
1468 &mapped_user);
1470 /* If the name normalization didnt' actually do anything,
1471 just use the original name */
1473 if (!NT_STATUS_IS_OK(name_map_status) &&
1474 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1476 mapped_user = state->request.data.auth.user;
1479 parse_domain_user(mapped_user, name_domain, name_user);
1481 if ( mapped_user != state->request.data.auth.user ) {
1482 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1483 safe_strcpy( state->request.data.auth.user, domain_user,
1484 sizeof(state->request.data.auth.user)-1 );
1487 if (domain->online == false) {
1488 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1489 if (domain->startup) {
1490 /* Logons are very important to users. If we're offline and
1491 we get a request within the first 30 seconds of startup,
1492 try very hard to find a DC and go online. */
1494 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1495 "request in startup mode.\n", domain->name ));
1497 winbindd_flush_negative_conn_cache(domain);
1498 result = init_dc_connection(domain);
1502 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1504 /* Check for Kerberos authentication */
1505 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1507 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1508 /* save for later */
1509 krb5_result = result;
1512 if (NT_STATUS_IS_OK(result)) {
1513 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1514 goto process_result;
1515 } else {
1516 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1519 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1520 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1522 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1523 set_domain_offline( domain );
1524 goto cached_logon;
1527 /* there are quite some NT_STATUS errors where there is no
1528 * point in retrying with a samlogon, we explictly have to take
1529 * care not to increase the bad logon counter on the DC */
1531 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1532 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1533 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1534 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1535 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1536 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1541 goto process_result;
1544 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1545 DEBUG(3,("falling back to samlogon\n"));
1546 goto sam_logon;
1547 } else {
1548 goto cached_logon;
1552 sam_logon:
1553 /* Check for Samlogon authentication */
1554 if (domain->online) {
1555 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1557 if (NT_STATUS_IS_OK(result)) {
1558 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1559 /* add the Krb5 err if we have one */
1560 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1561 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1563 goto process_result;
1566 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1567 nt_errstr(result)));
1569 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1570 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1571 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1573 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1574 set_domain_offline( domain );
1575 goto cached_logon;
1578 if (domain->online) {
1579 /* We're still online - fail. */
1580 goto done;
1584 cached_logon:
1585 /* Check for Cached logons */
1586 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1587 lp_winbind_offline_logon()) {
1589 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1591 if (NT_STATUS_IS_OK(result)) {
1592 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1593 goto process_result;
1594 } else {
1595 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1596 goto done;
1600 process_result:
1602 if (NT_STATUS_IS_OK(result)) {
1604 DOM_SID user_sid;
1606 /* In all codepaths where result == NT_STATUS_OK info3 must have
1607 been initialized. */
1608 if (!info3) {
1609 result = NT_STATUS_INTERNAL_ERROR;
1610 goto done;
1613 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1614 netsamlogon_cache_store(name_user, info3);
1616 /* save name_to_sid info as early as possible (only if
1617 this is our primary domain so we don't invalidate
1618 the cache entry by storing the seq_num for the wrong
1619 domain). */
1620 if ( domain->primary ) {
1621 sid_compose(&user_sid, info3->base.domain_sid,
1622 info3->base.rid);
1623 cache_name2sid(domain, name_domain, name_user,
1624 SID_NAME_USER, &user_sid);
1627 /* Check if the user is in the right group */
1629 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1630 state->request.data.auth.require_membership_of_sid))) {
1631 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1632 state->request.data.auth.user,
1633 state->request.data.auth.require_membership_of_sid));
1634 goto done;
1637 result = append_data(state, info3, name_domain, name_user);
1638 if (!NT_STATUS_IS_OK(result)) {
1639 goto done;
1642 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1644 /* Store in-memory creds for single-signon using ntlm_auth. */
1645 result = winbindd_add_memory_creds(state->request.data.auth.user,
1646 get_uid_from_state(state),
1647 state->request.data.auth.pass);
1649 if (!NT_STATUS_IS_OK(result)) {
1650 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1651 goto done;
1654 if (lp_winbind_offline_logon()) {
1655 result = winbindd_store_creds(domain,
1656 state->mem_ctx,
1657 state->request.data.auth.user,
1658 state->request.data.auth.pass,
1659 info3, NULL);
1660 if (!NT_STATUS_IS_OK(result)) {
1662 /* Release refcount. */
1663 winbindd_delete_memory_creds(state->request.data.auth.user);
1665 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1666 goto done;
1672 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1673 struct winbindd_domain *our_domain = find_our_domain();
1675 /* This is not entirely correct I believe, but it is
1676 consistent. Only apply the password policy settings
1677 too warn users for our own domain. Cannot obtain these
1678 from trusted DCs all the time so don't do it at all.
1679 -- jerry */
1681 result = NT_STATUS_NOT_SUPPORTED;
1682 if (our_domain == domain ) {
1683 result = fillup_password_policy(our_domain, state);
1686 if (!NT_STATUS_IS_OK(result)
1687 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1689 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1690 domain->name, nt_errstr(result)));
1691 goto done;
1695 result = NT_STATUS_OK;
1698 done:
1699 /* give us a more useful (more correct?) error code */
1700 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1701 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1702 result = NT_STATUS_NO_LOGON_SERVERS;
1705 set_auth_errors(&state->response, result);
1707 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1708 state->request.data.auth.user,
1709 state->response.data.auth.nt_status_string,
1710 state->response.data.auth.pam_error));
1712 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1716 /**********************************************************************
1717 Challenge Response Authentication Protocol
1718 **********************************************************************/
1720 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1722 struct winbindd_domain *domain = NULL;
1723 const char *domain_name = NULL;
1724 NTSTATUS result;
1726 if (!check_request_flags(state->request.flags)) {
1727 result = NT_STATUS_INVALID_PARAMETER_MIX;
1728 goto done;
1731 if (!state->privileged) {
1732 char *error_string = NULL;
1733 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1734 "denied. !\n"));
1735 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1736 "on %s are set correctly.\n",
1737 get_winbind_priv_pipe_dir()));
1738 /* send a better message than ACCESS_DENIED */
1739 error_string = talloc_asprintf(state->mem_ctx,
1740 "winbind client not authorized "
1741 "to use winbindd_pam_auth_crap."
1742 " Ensure permissions on %s "
1743 "are set correctly.",
1744 get_winbind_priv_pipe_dir());
1745 fstrcpy(state->response.data.auth.error_string, error_string);
1746 result = NT_STATUS_ACCESS_DENIED;
1747 goto done;
1750 /* Ensure null termination */
1751 state->request.data.auth_crap.user
1752 [sizeof(state->request.data.auth_crap.user)-1]=0;
1753 state->request.data.auth_crap.domain
1754 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1756 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1757 (unsigned long)state->pid,
1758 state->request.data.auth_crap.domain,
1759 state->request.data.auth_crap.user));
1761 if (*state->request.data.auth_crap.domain != '\0') {
1762 domain_name = state->request.data.auth_crap.domain;
1763 } else if (lp_winbind_use_default_domain()) {
1764 domain_name = lp_workgroup();
1767 if (domain_name != NULL)
1768 domain = find_auth_domain(state, domain_name);
1770 if (domain != NULL) {
1771 sendto_domain(state, domain);
1772 return;
1775 result = NT_STATUS_NO_SUCH_USER;
1777 done:
1778 set_auth_errors(&state->response, result);
1779 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1780 state->request.data.auth_crap.domain,
1781 state->request.data.auth_crap.user,
1782 state->response.data.auth.nt_status_string,
1783 state->response.data.auth.pam_error));
1784 request_error(state);
1785 return;
1789 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1790 struct winbindd_cli_state *state)
1792 NTSTATUS result;
1793 struct netr_SamInfo3 *info3 = NULL;
1794 struct rpc_pipe_client *netlogon_pipe;
1795 const char *name_user = NULL;
1796 const char *name_domain = NULL;
1797 const char *workstation;
1798 struct winbindd_domain *contact_domain;
1799 int attempts = 0;
1800 bool retry;
1802 DATA_BLOB lm_resp, nt_resp;
1804 /* This is child-only, so no check for privileged access is needed
1805 anymore */
1807 /* Ensure null termination */
1808 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1809 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1811 if (!check_request_flags(state->request.flags)) {
1812 result = NT_STATUS_INVALID_PARAMETER_MIX;
1813 goto done;
1816 name_user = state->request.data.auth_crap.user;
1818 if (*state->request.data.auth_crap.domain) {
1819 name_domain = state->request.data.auth_crap.domain;
1820 } else if (lp_winbind_use_default_domain()) {
1821 name_domain = lp_workgroup();
1822 } else {
1823 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1824 name_user));
1825 result = NT_STATUS_NO_SUCH_USER;
1826 goto done;
1829 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1830 name_domain, name_user));
1832 if (*state->request.data.auth_crap.workstation) {
1833 workstation = state->request.data.auth_crap.workstation;
1834 } else {
1835 workstation = global_myname();
1838 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1839 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1840 if (!(state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1841 state->request.extra_len != state->request.data.auth_crap.nt_resp_len) {
1842 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1843 state->request.data.auth_crap.lm_resp_len,
1844 state->request.data.auth_crap.nt_resp_len));
1845 result = NT_STATUS_INVALID_PARAMETER;
1846 goto done;
1850 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1851 state->request.data.auth_crap.lm_resp_len);
1853 if (state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) {
1854 nt_resp = data_blob_talloc(state->mem_ctx,
1855 state->request.extra_data.data,
1856 state->request.data.auth_crap.nt_resp_len);
1857 } else {
1858 nt_resp = data_blob_talloc(state->mem_ctx,
1859 state->request.data.auth_crap.nt_resp,
1860 state->request.data.auth_crap.nt_resp_len);
1863 /* what domain should we contact? */
1865 if ( IS_DC ) {
1866 if (!(contact_domain = find_domain_from_name(name_domain))) {
1867 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1868 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1869 result = NT_STATUS_NO_SUCH_USER;
1870 goto done;
1872 } else {
1873 if (is_myname(name_domain)) {
1874 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1875 result = NT_STATUS_NO_SUCH_USER;
1876 goto done;
1878 contact_domain = find_our_domain();
1881 do {
1882 netlogon_fn_t logon_fn;
1884 retry = false;
1886 netlogon_pipe = NULL;
1887 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1889 if (!NT_STATUS_IS_OK(result)) {
1890 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1891 nt_errstr(result)));
1892 goto done;
1895 logon_fn = contact_domain->can_do_samlogon_ex
1896 ? rpccli_netlogon_sam_network_logon_ex
1897 : rpccli_netlogon_sam_network_logon;
1899 result = logon_fn(netlogon_pipe,
1900 state->mem_ctx,
1901 state->request.data.auth_crap.logon_parameters,
1902 contact_domain->dcname,
1903 name_user,
1904 name_domain,
1905 /* Bug #3248 - found by Stefan Burkei. */
1906 workstation, /* We carefully set this above so use it... */
1907 state->request.data.auth_crap.chal,
1908 lm_resp,
1909 nt_resp,
1910 &info3);
1912 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1913 && contact_domain->can_do_samlogon_ex) {
1914 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1915 "retrying with NetSamLogon\n"));
1916 contact_domain->can_do_samlogon_ex = false;
1917 retry = true;
1918 continue;
1921 attempts += 1;
1923 /* We have to try a second time as cm_connect_netlogon
1924 might not yet have noticed that the DC has killed
1925 our connection. */
1927 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1928 retry = true;
1929 continue;
1932 /* if we get access denied, a possible cause was that we had and open
1933 connection to the DC, but someone changed our machine account password
1934 out from underneath us using 'net rpc changetrustpw' */
1936 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1937 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1938 "ACCESS_DENIED. Maybe the trust account "
1939 "password was changed and we didn't know it. "
1940 "Killing connections to domain %s\n",
1941 name_domain));
1942 invalidate_cm_connection(&contact_domain->conn);
1943 retry = true;
1946 } while ( (attempts < 2) && retry );
1948 if (NT_STATUS_IS_OK(result)) {
1950 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1951 netsamlogon_cache_store(name_user, info3);
1953 /* Check if the user is in the right group */
1955 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1956 state->request.data.auth_crap.require_membership_of_sid))) {
1957 DEBUG(3, ("User %s is not in the required group (%s), so "
1958 "crap authentication is rejected\n",
1959 state->request.data.auth_crap.user,
1960 state->request.data.auth_crap.require_membership_of_sid));
1961 goto done;
1964 result = append_data(state, info3, name_domain, name_user);
1965 if (!NT_STATUS_IS_OK(result)) {
1966 goto done;
1970 done:
1972 /* give us a more useful (more correct?) error code */
1973 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1974 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1975 result = NT_STATUS_NO_LOGON_SERVERS;
1978 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1979 result = nt_status_squash(result);
1982 set_auth_errors(&state->response, result);
1984 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1985 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1986 name_domain,
1987 name_user,
1988 state->response.data.auth.nt_status_string,
1989 state->response.data.auth.pam_error));
1991 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1994 /* Change a user password */
1996 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1998 fstring domain, user;
1999 char *mapped_user;
2000 struct winbindd_domain *contact_domain;
2001 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2003 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2004 state->request.data.chauthtok.user));
2006 /* Setup crap */
2008 nt_status = normalize_name_unmap(state->mem_ctx,
2009 state->request.data.chauthtok.user,
2010 &mapped_user);
2012 /* Update the chauthtok name if we did any mapping */
2014 if (NT_STATUS_IS_OK(nt_status) ||
2015 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2017 fstrcpy(state->request.data.chauthtok.user, mapped_user);
2020 /* Must pass in state->...chauthtok.user because
2021 canonicalize_username() assumes an fstring(). Since
2022 we have already copied it (if necessary), this is ok. */
2024 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2025 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2026 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2027 "(PAM: %d)\n",
2028 state->request.data.auth.user,
2029 state->response.data.auth.nt_status_string,
2030 state->response.data.auth.pam_error));
2031 request_error(state);
2032 return;
2035 contact_domain = find_domain_from_name(domain);
2036 if (!contact_domain) {
2037 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2038 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2039 state->request.data.chauthtok.user, domain, user, domain));
2040 request_error(state);
2041 return;
2044 sendto_domain(state, contact_domain);
2047 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2048 struct winbindd_cli_state *state)
2050 char *oldpass;
2051 char *newpass = NULL;
2052 struct policy_handle dom_pol;
2053 struct rpc_pipe_client *cli;
2054 bool got_info = false;
2055 struct samr_DomInfo1 *info = NULL;
2056 struct samr_ChangeReject *reject = NULL;
2057 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2058 fstring domain, user;
2060 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2061 state->request.data.auth.user));
2063 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2064 goto done;
2067 /* Change password */
2069 oldpass = state->request.data.chauthtok.oldpass;
2070 newpass = state->request.data.chauthtok.newpass;
2072 /* Initialize reject reason */
2073 state->response.data.auth.reject_reason = Undefined;
2075 /* Get sam handle */
2077 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2078 &dom_pol);
2079 if (!NT_STATUS_IS_OK(result)) {
2080 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2081 goto done;
2084 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2085 user,
2086 newpass,
2087 oldpass,
2088 &info,
2089 &reject);
2091 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2093 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2095 fill_in_password_policy(&state->response, info);
2097 state->response.data.auth.reject_reason =
2098 reject->reason;
2100 got_info = true;
2103 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2104 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2105 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2106 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2108 /* only fallback when the chgpasswd_user3 call is not supported */
2109 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2110 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2111 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2112 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2114 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2115 nt_errstr(result)));
2117 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2119 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2120 Map to the same status code as Windows 2003. */
2122 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2123 result = NT_STATUS_PASSWORD_RESTRICTION;
2127 done:
2129 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2131 /* Update the single sign-on memory creds. */
2132 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2133 newpass);
2135 /* When we login from gdm or xdm and password expires,
2136 * we change password, but there are no memory crendentials
2137 * So, winbindd_replace_memory_creds() returns
2138 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2139 * --- BoYang
2140 * */
2141 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2142 result = NT_STATUS_OK;
2145 if (!NT_STATUS_IS_OK(result)) {
2146 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2147 goto process_result;
2150 if (lp_winbind_offline_logon()) {
2151 result = winbindd_update_creds_by_name(contact_domain,
2152 state->mem_ctx, user,
2153 newpass);
2154 /* Again, this happens when we login from gdm or xdm
2155 * and the password expires, *BUT* cached crendentials
2156 * doesn't exist. winbindd_update_creds_by_name()
2157 * returns NT_STATUS_NO_SUCH_USER.
2158 * This is not a failure.
2159 * --- BoYang
2160 * */
2161 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2162 result = NT_STATUS_OK;
2165 if (!NT_STATUS_IS_OK(result)) {
2166 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2167 goto process_result;
2172 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2174 NTSTATUS policy_ret;
2176 policy_ret = fillup_password_policy(contact_domain, state);
2178 /* failure of this is non critical, it will just provide no
2179 * additional information to the client why the change has
2180 * failed - Guenther */
2182 if (!NT_STATUS_IS_OK(policy_ret)) {
2183 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2184 goto process_result;
2188 process_result:
2190 set_auth_errors(&state->response, result);
2192 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2193 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2194 domain,
2195 user,
2196 state->response.data.auth.nt_status_string,
2197 state->response.data.auth.pam_error));
2199 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2202 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2204 struct winbindd_domain *domain;
2205 fstring name_domain, user;
2206 uid_t caller_uid = (uid_t)-1;
2207 uid_t request_uid = state->request.data.logoff.uid;
2209 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2210 state->request.data.logoff.user));
2212 /* Ensure null termination */
2213 state->request.data.logoff.user
2214 [sizeof(state->request.data.logoff.user)-1]='\0';
2216 state->request.data.logoff.krb5ccname
2217 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2219 if (request_uid == (gid_t)-1) {
2220 goto failed;
2223 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2224 goto failed;
2227 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2228 goto failed;
2231 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2232 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2233 strerror(errno)));
2234 goto failed;
2237 switch (caller_uid) {
2238 case -1:
2239 goto failed;
2240 case 0:
2241 /* root must be able to logoff any user - gd */
2242 state->request.data.logoff.uid = request_uid;
2243 break;
2244 default:
2245 if (caller_uid != request_uid) {
2246 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2247 goto failed;
2249 state->request.data.logoff.uid = caller_uid;
2250 break;
2253 sendto_domain(state, domain);
2254 return;
2256 failed:
2257 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2258 DEBUG(5, ("Pam Logoff for %s returned %s "
2259 "(PAM: %d)\n",
2260 state->request.data.logoff.user,
2261 state->response.data.auth.nt_status_string,
2262 state->response.data.auth.pam_error));
2263 request_error(state);
2264 return;
2267 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2268 struct winbindd_cli_state *state)
2270 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2272 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2273 state->request.data.logoff.user));
2275 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2276 result = NT_STATUS_OK;
2277 goto process_result;
2280 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2281 result = NT_STATUS_OK;
2282 goto process_result;
2285 #ifdef HAVE_KRB5
2287 if (state->request.data.logoff.uid < 0) {
2288 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2289 goto process_result;
2292 /* what we need here is to find the corresponding krb5 ccache name *we*
2293 * created for a given username and destroy it */
2295 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2296 result = NT_STATUS_OK;
2297 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2298 goto process_result;
2301 if (!ccache_entry_identical(state->request.data.logoff.user,
2302 state->request.data.logoff.uid,
2303 state->request.data.logoff.krb5ccname)) {
2304 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2305 goto process_result;
2308 result = remove_ccache(state->request.data.logoff.user);
2309 if (!NT_STATUS_IS_OK(result)) {
2310 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2311 nt_errstr(result)));
2312 goto process_result;
2315 #else
2316 result = NT_STATUS_NOT_SUPPORTED;
2317 #endif
2319 process_result:
2321 winbindd_delete_memory_creds(state->request.data.logoff.user);
2323 set_auth_errors(&state->response, result);
2325 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2328 /* Change user password with auth crap*/
2330 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2332 struct winbindd_domain *domain = NULL;
2333 const char *domain_name = NULL;
2335 /* Ensure null termination */
2336 state->request.data.chng_pswd_auth_crap.user[
2337 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2338 state->request.data.chng_pswd_auth_crap.domain[
2339 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2341 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2342 (unsigned long)state->pid,
2343 state->request.data.chng_pswd_auth_crap.domain,
2344 state->request.data.chng_pswd_auth_crap.user));
2346 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2347 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2348 } else if (lp_winbind_use_default_domain()) {
2349 domain_name = lp_workgroup();
2352 if (domain_name != NULL)
2353 domain = find_domain_from_name(domain_name);
2355 if (domain != NULL) {
2356 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2357 "%s\n", (unsigned long)state->pid,domain->name));
2358 sendto_domain(state, domain);
2359 return;
2362 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2363 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2364 state->request.data.chng_pswd_auth_crap.domain,
2365 state->request.data.chng_pswd_auth_crap.user,
2366 state->response.data.auth.nt_status_string,
2367 state->response.data.auth.pam_error));
2368 request_error(state);
2369 return;
2372 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2374 NTSTATUS result;
2375 DATA_BLOB new_nt_password;
2376 DATA_BLOB old_nt_hash_enc;
2377 DATA_BLOB new_lm_password;
2378 DATA_BLOB old_lm_hash_enc;
2379 fstring domain,user;
2380 struct policy_handle dom_pol;
2381 struct winbindd_domain *contact_domain = domainSt;
2382 struct rpc_pipe_client *cli;
2384 /* Ensure null termination */
2385 state->request.data.chng_pswd_auth_crap.user[
2386 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2387 state->request.data.chng_pswd_auth_crap.domain[
2388 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2389 *domain = 0;
2390 *user = 0;
2392 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2393 (unsigned long)state->pid,
2394 state->request.data.chng_pswd_auth_crap.domain,
2395 state->request.data.chng_pswd_auth_crap.user));
2397 if (lp_winbind_offline_logon()) {
2398 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2399 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2400 result = NT_STATUS_ACCESS_DENIED;
2401 goto done;
2404 if (*state->request.data.chng_pswd_auth_crap.domain) {
2405 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2406 } else {
2407 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2408 domain, user);
2410 if(!*domain) {
2411 DEBUG(3,("no domain specified with username (%s) - "
2412 "failing auth\n",
2413 state->request.data.chng_pswd_auth_crap.user));
2414 result = NT_STATUS_NO_SUCH_USER;
2415 goto done;
2419 if (!*domain && lp_winbind_use_default_domain()) {
2420 fstrcpy(domain,(char *)lp_workgroup());
2423 if(!*user) {
2424 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2427 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2428 (unsigned long)state->pid, domain, user));
2430 /* Change password */
2431 new_nt_password = data_blob_talloc(
2432 state->mem_ctx,
2433 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2434 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2436 old_nt_hash_enc = data_blob_talloc(
2437 state->mem_ctx,
2438 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2439 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2441 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2442 new_lm_password = data_blob_talloc(
2443 state->mem_ctx,
2444 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2445 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2447 old_lm_hash_enc = data_blob_talloc(
2448 state->mem_ctx,
2449 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2450 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2451 } else {
2452 new_lm_password.length = 0;
2453 old_lm_hash_enc.length = 0;
2456 /* Get sam handle */
2458 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2459 if (!NT_STATUS_IS_OK(result)) {
2460 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2461 goto done;
2464 result = rpccli_samr_chng_pswd_auth_crap(
2465 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2466 new_lm_password, old_lm_hash_enc);
2468 done:
2470 set_auth_errors(&state->response, result);
2472 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2473 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2474 domain, user,
2475 state->response.data.auth.nt_status_string,
2476 state->response.data.auth.pam_error));
2478 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;