WHATSNEW: add more items.
[Samba.git] / source / winbindd / winbindd_pam.c
blobb37851c8ecabd3bf6fb9c0481323934dca99c7ca
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
32 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
33 struct winbindd_cli_state *state,
34 struct netr_SamInfo3 *info3)
36 char *ex;
37 size_t size;
38 uint32_t i;
40 state->response.data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response.data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response.data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response.data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response.data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response.data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response.data.auth.info3.logon_count = info3->base.logon_count;
54 state->response.data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response.data.auth.info3.user_rid = info3->base.rid;
57 state->response.data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response.data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response.data.auth.info3.num_groups = info3->base.groups.count;
61 state->response.data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response.data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response.data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response.data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response.data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response.data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response.data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response.data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response.data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response.data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response.data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
95 char *sid;
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
101 sid,
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 talloc_free(sid);
108 size = talloc_get_size(ex);
110 SAFE_FREE(state->response.extra_data.data);
111 state->response.extra_data.data = SMB_MALLOC(size);
112 if (!state->response.extra_data.data) {
113 return NT_STATUS_NO_MEMORY;
115 memcpy(state->response.extra_data.data, ex, size);
116 talloc_free(ex);
118 state->response.length += size;
120 return NT_STATUS_OK;
123 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
124 struct winbindd_cli_state *state,
125 struct netr_SamInfo3 *info3)
127 DATA_BLOB blob;
128 enum ndr_err_code ndr_err;
130 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
131 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
132 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
134 return ndr_map_error2ntstatus(ndr_err);
137 SAFE_FREE(state->response.extra_data.data);
138 state->response.extra_data.data = SMB_MALLOC(blob.length);
139 if (!state->response.extra_data.data) {
140 data_blob_free(&blob);
141 return NT_STATUS_NO_MEMORY;
144 memset(state->response.extra_data.data, '\0', blob.length);
145 memcpy(state->response.extra_data.data, blob.data, blob.length);
146 state->response.length += blob.length;
148 data_blob_free(&blob);
150 return NT_STATUS_OK;
153 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
154 struct winbindd_cli_state *state,
155 const struct netr_SamInfo3 *info3,
156 const char *name_domain,
157 const char *name_user)
159 /* We've been asked to return the unix username, per
160 'winbind use default domain' settings and the like */
162 const char *nt_username, *nt_domain;
164 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
165 if (!nt_domain) {
166 /* If the server didn't give us one, just use the one
167 * we sent them */
168 nt_domain = name_domain;
171 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
172 if (!nt_username) {
173 /* If the server didn't give us one, just use the one
174 * we sent them */
175 nt_username = name_user;
178 fill_domain_username(state->response.data.auth.unix_username,
179 nt_domain, nt_username, true);
181 DEBUG(5,("Setting unix username to [%s]\n",
182 state->response.data.auth.unix_username));
184 return NT_STATUS_OK;
187 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
188 struct winbindd_cli_state *state,
189 const struct netr_SamInfo3 *info3,
190 const char *name_domain,
191 const char *name_user)
193 char *afsname = NULL;
194 char *cell;
196 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197 if (afsname == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 afsname = talloc_string_sub(mem_ctx,
202 lp_afs_username_map(),
203 "%D", name_domain);
204 afsname = talloc_string_sub(mem_ctx, afsname,
205 "%u", name_user);
206 afsname = talloc_string_sub(mem_ctx, afsname,
207 "%U", name_user);
210 DOM_SID user_sid;
211 fstring sidstr;
213 sid_copy(&user_sid, info3->base.domain_sid);
214 sid_append_rid(&user_sid, info3->base.rid);
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
217 "%s", sidstr);
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 strlower_m(afsname);
226 DEBUG(10, ("Generating token for user %s\n", afsname));
228 cell = strchr(afsname, '@');
230 if (cell == NULL) {
231 return NT_STATUS_NO_MEMORY;
234 *cell = '\0';
235 cell += 1;
237 /* Append an AFS token string */
238 SAFE_FREE(state->response.extra_data.data);
239 state->response.extra_data.data =
240 afs_createtoken_str(afsname, cell);
242 if (state->response.extra_data.data != NULL) {
243 state->response.length +=
244 strlen((const char *)state->response.extra_data.data)+1;
247 return NT_STATUS_OK;
250 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
251 struct netr_SamInfo3 *info3,
252 const char *group_sid)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 DOM_SID *require_membership_of_sid;
266 size_t num_require_membership_of_sid;
267 char *req_sid;
268 const char *p;
269 DOM_SID sid;
270 size_t i;
271 struct nt_user_token *token;
272 TALLOC_CTX *frame = NULL;
273 NTSTATUS status;
275 /* Parse the 'required group' SID */
277 if (!group_sid || !group_sid[0]) {
278 /* NO sid supplied, all users may access */
279 return NT_STATUS_OK;
282 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
283 DEBUG(0, ("talloc failed\n"));
284 return NT_STATUS_NO_MEMORY;
287 num_require_membership_of_sid = 0;
288 require_membership_of_sid = NULL;
290 p = group_sid;
292 frame = talloc_stackframe();
293 while (next_token_talloc(frame, &p, &req_sid, ",")) {
294 if (!string_to_sid(&sid, req_sid)) {
295 DEBUG(0, ("check_info3_in_group: could not parse %s "
296 "as a SID!", req_sid));
297 TALLOC_FREE(frame);
298 return NT_STATUS_INVALID_PARAMETER;
301 status = add_sid_to_array(mem_ctx, &sid,
302 &require_membership_of_sid,
303 &num_require_membership_of_sid);
304 if (!NT_STATUS_IS_OK(status)) {
305 DEBUG(0, ("add_sid_to_array failed\n"));
306 TALLOC_FREE(frame);
307 return status;
311 TALLOC_FREE(frame);
313 status = sid_array_from_info3(mem_ctx, info3,
314 &token->user_sids,
315 &token->num_sids,
316 true, false);
317 if (!NT_STATUS_IS_OK(status)) {
318 return status;
321 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
322 token))
323 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
324 token))) {
325 DEBUG(3, ("could not add aliases: %s\n",
326 nt_errstr(status)));
327 return status;
330 debug_nt_user_token(DBGC_CLASS, 10, token);
332 for (i=0; i<num_require_membership_of_sid; i++) {
333 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
334 &require_membership_of_sid[i])));
335 if (nt_token_check_sid(&require_membership_of_sid[i],
336 token)) {
337 DEBUG(10, ("Access ok\n"));
338 return NT_STATUS_OK;
342 /* Do not distinguish this error from a wrong username/pw */
344 return NT_STATUS_LOGON_FAILURE;
347 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
348 const char *domain_name)
350 struct winbindd_domain *domain;
352 if (IS_DC) {
353 domain = find_domain_from_name_noinit(domain_name);
354 if (domain == NULL) {
355 DEBUG(3, ("Authentication for domain [%s] refused "
356 "as it is not a trusted domain\n",
357 domain_name));
359 return domain;
362 if (is_myname(domain_name)) {
363 DEBUG(3, ("Authentication for domain %s (local domain "
364 "to this server) not supported at this "
365 "stage\n", domain_name));
366 return NULL;
369 /* we can auth against trusted domains */
370 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
371 domain = find_domain_from_name_noinit(domain_name);
372 if (domain == NULL) {
373 DEBUG(3, ("Authentication for domain [%s] skipped "
374 "as it is not a trusted domain\n",
375 domain_name));
376 } else {
377 return domain;
381 return find_our_domain();
384 static void fill_in_password_policy(struct winbindd_response *r,
385 const struct samr_DomInfo1 *p)
387 r->data.auth.policy.min_length_password =
388 p->min_password_length;
389 r->data.auth.policy.password_history =
390 p->password_history_length;
391 r->data.auth.policy.password_properties =
392 p->password_properties;
393 r->data.auth.policy.expire =
394 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
395 r->data.auth.policy.min_passwordage =
396 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
399 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
400 struct winbindd_cli_state *state)
402 struct winbindd_methods *methods;
403 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
404 struct samr_DomInfo1 password_policy;
406 if ( !winbindd_can_contact_domain( domain ) ) {
407 DEBUG(5,("fillup_password_policy: No inbound trust to "
408 "contact domain %s\n", domain->name));
409 return NT_STATUS_NOT_SUPPORTED;
412 methods = domain->methods;
414 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
415 if (NT_STATUS_IS_ERR(status)) {
416 return status;
419 fill_in_password_policy(&state->response, &password_policy);
421 return NT_STATUS_OK;
424 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
425 TALLOC_CTX *mem_ctx,
426 uint16 *lockout_threshold)
428 struct winbindd_methods *methods;
429 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
430 struct samr_DomInfo12 lockout_policy;
432 *lockout_threshold = 0;
434 methods = domain->methods;
436 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
437 if (NT_STATUS_IS_ERR(status)) {
438 return status;
441 *lockout_threshold = lockout_policy.lockout_threshold;
443 return NT_STATUS_OK;
446 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
447 TALLOC_CTX *mem_ctx,
448 uint32 *password_properties)
450 struct winbindd_methods *methods;
451 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
452 struct samr_DomInfo1 password_policy;
454 *password_properties = 0;
456 methods = domain->methods;
458 status = methods->password_policy(domain, mem_ctx, &password_policy);
459 if (NT_STATUS_IS_ERR(status)) {
460 return status;
463 *password_properties = password_policy.password_properties;
465 return NT_STATUS_OK;
468 #ifdef HAVE_KRB5
470 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
471 const char *type,
472 uid_t uid,
473 bool *internal_ccache)
475 /* accept FILE and WRFILE as krb5_cc_type from the client and then
476 * build the full ccname string based on the user's uid here -
477 * Guenther*/
479 const char *gen_cc = NULL;
481 *internal_ccache = true;
483 if (uid == -1) {
484 goto memory_ccache;
487 if (!type || type[0] == '\0') {
488 goto memory_ccache;
491 if (strequal(type, "FILE")) {
492 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
493 } else if (strequal(type, "WRFILE")) {
494 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
495 } else {
496 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
497 goto memory_ccache;
500 *internal_ccache = false;
501 goto done;
503 memory_ccache:
504 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
506 done:
507 if (gen_cc == NULL) {
508 DEBUG(0,("out of memory\n"));
509 return NULL;
512 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
514 return gen_cc;
517 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
519 const char *type = state->request.data.auth.krb5_cc_type;
521 state->response.data.auth.krb5ccname[0] = '\0';
523 if (type[0] == '\0') {
524 return;
527 if (!strequal(type, "FILE") &&
528 !strequal(type, "WRFILE")) {
529 DEBUG(10,("won't return krbccname for a %s type ccache\n",
530 type));
531 return;
534 fstrcpy(state->response.data.auth.krb5ccname, cc);
537 #endif
539 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
541 uid_t uid = -1;
543 uid = state->request.data.auth.uid;
545 if (uid < 0) {
546 DEBUG(1,("invalid uid: '%d'\n", uid));
547 return -1;
549 return uid;
552 /**********************************************************************
553 Authenticate a user with a clear text password using Kerberos and fill up
554 ccache if required
555 **********************************************************************/
557 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
558 struct winbindd_cli_state *state,
559 struct netr_SamInfo3 **info3)
561 #ifdef HAVE_KRB5
562 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
563 krb5_error_code krb5_ret;
564 const char *cc = NULL;
565 const char *principal_s = NULL;
566 const char *service = NULL;
567 char *realm = NULL;
568 fstring name_domain, name_user;
569 time_t ticket_lifetime = 0;
570 time_t renewal_until = 0;
571 uid_t uid = -1;
572 ADS_STRUCT *ads;
573 time_t time_offset = 0;
574 bool internal_ccache = true;
576 ZERO_STRUCTP(info3);
578 *info3 = NULL;
580 /* 1st step:
581 * prepare a krb5_cc_cache string for the user */
583 uid = get_uid_from_state(state);
584 if (uid == -1) {
585 DEBUG(0,("no valid uid\n"));
588 cc = generate_krb5_ccache(state->mem_ctx,
589 state->request.data.auth.krb5_cc_type,
590 state->request.data.auth.uid,
591 &internal_ccache);
592 if (cc == NULL) {
593 return NT_STATUS_NO_MEMORY;
597 /* 2nd step:
598 * get kerberos properties */
600 if (domain->private_data) {
601 ads = (ADS_STRUCT *)domain->private_data;
602 time_offset = ads->auth.time_offset;
606 /* 3rd step:
607 * do kerberos auth and setup ccache as the user */
609 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
611 realm = domain->alt_name;
612 strupper_m(realm);
614 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
615 if (principal_s == NULL) {
616 return NT_STATUS_NO_MEMORY;
619 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
620 if (service == NULL) {
621 return NT_STATUS_NO_MEMORY;
624 /* if this is a user ccache, we need to act as the user to let the krb5
625 * library handle the chown, etc. */
627 /************************ ENTERING NON-ROOT **********************/
629 if (!internal_ccache) {
630 set_effective_uid(uid);
631 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
634 result = kerberos_return_info3_from_pac(state->mem_ctx,
635 principal_s,
636 state->request.data.auth.pass,
637 time_offset,
638 &ticket_lifetime,
639 &renewal_until,
641 true,
642 true,
643 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
644 info3);
645 if (!internal_ccache) {
646 gain_root_privilege();
649 /************************ RETURNED TO ROOT **********************/
651 if (!NT_STATUS_IS_OK(result)) {
652 goto failed;
655 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
656 principal_s));
658 /* if we had a user's ccache then return that string for the pam
659 * environment */
661 if (!internal_ccache) {
663 setup_return_cc_name(state, cc);
665 result = add_ccache_to_list(principal_s,
667 service,
668 state->request.data.auth.user,
669 realm,
670 uid,
671 time(NULL),
672 ticket_lifetime,
673 renewal_until,
674 false);
676 if (!NT_STATUS_IS_OK(result)) {
677 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
678 nt_errstr(result)));
680 } else {
682 /* need to delete the memory cred cache, it is not used anymore */
684 krb5_ret = ads_kdestroy(cc);
685 if (krb5_ret) {
686 DEBUG(3,("winbindd_raw_kerberos_login: "
687 "could not destroy krb5 credential cache: "
688 "%s\n", error_message(krb5_ret)));
693 return NT_STATUS_OK;
695 failed:
697 /* we could have created a new credential cache with a valid tgt in it
698 * but we werent able to get or verify the service ticket for this
699 * local host and therefor didn't get the PAC, we need to remove that
700 * cache entirely now */
702 krb5_ret = ads_kdestroy(cc);
703 if (krb5_ret) {
704 DEBUG(3,("winbindd_raw_kerberos_login: "
705 "could not destroy krb5 credential cache: "
706 "%s\n", error_message(krb5_ret)));
709 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
710 DEBUG(3,("winbindd_raw_kerberos_login: "
711 "could not remove ccache for user %s\n",
712 state->request.data.auth.user));
715 return result;
716 #else
717 return NT_STATUS_NOT_SUPPORTED;
718 #endif /* HAVE_KRB5 */
721 /****************************************************************
722 ****************************************************************/
724 static bool check_request_flags(uint32_t flags)
726 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
727 WBFLAG_PAM_INFO3_TEXT |
728 WBFLAG_PAM_INFO3_NDR;
730 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
731 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
732 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
733 !(flags & flags_edata) ) {
734 return true;
737 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
739 return false;
742 /****************************************************************
743 ****************************************************************/
745 static NTSTATUS append_data(struct winbindd_cli_state *state,
746 struct netr_SamInfo3 *info3,
747 const char *name_domain,
748 const char *name_user)
750 NTSTATUS result;
751 uint32_t flags = state->request.flags;
753 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
754 memcpy(state->response.data.auth.user_session_key,
755 info3->base.key.key,
756 sizeof(state->response.data.auth.user_session_key)
757 /* 16 */);
760 if (flags & WBFLAG_PAM_LMKEY) {
761 memcpy(state->response.data.auth.first_8_lm_hash,
762 info3->base.LMSessKey.key,
763 sizeof(state->response.data.auth.first_8_lm_hash)
764 /* 8 */);
767 if (flags & WBFLAG_PAM_INFO3_TEXT) {
768 result = append_info3_as_txt(state->mem_ctx, state, info3);
769 if (!NT_STATUS_IS_OK(result)) {
770 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
771 nt_errstr(result)));
772 return result;
776 /* currently, anything from here on potentially overwrites extra_data. */
778 if (flags & WBFLAG_PAM_INFO3_NDR) {
779 result = append_info3_as_ndr(state->mem_ctx, state, info3);
780 if (!NT_STATUS_IS_OK(result)) {
781 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
782 nt_errstr(result)));
783 return result;
787 if (flags & WBFLAG_PAM_UNIX_NAME) {
788 result = append_unix_username(state->mem_ctx, state, info3,
789 name_domain, name_user);
790 if (!NT_STATUS_IS_OK(result)) {
791 DEBUG(10,("Failed to append Unix Username: %s\n",
792 nt_errstr(result)));
793 return result;
797 if (flags & WBFLAG_PAM_AFS_TOKEN) {
798 result = append_afs_token(state->mem_ctx, state, info3,
799 name_domain, name_user);
800 if (!NT_STATUS_IS_OK(result)) {
801 DEBUG(10,("Failed to append AFS token: %s\n",
802 nt_errstr(result)));
803 return result;
807 return NT_STATUS_OK;
810 void winbindd_pam_auth(struct winbindd_cli_state *state)
812 struct winbindd_domain *domain;
813 fstring name_domain, name_user;
814 char *mapped_user = 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_user);
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))
846 mapped_user = state->request.data.auth.user;
849 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
850 result = NT_STATUS_NO_SUCH_USER;
851 goto done;
854 domain = find_auth_domain(state, name_domain);
856 if (domain == NULL) {
857 result = NT_STATUS_NO_SUCH_USER;
858 goto done;
861 sendto_domain(state, domain);
862 return;
863 done:
864 set_auth_errors(&state->response, result);
865 DEBUG(5, ("Plain text authentication for %s returned %s "
866 "(PAM: %d)\n",
867 state->request.data.auth.user,
868 state->response.data.auth.nt_status_string,
869 state->response.data.auth.pam_error));
870 request_error(state);
873 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
874 struct winbindd_cli_state *state,
875 struct netr_SamInfo3 **info3)
877 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
878 uint16 max_allowed_bad_attempts;
879 fstring name_domain, name_user;
880 DOM_SID sid;
881 enum lsa_SidType type;
882 uchar new_nt_pass[NT_HASH_LEN];
883 const uint8 *cached_nt_pass;
884 const uint8 *cached_salt;
885 struct netr_SamInfo3 *my_info3;
886 time_t kickoff_time, must_change_time;
887 bool password_good = false;
888 #ifdef HAVE_KRB5
889 struct winbindd_tdc_domain *tdc_domain = NULL;
890 #endif
892 *info3 = NULL;
894 ZERO_STRUCTP(info3);
896 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
898 /* Parse domain and username */
900 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
903 if (!lookup_cached_name(state->mem_ctx,
904 name_domain,
905 name_user,
906 &sid,
907 &type)) {
908 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
909 return NT_STATUS_NO_SUCH_USER;
912 if (type != SID_NAME_USER) {
913 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
914 return NT_STATUS_LOGON_FAILURE;
917 result = winbindd_get_creds(domain,
918 state->mem_ctx,
919 &sid,
920 &my_info3,
921 &cached_nt_pass,
922 &cached_salt);
923 if (!NT_STATUS_IS_OK(result)) {
924 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
925 return result;
928 *info3 = my_info3;
930 E_md4hash(state->request.data.auth.pass, new_nt_pass);
932 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
933 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
934 if (cached_salt) {
935 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
938 if (cached_salt) {
939 /* In this case we didn't store the nt_hash itself,
940 but the MD5 combination of salt + nt_hash. */
941 uchar salted_hash[NT_HASH_LEN];
942 E_md5hash(cached_salt, new_nt_pass, salted_hash);
944 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
945 true : false;
946 } else {
947 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
948 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
949 true : false;
952 if (password_good) {
954 /* User *DOES* know the password, update logon_time and reset
955 * bad_pw_count */
957 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
959 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
960 return NT_STATUS_ACCOUNT_LOCKED_OUT;
963 if (my_info3->base.acct_flags & ACB_DISABLED) {
964 return NT_STATUS_ACCOUNT_DISABLED;
967 if (my_info3->base.acct_flags & ACB_WSTRUST) {
968 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
971 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
972 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
975 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
976 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
979 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
980 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
981 my_info3->base.acct_flags));
982 return NT_STATUS_LOGON_FAILURE;
985 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
986 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
987 return NT_STATUS_ACCOUNT_EXPIRED;
990 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
991 if (must_change_time != 0 && must_change_time < time(NULL)) {
992 /* we allow grace logons when the password has expired */
993 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
994 /* return NT_STATUS_PASSWORD_EXPIRED; */
995 goto success;
998 #ifdef HAVE_KRB5
999 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
1000 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1001 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
1003 uid_t uid = -1;
1004 const char *cc = NULL;
1005 char *realm = NULL;
1006 const char *principal_s = NULL;
1007 const char *service = NULL;
1008 bool internal_ccache = false;
1010 uid = get_uid_from_state(state);
1011 if (uid == -1) {
1012 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1013 return NT_STATUS_INVALID_PARAMETER;
1016 cc = generate_krb5_ccache(state->mem_ctx,
1017 state->request.data.auth.krb5_cc_type,
1018 state->request.data.auth.uid,
1019 &internal_ccache);
1020 if (cc == NULL) {
1021 return NT_STATUS_NO_MEMORY;
1024 realm = domain->alt_name;
1025 strupper_m(realm);
1027 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1028 if (principal_s == NULL) {
1029 return NT_STATUS_NO_MEMORY;
1032 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1033 if (service == NULL) {
1034 return NT_STATUS_NO_MEMORY;
1037 if (!internal_ccache) {
1039 setup_return_cc_name(state, cc);
1041 result = add_ccache_to_list(principal_s,
1043 service,
1044 state->request.data.auth.user,
1045 domain->alt_name,
1046 uid,
1047 time(NULL),
1048 time(NULL) + lp_winbind_cache_time(),
1049 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1050 true);
1052 if (!NT_STATUS_IS_OK(result)) {
1053 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1054 "to add ccache to list: %s\n",
1055 nt_errstr(result)));
1059 #endif /* HAVE_KRB5 */
1060 success:
1061 /* FIXME: we possibly should handle logon hours as well (does xp when
1062 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1064 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1065 my_info3->base.bad_password_count = 0;
1067 result = winbindd_update_creds_by_info3(domain,
1068 state->mem_ctx,
1069 state->request.data.auth.user,
1070 state->request.data.auth.pass,
1071 my_info3);
1072 if (!NT_STATUS_IS_OK(result)) {
1073 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1074 nt_errstr(result)));
1075 return result;
1078 return NT_STATUS_OK;
1082 /* User does *NOT* know the correct password, modify info3 accordingly */
1084 /* failure of this is not critical */
1085 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1086 if (!NT_STATUS_IS_OK(result)) {
1087 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1088 "Won't be able to honour account lockout policies\n"));
1091 /* increase counter */
1092 my_info3->base.bad_password_count++;
1094 if (max_allowed_bad_attempts == 0) {
1095 goto failed;
1098 /* lockout user */
1099 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1101 uint32 password_properties;
1103 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1104 if (!NT_STATUS_IS_OK(result)) {
1105 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1108 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1109 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1110 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1114 failed:
1115 result = winbindd_update_creds_by_info3(domain,
1116 state->mem_ctx,
1117 state->request.data.auth.user,
1118 NULL,
1119 my_info3);
1121 if (!NT_STATUS_IS_OK(result)) {
1122 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1123 nt_errstr(result)));
1126 return NT_STATUS_LOGON_FAILURE;
1129 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1130 struct winbindd_cli_state *state,
1131 struct netr_SamInfo3 **info3)
1133 struct winbindd_domain *contact_domain;
1134 fstring name_domain, name_user;
1135 NTSTATUS result;
1137 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1139 /* Parse domain and username */
1141 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1143 /* what domain should we contact? */
1145 if ( IS_DC ) {
1146 if (!(contact_domain = find_domain_from_name(name_domain))) {
1147 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1148 state->request.data.auth.user, name_domain, name_user, name_domain));
1149 result = NT_STATUS_NO_SUCH_USER;
1150 goto done;
1153 } else {
1154 if (is_myname(name_domain)) {
1155 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1156 result = NT_STATUS_NO_SUCH_USER;
1157 goto done;
1160 contact_domain = find_domain_from_name(name_domain);
1161 if (contact_domain == NULL) {
1162 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1163 state->request.data.auth.user, name_domain, name_user, name_domain));
1165 contact_domain = find_our_domain();
1169 if (contact_domain->initialized &&
1170 contact_domain->active_directory) {
1171 goto try_login;
1174 if (!contact_domain->initialized) {
1175 init_dc_connection(contact_domain);
1178 if (!contact_domain->active_directory) {
1179 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1180 return NT_STATUS_INVALID_LOGON_TYPE;
1182 try_login:
1183 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1184 done:
1185 return result;
1188 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1189 TALLOC_CTX *mem_ctx,
1190 uint32 logon_parameters,
1191 const char *server,
1192 const char *username,
1193 const char *domain,
1194 const char *workstation,
1195 const uint8 chal[8],
1196 DATA_BLOB lm_response,
1197 DATA_BLOB nt_response,
1198 struct netr_SamInfo3 **info3);
1200 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1201 struct winbindd_cli_state *state,
1202 struct netr_SamInfo3 **info3)
1205 struct rpc_pipe_client *netlogon_pipe;
1206 uchar chal[8];
1207 DATA_BLOB lm_resp;
1208 DATA_BLOB nt_resp;
1209 int attempts = 0;
1210 unsigned char local_lm_response[24];
1211 unsigned char local_nt_response[24];
1212 struct winbindd_domain *contact_domain;
1213 fstring name_domain, name_user;
1214 bool retry;
1215 NTSTATUS result;
1216 struct netr_SamInfo3 *my_info3 = NULL;
1218 *info3 = NULL;
1220 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1222 /* Parse domain and username */
1224 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1226 /* do password magic */
1229 generate_random_buffer(chal, 8);
1230 if (lp_client_ntlmv2_auth()) {
1231 DATA_BLOB server_chal;
1232 DATA_BLOB names_blob;
1233 DATA_BLOB nt_response;
1234 DATA_BLOB lm_response;
1235 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1237 /* note that the 'workgroup' here is a best guess - we don't know
1238 the server's domain at this point. The 'server name' is also
1239 dodgy...
1241 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1243 if (!SMBNTLMv2encrypt(name_user, name_domain,
1244 state->request.data.auth.pass,
1245 &server_chal,
1246 &names_blob,
1247 &lm_response, &nt_response, NULL)) {
1248 data_blob_free(&names_blob);
1249 data_blob_free(&server_chal);
1250 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1251 result = NT_STATUS_NO_MEMORY;
1252 goto done;
1254 data_blob_free(&names_blob);
1255 data_blob_free(&server_chal);
1256 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1257 lm_response.length);
1258 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1259 nt_response.length);
1260 data_blob_free(&lm_response);
1261 data_blob_free(&nt_response);
1263 } else {
1264 if (lp_client_lanman_auth()
1265 && SMBencrypt(state->request.data.auth.pass,
1266 chal,
1267 local_lm_response)) {
1268 lm_resp = data_blob_talloc(state->mem_ctx,
1269 local_lm_response,
1270 sizeof(local_lm_response));
1271 } else {
1272 lm_resp = data_blob_null;
1274 SMBNTencrypt(state->request.data.auth.pass,
1275 chal,
1276 local_nt_response);
1278 nt_resp = data_blob_talloc(state->mem_ctx,
1279 local_nt_response,
1280 sizeof(local_nt_response));
1283 /* what domain should we contact? */
1285 if ( IS_DC ) {
1286 if (!(contact_domain = find_domain_from_name(name_domain))) {
1287 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1288 state->request.data.auth.user, name_domain, name_user, name_domain));
1289 result = NT_STATUS_NO_SUCH_USER;
1290 goto done;
1293 } else {
1294 if (is_myname(name_domain)) {
1295 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1296 result = NT_STATUS_NO_SUCH_USER;
1297 goto done;
1300 contact_domain = find_our_domain();
1303 /* check authentication loop */
1305 do {
1306 netlogon_fn_t logon_fn;
1308 ZERO_STRUCTP(my_info3);
1309 retry = false;
1311 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1313 if (!NT_STATUS_IS_OK(result)) {
1314 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1315 goto done;
1318 /* It is really important to try SamLogonEx here,
1319 * because in a clustered environment, we want to use
1320 * one machine account from multiple physical
1321 * computers.
1323 * With a normal SamLogon call, we must keep the
1324 * credentials chain updated and intact between all
1325 * users of the machine account (which would imply
1326 * cross-node communication for every NTLM logon).
1328 * (The credentials chain is not per NETLOGON pipe
1329 * connection, but globally on the server/client pair
1330 * by machine name).
1332 * When using SamLogonEx, the credentials are not
1333 * supplied, but the session key is implied by the
1334 * wrapping SamLogon context.
1336 * -- abartlet 21 April 2008
1339 logon_fn = contact_domain->can_do_samlogon_ex
1340 ? rpccli_netlogon_sam_network_logon_ex
1341 : rpccli_netlogon_sam_network_logon;
1343 result = logon_fn(netlogon_pipe,
1344 state->mem_ctx,
1346 contact_domain->dcname, /* server name */
1347 name_user, /* user name */
1348 name_domain, /* target domain */
1349 global_myname(), /* workstation */
1350 chal,
1351 lm_resp,
1352 nt_resp,
1353 &my_info3);
1354 attempts += 1;
1356 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1357 && contact_domain->can_do_samlogon_ex) {
1358 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1359 "retrying with NetSamLogon\n"));
1360 contact_domain->can_do_samlogon_ex = false;
1361 retry = true;
1362 continue;
1365 /* We have to try a second time as cm_connect_netlogon
1366 might not yet have noticed that the DC has killed
1367 our connection. */
1369 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1370 retry = true;
1371 continue;
1374 /* if we get access denied, a possible cause was that we had
1375 and open connection to the DC, but someone changed our
1376 machine account password out from underneath us using 'net
1377 rpc changetrustpw' */
1379 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1380 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1381 "ACCESS_DENIED. Maybe the trust account "
1382 "password was changed and we didn't know it. "
1383 "Killing connections to domain %s\n",
1384 name_domain));
1385 invalidate_cm_connection(&contact_domain->conn);
1386 retry = true;
1389 } while ( (attempts < 2) && retry );
1391 /* handle the case where a NT4 DC does not fill in the acct_flags in
1392 * the samlogon reply info3. When accurate info3 is required by the
1393 * caller, we look up the account flags ourselve - gd */
1395 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1396 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1398 struct rpc_pipe_client *samr_pipe;
1399 POLICY_HND samr_domain_handle, user_pol;
1400 union samr_UserInfo *info = NULL;
1401 NTSTATUS status_tmp;
1402 uint32 acct_flags;
1404 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1405 &samr_pipe, &samr_domain_handle);
1407 if (!NT_STATUS_IS_OK(status_tmp)) {
1408 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1409 nt_errstr(status_tmp)));
1410 goto done;
1413 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1414 &samr_domain_handle,
1415 MAXIMUM_ALLOWED_ACCESS,
1416 my_info3->base.rid,
1417 &user_pol);
1419 if (!NT_STATUS_IS_OK(status_tmp)) {
1420 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1421 nt_errstr(status_tmp)));
1422 goto done;
1425 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1426 &user_pol,
1428 &info);
1430 if (!NT_STATUS_IS_OK(status_tmp)) {
1431 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1432 nt_errstr(status_tmp)));
1433 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1434 goto done;
1437 acct_flags = info->info16.acct_flags;
1439 if (acct_flags == 0) {
1440 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1441 goto done;
1444 my_info3->base.acct_flags = acct_flags;
1446 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1448 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1451 *info3 = my_info3;
1452 done:
1453 return result;
1456 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1457 struct winbindd_cli_state *state)
1459 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1460 NTSTATUS krb5_result = NT_STATUS_OK;
1461 fstring name_domain, name_user;
1462 char *mapped_user;
1463 fstring domain_user;
1464 struct netr_SamInfo3 *info3 = NULL;
1465 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1467 /* Ensure null termination */
1468 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1470 /* Ensure null termination */
1471 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1473 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1474 state->request.data.auth.user));
1476 if (!check_request_flags(state->request.flags)) {
1477 result = NT_STATUS_INVALID_PARAMETER_MIX;
1478 goto done;
1481 /* Parse domain and username */
1483 name_map_status = normalize_name_unmap(state->mem_ctx,
1484 state->request.data.auth.user,
1485 &mapped_user);
1487 /* If the name normalization didnt' actually do anything,
1488 just use the original name */
1490 if (!NT_STATUS_IS_OK(name_map_status) &&
1491 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1493 mapped_user = state->request.data.auth.user;
1496 parse_domain_user(mapped_user, name_domain, name_user);
1498 if ( mapped_user != state->request.data.auth.user ) {
1499 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1500 safe_strcpy( state->request.data.auth.user, domain_user,
1501 sizeof(state->request.data.auth.user)-1 );
1504 if (domain->online == false) {
1505 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1506 if (domain->startup) {
1507 /* Logons are very important to users. If we're offline and
1508 we get a request within the first 30 seconds of startup,
1509 try very hard to find a DC and go online. */
1511 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1512 "request in startup mode.\n", domain->name ));
1514 winbindd_flush_negative_conn_cache(domain);
1515 result = init_dc_connection(domain);
1519 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1521 /* Check for Kerberos authentication */
1522 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1524 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1525 /* save for later */
1526 krb5_result = result;
1529 if (NT_STATUS_IS_OK(result)) {
1530 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1531 goto process_result;
1532 } else {
1533 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1536 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1539 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1540 set_domain_offline( domain );
1541 goto cached_logon;
1544 /* there are quite some NT_STATUS errors where there is no
1545 * point in retrying with a samlogon, we explictly have to take
1546 * care not to increase the bad logon counter on the DC */
1548 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1549 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1550 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1551 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1553 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1554 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1555 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1556 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1557 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1558 goto process_result;
1561 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1562 DEBUG(3,("falling back to samlogon\n"));
1563 goto sam_logon;
1564 } else {
1565 goto cached_logon;
1569 sam_logon:
1570 /* Check for Samlogon authentication */
1571 if (domain->online) {
1572 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1574 if (NT_STATUS_IS_OK(result)) {
1575 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1576 /* add the Krb5 err if we have one */
1577 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1578 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1580 goto process_result;
1583 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1584 nt_errstr(result)));
1586 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1587 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1588 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1590 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1591 set_domain_offline( domain );
1592 goto cached_logon;
1595 if (domain->online) {
1596 /* We're still online - fail. */
1597 goto done;
1601 cached_logon:
1602 /* Check for Cached logons */
1603 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1604 lp_winbind_offline_logon()) {
1606 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1608 if (NT_STATUS_IS_OK(result)) {
1609 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1610 goto process_result;
1611 } else {
1612 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1613 goto done;
1617 process_result:
1619 if (NT_STATUS_IS_OK(result)) {
1621 DOM_SID user_sid;
1623 /* In all codepaths where result == NT_STATUS_OK info3 must have
1624 been initialized. */
1625 if (!info3) {
1626 result = NT_STATUS_INTERNAL_ERROR;
1627 goto done;
1630 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1631 netsamlogon_cache_store(name_user, info3);
1633 /* save name_to_sid info as early as possible (only if
1634 this is our primary domain so we don't invalidate
1635 the cache entry by storing the seq_num for the wrong
1636 domain). */
1637 if ( domain->primary ) {
1638 sid_compose(&user_sid, info3->base.domain_sid,
1639 info3->base.rid);
1640 cache_name2sid(domain, name_domain, name_user,
1641 SID_NAME_USER, &user_sid);
1644 /* Check if the user is in the right group */
1646 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1647 state->request.data.auth.require_membership_of_sid))) {
1648 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1649 state->request.data.auth.user,
1650 state->request.data.auth.require_membership_of_sid));
1651 goto done;
1654 result = append_data(state, info3, name_domain, name_user);
1655 if (!NT_STATUS_IS_OK(result)) {
1656 goto done;
1659 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1661 /* Store in-memory creds for single-signon using ntlm_auth. */
1662 result = winbindd_add_memory_creds(state->request.data.auth.user,
1663 get_uid_from_state(state),
1664 state->request.data.auth.pass);
1666 if (!NT_STATUS_IS_OK(result)) {
1667 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1668 goto done;
1671 if (lp_winbind_offline_logon()) {
1672 result = winbindd_store_creds(domain,
1673 state->mem_ctx,
1674 state->request.data.auth.user,
1675 state->request.data.auth.pass,
1676 info3, NULL);
1677 if (!NT_STATUS_IS_OK(result)) {
1679 /* Release refcount. */
1680 winbindd_delete_memory_creds(state->request.data.auth.user);
1682 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1683 goto done;
1689 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1690 struct winbindd_domain *our_domain = find_our_domain();
1692 /* This is not entirely correct I believe, but it is
1693 consistent. Only apply the password policy settings
1694 too warn users for our own domain. Cannot obtain these
1695 from trusted DCs all the time so don't do it at all.
1696 -- jerry */
1698 result = NT_STATUS_NOT_SUPPORTED;
1699 if (our_domain == domain ) {
1700 result = fillup_password_policy(our_domain, state);
1703 if (!NT_STATUS_IS_OK(result)
1704 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1706 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1707 domain->name, nt_errstr(result)));
1708 goto done;
1712 result = NT_STATUS_OK;
1715 done:
1716 /* give us a more useful (more correct?) error code */
1717 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1718 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1719 result = NT_STATUS_NO_LOGON_SERVERS;
1722 set_auth_errors(&state->response, result);
1724 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1725 state->request.data.auth.user,
1726 state->response.data.auth.nt_status_string,
1727 state->response.data.auth.pam_error));
1729 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1733 /**********************************************************************
1734 Challenge Response Authentication Protocol
1735 **********************************************************************/
1737 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1739 struct winbindd_domain *domain = NULL;
1740 const char *domain_name = NULL;
1741 NTSTATUS result;
1743 if (!check_request_flags(state->request.flags)) {
1744 result = NT_STATUS_INVALID_PARAMETER_MIX;
1745 goto done;
1748 if (!state->privileged) {
1749 char *error_string = NULL;
1750 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1751 "denied. !\n"));
1752 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1753 "on %s are set correctly.\n",
1754 get_winbind_priv_pipe_dir()));
1755 /* send a better message than ACCESS_DENIED */
1756 error_string = talloc_asprintf(state->mem_ctx,
1757 "winbind client not authorized "
1758 "to use winbindd_pam_auth_crap."
1759 " Ensure permissions on %s "
1760 "are set correctly.",
1761 get_winbind_priv_pipe_dir());
1762 fstrcpy(state->response.data.auth.error_string, error_string);
1763 result = NT_STATUS_ACCESS_DENIED;
1764 goto done;
1767 /* Ensure null termination */
1768 state->request.data.auth_crap.user
1769 [sizeof(state->request.data.auth_crap.user)-1]=0;
1770 state->request.data.auth_crap.domain
1771 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1773 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1774 (unsigned long)state->pid,
1775 state->request.data.auth_crap.domain,
1776 state->request.data.auth_crap.user));
1778 if (*state->request.data.auth_crap.domain != '\0') {
1779 domain_name = state->request.data.auth_crap.domain;
1780 } else if (lp_winbind_use_default_domain()) {
1781 domain_name = lp_workgroup();
1784 if (domain_name != NULL)
1785 domain = find_auth_domain(state, domain_name);
1787 if (domain != NULL) {
1788 sendto_domain(state, domain);
1789 return;
1792 result = NT_STATUS_NO_SUCH_USER;
1794 done:
1795 set_auth_errors(&state->response, result);
1796 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1797 state->request.data.auth_crap.domain,
1798 state->request.data.auth_crap.user,
1799 state->response.data.auth.nt_status_string,
1800 state->response.data.auth.pam_error));
1801 request_error(state);
1802 return;
1806 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1807 struct winbindd_cli_state *state)
1809 NTSTATUS result;
1810 struct netr_SamInfo3 *info3 = NULL;
1811 struct rpc_pipe_client *netlogon_pipe;
1812 const char *name_user = NULL;
1813 const char *name_domain = NULL;
1814 const char *workstation;
1815 struct winbindd_domain *contact_domain;
1816 int attempts = 0;
1817 bool retry;
1819 DATA_BLOB lm_resp, nt_resp;
1821 /* This is child-only, so no check for privileged access is needed
1822 anymore */
1824 /* Ensure null termination */
1825 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1826 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1828 if (!check_request_flags(state->request.flags)) {
1829 result = NT_STATUS_INVALID_PARAMETER_MIX;
1830 goto done;
1833 name_user = state->request.data.auth_crap.user;
1835 if (*state->request.data.auth_crap.domain) {
1836 name_domain = state->request.data.auth_crap.domain;
1837 } else if (lp_winbind_use_default_domain()) {
1838 name_domain = lp_workgroup();
1839 } else {
1840 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1841 name_user));
1842 result = NT_STATUS_NO_SUCH_USER;
1843 goto done;
1846 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1847 name_domain, name_user));
1849 if (*state->request.data.auth_crap.workstation) {
1850 workstation = state->request.data.auth_crap.workstation;
1851 } else {
1852 workstation = global_myname();
1855 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1856 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1857 if (!state->request.flags & WBFLAG_BIG_NTLMV2_BLOB ||
1858 state->request.extra_len != state->request.data.auth_crap.nt_resp_len) {
1859 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1860 state->request.data.auth_crap.lm_resp_len,
1861 state->request.data.auth_crap.nt_resp_len));
1862 result = NT_STATUS_INVALID_PARAMETER;
1863 goto done;
1867 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1868 state->request.data.auth_crap.lm_resp_len);
1870 if (state->request.flags & WBFLAG_BIG_NTLMV2_BLOB) {
1871 nt_resp = data_blob_talloc(state->mem_ctx,
1872 state->request.extra_data.data,
1873 state->request.data.auth_crap.nt_resp_len);
1874 } else {
1875 nt_resp = data_blob_talloc(state->mem_ctx,
1876 state->request.data.auth_crap.nt_resp,
1877 state->request.data.auth_crap.nt_resp_len);
1880 /* what domain should we contact? */
1882 if ( IS_DC ) {
1883 if (!(contact_domain = find_domain_from_name(name_domain))) {
1884 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1885 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1886 result = NT_STATUS_NO_SUCH_USER;
1887 goto done;
1889 } else {
1890 if (is_myname(name_domain)) {
1891 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1892 result = NT_STATUS_NO_SUCH_USER;
1893 goto done;
1895 contact_domain = find_our_domain();
1898 do {
1899 netlogon_fn_t logon_fn;
1901 retry = false;
1903 netlogon_pipe = NULL;
1904 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1906 if (!NT_STATUS_IS_OK(result)) {
1907 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1908 nt_errstr(result)));
1909 goto done;
1912 logon_fn = contact_domain->can_do_samlogon_ex
1913 ? rpccli_netlogon_sam_network_logon_ex
1914 : rpccli_netlogon_sam_network_logon;
1916 result = logon_fn(netlogon_pipe,
1917 state->mem_ctx,
1918 state->request.data.auth_crap.logon_parameters,
1919 contact_domain->dcname,
1920 name_user,
1921 name_domain,
1922 /* Bug #3248 - found by Stefan Burkei. */
1923 workstation, /* We carefully set this above so use it... */
1924 state->request.data.auth_crap.chal,
1925 lm_resp,
1926 nt_resp,
1927 &info3);
1929 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1930 && contact_domain->can_do_samlogon_ex) {
1931 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1932 "retrying with NetSamLogon\n"));
1933 contact_domain->can_do_samlogon_ex = false;
1934 retry = true;
1935 continue;
1938 attempts += 1;
1940 /* We have to try a second time as cm_connect_netlogon
1941 might not yet have noticed that the DC has killed
1942 our connection. */
1944 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1945 retry = true;
1946 continue;
1949 /* if we get access denied, a possible cause was that we had and open
1950 connection to the DC, but someone changed our machine account password
1951 out from underneath us using 'net rpc changetrustpw' */
1953 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1954 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1955 "ACCESS_DENIED. Maybe the trust account "
1956 "password was changed and we didn't know it. "
1957 "Killing connections to domain %s\n",
1958 name_domain));
1959 invalidate_cm_connection(&contact_domain->conn);
1960 retry = true;
1963 } while ( (attempts < 2) && retry );
1965 if (NT_STATUS_IS_OK(result)) {
1967 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1968 netsamlogon_cache_store(name_user, info3);
1970 /* Check if the user is in the right group */
1972 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1973 state->request.data.auth_crap.require_membership_of_sid))) {
1974 DEBUG(3, ("User %s is not in the required group (%s), so "
1975 "crap authentication is rejected\n",
1976 state->request.data.auth_crap.user,
1977 state->request.data.auth_crap.require_membership_of_sid));
1978 goto done;
1981 result = append_data(state, info3, name_domain, name_user);
1982 if (!NT_STATUS_IS_OK(result)) {
1983 goto done;
1987 done:
1989 /* give us a more useful (more correct?) error code */
1990 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1991 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1992 result = NT_STATUS_NO_LOGON_SERVERS;
1995 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1996 result = nt_status_squash(result);
1999 set_auth_errors(&state->response, result);
2001 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2002 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2003 name_domain,
2004 name_user,
2005 state->response.data.auth.nt_status_string,
2006 state->response.data.auth.pam_error));
2008 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2011 /* Change a user password */
2013 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2015 fstring domain, user;
2016 char *mapped_user;
2017 struct winbindd_domain *contact_domain;
2018 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2020 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2021 state->request.data.chauthtok.user));
2023 /* Setup crap */
2025 nt_status = normalize_name_unmap(state->mem_ctx,
2026 state->request.data.chauthtok.user,
2027 &mapped_user);
2029 /* Update the chauthtok name if we did any mapping */
2031 if (NT_STATUS_IS_OK(nt_status) ||
2032 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2034 fstrcpy(state->request.data.chauthtok.user, mapped_user);
2037 /* Must pass in state->...chauthtok.user because
2038 canonicalize_username() assumes an fstring(). Since
2039 we have already copied it (if necessary), this is ok. */
2041 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2042 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2043 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2044 "(PAM: %d)\n",
2045 state->request.data.auth.user,
2046 state->response.data.auth.nt_status_string,
2047 state->response.data.auth.pam_error));
2048 request_error(state);
2049 return;
2052 contact_domain = find_domain_from_name(domain);
2053 if (!contact_domain) {
2054 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2055 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2056 state->request.data.chauthtok.user, domain, user, domain));
2057 request_error(state);
2058 return;
2061 sendto_domain(state, contact_domain);
2064 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2065 struct winbindd_cli_state *state)
2067 char *oldpass;
2068 char *newpass = NULL;
2069 POLICY_HND dom_pol;
2070 struct rpc_pipe_client *cli;
2071 bool got_info = false;
2072 struct samr_DomInfo1 *info = NULL;
2073 struct samr_ChangeReject *reject = NULL;
2074 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2075 fstring domain, user;
2077 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2078 state->request.data.auth.user));
2080 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2081 goto done;
2084 /* Change password */
2086 oldpass = state->request.data.chauthtok.oldpass;
2087 newpass = state->request.data.chauthtok.newpass;
2089 /* Initialize reject reason */
2090 state->response.data.auth.reject_reason = Undefined;
2092 /* Get sam handle */
2094 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2095 &dom_pol);
2096 if (!NT_STATUS_IS_OK(result)) {
2097 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2098 goto done;
2101 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2102 user,
2103 newpass,
2104 oldpass,
2105 &info,
2106 &reject);
2108 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2110 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2112 fill_in_password_policy(&state->response, info);
2114 state->response.data.auth.reject_reason =
2115 reject->reason;
2117 got_info = true;
2120 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2121 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2122 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2123 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2125 /* only fallback when the chgpasswd_user3 call is not supported */
2126 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2127 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2128 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2129 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2131 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2132 nt_errstr(result)));
2134 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2136 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2137 Map to the same status code as Windows 2003. */
2139 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2140 result = NT_STATUS_PASSWORD_RESTRICTION;
2144 done:
2146 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2148 /* Update the single sign-on memory creds. */
2149 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2150 newpass);
2152 /* When we login from gdm or xdm and password expires,
2153 * we change password, but there are no memory crendentials
2154 * So, winbindd_replace_memory_creds() returns
2155 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2156 * --- BoYang
2157 * */
2158 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2159 result = NT_STATUS_OK;
2162 if (!NT_STATUS_IS_OK(result)) {
2163 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2164 goto process_result;
2167 if (lp_winbind_offline_logon()) {
2168 result = winbindd_update_creds_by_name(contact_domain,
2169 state->mem_ctx, user,
2170 newpass);
2171 /* Again, this happens when we login from gdm or xdm
2172 * and the password expires, *BUT* cached crendentials
2173 * doesn't exist. winbindd_update_creds_by_name()
2174 * returns NT_STATUS_NO_SUCH_USER.
2175 * This is not a failure.
2176 * --- BoYang
2177 * */
2178 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2179 result = NT_STATUS_OK;
2182 if (!NT_STATUS_IS_OK(result)) {
2183 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2184 goto process_result;
2189 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2191 NTSTATUS policy_ret;
2193 policy_ret = fillup_password_policy(contact_domain, state);
2195 /* failure of this is non critical, it will just provide no
2196 * additional information to the client why the change has
2197 * failed - Guenther */
2199 if (!NT_STATUS_IS_OK(policy_ret)) {
2200 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2201 goto process_result;
2205 process_result:
2207 set_auth_errors(&state->response, result);
2209 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2210 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2211 domain,
2212 user,
2213 state->response.data.auth.nt_status_string,
2214 state->response.data.auth.pam_error));
2216 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2219 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2221 struct winbindd_domain *domain;
2222 fstring name_domain, user;
2223 uid_t caller_uid = (uid_t)-1;
2224 uid_t request_uid = state->request.data.logoff.uid;
2226 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2227 state->request.data.logoff.user));
2229 /* Ensure null termination */
2230 state->request.data.logoff.user
2231 [sizeof(state->request.data.logoff.user)-1]='\0';
2233 state->request.data.logoff.krb5ccname
2234 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2236 if (request_uid == (gid_t)-1) {
2237 goto failed;
2240 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2241 goto failed;
2244 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2245 goto failed;
2248 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2249 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2250 strerror(errno)));
2251 goto failed;
2254 switch (caller_uid) {
2255 case -1:
2256 goto failed;
2257 case 0:
2258 /* root must be able to logoff any user - gd */
2259 state->request.data.logoff.uid = request_uid;
2260 break;
2261 default:
2262 if (caller_uid != request_uid) {
2263 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2264 goto failed;
2266 state->request.data.logoff.uid = caller_uid;
2267 break;
2270 sendto_domain(state, domain);
2271 return;
2273 failed:
2274 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2275 DEBUG(5, ("Pam Logoff for %s returned %s "
2276 "(PAM: %d)\n",
2277 state->request.data.logoff.user,
2278 state->response.data.auth.nt_status_string,
2279 state->response.data.auth.pam_error));
2280 request_error(state);
2281 return;
2284 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2285 struct winbindd_cli_state *state)
2287 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2289 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2290 state->request.data.logoff.user));
2292 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2293 result = NT_STATUS_OK;
2294 goto process_result;
2297 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2298 result = NT_STATUS_OK;
2299 goto process_result;
2302 #ifdef HAVE_KRB5
2304 if (state->request.data.logoff.uid < 0) {
2305 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2306 goto process_result;
2309 /* what we need here is to find the corresponding krb5 ccache name *we*
2310 * created for a given username and destroy it */
2312 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2313 result = NT_STATUS_OK;
2314 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2315 goto process_result;
2318 if (!ccache_entry_identical(state->request.data.logoff.user,
2319 state->request.data.logoff.uid,
2320 state->request.data.logoff.krb5ccname)) {
2321 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2322 goto process_result;
2325 result = remove_ccache(state->request.data.logoff.user);
2326 if (!NT_STATUS_IS_OK(result)) {
2327 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2328 nt_errstr(result)));
2329 goto process_result;
2332 #else
2333 result = NT_STATUS_NOT_SUPPORTED;
2334 #endif
2336 process_result:
2338 winbindd_delete_memory_creds(state->request.data.logoff.user);
2340 set_auth_errors(&state->response, result);
2342 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2345 /* Change user password with auth crap*/
2347 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2349 struct winbindd_domain *domain = NULL;
2350 const char *domain_name = NULL;
2352 /* Ensure null termination */
2353 state->request.data.chng_pswd_auth_crap.user[
2354 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2355 state->request.data.chng_pswd_auth_crap.domain[
2356 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2358 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2359 (unsigned long)state->pid,
2360 state->request.data.chng_pswd_auth_crap.domain,
2361 state->request.data.chng_pswd_auth_crap.user));
2363 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2364 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2365 } else if (lp_winbind_use_default_domain()) {
2366 domain_name = lp_workgroup();
2369 if (domain_name != NULL)
2370 domain = find_domain_from_name(domain_name);
2372 if (domain != NULL) {
2373 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2374 "%s\n", (unsigned long)state->pid,domain->name));
2375 sendto_domain(state, domain);
2376 return;
2379 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2380 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2381 state->request.data.chng_pswd_auth_crap.domain,
2382 state->request.data.chng_pswd_auth_crap.user,
2383 state->response.data.auth.nt_status_string,
2384 state->response.data.auth.pam_error));
2385 request_error(state);
2386 return;
2389 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2391 NTSTATUS result;
2392 DATA_BLOB new_nt_password;
2393 DATA_BLOB old_nt_hash_enc;
2394 DATA_BLOB new_lm_password;
2395 DATA_BLOB old_lm_hash_enc;
2396 fstring domain,user;
2397 POLICY_HND dom_pol;
2398 struct winbindd_domain *contact_domain = domainSt;
2399 struct rpc_pipe_client *cli;
2401 /* Ensure null termination */
2402 state->request.data.chng_pswd_auth_crap.user[
2403 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2404 state->request.data.chng_pswd_auth_crap.domain[
2405 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2406 *domain = 0;
2407 *user = 0;
2409 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2410 (unsigned long)state->pid,
2411 state->request.data.chng_pswd_auth_crap.domain,
2412 state->request.data.chng_pswd_auth_crap.user));
2414 if (lp_winbind_offline_logon()) {
2415 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2416 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2417 result = NT_STATUS_ACCESS_DENIED;
2418 goto done;
2421 if (*state->request.data.chng_pswd_auth_crap.domain) {
2422 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2423 } else {
2424 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2425 domain, user);
2427 if(!*domain) {
2428 DEBUG(3,("no domain specified with username (%s) - "
2429 "failing auth\n",
2430 state->request.data.chng_pswd_auth_crap.user));
2431 result = NT_STATUS_NO_SUCH_USER;
2432 goto done;
2436 if (!*domain && lp_winbind_use_default_domain()) {
2437 fstrcpy(domain,(char *)lp_workgroup());
2440 if(!*user) {
2441 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2444 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2445 (unsigned long)state->pid, domain, user));
2447 /* Change password */
2448 new_nt_password = data_blob_talloc(
2449 state->mem_ctx,
2450 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2451 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2453 old_nt_hash_enc = data_blob_talloc(
2454 state->mem_ctx,
2455 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2456 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2458 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2459 new_lm_password = data_blob_talloc(
2460 state->mem_ctx,
2461 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2462 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2464 old_lm_hash_enc = data_blob_talloc(
2465 state->mem_ctx,
2466 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2467 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2468 } else {
2469 new_lm_password.length = 0;
2470 old_lm_hash_enc.length = 0;
2473 /* Get sam handle */
2475 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2476 if (!NT_STATUS_IS_OK(result)) {
2477 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2478 goto done;
2481 result = rpccli_samr_chng_pswd_auth_crap(
2482 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2483 new_lm_password, old_lm_hash_enc);
2485 done:
2487 set_auth_errors(&state->response, result);
2489 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2490 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2491 domain, user,
2492 state->response.data.auth.nt_status_string,
2493 state->response.data.auth.pam_error));
2495 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;