s3:loadparm: fix the reload of the configuration: also reload activated registry...
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob5c56b8731ab5838faf5bfe5a2d83bf50c11a19ad
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "smb_krb5.h"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
36 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
37 struct winbindd_cli_state *state,
38 struct netr_SamInfo3 *info3)
40 char *ex;
41 uint32_t i;
43 state->response->data.auth.info3.logon_time =
44 nt_time_to_unix(info3->base.last_logon);
45 state->response->data.auth.info3.logoff_time =
46 nt_time_to_unix(info3->base.last_logoff);
47 state->response->data.auth.info3.kickoff_time =
48 nt_time_to_unix(info3->base.acct_expiry);
49 state->response->data.auth.info3.pass_last_set_time =
50 nt_time_to_unix(info3->base.last_password_change);
51 state->response->data.auth.info3.pass_can_change_time =
52 nt_time_to_unix(info3->base.allow_password_change);
53 state->response->data.auth.info3.pass_must_change_time =
54 nt_time_to_unix(info3->base.force_password_change);
56 state->response->data.auth.info3.logon_count = info3->base.logon_count;
57 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
59 state->response->data.auth.info3.user_rid = info3->base.rid;
60 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
61 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
63 state->response->data.auth.info3.num_groups = info3->base.groups.count;
64 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
66 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
67 state->response->data.auth.info3.num_other_sids = info3->sidcount;
69 fstrcpy(state->response->data.auth.info3.user_name,
70 info3->base.account_name.string);
71 fstrcpy(state->response->data.auth.info3.full_name,
72 info3->base.full_name.string);
73 fstrcpy(state->response->data.auth.info3.logon_script,
74 info3->base.logon_script.string);
75 fstrcpy(state->response->data.auth.info3.profile_path,
76 info3->base.profile_path.string);
77 fstrcpy(state->response->data.auth.info3.home_dir,
78 info3->base.home_directory.string);
79 fstrcpy(state->response->data.auth.info3.dir_drive,
80 info3->base.home_drive.string);
82 fstrcpy(state->response->data.auth.info3.logon_srv,
83 info3->base.logon_server.string);
84 fstrcpy(state->response->data.auth.info3.logon_dom,
85 info3->base.domain.string);
87 ex = talloc_strdup(state->mem_ctx, "");
88 NT_STATUS_HAVE_NO_MEMORY(ex);
90 for (i=0; i < info3->base.groups.count; i++) {
91 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
92 info3->base.groups.rids[i].rid,
93 info3->base.groups.rids[i].attributes);
94 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->sidcount; i++) {
98 char *sid;
100 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
101 NT_STATUS_HAVE_NO_MEMORY(sid);
103 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
104 sid,
105 info3->sids[i].attributes);
106 NT_STATUS_HAVE_NO_MEMORY(ex);
108 talloc_free(sid);
111 state->response->extra_data.data = ex;
112 state->response->length += talloc_get_size(ex);
114 return NT_STATUS_OK;
117 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
118 struct winbindd_cli_state *state,
119 struct netr_SamInfo3 *info3)
121 DATA_BLOB blob;
122 enum ndr_err_code ndr_err;
124 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
125 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
126 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
127 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
128 return ndr_map_error2ntstatus(ndr_err);
131 state->response->extra_data.data = blob.data;
132 state->response->length += blob.length;
134 return NT_STATUS_OK;
137 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
138 struct winbindd_cli_state *state,
139 const struct netr_SamInfo3 *info3,
140 const char *name_domain,
141 const char *name_user)
143 /* We've been asked to return the unix username, per
144 'winbind use default domain' settings and the like */
146 const char *nt_username, *nt_domain;
148 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
149 if (!nt_domain) {
150 /* If the server didn't give us one, just use the one
151 * we sent them */
152 nt_domain = name_domain;
155 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
156 if (!nt_username) {
157 /* If the server didn't give us one, just use the one
158 * we sent them */
159 nt_username = name_user;
162 fill_domain_username(state->response->data.auth.unix_username,
163 nt_domain, nt_username, true);
165 DEBUG(5,("Setting unix username to [%s]\n",
166 state->response->data.auth.unix_username));
168 return NT_STATUS_OK;
171 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
172 struct winbindd_cli_state *state,
173 const struct netr_SamInfo3 *info3,
174 const char *name_domain,
175 const char *name_user)
177 char *afsname = NULL;
178 char *cell;
179 char *token;
181 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
182 if (afsname == NULL) {
183 return NT_STATUS_NO_MEMORY;
186 afsname = talloc_string_sub(mem_ctx,
187 lp_afs_username_map(),
188 "%D", name_domain);
189 afsname = talloc_string_sub(mem_ctx, afsname,
190 "%u", name_user);
191 afsname = talloc_string_sub(mem_ctx, afsname,
192 "%U", name_user);
195 DOM_SID user_sid;
196 fstring sidstr;
198 sid_copy(&user_sid, info3->base.domain_sid);
199 sid_append_rid(&user_sid, info3->base.rid);
200 sid_to_fstring(sidstr, &user_sid);
201 afsname = talloc_string_sub(mem_ctx, afsname,
202 "%s", sidstr);
205 if (afsname == NULL) {
206 return NT_STATUS_NO_MEMORY;
209 strlower_m(afsname);
211 DEBUG(10, ("Generating token for user %s\n", afsname));
213 cell = strchr(afsname, '@');
215 if (cell == NULL) {
216 return NT_STATUS_NO_MEMORY;
219 *cell = '\0';
220 cell += 1;
222 token = afs_createtoken_str(afsname, cell);
223 if (token == NULL) {
224 return NT_STATUS_OK;
226 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
227 token);
228 if (state->response->extra_data.data == NULL) {
229 return NT_STATUS_NO_MEMORY;
231 state->response->length +=
232 strlen((const char *)state->response->extra_data.data)+1;
234 return NT_STATUS_OK;
237 NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
238 const char *group_sid)
240 * Check whether a user belongs to a group or list of groups.
242 * @param mem_ctx talloc memory context.
243 * @param info3 user information, including group membership info.
244 * @param group_sid One or more groups , separated by commas.
246 * @return NT_STATUS_OK on success,
247 * NT_STATUS_LOGON_FAILURE if the user does not belong,
248 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
251 DOM_SID *require_membership_of_sid;
252 size_t num_require_membership_of_sid;
253 char *req_sid;
254 const char *p;
255 DOM_SID sid;
256 size_t i;
257 struct nt_user_token *token;
258 TALLOC_CTX *frame = talloc_stackframe();
259 NTSTATUS status;
261 /* Parse the 'required group' SID */
263 if (!group_sid || !group_sid[0]) {
264 /* NO sid supplied, all users may access */
265 return NT_STATUS_OK;
268 token = talloc_zero(talloc_tos(), struct nt_user_token);
269 if (token == NULL) {
270 DEBUG(0, ("talloc failed\n"));
271 TALLOC_FREE(frame);
272 return NT_STATUS_NO_MEMORY;
275 num_require_membership_of_sid = 0;
276 require_membership_of_sid = NULL;
278 p = group_sid;
280 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
281 if (!string_to_sid(&sid, req_sid)) {
282 DEBUG(0, ("check_info3_in_group: could not parse %s "
283 "as a SID!", req_sid));
284 TALLOC_FREE(frame);
285 return NT_STATUS_INVALID_PARAMETER;
288 status = add_sid_to_array(talloc_tos(), &sid,
289 &require_membership_of_sid,
290 &num_require_membership_of_sid);
291 if (!NT_STATUS_IS_OK(status)) {
292 DEBUG(0, ("add_sid_to_array failed\n"));
293 TALLOC_FREE(frame);
294 return status;
298 status = sid_array_from_info3(talloc_tos(), info3,
299 &token->user_sids,
300 &token->num_sids,
301 true, false);
302 if (!NT_STATUS_IS_OK(status)) {
303 TALLOC_FREE(frame);
304 return status;
307 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
308 token))
309 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
310 token))) {
311 DEBUG(3, ("could not add aliases: %s\n",
312 nt_errstr(status)));
313 TALLOC_FREE(frame);
314 return status;
317 debug_nt_user_token(DBGC_CLASS, 10, token);
319 for (i=0; i<num_require_membership_of_sid; i++) {
320 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
321 &require_membership_of_sid[i])));
322 if (nt_token_check_sid(&require_membership_of_sid[i],
323 token)) {
324 DEBUG(10, ("Access ok\n"));
325 TALLOC_FREE(frame);
326 return NT_STATUS_OK;
330 /* Do not distinguish this error from a wrong username/pw */
332 TALLOC_FREE(frame);
333 return NT_STATUS_LOGON_FAILURE;
336 struct winbindd_domain *find_auth_domain(uint8_t flags,
337 const char *domain_name)
339 struct winbindd_domain *domain;
341 if (IS_DC) {
342 domain = find_domain_from_name_noinit(domain_name);
343 if (domain == NULL) {
344 DEBUG(3, ("Authentication for domain [%s] refused "
345 "as it is not a trusted domain\n",
346 domain_name));
348 return domain;
351 if (is_myname(domain_name)) {
352 DEBUG(3, ("Authentication for domain %s (local domain "
353 "to this server) not supported at this "
354 "stage\n", domain_name));
355 return NULL;
358 /* we can auth against trusted domains */
359 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
360 domain = find_domain_from_name_noinit(domain_name);
361 if (domain == NULL) {
362 DEBUG(3, ("Authentication for domain [%s] skipped "
363 "as it is not a trusted domain\n",
364 domain_name));
365 } else {
366 return domain;
370 return find_our_domain();
373 static void fill_in_password_policy(struct winbindd_response *r,
374 const struct samr_DomInfo1 *p)
376 r->data.auth.policy.min_length_password =
377 p->min_password_length;
378 r->data.auth.policy.password_history =
379 p->password_history_length;
380 r->data.auth.policy.password_properties =
381 p->password_properties;
382 r->data.auth.policy.expire =
383 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
384 r->data.auth.policy.min_passwordage =
385 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
388 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
389 struct winbindd_cli_state *state)
391 struct winbindd_methods *methods;
392 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
393 struct samr_DomInfo1 password_policy;
395 if ( !winbindd_can_contact_domain( domain ) ) {
396 DEBUG(5,("fillup_password_policy: No inbound trust to "
397 "contact domain %s\n", domain->name));
398 return NT_STATUS_NOT_SUPPORTED;
401 methods = domain->methods;
403 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
404 if (NT_STATUS_IS_ERR(status)) {
405 return status;
408 fill_in_password_policy(state->response, &password_policy);
410 return NT_STATUS_OK;
413 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
414 TALLOC_CTX *mem_ctx,
415 uint16 *lockout_threshold)
417 struct winbindd_methods *methods;
418 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
419 struct samr_DomInfo12 lockout_policy;
421 *lockout_threshold = 0;
423 methods = domain->methods;
425 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
426 if (NT_STATUS_IS_ERR(status)) {
427 return status;
430 *lockout_threshold = lockout_policy.lockout_threshold;
432 return NT_STATUS_OK;
435 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
436 TALLOC_CTX *mem_ctx,
437 uint32 *password_properties)
439 struct winbindd_methods *methods;
440 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
441 struct samr_DomInfo1 password_policy;
443 *password_properties = 0;
445 methods = domain->methods;
447 status = methods->password_policy(domain, mem_ctx, &password_policy);
448 if (NT_STATUS_IS_ERR(status)) {
449 return status;
452 *password_properties = password_policy.password_properties;
454 return NT_STATUS_OK;
457 #ifdef HAVE_KRB5
459 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
460 const char *type,
461 uid_t uid,
462 bool *internal_ccache)
464 /* accept FILE and WRFILE as krb5_cc_type from the client and then
465 * build the full ccname string based on the user's uid here -
466 * Guenther*/
468 const char *gen_cc = NULL;
470 *internal_ccache = true;
472 if (uid == -1) {
473 goto memory_ccache;
476 if (!type || type[0] == '\0') {
477 goto memory_ccache;
480 if (strequal(type, "FILE")) {
481 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
482 } else if (strequal(type, "WRFILE")) {
483 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
484 } else {
485 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
486 goto memory_ccache;
489 *internal_ccache = false;
490 goto done;
492 memory_ccache:
493 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
495 done:
496 if (gen_cc == NULL) {
497 DEBUG(0,("out of memory\n"));
498 return NULL;
501 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
503 return gen_cc;
506 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
508 const char *type = state->request->data.auth.krb5_cc_type;
510 state->response->data.auth.krb5ccname[0] = '\0';
512 if (type[0] == '\0') {
513 return;
516 if (!strequal(type, "FILE") &&
517 !strequal(type, "WRFILE")) {
518 DEBUG(10,("won't return krbccname for a %s type ccache\n",
519 type));
520 return;
523 fstrcpy(state->response->data.auth.krb5ccname, cc);
526 #endif
528 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
530 uid_t uid;
532 uid = state->request->data.auth.uid;
534 if (uid < 0) {
535 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
536 return -1;
538 return uid;
541 /**********************************************************************
542 Authenticate a user with a clear text password using Kerberos and fill up
543 ccache if required
544 **********************************************************************/
546 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
547 struct winbindd_cli_state *state,
548 struct netr_SamInfo3 **info3)
550 #ifdef HAVE_KRB5
551 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
552 krb5_error_code krb5_ret;
553 const char *cc = NULL;
554 const char *principal_s = NULL;
555 const char *service = NULL;
556 char *realm = NULL;
557 fstring name_domain, name_user;
558 time_t ticket_lifetime = 0;
559 time_t renewal_until = 0;
560 uid_t uid = -1;
561 ADS_STRUCT *ads;
562 time_t time_offset = 0;
563 bool internal_ccache = true;
565 ZERO_STRUCTP(info3);
567 *info3 = NULL;
569 /* 1st step:
570 * prepare a krb5_cc_cache string for the user */
572 uid = get_uid_from_state(state);
573 if (uid == -1) {
574 DEBUG(0,("no valid uid\n"));
577 cc = generate_krb5_ccache(state->mem_ctx,
578 state->request->data.auth.krb5_cc_type,
579 state->request->data.auth.uid,
580 &internal_ccache);
581 if (cc == NULL) {
582 return NT_STATUS_NO_MEMORY;
586 /* 2nd step:
587 * get kerberos properties */
589 if (domain->private_data) {
590 ads = (ADS_STRUCT *)domain->private_data;
591 time_offset = ads->auth.time_offset;
595 /* 3rd step:
596 * do kerberos auth and setup ccache as the user */
598 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
600 realm = domain->alt_name;
601 strupper_m(realm);
603 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
604 if (principal_s == NULL) {
605 return NT_STATUS_NO_MEMORY;
608 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
609 if (service == NULL) {
610 return NT_STATUS_NO_MEMORY;
613 /* if this is a user ccache, we need to act as the user to let the krb5
614 * library handle the chown, etc. */
616 /************************ ENTERING NON-ROOT **********************/
618 if (!internal_ccache) {
619 set_effective_uid(uid);
620 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
623 result = kerberos_return_info3_from_pac(state->mem_ctx,
624 principal_s,
625 state->request->data.auth.pass,
626 time_offset,
627 &ticket_lifetime,
628 &renewal_until,
630 true,
631 true,
632 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
633 NULL,
634 info3);
635 if (!internal_ccache) {
636 gain_root_privilege();
639 /************************ RETURNED TO ROOT **********************/
641 if (!NT_STATUS_IS_OK(result)) {
642 goto failed;
645 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
646 principal_s));
648 /* if we had a user's ccache then return that string for the pam
649 * environment */
651 if (!internal_ccache) {
653 setup_return_cc_name(state, cc);
655 result = add_ccache_to_list(principal_s,
657 service,
658 state->request->data.auth.user,
659 realm,
660 uid,
661 time(NULL),
662 ticket_lifetime,
663 renewal_until,
664 false);
666 if (!NT_STATUS_IS_OK(result)) {
667 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
668 nt_errstr(result)));
670 } else {
672 /* need to delete the memory cred cache, it is not used anymore */
674 krb5_ret = ads_kdestroy(cc);
675 if (krb5_ret) {
676 DEBUG(3,("winbindd_raw_kerberos_login: "
677 "could not destroy krb5 credential cache: "
678 "%s\n", error_message(krb5_ret)));
683 return NT_STATUS_OK;
685 failed:
687 /* we could have created a new credential cache with a valid tgt in it
688 * but we werent able to get or verify the service ticket for this
689 * local host and therefor didn't get the PAC, we need to remove that
690 * cache entirely now */
692 krb5_ret = ads_kdestroy(cc);
693 if (krb5_ret) {
694 DEBUG(3,("winbindd_raw_kerberos_login: "
695 "could not destroy krb5 credential cache: "
696 "%s\n", error_message(krb5_ret)));
699 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
700 DEBUG(3,("winbindd_raw_kerberos_login: "
701 "could not remove ccache for user %s\n",
702 state->request->data.auth.user));
705 return result;
706 #else
707 return NT_STATUS_NOT_SUPPORTED;
708 #endif /* HAVE_KRB5 */
711 /****************************************************************
712 ****************************************************************/
714 bool check_request_flags(uint32_t flags)
716 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
717 WBFLAG_PAM_INFO3_TEXT |
718 WBFLAG_PAM_INFO3_NDR;
720 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
721 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
722 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
723 !(flags & flags_edata) ) {
724 return true;
727 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
728 flags));
730 return false;
733 /****************************************************************
734 ****************************************************************/
736 NTSTATUS append_auth_data(struct winbindd_cli_state *state,
737 struct netr_SamInfo3 *info3,
738 const char *name_domain,
739 const char *name_user)
741 NTSTATUS result;
742 uint32_t flags = state->request->flags;
744 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
745 memcpy(state->response->data.auth.user_session_key,
746 info3->base.key.key,
747 sizeof(state->response->data.auth.user_session_key)
748 /* 16 */);
751 if (flags & WBFLAG_PAM_LMKEY) {
752 memcpy(state->response->data.auth.first_8_lm_hash,
753 info3->base.LMSessKey.key,
754 sizeof(state->response->data.auth.first_8_lm_hash)
755 /* 8 */);
758 if (flags & WBFLAG_PAM_INFO3_TEXT) {
759 result = append_info3_as_txt(state->mem_ctx, state, info3);
760 if (!NT_STATUS_IS_OK(result)) {
761 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
762 nt_errstr(result)));
763 return result;
767 /* currently, anything from here on potentially overwrites extra_data. */
769 if (flags & WBFLAG_PAM_INFO3_NDR) {
770 result = append_info3_as_ndr(state->mem_ctx, state, info3);
771 if (!NT_STATUS_IS_OK(result)) {
772 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
773 nt_errstr(result)));
774 return result;
778 if (flags & WBFLAG_PAM_UNIX_NAME) {
779 result = append_unix_username(state->mem_ctx, state, info3,
780 name_domain, name_user);
781 if (!NT_STATUS_IS_OK(result)) {
782 DEBUG(10,("Failed to append Unix Username: %s\n",
783 nt_errstr(result)));
784 return result;
788 if (flags & WBFLAG_PAM_AFS_TOKEN) {
789 result = append_afs_token(state->mem_ctx, state, info3,
790 name_domain, name_user);
791 if (!NT_STATUS_IS_OK(result)) {
792 DEBUG(10,("Failed to append AFS token: %s\n",
793 nt_errstr(result)));
794 return result;
798 return NT_STATUS_OK;
801 void winbindd_pam_auth(struct winbindd_cli_state *state)
803 struct winbindd_domain *domain;
804 fstring name_domain, name_user;
805 char *mapped = NULL;
806 NTSTATUS result;
807 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
809 /* Ensure null termination */
810 state->request->data.auth.user
811 [sizeof(state->request->data.auth.user)-1]='\0';
813 /* Ensure null termination */
814 state->request->data.auth.pass
815 [sizeof(state->request->data.auth.pass)-1]='\0';
817 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
818 state->request->data.auth.user));
820 if (!check_request_flags(state->request->flags)) {
821 result = NT_STATUS_INVALID_PARAMETER_MIX;
822 goto done;
825 /* Parse domain and username */
827 name_map_status = normalize_name_unmap(state->mem_ctx,
828 state->request->data.auth.user,
829 &mapped);
831 /* Update the auth name if we did any mapping */
833 if (NT_STATUS_IS_OK(name_map_status) ||
834 NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
836 fstrcpy(state->request->data.auth.user, mapped);
839 if (!canonicalize_username(state->request->data.auth.user, name_domain, name_user)) {
840 result = NT_STATUS_NO_SUCH_USER;
841 goto done;
844 domain = find_auth_domain(state->request->flags, name_domain);
846 if (domain == NULL) {
847 result = NT_STATUS_NO_SUCH_USER;
848 goto done;
851 sendto_domain(state, domain);
852 return;
853 done:
854 set_auth_errors(state->response, result);
855 DEBUG(5, ("Plain text authentication for %s returned %s "
856 "(PAM: %d)\n",
857 state->request->data.auth.user,
858 state->response->data.auth.nt_status_string,
859 state->response->data.auth.pam_error));
860 request_error(state);
863 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
864 struct winbindd_cli_state *state,
865 struct netr_SamInfo3 **info3)
867 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
868 uint16 max_allowed_bad_attempts;
869 fstring name_domain, name_user;
870 DOM_SID sid;
871 enum lsa_SidType type;
872 uchar new_nt_pass[NT_HASH_LEN];
873 const uint8 *cached_nt_pass;
874 const uint8 *cached_salt;
875 struct netr_SamInfo3 *my_info3;
876 time_t kickoff_time, must_change_time;
877 bool password_good = false;
878 #ifdef HAVE_KRB5
879 struct winbindd_tdc_domain *tdc_domain = NULL;
880 #endif
882 *info3 = NULL;
884 ZERO_STRUCTP(info3);
886 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
888 /* Parse domain and username */
890 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
893 if (!lookup_cached_name(state->mem_ctx,
894 name_domain,
895 name_user,
896 &sid,
897 &type)) {
898 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
899 return NT_STATUS_NO_SUCH_USER;
902 if (type != SID_NAME_USER) {
903 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
904 return NT_STATUS_LOGON_FAILURE;
907 result = winbindd_get_creds(domain,
908 state->mem_ctx,
909 &sid,
910 &my_info3,
911 &cached_nt_pass,
912 &cached_salt);
913 if (!NT_STATUS_IS_OK(result)) {
914 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
915 return result;
918 *info3 = my_info3;
920 E_md4hash(state->request->data.auth.pass, new_nt_pass);
922 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
923 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
924 if (cached_salt) {
925 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
928 if (cached_salt) {
929 /* In this case we didn't store the nt_hash itself,
930 but the MD5 combination of salt + nt_hash. */
931 uchar salted_hash[NT_HASH_LEN];
932 E_md5hash(cached_salt, new_nt_pass, salted_hash);
934 password_good = (memcmp(cached_nt_pass, salted_hash,
935 NT_HASH_LEN) == 0);
936 } else {
937 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
938 password_good = (memcmp(cached_nt_pass, new_nt_pass,
939 NT_HASH_LEN) == 0);
942 if (password_good) {
944 /* User *DOES* know the password, update logon_time and reset
945 * bad_pw_count */
947 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
949 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
950 return NT_STATUS_ACCOUNT_LOCKED_OUT;
953 if (my_info3->base.acct_flags & ACB_DISABLED) {
954 return NT_STATUS_ACCOUNT_DISABLED;
957 if (my_info3->base.acct_flags & ACB_WSTRUST) {
958 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
961 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
962 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
965 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
966 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
969 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
970 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
971 my_info3->base.acct_flags));
972 return NT_STATUS_LOGON_FAILURE;
975 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
976 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
977 return NT_STATUS_ACCOUNT_EXPIRED;
980 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
981 if (must_change_time != 0 && must_change_time < time(NULL)) {
982 /* we allow grace logons when the password has expired */
983 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
984 /* return NT_STATUS_PASSWORD_EXPIRED; */
985 goto success;
988 #ifdef HAVE_KRB5
989 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
990 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
991 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
992 /* used to cope with the case winbindd starting without network. */
993 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
995 uid_t uid = -1;
996 const char *cc = NULL;
997 char *realm = NULL;
998 const char *principal_s = NULL;
999 const char *service = NULL;
1000 bool internal_ccache = false;
1002 uid = get_uid_from_state(state);
1003 if (uid == -1) {
1004 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1005 return NT_STATUS_INVALID_PARAMETER;
1008 cc = generate_krb5_ccache(state->mem_ctx,
1009 state->request->data.auth.krb5_cc_type,
1010 state->request->data.auth.uid,
1011 &internal_ccache);
1012 if (cc == NULL) {
1013 return NT_STATUS_NO_MEMORY;
1016 realm = domain->alt_name;
1017 strupper_m(realm);
1019 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1020 if (principal_s == NULL) {
1021 return NT_STATUS_NO_MEMORY;
1024 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1025 if (service == NULL) {
1026 return NT_STATUS_NO_MEMORY;
1029 if (!internal_ccache) {
1031 setup_return_cc_name(state, cc);
1033 result = add_ccache_to_list(principal_s,
1035 service,
1036 state->request->data.auth.user,
1037 domain->alt_name,
1038 uid,
1039 time(NULL),
1040 time(NULL) + lp_winbind_cache_time(),
1041 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1042 true);
1044 if (!NT_STATUS_IS_OK(result)) {
1045 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1046 "to add ccache to list: %s\n",
1047 nt_errstr(result)));
1051 #endif /* HAVE_KRB5 */
1052 success:
1053 /* FIXME: we possibly should handle logon hours as well (does xp when
1054 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1056 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1057 my_info3->base.bad_password_count = 0;
1059 result = winbindd_update_creds_by_info3(domain,
1060 state->mem_ctx,
1061 state->request->data.auth.user,
1062 state->request->data.auth.pass,
1063 my_info3);
1064 if (!NT_STATUS_IS_OK(result)) {
1065 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1066 nt_errstr(result)));
1067 return result;
1070 return NT_STATUS_OK;
1074 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1075 if (domain->online == false) {
1076 goto failed;
1079 /* failure of this is not critical */
1080 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1081 if (!NT_STATUS_IS_OK(result)) {
1082 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1083 "Won't be able to honour account lockout policies\n"));
1086 /* increase counter */
1087 my_info3->base.bad_password_count++;
1089 if (max_allowed_bad_attempts == 0) {
1090 goto failed;
1093 /* lockout user */
1094 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1096 uint32 password_properties;
1098 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1099 if (!NT_STATUS_IS_OK(result)) {
1100 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1103 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1104 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1105 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1109 failed:
1110 result = winbindd_update_creds_by_info3(domain,
1111 state->mem_ctx,
1112 state->request->data.auth.user,
1113 NULL,
1114 my_info3);
1116 if (!NT_STATUS_IS_OK(result)) {
1117 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1118 nt_errstr(result)));
1121 return NT_STATUS_LOGON_FAILURE;
1124 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1125 struct winbindd_cli_state *state,
1126 struct netr_SamInfo3 **info3)
1128 struct winbindd_domain *contact_domain;
1129 fstring name_domain, name_user;
1130 NTSTATUS result;
1132 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1134 /* Parse domain and username */
1136 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1138 /* what domain should we contact? */
1140 if ( IS_DC ) {
1141 if (!(contact_domain = find_domain_from_name(name_domain))) {
1142 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1143 state->request->data.auth.user, name_domain, name_user, name_domain));
1144 result = NT_STATUS_NO_SUCH_USER;
1145 goto done;
1148 } else {
1149 if (is_myname(name_domain)) {
1150 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1151 result = NT_STATUS_NO_SUCH_USER;
1152 goto done;
1155 contact_domain = find_domain_from_name(name_domain);
1156 if (contact_domain == NULL) {
1157 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1158 state->request->data.auth.user, name_domain, name_user, name_domain));
1160 contact_domain = find_our_domain();
1164 if (contact_domain->initialized &&
1165 contact_domain->active_directory) {
1166 goto try_login;
1169 if (!contact_domain->initialized) {
1170 init_dc_connection(contact_domain);
1173 if (!contact_domain->active_directory) {
1174 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1175 return NT_STATUS_INVALID_LOGON_TYPE;
1177 try_login:
1178 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1179 done:
1180 return result;
1183 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1184 TALLOC_CTX *mem_ctx,
1185 uint32 logon_parameters,
1186 const char *server,
1187 const char *username,
1188 const char *domain,
1189 const char *workstation,
1190 const uint8 chal[8],
1191 uint16_t validation_level,
1192 DATA_BLOB lm_response,
1193 DATA_BLOB nt_response,
1194 struct netr_SamInfo3 **info3);
1196 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1197 struct winbindd_cli_state *state,
1198 struct netr_SamInfo3 **info3)
1201 struct rpc_pipe_client *netlogon_pipe;
1202 uchar chal[8];
1203 DATA_BLOB lm_resp;
1204 DATA_BLOB nt_resp;
1205 int attempts = 0;
1206 unsigned char local_lm_response[24];
1207 unsigned char local_nt_response[24];
1208 struct winbindd_domain *contact_domain;
1209 fstring name_domain, name_user;
1210 bool retry;
1211 NTSTATUS result;
1212 struct netr_SamInfo3 *my_info3 = NULL;
1214 *info3 = NULL;
1216 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1218 /* Parse domain and username */
1220 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1222 /* do password magic */
1225 generate_random_buffer(chal, 8);
1226 if (lp_client_ntlmv2_auth()) {
1227 DATA_BLOB server_chal;
1228 DATA_BLOB names_blob;
1229 DATA_BLOB nt_response;
1230 DATA_BLOB lm_response;
1231 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1233 /* note that the 'workgroup' here is a best guess - we don't know
1234 the server's domain at this point. The 'server name' is also
1235 dodgy...
1237 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1239 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1240 state->request->data.auth.pass,
1241 &server_chal,
1242 &names_blob,
1243 &lm_response, &nt_response, NULL, NULL)) {
1244 data_blob_free(&names_blob);
1245 data_blob_free(&server_chal);
1246 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1247 result = NT_STATUS_NO_MEMORY;
1248 goto done;
1250 data_blob_free(&names_blob);
1251 data_blob_free(&server_chal);
1252 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1253 lm_response.length);
1254 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1255 nt_response.length);
1256 data_blob_free(&lm_response);
1257 data_blob_free(&nt_response);
1259 } else {
1260 if (lp_client_lanman_auth()
1261 && SMBencrypt(state->request->data.auth.pass,
1262 chal,
1263 local_lm_response)) {
1264 lm_resp = data_blob_talloc(state->mem_ctx,
1265 local_lm_response,
1266 sizeof(local_lm_response));
1267 } else {
1268 lm_resp = data_blob_null;
1270 SMBNTencrypt(state->request->data.auth.pass,
1271 chal,
1272 local_nt_response);
1274 nt_resp = data_blob_talloc(state->mem_ctx,
1275 local_nt_response,
1276 sizeof(local_nt_response));
1279 /* what domain should we contact? */
1281 if ( IS_DC ) {
1282 if (!(contact_domain = find_domain_from_name(name_domain))) {
1283 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1284 state->request->data.auth.user, name_domain, name_user, name_domain));
1285 result = NT_STATUS_NO_SUCH_USER;
1286 goto done;
1289 } else {
1290 if (is_myname(name_domain)) {
1291 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1292 result = NT_STATUS_NO_SUCH_USER;
1293 goto done;
1296 contact_domain = find_our_domain();
1299 /* check authentication loop */
1301 do {
1302 netlogon_fn_t logon_fn;
1303 const struct cli_pipe_auth_data *auth;
1304 uint32_t neg_flags = 0;
1306 ZERO_STRUCTP(my_info3);
1307 retry = false;
1309 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1311 if (!NT_STATUS_IS_OK(result)) {
1312 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1313 goto done;
1315 auth = netlogon_pipe->auth;
1316 if (netlogon_pipe->dc) {
1317 neg_flags = netlogon_pipe->dc->negotiate_flags;
1320 /* It is really important to try SamLogonEx here,
1321 * because in a clustered environment, we want to use
1322 * one machine account from multiple physical
1323 * computers.
1325 * With a normal SamLogon call, we must keep the
1326 * credentials chain updated and intact between all
1327 * users of the machine account (which would imply
1328 * cross-node communication for every NTLM logon).
1330 * (The credentials chain is not per NETLOGON pipe
1331 * connection, but globally on the server/client pair
1332 * by machine name).
1334 * When using SamLogonEx, the credentials are not
1335 * supplied, but the session key is implied by the
1336 * wrapping SamLogon context.
1338 * -- abartlet 21 April 2008
1340 * It's also important to use NetlogonValidationSamInfo4 (6),
1341 * because it relies on the rpc transport encryption
1342 * and avoids using the global netlogon schannel
1343 * session key to en/decrypt secret information
1344 * like the user_session_key for network logons.
1346 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1347 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1348 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1349 * are the indication that the server supports
1350 * NetlogonValidationSamInfo4 (6). And must only
1351 * be used if "SealSecureChannel" is used.
1353 * -- metze 4 February 2011
1356 if (auth == NULL) {
1357 domain->can_do_validation6 = false;
1358 } else if (auth->auth_type != PIPE_AUTH_TYPE_SCHANNEL) {
1359 domain->can_do_validation6 = false;
1360 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1361 domain->can_do_validation6 = false;
1362 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1363 domain->can_do_validation6 = false;
1364 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1365 domain->can_do_validation6 = false;
1368 logon_fn = contact_domain->can_do_samlogon_ex
1369 ? rpccli_netlogon_sam_network_logon_ex
1370 : rpccli_netlogon_sam_network_logon;
1372 result = logon_fn(netlogon_pipe,
1373 state->mem_ctx,
1375 contact_domain->dcname, /* server name */
1376 name_user, /* user name */
1377 name_domain, /* target domain */
1378 global_myname(), /* workstation */
1379 chal,
1380 domain->can_do_validation6 ? 6 : 3,
1381 lm_resp,
1382 nt_resp,
1383 &my_info3);
1385 if (NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) {
1388 * It's likely that the server also does not support
1389 * validation level 6
1391 domain->can_do_validation6 = false;
1393 if (contact_domain->can_do_samlogon_ex) {
1394 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1395 "retrying with NetSamLogon\n"));
1396 contact_domain->can_do_samlogon_ex = false;
1397 retry = true;
1398 continue;
1401 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1402 * (no Ex). This happens against old Samba
1403 * DCs. Drop the connection.
1405 invalidate_cm_connection(&contact_domain->conn);
1406 result = NT_STATUS_LOGON_FAILURE;
1407 break;
1410 if (domain->can_do_validation6 &&
1411 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1412 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1413 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1414 DEBUG(3,("Got a DC that can not do validation level 6, "
1415 "retrying with level 3\n"));
1416 domain->can_do_validation6 = false;
1417 retry = true;
1418 continue;
1422 * we increment this after the "feature negotiation"
1423 * for can_do_samlogon_ex and can_do_validation6
1425 attempts += 1;
1427 /* We have to try a second time as cm_connect_netlogon
1428 might not yet have noticed that the DC has killed
1429 our connection. */
1431 if (!rpccli_is_connected(netlogon_pipe)) {
1432 retry = true;
1433 continue;
1436 /* if we get access denied, a possible cause was that we had
1437 and open connection to the DC, but someone changed our
1438 machine account password out from underneath us using 'net
1439 rpc changetrustpw' */
1441 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1442 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1443 "ACCESS_DENIED. Maybe the trust account "
1444 "password was changed and we didn't know it. "
1445 "Killing connections to domain %s\n",
1446 name_domain));
1447 invalidate_cm_connection(&contact_domain->conn);
1448 retry = true;
1451 } while ( (attempts < 2) && retry );
1453 /* handle the case where a NT4 DC does not fill in the acct_flags in
1454 * the samlogon reply info3. When accurate info3 is required by the
1455 * caller, we look up the account flags ourselve - gd */
1457 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1458 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1460 struct rpc_pipe_client *samr_pipe;
1461 struct policy_handle samr_domain_handle, user_pol;
1462 union samr_UserInfo *info = NULL;
1463 NTSTATUS status_tmp;
1464 uint32 acct_flags;
1466 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1467 &samr_pipe, &samr_domain_handle);
1469 if (!NT_STATUS_IS_OK(status_tmp)) {
1470 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1471 nt_errstr(status_tmp)));
1472 goto done;
1475 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1476 &samr_domain_handle,
1477 MAXIMUM_ALLOWED_ACCESS,
1478 my_info3->base.rid,
1479 &user_pol);
1481 if (!NT_STATUS_IS_OK(status_tmp)) {
1482 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1483 nt_errstr(status_tmp)));
1484 goto done;
1487 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1488 &user_pol,
1490 &info);
1492 if (!NT_STATUS_IS_OK(status_tmp)) {
1493 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1494 nt_errstr(status_tmp)));
1495 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1496 goto done;
1499 acct_flags = info->info16.acct_flags;
1501 if (acct_flags == 0) {
1502 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1503 goto done;
1506 my_info3->base.acct_flags = acct_flags;
1508 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1510 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1513 *info3 = my_info3;
1514 done:
1515 return result;
1518 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1519 struct winbindd_cli_state *state)
1521 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1522 NTSTATUS krb5_result = NT_STATUS_OK;
1523 fstring name_domain, name_user;
1524 char *mapped_user;
1525 fstring domain_user;
1526 struct netr_SamInfo3 *info3 = NULL;
1527 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1529 /* Ensure null termination */
1530 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1532 /* Ensure null termination */
1533 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1535 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1536 state->request->data.auth.user));
1538 if (!check_request_flags(state->request->flags)) {
1539 result = NT_STATUS_INVALID_PARAMETER_MIX;
1540 goto done;
1543 /* Parse domain and username */
1545 name_map_status = normalize_name_unmap(state->mem_ctx,
1546 state->request->data.auth.user,
1547 &mapped_user);
1549 /* If the name normalization didnt' actually do anything,
1550 just use the original name */
1552 if (!NT_STATUS_IS_OK(name_map_status) &&
1553 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1555 mapped_user = state->request->data.auth.user;
1558 parse_domain_user(mapped_user, name_domain, name_user);
1560 if ( mapped_user != state->request->data.auth.user ) {
1561 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1562 safe_strcpy( state->request->data.auth.user, domain_user,
1563 sizeof(state->request->data.auth.user)-1 );
1566 if (domain->online == false) {
1567 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1568 if (domain->startup) {
1569 /* Logons are very important to users. If we're offline and
1570 we get a request within the first 30 seconds of startup,
1571 try very hard to find a DC and go online. */
1573 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1574 "request in startup mode.\n", domain->name ));
1576 winbindd_flush_negative_conn_cache(domain);
1577 result = init_dc_connection(domain);
1581 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1583 /* Check for Kerberos authentication */
1584 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1586 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1587 /* save for later */
1588 krb5_result = result;
1591 if (NT_STATUS_IS_OK(result)) {
1592 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1593 goto process_result;
1594 } else {
1595 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1598 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1599 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1600 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1601 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1602 set_domain_offline( domain );
1603 goto cached_logon;
1606 /* there are quite some NT_STATUS errors where there is no
1607 * point in retrying with a samlogon, we explictly have to take
1608 * care not to increase the bad logon counter on the DC */
1610 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1611 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1612 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1613 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1614 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1615 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1616 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1617 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1618 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1619 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1620 goto process_result;
1623 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1624 DEBUG(3,("falling back to samlogon\n"));
1625 goto sam_logon;
1626 } else {
1627 goto cached_logon;
1631 sam_logon:
1632 /* Check for Samlogon authentication */
1633 if (domain->online) {
1634 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1636 if (NT_STATUS_IS_OK(result)) {
1637 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1638 /* add the Krb5 err if we have one */
1639 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1640 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1642 goto process_result;
1645 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1646 nt_errstr(result)));
1648 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1649 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1650 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1652 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1653 set_domain_offline( domain );
1654 goto cached_logon;
1657 if (domain->online) {
1658 /* We're still online - fail. */
1659 goto done;
1663 cached_logon:
1664 /* Check for Cached logons */
1665 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1666 lp_winbind_offline_logon()) {
1668 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1670 if (NT_STATUS_IS_OK(result)) {
1671 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1672 goto process_result;
1673 } else {
1674 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1675 goto done;
1679 process_result:
1681 if (NT_STATUS_IS_OK(result)) {
1683 DOM_SID user_sid;
1685 /* In all codepaths where result == NT_STATUS_OK info3 must have
1686 been initialized. */
1687 if (!info3) {
1688 result = NT_STATUS_INTERNAL_ERROR;
1689 goto done;
1692 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1693 netsamlogon_cache_store(name_user, info3);
1695 /* save name_to_sid info as early as possible (only if
1696 this is our primary domain so we don't invalidate
1697 the cache entry by storing the seq_num for the wrong
1698 domain). */
1699 if ( domain->primary ) {
1700 sid_compose(&user_sid, info3->base.domain_sid,
1701 info3->base.rid);
1702 cache_name2sid(domain, name_domain, name_user,
1703 SID_NAME_USER, &user_sid);
1706 /* Check if the user is in the right group */
1708 result = check_info3_in_group(
1709 info3,
1710 state->request->data.auth.require_membership_of_sid);
1711 if (!NT_STATUS_IS_OK(result)) {
1712 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1713 state->request->data.auth.user,
1714 state->request->data.auth.require_membership_of_sid));
1715 goto done;
1718 result = append_auth_data(state, info3, name_domain,
1719 name_user);
1720 if (!NT_STATUS_IS_OK(result)) {
1721 goto done;
1724 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1726 /* Store in-memory creds for single-signon using ntlm_auth. */
1727 result = winbindd_add_memory_creds(state->request->data.auth.user,
1728 get_uid_from_state(state),
1729 state->request->data.auth.pass);
1731 if (!NT_STATUS_IS_OK(result)) {
1732 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1733 goto done;
1736 if (lp_winbind_offline_logon()) {
1737 result = winbindd_store_creds(domain,
1738 state->mem_ctx,
1739 state->request->data.auth.user,
1740 state->request->data.auth.pass,
1741 info3, NULL);
1742 if (!NT_STATUS_IS_OK(result)) {
1744 /* Release refcount. */
1745 winbindd_delete_memory_creds(state->request->data.auth.user);
1747 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1748 goto done;
1754 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1755 struct winbindd_domain *our_domain = find_our_domain();
1757 /* This is not entirely correct I believe, but it is
1758 consistent. Only apply the password policy settings
1759 too warn users for our own domain. Cannot obtain these
1760 from trusted DCs all the time so don't do it at all.
1761 -- jerry */
1763 result = NT_STATUS_NOT_SUPPORTED;
1764 if (our_domain == domain ) {
1765 result = fillup_password_policy(our_domain, state);
1768 if (!NT_STATUS_IS_OK(result)
1769 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1771 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1772 domain->name, nt_errstr(result)));
1773 goto done;
1777 result = NT_STATUS_OK;
1780 done:
1781 /* give us a more useful (more correct?) error code */
1782 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1783 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1784 result = NT_STATUS_NO_LOGON_SERVERS;
1787 set_auth_errors(state->response, result);
1789 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1790 state->request->data.auth.user,
1791 state->response->data.auth.nt_status_string,
1792 state->response->data.auth.pam_error));
1794 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1798 /**********************************************************************
1799 Challenge Response Authentication Protocol
1800 **********************************************************************/
1802 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1804 struct winbindd_domain *domain = NULL;
1805 const char *domain_name = NULL;
1806 NTSTATUS result;
1808 if (!check_request_flags(state->request->flags)) {
1809 result = NT_STATUS_INVALID_PARAMETER_MIX;
1810 goto done;
1813 if (!state->privileged) {
1814 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1815 "denied. !\n"));
1816 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1817 "on %s are set correctly.\n",
1818 get_winbind_priv_pipe_dir()));
1819 /* send a better message than ACCESS_DENIED */
1820 fstr_sprintf(state->response->data.auth.error_string,
1821 "winbind client not authorized to use "
1822 "winbindd_pam_auth_crap. Ensure permissions on "
1823 "%s are set correctly.",
1824 get_winbind_priv_pipe_dir());
1825 result = NT_STATUS_ACCESS_DENIED;
1826 goto done;
1829 /* Ensure null termination */
1830 state->request->data.auth_crap.user
1831 [sizeof(state->request->data.auth_crap.user)-1]=0;
1832 state->request->data.auth_crap.domain
1833 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1835 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1836 (unsigned long)state->pid,
1837 state->request->data.auth_crap.domain,
1838 state->request->data.auth_crap.user));
1840 if (*state->request->data.auth_crap.domain != '\0') {
1841 domain_name = state->request->data.auth_crap.domain;
1842 } else if (lp_winbind_use_default_domain()) {
1843 domain_name = lp_workgroup();
1846 if (domain_name != NULL)
1847 domain = find_auth_domain(state->request->flags, domain_name);
1849 if (domain != NULL) {
1850 sendto_domain(state, domain);
1851 return;
1854 result = NT_STATUS_NO_SUCH_USER;
1856 done:
1857 set_auth_errors(state->response, result);
1858 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1859 state->request->data.auth_crap.domain,
1860 state->request->data.auth_crap.user,
1861 state->response->data.auth.nt_status_string,
1862 state->response->data.auth.pam_error));
1863 request_error(state);
1864 return;
1868 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1869 struct winbindd_cli_state *state)
1871 NTSTATUS result;
1872 struct netr_SamInfo3 *info3 = NULL;
1873 struct rpc_pipe_client *netlogon_pipe;
1874 const char *name_user = NULL;
1875 const char *name_domain = NULL;
1876 const char *workstation;
1877 struct winbindd_domain *contact_domain;
1878 int attempts = 0;
1879 bool retry;
1881 DATA_BLOB lm_resp, nt_resp;
1883 /* This is child-only, so no check for privileged access is needed
1884 anymore */
1886 /* Ensure null termination */
1887 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1888 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1890 if (!check_request_flags(state->request->flags)) {
1891 result = NT_STATUS_INVALID_PARAMETER_MIX;
1892 goto done;
1895 name_user = state->request->data.auth_crap.user;
1897 if (*state->request->data.auth_crap.domain) {
1898 name_domain = state->request->data.auth_crap.domain;
1899 } else if (lp_winbind_use_default_domain()) {
1900 name_domain = lp_workgroup();
1901 } else {
1902 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1903 name_user));
1904 result = NT_STATUS_NO_SUCH_USER;
1905 goto done;
1908 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1909 name_domain, name_user));
1911 if (*state->request->data.auth_crap.workstation) {
1912 workstation = state->request->data.auth_crap.workstation;
1913 } else {
1914 workstation = global_myname();
1917 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1918 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1919 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1920 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1921 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1922 state->request->data.auth_crap.lm_resp_len,
1923 state->request->data.auth_crap.nt_resp_len));
1924 result = NT_STATUS_INVALID_PARAMETER;
1925 goto done;
1929 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1930 state->request->data.auth_crap.lm_resp_len);
1932 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1933 nt_resp = data_blob_talloc(state->mem_ctx,
1934 state->request->extra_data.data,
1935 state->request->data.auth_crap.nt_resp_len);
1936 } else {
1937 nt_resp = data_blob_talloc(state->mem_ctx,
1938 state->request->data.auth_crap.nt_resp,
1939 state->request->data.auth_crap.nt_resp_len);
1942 /* what domain should we contact? */
1944 if ( IS_DC ) {
1945 if (!(contact_domain = find_domain_from_name(name_domain))) {
1946 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1947 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1948 result = NT_STATUS_NO_SUCH_USER;
1949 goto done;
1951 } else {
1952 if (is_myname(name_domain)) {
1953 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1954 result = NT_STATUS_NO_SUCH_USER;
1955 goto done;
1957 contact_domain = find_our_domain();
1960 do {
1961 netlogon_fn_t logon_fn;
1962 const struct cli_pipe_auth_data *auth;
1963 uint32_t neg_flags = 0;
1965 retry = false;
1967 netlogon_pipe = NULL;
1968 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1970 if (!NT_STATUS_IS_OK(result)) {
1971 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1972 nt_errstr(result)));
1973 goto done;
1975 auth = netlogon_pipe->auth;
1976 if (netlogon_pipe->dc) {
1977 neg_flags = netlogon_pipe->dc->negotiate_flags;
1980 if (auth == NULL) {
1981 domain->can_do_validation6 = false;
1982 } else if (auth->auth_type != PIPE_AUTH_TYPE_SCHANNEL) {
1983 domain->can_do_validation6 = false;
1984 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1985 domain->can_do_validation6 = false;
1986 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1987 domain->can_do_validation6 = false;
1988 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1989 domain->can_do_validation6 = false;
1992 logon_fn = contact_domain->can_do_samlogon_ex
1993 ? rpccli_netlogon_sam_network_logon_ex
1994 : rpccli_netlogon_sam_network_logon;
1996 result = logon_fn(netlogon_pipe,
1997 state->mem_ctx,
1998 state->request->data.auth_crap.logon_parameters,
1999 contact_domain->dcname,
2000 name_user,
2001 name_domain,
2002 /* Bug #3248 - found by Stefan Burkei. */
2003 workstation, /* We carefully set this above so use it... */
2004 state->request->data.auth_crap.chal,
2005 domain->can_do_validation6 ? 6 : 3,
2006 lm_resp,
2007 nt_resp,
2008 &info3);
2010 if (NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) {
2013 * It's likely that the server also does not support
2014 * validation level 6
2016 domain->can_do_validation6 = false;
2018 if (contact_domain->can_do_samlogon_ex) {
2019 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
2020 "retrying with NetSamLogon\n"));
2021 contact_domain->can_do_samlogon_ex = false;
2022 retry = true;
2023 continue;
2026 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
2027 * (no Ex). This happens against old Samba
2028 * DCs. Drop the connection.
2030 invalidate_cm_connection(&contact_domain->conn);
2031 result = NT_STATUS_LOGON_FAILURE;
2032 break;
2035 if (domain->can_do_validation6 &&
2036 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
2037 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
2038 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
2039 DEBUG(3,("Got a DC that can not do validation level 6, "
2040 "retrying with level 3\n"));
2041 domain->can_do_validation6 = false;
2042 retry = true;
2043 continue;
2047 * we increment this after the "feature negotiation"
2048 * for can_do_samlogon_ex and can_do_validation6
2050 attempts += 1;
2052 /* We have to try a second time as cm_connect_netlogon
2053 might not yet have noticed that the DC has killed
2054 our connection. */
2056 if (!rpccli_is_connected(netlogon_pipe)) {
2057 retry = true;
2058 continue;
2061 /* if we get access denied, a possible cause was that we had and open
2062 connection to the DC, but someone changed our machine account password
2063 out from underneath us using 'net rpc changetrustpw' */
2065 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
2066 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
2067 "ACCESS_DENIED. Maybe the trust account "
2068 "password was changed and we didn't know it. "
2069 "Killing connections to domain %s\n",
2070 name_domain));
2071 invalidate_cm_connection(&contact_domain->conn);
2072 retry = true;
2075 } while ( (attempts < 2) && retry );
2077 if (NT_STATUS_IS_OK(result)) {
2079 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
2080 netsamlogon_cache_store(name_user, info3);
2082 /* Check if the user is in the right group */
2084 result = check_info3_in_group(
2085 info3,
2086 state->request->data.auth_crap.require_membership_of_sid);
2087 if (!NT_STATUS_IS_OK(result)) {
2088 DEBUG(3, ("User %s is not in the required group (%s), so "
2089 "crap authentication is rejected\n",
2090 state->request->data.auth_crap.user,
2091 state->request->data.auth_crap.require_membership_of_sid));
2092 goto done;
2095 result = append_auth_data(state, info3, name_domain,
2096 name_user);
2097 if (!NT_STATUS_IS_OK(result)) {
2098 goto done;
2102 done:
2104 /* give us a more useful (more correct?) error code */
2105 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2106 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2107 result = NT_STATUS_NO_LOGON_SERVERS;
2110 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2111 result = nt_status_squash(result);
2114 set_auth_errors(state->response, result);
2116 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2117 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
2118 name_domain,
2119 name_user,
2120 state->response->data.auth.nt_status_string,
2121 state->response->data.auth.pam_error));
2123 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2126 /* Change a user password */
2128 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2130 fstring domain, user;
2131 char *mapped_user;
2132 struct winbindd_domain *contact_domain;
2133 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2135 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2136 state->request->data.chauthtok.user));
2138 /* Setup crap */
2140 nt_status = normalize_name_unmap(state->mem_ctx,
2141 state->request->data.chauthtok.user,
2142 &mapped_user);
2144 /* Update the chauthtok name if we did any mapping */
2146 if (NT_STATUS_IS_OK(nt_status) ||
2147 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2149 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2152 /* Must pass in state->...chauthtok.user because
2153 canonicalize_username() assumes an fstring(). Since
2154 we have already copied it (if necessary), this is ok. */
2156 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2157 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2158 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2159 "(PAM: %d)\n",
2160 state->request->data.auth.user,
2161 state->response->data.auth.nt_status_string,
2162 state->response->data.auth.pam_error));
2163 request_error(state);
2164 return;
2167 contact_domain = find_domain_from_name(domain);
2168 if (!contact_domain) {
2169 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2170 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2171 state->request->data.chauthtok.user, domain, user, domain));
2172 request_error(state);
2173 return;
2176 sendto_domain(state, contact_domain);
2179 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2180 struct winbindd_cli_state *state)
2182 char *oldpass;
2183 char *newpass = NULL;
2184 struct policy_handle dom_pol;
2185 struct rpc_pipe_client *cli;
2186 bool got_info = false;
2187 struct samr_DomInfo1 *info = NULL;
2188 struct samr_ChangeReject *reject = NULL;
2189 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2190 fstring domain, user;
2192 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2193 state->request->data.auth.user));
2195 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2196 goto done;
2199 /* Change password */
2201 oldpass = state->request->data.chauthtok.oldpass;
2202 newpass = state->request->data.chauthtok.newpass;
2204 /* Initialize reject reason */
2205 state->response->data.auth.reject_reason = Undefined;
2207 /* Get sam handle */
2209 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2210 &dom_pol);
2211 if (!NT_STATUS_IS_OK(result)) {
2212 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2213 goto done;
2216 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2217 user,
2218 newpass,
2219 oldpass,
2220 &info,
2221 &reject);
2223 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2225 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2227 fill_in_password_policy(state->response, info);
2229 state->response->data.auth.reject_reason =
2230 reject->reason;
2232 got_info = true;
2235 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2236 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2237 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2238 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2240 /* only fallback when the chgpasswd_user3 call is not supported */
2241 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2242 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2243 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2244 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2246 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2247 nt_errstr(result)));
2249 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2251 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2252 Map to the same status code as Windows 2003. */
2254 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2255 result = NT_STATUS_PASSWORD_RESTRICTION;
2259 done:
2261 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2263 /* Update the single sign-on memory creds. */
2264 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2265 newpass);
2267 /* When we login from gdm or xdm and password expires,
2268 * we change password, but there are no memory crendentials
2269 * So, winbindd_replace_memory_creds() returns
2270 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2271 * --- BoYang
2272 * */
2273 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2274 result = NT_STATUS_OK;
2277 if (!NT_STATUS_IS_OK(result)) {
2278 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2279 goto process_result;
2282 if (lp_winbind_offline_logon()) {
2283 result = winbindd_update_creds_by_name(contact_domain,
2284 state->mem_ctx, user,
2285 newpass);
2286 /* Again, this happens when we login from gdm or xdm
2287 * and the password expires, *BUT* cached crendentials
2288 * doesn't exist. winbindd_update_creds_by_name()
2289 * returns NT_STATUS_NO_SUCH_USER.
2290 * This is not a failure.
2291 * --- BoYang
2292 * */
2293 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2294 result = NT_STATUS_OK;
2297 if (!NT_STATUS_IS_OK(result)) {
2298 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2299 goto process_result;
2304 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2306 NTSTATUS policy_ret;
2308 policy_ret = fillup_password_policy(contact_domain, state);
2310 /* failure of this is non critical, it will just provide no
2311 * additional information to the client why the change has
2312 * failed - Guenther */
2314 if (!NT_STATUS_IS_OK(policy_ret)) {
2315 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2316 goto process_result;
2320 process_result:
2322 set_auth_errors(state->response, result);
2324 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2325 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2326 domain,
2327 user,
2328 state->response->data.auth.nt_status_string,
2329 state->response->data.auth.pam_error));
2331 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2334 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2336 struct winbindd_domain *domain;
2337 fstring name_domain, user;
2338 uid_t caller_uid = (uid_t)-1;
2339 uid_t request_uid = state->request->data.logoff.uid;
2341 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2342 state->request->data.logoff.user));
2344 /* Ensure null termination */
2345 state->request->data.logoff.user
2346 [sizeof(state->request->data.logoff.user)-1]='\0';
2348 state->request->data.logoff.krb5ccname
2349 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2351 if (request_uid == (gid_t)-1) {
2352 goto failed;
2355 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2356 goto failed;
2359 if ((domain = find_auth_domain(state->request->flags,
2360 name_domain)) == NULL) {
2361 goto failed;
2364 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2365 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2366 strerror(errno)));
2367 goto failed;
2370 switch (caller_uid) {
2371 case -1:
2372 goto failed;
2373 case 0:
2374 /* root must be able to logoff any user - gd */
2375 state->request->data.logoff.uid = request_uid;
2376 break;
2377 default:
2378 if (caller_uid != request_uid) {
2379 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2380 goto failed;
2382 state->request->data.logoff.uid = caller_uid;
2383 break;
2386 sendto_domain(state, domain);
2387 return;
2389 failed:
2390 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2391 DEBUG(5, ("Pam Logoff for %s returned %s "
2392 "(PAM: %d)\n",
2393 state->request->data.logoff.user,
2394 state->response->data.auth.nt_status_string,
2395 state->response->data.auth.pam_error));
2396 request_error(state);
2397 return;
2400 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2401 struct winbindd_cli_state *state)
2403 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2405 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2406 state->request->data.logoff.user));
2408 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2409 result = NT_STATUS_OK;
2410 goto process_result;
2413 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2414 result = NT_STATUS_OK;
2415 goto process_result;
2418 #ifdef HAVE_KRB5
2420 if (state->request->data.logoff.uid < 0) {
2421 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2422 goto process_result;
2425 /* what we need here is to find the corresponding krb5 ccache name *we*
2426 * created for a given username and destroy it */
2428 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2429 result = NT_STATUS_OK;
2430 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2431 goto process_result;
2434 if (!ccache_entry_identical(state->request->data.logoff.user,
2435 state->request->data.logoff.uid,
2436 state->request->data.logoff.krb5ccname)) {
2437 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2438 goto process_result;
2441 result = remove_ccache(state->request->data.logoff.user);
2442 if (!NT_STATUS_IS_OK(result)) {
2443 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2444 nt_errstr(result)));
2445 goto process_result;
2448 #else
2449 result = NT_STATUS_NOT_SUPPORTED;
2450 #endif
2452 process_result:
2454 winbindd_delete_memory_creds(state->request->data.logoff.user);
2456 set_auth_errors(state->response, result);
2458 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2461 /* Change user password with auth crap*/
2463 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2465 struct winbindd_domain *domain = NULL;
2466 const char *domain_name = NULL;
2468 /* Ensure null termination */
2469 state->request->data.chng_pswd_auth_crap.user[
2470 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2471 state->request->data.chng_pswd_auth_crap.domain[
2472 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2474 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2475 (unsigned long)state->pid,
2476 state->request->data.chng_pswd_auth_crap.domain,
2477 state->request->data.chng_pswd_auth_crap.user));
2479 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2480 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2481 } else if (lp_winbind_use_default_domain()) {
2482 domain_name = lp_workgroup();
2485 if (domain_name != NULL)
2486 domain = find_domain_from_name(domain_name);
2488 if (domain != NULL) {
2489 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2490 "%s\n", (unsigned long)state->pid,domain->name));
2491 sendto_domain(state, domain);
2492 return;
2495 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2496 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2497 state->request->data.chng_pswd_auth_crap.domain,
2498 state->request->data.chng_pswd_auth_crap.user,
2499 state->response->data.auth.nt_status_string,
2500 state->response->data.auth.pam_error));
2501 request_error(state);
2502 return;
2505 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2507 NTSTATUS result;
2508 DATA_BLOB new_nt_password;
2509 DATA_BLOB old_nt_hash_enc;
2510 DATA_BLOB new_lm_password;
2511 DATA_BLOB old_lm_hash_enc;
2512 fstring domain,user;
2513 struct policy_handle dom_pol;
2514 struct winbindd_domain *contact_domain = domainSt;
2515 struct rpc_pipe_client *cli;
2517 /* Ensure null termination */
2518 state->request->data.chng_pswd_auth_crap.user[
2519 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2520 state->request->data.chng_pswd_auth_crap.domain[
2521 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2522 *domain = 0;
2523 *user = 0;
2525 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2526 (unsigned long)state->pid,
2527 state->request->data.chng_pswd_auth_crap.domain,
2528 state->request->data.chng_pswd_auth_crap.user));
2530 if (lp_winbind_offline_logon()) {
2531 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2532 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2533 result = NT_STATUS_ACCESS_DENIED;
2534 goto done;
2537 if (*state->request->data.chng_pswd_auth_crap.domain) {
2538 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2539 } else {
2540 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2541 domain, user);
2543 if(!*domain) {
2544 DEBUG(3,("no domain specified with username (%s) - "
2545 "failing auth\n",
2546 state->request->data.chng_pswd_auth_crap.user));
2547 result = NT_STATUS_NO_SUCH_USER;
2548 goto done;
2552 if (!*domain && lp_winbind_use_default_domain()) {
2553 fstrcpy(domain,(char *)lp_workgroup());
2556 if(!*user) {
2557 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2560 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2561 (unsigned long)state->pid, domain, user));
2563 /* Change password */
2564 new_nt_password = data_blob_talloc(
2565 state->mem_ctx,
2566 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2567 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2569 old_nt_hash_enc = data_blob_talloc(
2570 state->mem_ctx,
2571 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2572 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2574 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2575 new_lm_password = data_blob_talloc(
2576 state->mem_ctx,
2577 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2578 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2580 old_lm_hash_enc = data_blob_talloc(
2581 state->mem_ctx,
2582 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2583 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2584 } else {
2585 new_lm_password.length = 0;
2586 old_lm_hash_enc.length = 0;
2589 /* Get sam handle */
2591 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2592 if (!NT_STATUS_IS_OK(result)) {
2593 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2594 goto done;
2597 result = rpccli_samr_chng_pswd_auth_crap(
2598 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2599 new_lm_password, old_lm_hash_enc);
2601 done:
2603 set_auth_errors(state->response, result);
2605 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2606 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2607 domain, user,
2608 state->response->data.auth.nt_status_string,
2609 state->response->data.auth.pam_error));
2611 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;