s3/packaging: pam_winbind has been moved to section 8.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blob56657928a47eead79c24763aa895874d6f60177f
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
33 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
34 struct winbindd_cli_state *state,
35 struct netr_SamInfo3 *info3)
37 char *ex;
38 uint32_t i;
40 state->response->data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response->data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response->data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response->data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response->data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response->data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response->data.auth.info3.logon_count = info3->base.logon_count;
54 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response->data.auth.info3.user_rid = info3->base.rid;
57 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response->data.auth.info3.num_groups = info3->base.groups.count;
61 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response->data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response->data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response->data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response->data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response->data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response->data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response->data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response->data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response->data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(state->mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
95 char *sid;
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
101 sid,
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 talloc_free(sid);
108 state->response->extra_data.data = ex;
109 state->response->length += talloc_get_size(ex);
111 return NT_STATUS_OK;
114 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
115 struct winbindd_cli_state *state,
116 struct netr_SamInfo3 *info3)
118 DATA_BLOB blob;
119 enum ndr_err_code ndr_err;
121 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
122 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
125 return ndr_map_error2ntstatus(ndr_err);
128 state->response->extra_data.data = blob.data;
129 state->response->length += blob.length;
131 return NT_STATUS_OK;
134 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
135 struct winbindd_cli_state *state,
136 const struct netr_SamInfo3 *info3,
137 const char *name_domain,
138 const char *name_user)
140 /* We've been asked to return the unix username, per
141 'winbind use default domain' settings and the like */
143 const char *nt_username, *nt_domain;
145 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
146 if (!nt_domain) {
147 /* If the server didn't give us one, just use the one
148 * we sent them */
149 nt_domain = name_domain;
152 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
153 if (!nt_username) {
154 /* If the server didn't give us one, just use the one
155 * we sent them */
156 nt_username = name_user;
159 fill_domain_username(state->response->data.auth.unix_username,
160 nt_domain, nt_username, true);
162 DEBUG(5,("Setting unix username to [%s]\n",
163 state->response->data.auth.unix_username));
165 return NT_STATUS_OK;
168 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
169 struct winbindd_cli_state *state,
170 const struct netr_SamInfo3 *info3,
171 const char *name_domain,
172 const char *name_user)
174 char *afsname = NULL;
175 char *cell;
176 char *token;
178 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
179 if (afsname == NULL) {
180 return NT_STATUS_NO_MEMORY;
183 afsname = talloc_string_sub(mem_ctx,
184 lp_afs_username_map(),
185 "%D", name_domain);
186 afsname = talloc_string_sub(mem_ctx, afsname,
187 "%u", name_user);
188 afsname = talloc_string_sub(mem_ctx, afsname,
189 "%U", name_user);
192 DOM_SID user_sid;
193 fstring sidstr;
195 sid_copy(&user_sid, info3->base.domain_sid);
196 sid_append_rid(&user_sid, info3->base.rid);
197 sid_to_fstring(sidstr, &user_sid);
198 afsname = talloc_string_sub(mem_ctx, afsname,
199 "%s", sidstr);
202 if (afsname == NULL) {
203 return NT_STATUS_NO_MEMORY;
206 strlower_m(afsname);
208 DEBUG(10, ("Generating token for user %s\n", afsname));
210 cell = strchr(afsname, '@');
212 if (cell == NULL) {
213 return NT_STATUS_NO_MEMORY;
216 *cell = '\0';
217 cell += 1;
219 token = afs_createtoken_str(afsname, cell);
220 if (token == NULL) {
221 return NT_STATUS_OK;
223 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
224 token);
225 if (state->response->extra_data.data == NULL) {
226 return NT_STATUS_NO_MEMORY;
228 state->response->length +=
229 strlen((const char *)state->response->extra_data.data)+1;
231 return NT_STATUS_OK;
234 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
235 struct netr_SamInfo3 *info3,
236 const char *group_sid)
238 * Check whether a user belongs to a group or list of groups.
240 * @param mem_ctx talloc memory context.
241 * @param info3 user information, including group membership info.
242 * @param group_sid One or more groups , separated by commas.
244 * @return NT_STATUS_OK on success,
245 * NT_STATUS_LOGON_FAILURE if the user does not belong,
246 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
249 DOM_SID *require_membership_of_sid;
250 size_t num_require_membership_of_sid;
251 char *req_sid;
252 const char *p;
253 DOM_SID sid;
254 size_t i;
255 struct nt_user_token *token;
256 TALLOC_CTX *frame = NULL;
257 NTSTATUS status;
259 /* Parse the 'required group' SID */
261 if (!group_sid || !group_sid[0]) {
262 /* NO sid supplied, all users may access */
263 return NT_STATUS_OK;
266 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
267 DEBUG(0, ("talloc failed\n"));
268 return NT_STATUS_NO_MEMORY;
271 num_require_membership_of_sid = 0;
272 require_membership_of_sid = NULL;
274 p = group_sid;
276 frame = talloc_stackframe();
277 while (next_token_talloc(frame, &p, &req_sid, ",")) {
278 if (!string_to_sid(&sid, req_sid)) {
279 DEBUG(0, ("check_info3_in_group: could not parse %s "
280 "as a SID!", req_sid));
281 TALLOC_FREE(frame);
282 return NT_STATUS_INVALID_PARAMETER;
285 status = add_sid_to_array(mem_ctx, &sid,
286 &require_membership_of_sid,
287 &num_require_membership_of_sid);
288 if (!NT_STATUS_IS_OK(status)) {
289 DEBUG(0, ("add_sid_to_array failed\n"));
290 TALLOC_FREE(frame);
291 return status;
295 TALLOC_FREE(frame);
297 status = sid_array_from_info3(mem_ctx, info3,
298 &token->user_sids,
299 &token->num_sids,
300 true, false);
301 if (!NT_STATUS_IS_OK(status)) {
302 return status;
305 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
306 token))
307 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
308 token))) {
309 DEBUG(3, ("could not add aliases: %s\n",
310 nt_errstr(status)));
311 return status;
314 debug_nt_user_token(DBGC_CLASS, 10, token);
316 for (i=0; i<num_require_membership_of_sid; i++) {
317 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
318 &require_membership_of_sid[i])));
319 if (nt_token_check_sid(&require_membership_of_sid[i],
320 token)) {
321 DEBUG(10, ("Access ok\n"));
322 return NT_STATUS_OK;
326 /* Do not distinguish this error from a wrong username/pw */
328 return NT_STATUS_LOGON_FAILURE;
331 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
332 const char *domain_name)
334 struct winbindd_domain *domain;
336 if (IS_DC) {
337 domain = find_domain_from_name_noinit(domain_name);
338 if (domain == NULL) {
339 DEBUG(3, ("Authentication for domain [%s] refused "
340 "as it is not a trusted domain\n",
341 domain_name));
343 return domain;
346 if (is_myname(domain_name)) {
347 DEBUG(3, ("Authentication for domain %s (local domain "
348 "to this server) not supported at this "
349 "stage\n", domain_name));
350 return NULL;
353 /* we can auth against trusted domains */
354 if (state->request->flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
355 domain = find_domain_from_name_noinit(domain_name);
356 if (domain == NULL) {
357 DEBUG(3, ("Authentication for domain [%s] skipped "
358 "as it is not a trusted domain\n",
359 domain_name));
360 } else {
361 return domain;
365 return find_our_domain();
368 static void fill_in_password_policy(struct winbindd_response *r,
369 const struct samr_DomInfo1 *p)
371 r->data.auth.policy.min_length_password =
372 p->min_password_length;
373 r->data.auth.policy.password_history =
374 p->password_history_length;
375 r->data.auth.policy.password_properties =
376 p->password_properties;
377 r->data.auth.policy.expire =
378 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
379 r->data.auth.policy.min_passwordage =
380 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
383 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
384 struct winbindd_cli_state *state)
386 struct winbindd_methods *methods;
387 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
388 struct samr_DomInfo1 password_policy;
390 if ( !winbindd_can_contact_domain( domain ) ) {
391 DEBUG(5,("fillup_password_policy: No inbound trust to "
392 "contact domain %s\n", domain->name));
393 return NT_STATUS_NOT_SUPPORTED;
396 methods = domain->methods;
398 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
399 if (NT_STATUS_IS_ERR(status)) {
400 return status;
403 fill_in_password_policy(state->response, &password_policy);
405 return NT_STATUS_OK;
408 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
409 TALLOC_CTX *mem_ctx,
410 uint16 *lockout_threshold)
412 struct winbindd_methods *methods;
413 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
414 struct samr_DomInfo12 lockout_policy;
416 *lockout_threshold = 0;
418 methods = domain->methods;
420 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
421 if (NT_STATUS_IS_ERR(status)) {
422 return status;
425 *lockout_threshold = lockout_policy.lockout_threshold;
427 return NT_STATUS_OK;
430 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
431 TALLOC_CTX *mem_ctx,
432 uint32 *password_properties)
434 struct winbindd_methods *methods;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 struct samr_DomInfo1 password_policy;
438 *password_properties = 0;
440 methods = domain->methods;
442 status = methods->password_policy(domain, mem_ctx, &password_policy);
443 if (NT_STATUS_IS_ERR(status)) {
444 return status;
447 *password_properties = password_policy.password_properties;
449 return NT_STATUS_OK;
452 #ifdef HAVE_KRB5
454 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
455 const char *type,
456 uid_t uid,
457 bool *internal_ccache)
459 /* accept FILE and WRFILE as krb5_cc_type from the client and then
460 * build the full ccname string based on the user's uid here -
461 * Guenther*/
463 const char *gen_cc = NULL;
465 *internal_ccache = true;
467 if (uid == -1) {
468 goto memory_ccache;
471 if (!type || type[0] == '\0') {
472 goto memory_ccache;
475 if (strequal(type, "FILE")) {
476 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
477 } else if (strequal(type, "WRFILE")) {
478 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
479 } else {
480 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
481 goto memory_ccache;
484 *internal_ccache = false;
485 goto done;
487 memory_ccache:
488 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
490 done:
491 if (gen_cc == NULL) {
492 DEBUG(0,("out of memory\n"));
493 return NULL;
496 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
498 return gen_cc;
501 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
503 const char *type = state->request->data.auth.krb5_cc_type;
505 state->response->data.auth.krb5ccname[0] = '\0';
507 if (type[0] == '\0') {
508 return;
511 if (!strequal(type, "FILE") &&
512 !strequal(type, "WRFILE")) {
513 DEBUG(10,("won't return krbccname for a %s type ccache\n",
514 type));
515 return;
518 fstrcpy(state->response->data.auth.krb5ccname, cc);
521 #endif
523 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
525 uid_t uid = -1;
527 uid = state->request->data.auth.uid;
529 if (uid < 0) {
530 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
531 return -1;
533 return uid;
536 /**********************************************************************
537 Authenticate a user with a clear text password using Kerberos and fill up
538 ccache if required
539 **********************************************************************/
541 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
542 struct winbindd_cli_state *state,
543 struct netr_SamInfo3 **info3)
545 #ifdef HAVE_KRB5
546 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
547 krb5_error_code krb5_ret;
548 const char *cc = NULL;
549 const char *principal_s = NULL;
550 const char *service = NULL;
551 char *realm = NULL;
552 fstring name_domain, name_user;
553 time_t ticket_lifetime = 0;
554 time_t renewal_until = 0;
555 uid_t uid = -1;
556 ADS_STRUCT *ads;
557 time_t time_offset = 0;
558 bool internal_ccache = true;
560 ZERO_STRUCTP(info3);
562 *info3 = NULL;
564 /* 1st step:
565 * prepare a krb5_cc_cache string for the user */
567 uid = get_uid_from_state(state);
568 if (uid == -1) {
569 DEBUG(0,("no valid uid\n"));
572 cc = generate_krb5_ccache(state->mem_ctx,
573 state->request->data.auth.krb5_cc_type,
574 state->request->data.auth.uid,
575 &internal_ccache);
576 if (cc == NULL) {
577 return NT_STATUS_NO_MEMORY;
581 /* 2nd step:
582 * get kerberos properties */
584 if (domain->private_data) {
585 ads = (ADS_STRUCT *)domain->private_data;
586 time_offset = ads->auth.time_offset;
590 /* 3rd step:
591 * do kerberos auth and setup ccache as the user */
593 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
595 realm = domain->alt_name;
596 strupper_m(realm);
598 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
599 if (principal_s == NULL) {
600 return NT_STATUS_NO_MEMORY;
603 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
604 if (service == NULL) {
605 return NT_STATUS_NO_MEMORY;
608 /* if this is a user ccache, we need to act as the user to let the krb5
609 * library handle the chown, etc. */
611 /************************ ENTERING NON-ROOT **********************/
613 if (!internal_ccache) {
614 set_effective_uid(uid);
615 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
618 result = kerberos_return_info3_from_pac(state->mem_ctx,
619 principal_s,
620 state->request->data.auth.pass,
621 time_offset,
622 &ticket_lifetime,
623 &renewal_until,
625 true,
626 true,
627 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
628 info3);
629 if (!internal_ccache) {
630 gain_root_privilege();
633 /************************ RETURNED TO ROOT **********************/
635 if (!NT_STATUS_IS_OK(result)) {
636 goto failed;
639 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
640 principal_s));
642 /* if we had a user's ccache then return that string for the pam
643 * environment */
645 if (!internal_ccache) {
647 setup_return_cc_name(state, cc);
649 result = add_ccache_to_list(principal_s,
651 service,
652 state->request->data.auth.user,
653 realm,
654 uid,
655 time(NULL),
656 ticket_lifetime,
657 renewal_until,
658 false);
660 if (!NT_STATUS_IS_OK(result)) {
661 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
662 nt_errstr(result)));
664 } else {
666 /* need to delete the memory cred cache, it is not used anymore */
668 krb5_ret = ads_kdestroy(cc);
669 if (krb5_ret) {
670 DEBUG(3,("winbindd_raw_kerberos_login: "
671 "could not destroy krb5 credential cache: "
672 "%s\n", error_message(krb5_ret)));
677 return NT_STATUS_OK;
679 failed:
681 /* we could have created a new credential cache with a valid tgt in it
682 * but we werent able to get or verify the service ticket for this
683 * local host and therefor didn't get the PAC, we need to remove that
684 * cache entirely now */
686 krb5_ret = ads_kdestroy(cc);
687 if (krb5_ret) {
688 DEBUG(3,("winbindd_raw_kerberos_login: "
689 "could not destroy krb5 credential cache: "
690 "%s\n", error_message(krb5_ret)));
693 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
694 DEBUG(3,("winbindd_raw_kerberos_login: "
695 "could not remove ccache for user %s\n",
696 state->request->data.auth.user));
699 return result;
700 #else
701 return NT_STATUS_NOT_SUPPORTED;
702 #endif /* HAVE_KRB5 */
705 /****************************************************************
706 ****************************************************************/
708 static bool check_request_flags(uint32_t flags)
710 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
711 WBFLAG_PAM_INFO3_TEXT |
712 WBFLAG_PAM_INFO3_NDR;
714 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
715 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
716 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
717 !(flags & flags_edata) ) {
718 return true;
721 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
723 return false;
726 /****************************************************************
727 ****************************************************************/
729 static NTSTATUS append_data(struct winbindd_cli_state *state,
730 struct netr_SamInfo3 *info3,
731 const char *name_domain,
732 const char *name_user)
734 NTSTATUS result;
735 uint32_t flags = state->request->flags;
737 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
738 memcpy(state->response->data.auth.user_session_key,
739 info3->base.key.key,
740 sizeof(state->response->data.auth.user_session_key)
741 /* 16 */);
744 if (flags & WBFLAG_PAM_LMKEY) {
745 memcpy(state->response->data.auth.first_8_lm_hash,
746 info3->base.LMSessKey.key,
747 sizeof(state->response->data.auth.first_8_lm_hash)
748 /* 8 */);
751 if (flags & WBFLAG_PAM_INFO3_TEXT) {
752 result = append_info3_as_txt(state->mem_ctx, state, info3);
753 if (!NT_STATUS_IS_OK(result)) {
754 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
755 nt_errstr(result)));
756 return result;
760 /* currently, anything from here on potentially overwrites extra_data. */
762 if (flags & WBFLAG_PAM_INFO3_NDR) {
763 result = append_info3_as_ndr(state->mem_ctx, state, info3);
764 if (!NT_STATUS_IS_OK(result)) {
765 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
766 nt_errstr(result)));
767 return result;
771 if (flags & WBFLAG_PAM_UNIX_NAME) {
772 result = append_unix_username(state->mem_ctx, state, info3,
773 name_domain, name_user);
774 if (!NT_STATUS_IS_OK(result)) {
775 DEBUG(10,("Failed to append Unix Username: %s\n",
776 nt_errstr(result)));
777 return result;
781 if (flags & WBFLAG_PAM_AFS_TOKEN) {
782 result = append_afs_token(state->mem_ctx, state, info3,
783 name_domain, name_user);
784 if (!NT_STATUS_IS_OK(result)) {
785 DEBUG(10,("Failed to append AFS token: %s\n",
786 nt_errstr(result)));
787 return result;
791 return NT_STATUS_OK;
794 void winbindd_pam_auth(struct winbindd_cli_state *state)
796 struct winbindd_domain *domain;
797 fstring name_domain, name_user;
798 char *mapped_user = NULL;
799 NTSTATUS result;
800 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
802 /* Ensure null termination */
803 state->request->data.auth.user
804 [sizeof(state->request->data.auth.user)-1]='\0';
806 /* Ensure null termination */
807 state->request->data.auth.pass
808 [sizeof(state->request->data.auth.pass)-1]='\0';
810 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
811 state->request->data.auth.user));
813 if (!check_request_flags(state->request->flags)) {
814 result = NT_STATUS_INVALID_PARAMETER_MIX;
815 goto done;
818 /* Parse domain and username */
820 name_map_status = normalize_name_unmap(state->mem_ctx,
821 state->request->data.auth.user,
822 &mapped_user);
824 /* If the name normalization didnt' actually do anything,
825 just use the original name */
827 if (!NT_STATUS_IS_OK(name_map_status) &&
828 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
830 mapped_user = state->request->data.auth.user;
833 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
834 result = NT_STATUS_NO_SUCH_USER;
835 goto done;
838 domain = find_auth_domain(state, name_domain);
840 if (domain == NULL) {
841 result = NT_STATUS_NO_SUCH_USER;
842 goto done;
845 sendto_domain(state, domain);
846 return;
847 done:
848 set_auth_errors(state->response, result);
849 DEBUG(5, ("Plain text authentication for %s returned %s "
850 "(PAM: %d)\n",
851 state->request->data.auth.user,
852 state->response->data.auth.nt_status_string,
853 state->response->data.auth.pam_error));
854 request_error(state);
857 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
858 struct winbindd_cli_state *state,
859 struct netr_SamInfo3 **info3)
861 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
862 uint16 max_allowed_bad_attempts;
863 fstring name_domain, name_user;
864 DOM_SID sid;
865 enum lsa_SidType type;
866 uchar new_nt_pass[NT_HASH_LEN];
867 const uint8 *cached_nt_pass;
868 const uint8 *cached_salt;
869 struct netr_SamInfo3 *my_info3;
870 time_t kickoff_time, must_change_time;
871 bool password_good = false;
872 #ifdef HAVE_KRB5
873 struct winbindd_tdc_domain *tdc_domain = NULL;
874 #endif
876 *info3 = NULL;
878 ZERO_STRUCTP(info3);
880 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
882 /* Parse domain and username */
884 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
887 if (!lookup_cached_name(state->mem_ctx,
888 name_domain,
889 name_user,
890 &sid,
891 &type)) {
892 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
893 return NT_STATUS_NO_SUCH_USER;
896 if (type != SID_NAME_USER) {
897 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
898 return NT_STATUS_LOGON_FAILURE;
901 result = winbindd_get_creds(domain,
902 state->mem_ctx,
903 &sid,
904 &my_info3,
905 &cached_nt_pass,
906 &cached_salt);
907 if (!NT_STATUS_IS_OK(result)) {
908 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
909 return result;
912 *info3 = my_info3;
914 E_md4hash(state->request->data.auth.pass, new_nt_pass);
916 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
917 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
918 if (cached_salt) {
919 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
922 if (cached_salt) {
923 /* In this case we didn't store the nt_hash itself,
924 but the MD5 combination of salt + nt_hash. */
925 uchar salted_hash[NT_HASH_LEN];
926 E_md5hash(cached_salt, new_nt_pass, salted_hash);
928 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
929 true : false;
930 } else {
931 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
932 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
933 true : false;
936 if (password_good) {
938 /* User *DOES* know the password, update logon_time and reset
939 * bad_pw_count */
941 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
943 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
944 return NT_STATUS_ACCOUNT_LOCKED_OUT;
947 if (my_info3->base.acct_flags & ACB_DISABLED) {
948 return NT_STATUS_ACCOUNT_DISABLED;
951 if (my_info3->base.acct_flags & ACB_WSTRUST) {
952 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
955 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
956 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
959 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
960 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
963 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
964 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
965 my_info3->base.acct_flags));
966 return NT_STATUS_LOGON_FAILURE;
969 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
970 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
971 return NT_STATUS_ACCOUNT_EXPIRED;
974 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
975 if (must_change_time != 0 && must_change_time < time(NULL)) {
976 /* we allow grace logons when the password has expired */
977 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
978 /* return NT_STATUS_PASSWORD_EXPIRED; */
979 goto success;
982 #ifdef HAVE_KRB5
983 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
984 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
985 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
987 uid_t uid = -1;
988 const char *cc = NULL;
989 char *realm = NULL;
990 const char *principal_s = NULL;
991 const char *service = NULL;
992 bool internal_ccache = false;
994 uid = get_uid_from_state(state);
995 if (uid == -1) {
996 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
997 return NT_STATUS_INVALID_PARAMETER;
1000 cc = generate_krb5_ccache(state->mem_ctx,
1001 state->request->data.auth.krb5_cc_type,
1002 state->request->data.auth.uid,
1003 &internal_ccache);
1004 if (cc == NULL) {
1005 return NT_STATUS_NO_MEMORY;
1008 realm = domain->alt_name;
1009 strupper_m(realm);
1011 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1012 if (principal_s == NULL) {
1013 return NT_STATUS_NO_MEMORY;
1016 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1017 if (service == NULL) {
1018 return NT_STATUS_NO_MEMORY;
1021 if (!internal_ccache) {
1023 setup_return_cc_name(state, cc);
1025 result = add_ccache_to_list(principal_s,
1027 service,
1028 state->request->data.auth.user,
1029 domain->alt_name,
1030 uid,
1031 time(NULL),
1032 time(NULL) + lp_winbind_cache_time(),
1033 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1034 true);
1036 if (!NT_STATUS_IS_OK(result)) {
1037 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1038 "to add ccache to list: %s\n",
1039 nt_errstr(result)));
1043 #endif /* HAVE_KRB5 */
1044 success:
1045 /* FIXME: we possibly should handle logon hours as well (does xp when
1046 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1048 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1049 my_info3->base.bad_password_count = 0;
1051 result = winbindd_update_creds_by_info3(domain,
1052 state->mem_ctx,
1053 state->request->data.auth.user,
1054 state->request->data.auth.pass,
1055 my_info3);
1056 if (!NT_STATUS_IS_OK(result)) {
1057 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1058 nt_errstr(result)));
1059 return result;
1062 return NT_STATUS_OK;
1066 /* User does *NOT* know the correct password, modify info3 accordingly */
1068 /* failure of this is not critical */
1069 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1070 if (!NT_STATUS_IS_OK(result)) {
1071 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1072 "Won't be able to honour account lockout policies\n"));
1075 /* increase counter */
1076 my_info3->base.bad_password_count++;
1078 if (max_allowed_bad_attempts == 0) {
1079 goto failed;
1082 /* lockout user */
1083 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1085 uint32 password_properties;
1087 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1088 if (!NT_STATUS_IS_OK(result)) {
1089 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1092 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1093 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1094 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1098 failed:
1099 result = winbindd_update_creds_by_info3(domain,
1100 state->mem_ctx,
1101 state->request->data.auth.user,
1102 NULL,
1103 my_info3);
1105 if (!NT_STATUS_IS_OK(result)) {
1106 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1107 nt_errstr(result)));
1110 return NT_STATUS_LOGON_FAILURE;
1113 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1114 struct winbindd_cli_state *state,
1115 struct netr_SamInfo3 **info3)
1117 struct winbindd_domain *contact_domain;
1118 fstring name_domain, name_user;
1119 NTSTATUS result;
1121 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1123 /* Parse domain and username */
1125 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1127 /* what domain should we contact? */
1129 if ( IS_DC ) {
1130 if (!(contact_domain = find_domain_from_name(name_domain))) {
1131 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1132 state->request->data.auth.user, name_domain, name_user, name_domain));
1133 result = NT_STATUS_NO_SUCH_USER;
1134 goto done;
1137 } else {
1138 if (is_myname(name_domain)) {
1139 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1140 result = NT_STATUS_NO_SUCH_USER;
1141 goto done;
1144 contact_domain = find_domain_from_name(name_domain);
1145 if (contact_domain == NULL) {
1146 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1147 state->request->data.auth.user, name_domain, name_user, name_domain));
1149 contact_domain = find_our_domain();
1153 if (contact_domain->initialized &&
1154 contact_domain->active_directory) {
1155 goto try_login;
1158 if (!contact_domain->initialized) {
1159 init_dc_connection(contact_domain);
1162 if (!contact_domain->active_directory) {
1163 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1164 return NT_STATUS_INVALID_LOGON_TYPE;
1166 try_login:
1167 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1168 done:
1169 return result;
1172 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1173 TALLOC_CTX *mem_ctx,
1174 uint32 logon_parameters,
1175 const char *server,
1176 const char *username,
1177 const char *domain,
1178 const char *workstation,
1179 const uint8 chal[8],
1180 DATA_BLOB lm_response,
1181 DATA_BLOB nt_response,
1182 struct netr_SamInfo3 **info3);
1184 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1185 struct winbindd_cli_state *state,
1186 struct netr_SamInfo3 **info3)
1189 struct rpc_pipe_client *netlogon_pipe;
1190 uchar chal[8];
1191 DATA_BLOB lm_resp;
1192 DATA_BLOB nt_resp;
1193 int attempts = 0;
1194 unsigned char local_lm_response[24];
1195 unsigned char local_nt_response[24];
1196 struct winbindd_domain *contact_domain;
1197 fstring name_domain, name_user;
1198 bool retry;
1199 NTSTATUS result;
1200 struct netr_SamInfo3 *my_info3 = NULL;
1202 *info3 = NULL;
1204 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1206 /* Parse domain and username */
1208 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1210 /* do password magic */
1213 generate_random_buffer(chal, 8);
1214 if (lp_client_ntlmv2_auth()) {
1215 DATA_BLOB server_chal;
1216 DATA_BLOB names_blob;
1217 DATA_BLOB nt_response;
1218 DATA_BLOB lm_response;
1219 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1221 /* note that the 'workgroup' here is a best guess - we don't know
1222 the server's domain at this point. The 'server name' is also
1223 dodgy...
1225 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1227 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1228 state->request->data.auth.pass,
1229 &server_chal,
1230 &names_blob,
1231 &lm_response, &nt_response, NULL, NULL)) {
1232 data_blob_free(&names_blob);
1233 data_blob_free(&server_chal);
1234 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1235 result = NT_STATUS_NO_MEMORY;
1236 goto done;
1238 data_blob_free(&names_blob);
1239 data_blob_free(&server_chal);
1240 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1241 lm_response.length);
1242 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1243 nt_response.length);
1244 data_blob_free(&lm_response);
1245 data_blob_free(&nt_response);
1247 } else {
1248 if (lp_client_lanman_auth()
1249 && SMBencrypt(state->request->data.auth.pass,
1250 chal,
1251 local_lm_response)) {
1252 lm_resp = data_blob_talloc(state->mem_ctx,
1253 local_lm_response,
1254 sizeof(local_lm_response));
1255 } else {
1256 lm_resp = data_blob_null;
1258 SMBNTencrypt(state->request->data.auth.pass,
1259 chal,
1260 local_nt_response);
1262 nt_resp = data_blob_talloc(state->mem_ctx,
1263 local_nt_response,
1264 sizeof(local_nt_response));
1267 /* what domain should we contact? */
1269 if ( IS_DC ) {
1270 if (!(contact_domain = find_domain_from_name(name_domain))) {
1271 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1272 state->request->data.auth.user, name_domain, name_user, name_domain));
1273 result = NT_STATUS_NO_SUCH_USER;
1274 goto done;
1277 } else {
1278 if (is_myname(name_domain)) {
1279 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1280 result = NT_STATUS_NO_SUCH_USER;
1281 goto done;
1284 contact_domain = find_our_domain();
1287 /* check authentication loop */
1289 do {
1290 netlogon_fn_t logon_fn;
1292 ZERO_STRUCTP(my_info3);
1293 retry = false;
1295 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1297 if (!NT_STATUS_IS_OK(result)) {
1298 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1299 goto done;
1302 /* It is really important to try SamLogonEx here,
1303 * because in a clustered environment, we want to use
1304 * one machine account from multiple physical
1305 * computers.
1307 * With a normal SamLogon call, we must keep the
1308 * credentials chain updated and intact between all
1309 * users of the machine account (which would imply
1310 * cross-node communication for every NTLM logon).
1312 * (The credentials chain is not per NETLOGON pipe
1313 * connection, but globally on the server/client pair
1314 * by machine name).
1316 * When using SamLogonEx, the credentials are not
1317 * supplied, but the session key is implied by the
1318 * wrapping SamLogon context.
1320 * -- abartlet 21 April 2008
1323 logon_fn = contact_domain->can_do_samlogon_ex
1324 ? rpccli_netlogon_sam_network_logon_ex
1325 : rpccli_netlogon_sam_network_logon;
1327 result = logon_fn(netlogon_pipe,
1328 state->mem_ctx,
1330 contact_domain->dcname, /* server name */
1331 name_user, /* user name */
1332 name_domain, /* target domain */
1333 global_myname(), /* workstation */
1334 chal,
1335 lm_resp,
1336 nt_resp,
1337 &my_info3);
1338 attempts += 1;
1340 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1341 && contact_domain->can_do_samlogon_ex) {
1342 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1343 "retrying with NetSamLogon\n"));
1344 contact_domain->can_do_samlogon_ex = false;
1345 retry = true;
1346 continue;
1349 /* We have to try a second time as cm_connect_netlogon
1350 might not yet have noticed that the DC has killed
1351 our connection. */
1353 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1354 retry = true;
1355 continue;
1358 /* if we get access denied, a possible cause was that we had
1359 and open connection to the DC, but someone changed our
1360 machine account password out from underneath us using 'net
1361 rpc changetrustpw' */
1363 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1364 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1365 "ACCESS_DENIED. Maybe the trust account "
1366 "password was changed and we didn't know it. "
1367 "Killing connections to domain %s\n",
1368 name_domain));
1369 invalidate_cm_connection(&contact_domain->conn);
1370 retry = true;
1373 } while ( (attempts < 2) && retry );
1375 /* handle the case where a NT4 DC does not fill in the acct_flags in
1376 * the samlogon reply info3. When accurate info3 is required by the
1377 * caller, we look up the account flags ourselve - gd */
1379 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1380 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1382 struct rpc_pipe_client *samr_pipe;
1383 struct policy_handle samr_domain_handle, user_pol;
1384 union samr_UserInfo *info = NULL;
1385 NTSTATUS status_tmp;
1386 uint32 acct_flags;
1388 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1389 &samr_pipe, &samr_domain_handle);
1391 if (!NT_STATUS_IS_OK(status_tmp)) {
1392 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1393 nt_errstr(status_tmp)));
1394 goto done;
1397 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1398 &samr_domain_handle,
1399 MAXIMUM_ALLOWED_ACCESS,
1400 my_info3->base.rid,
1401 &user_pol);
1403 if (!NT_STATUS_IS_OK(status_tmp)) {
1404 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1405 nt_errstr(status_tmp)));
1406 goto done;
1409 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1410 &user_pol,
1412 &info);
1414 if (!NT_STATUS_IS_OK(status_tmp)) {
1415 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1416 nt_errstr(status_tmp)));
1417 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1418 goto done;
1421 acct_flags = info->info16.acct_flags;
1423 if (acct_flags == 0) {
1424 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1425 goto done;
1428 my_info3->base.acct_flags = acct_flags;
1430 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1432 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1435 *info3 = my_info3;
1436 done:
1437 return result;
1440 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1441 struct winbindd_cli_state *state)
1443 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1444 NTSTATUS krb5_result = NT_STATUS_OK;
1445 fstring name_domain, name_user;
1446 char *mapped_user;
1447 fstring domain_user;
1448 struct netr_SamInfo3 *info3 = NULL;
1449 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1451 /* Ensure null termination */
1452 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1454 /* Ensure null termination */
1455 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1457 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1458 state->request->data.auth.user));
1460 if (!check_request_flags(state->request->flags)) {
1461 result = NT_STATUS_INVALID_PARAMETER_MIX;
1462 goto done;
1465 /* Parse domain and username */
1467 name_map_status = normalize_name_unmap(state->mem_ctx,
1468 state->request->data.auth.user,
1469 &mapped_user);
1471 /* If the name normalization didnt' actually do anything,
1472 just use the original name */
1474 if (!NT_STATUS_IS_OK(name_map_status) &&
1475 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1477 mapped_user = state->request->data.auth.user;
1480 parse_domain_user(mapped_user, name_domain, name_user);
1482 if ( mapped_user != state->request->data.auth.user ) {
1483 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1484 safe_strcpy( state->request->data.auth.user, domain_user,
1485 sizeof(state->request->data.auth.user)-1 );
1488 if (domain->online == false) {
1489 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1490 if (domain->startup) {
1491 /* Logons are very important to users. If we're offline and
1492 we get a request within the first 30 seconds of startup,
1493 try very hard to find a DC and go online. */
1495 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1496 "request in startup mode.\n", domain->name ));
1498 winbindd_flush_negative_conn_cache(domain);
1499 result = init_dc_connection(domain);
1503 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1505 /* Check for Kerberos authentication */
1506 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1508 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1509 /* save for later */
1510 krb5_result = result;
1513 if (NT_STATUS_IS_OK(result)) {
1514 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1515 goto process_result;
1516 } else {
1517 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1520 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1523 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1524 set_domain_offline( domain );
1525 goto cached_logon;
1528 /* there are quite some NT_STATUS errors where there is no
1529 * point in retrying with a samlogon, we explictly have to take
1530 * care not to increase the bad logon counter on the DC */
1532 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1533 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1534 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1535 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1536 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1542 goto process_result;
1545 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1546 DEBUG(3,("falling back to samlogon\n"));
1547 goto sam_logon;
1548 } else {
1549 goto cached_logon;
1553 sam_logon:
1554 /* Check for Samlogon authentication */
1555 if (domain->online) {
1556 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1558 if (NT_STATUS_IS_OK(result)) {
1559 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1560 /* add the Krb5 err if we have one */
1561 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1562 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1564 goto process_result;
1567 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1568 nt_errstr(result)));
1570 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1571 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1572 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1574 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1575 set_domain_offline( domain );
1576 goto cached_logon;
1579 if (domain->online) {
1580 /* We're still online - fail. */
1581 goto done;
1585 cached_logon:
1586 /* Check for Cached logons */
1587 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1588 lp_winbind_offline_logon()) {
1590 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1592 if (NT_STATUS_IS_OK(result)) {
1593 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1594 goto process_result;
1595 } else {
1596 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1597 goto done;
1601 process_result:
1603 if (NT_STATUS_IS_OK(result)) {
1605 DOM_SID user_sid;
1607 /* In all codepaths where result == NT_STATUS_OK info3 must have
1608 been initialized. */
1609 if (!info3) {
1610 result = NT_STATUS_INTERNAL_ERROR;
1611 goto done;
1614 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1615 netsamlogon_cache_store(name_user, info3);
1617 /* save name_to_sid info as early as possible (only if
1618 this is our primary domain so we don't invalidate
1619 the cache entry by storing the seq_num for the wrong
1620 domain). */
1621 if ( domain->primary ) {
1622 sid_compose(&user_sid, info3->base.domain_sid,
1623 info3->base.rid);
1624 cache_name2sid(domain, name_domain, name_user,
1625 SID_NAME_USER, &user_sid);
1628 /* Check if the user is in the right group */
1630 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1631 state->request->data.auth.require_membership_of_sid))) {
1632 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1633 state->request->data.auth.user,
1634 state->request->data.auth.require_membership_of_sid));
1635 goto done;
1638 result = append_data(state, info3, name_domain, name_user);
1639 if (!NT_STATUS_IS_OK(result)) {
1640 goto done;
1643 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1645 /* Store in-memory creds for single-signon using ntlm_auth. */
1646 result = winbindd_add_memory_creds(state->request->data.auth.user,
1647 get_uid_from_state(state),
1648 state->request->data.auth.pass);
1650 if (!NT_STATUS_IS_OK(result)) {
1651 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1652 goto done;
1655 if (lp_winbind_offline_logon()) {
1656 result = winbindd_store_creds(domain,
1657 state->mem_ctx,
1658 state->request->data.auth.user,
1659 state->request->data.auth.pass,
1660 info3, NULL);
1661 if (!NT_STATUS_IS_OK(result)) {
1663 /* Release refcount. */
1664 winbindd_delete_memory_creds(state->request->data.auth.user);
1666 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1667 goto done;
1673 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1674 struct winbindd_domain *our_domain = find_our_domain();
1676 /* This is not entirely correct I believe, but it is
1677 consistent. Only apply the password policy settings
1678 too warn users for our own domain. Cannot obtain these
1679 from trusted DCs all the time so don't do it at all.
1680 -- jerry */
1682 result = NT_STATUS_NOT_SUPPORTED;
1683 if (our_domain == domain ) {
1684 result = fillup_password_policy(our_domain, state);
1687 if (!NT_STATUS_IS_OK(result)
1688 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1690 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1691 domain->name, nt_errstr(result)));
1692 goto done;
1696 result = NT_STATUS_OK;
1699 done:
1700 /* give us a more useful (more correct?) error code */
1701 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1702 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1703 result = NT_STATUS_NO_LOGON_SERVERS;
1706 set_auth_errors(state->response, result);
1708 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1709 state->request->data.auth.user,
1710 state->response->data.auth.nt_status_string,
1711 state->response->data.auth.pam_error));
1713 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1717 /**********************************************************************
1718 Challenge Response Authentication Protocol
1719 **********************************************************************/
1721 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1723 struct winbindd_domain *domain = NULL;
1724 const char *domain_name = NULL;
1725 NTSTATUS result;
1727 if (!check_request_flags(state->request->flags)) {
1728 result = NT_STATUS_INVALID_PARAMETER_MIX;
1729 goto done;
1732 if (!state->privileged) {
1733 char *error_string = NULL;
1734 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1735 "denied. !\n"));
1736 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1737 "on %s are set correctly.\n",
1738 get_winbind_priv_pipe_dir()));
1739 /* send a better message than ACCESS_DENIED */
1740 error_string = talloc_asprintf(state->mem_ctx,
1741 "winbind client not authorized "
1742 "to use winbindd_pam_auth_crap."
1743 " Ensure permissions on %s "
1744 "are set correctly.",
1745 get_winbind_priv_pipe_dir());
1746 fstrcpy(state->response->data.auth.error_string, error_string);
1747 result = NT_STATUS_ACCESS_DENIED;
1748 goto done;
1751 /* Ensure null termination */
1752 state->request->data.auth_crap.user
1753 [sizeof(state->request->data.auth_crap.user)-1]=0;
1754 state->request->data.auth_crap.domain
1755 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1757 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1758 (unsigned long)state->pid,
1759 state->request->data.auth_crap.domain,
1760 state->request->data.auth_crap.user));
1762 if (*state->request->data.auth_crap.domain != '\0') {
1763 domain_name = state->request->data.auth_crap.domain;
1764 } else if (lp_winbind_use_default_domain()) {
1765 domain_name = lp_workgroup();
1768 if (domain_name != NULL)
1769 domain = find_auth_domain(state, domain_name);
1771 if (domain != NULL) {
1772 sendto_domain(state, domain);
1773 return;
1776 result = NT_STATUS_NO_SUCH_USER;
1778 done:
1779 set_auth_errors(state->response, result);
1780 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1781 state->request->data.auth_crap.domain,
1782 state->request->data.auth_crap.user,
1783 state->response->data.auth.nt_status_string,
1784 state->response->data.auth.pam_error));
1785 request_error(state);
1786 return;
1790 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1791 struct winbindd_cli_state *state)
1793 NTSTATUS result;
1794 struct netr_SamInfo3 *info3 = NULL;
1795 struct rpc_pipe_client *netlogon_pipe;
1796 const char *name_user = NULL;
1797 const char *name_domain = NULL;
1798 const char *workstation;
1799 struct winbindd_domain *contact_domain;
1800 int attempts = 0;
1801 bool retry;
1803 DATA_BLOB lm_resp, nt_resp;
1805 /* This is child-only, so no check for privileged access is needed
1806 anymore */
1808 /* Ensure null termination */
1809 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1810 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1812 if (!check_request_flags(state->request->flags)) {
1813 result = NT_STATUS_INVALID_PARAMETER_MIX;
1814 goto done;
1817 name_user = state->request->data.auth_crap.user;
1819 if (*state->request->data.auth_crap.domain) {
1820 name_domain = state->request->data.auth_crap.domain;
1821 } else if (lp_winbind_use_default_domain()) {
1822 name_domain = lp_workgroup();
1823 } else {
1824 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1825 name_user));
1826 result = NT_STATUS_NO_SUCH_USER;
1827 goto done;
1830 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1831 name_domain, name_user));
1833 if (*state->request->data.auth_crap.workstation) {
1834 workstation = state->request->data.auth_crap.workstation;
1835 } else {
1836 workstation = global_myname();
1839 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1840 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1841 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1842 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1843 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1844 state->request->data.auth_crap.lm_resp_len,
1845 state->request->data.auth_crap.nt_resp_len));
1846 result = NT_STATUS_INVALID_PARAMETER;
1847 goto done;
1851 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1852 state->request->data.auth_crap.lm_resp_len);
1854 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1855 nt_resp = data_blob_talloc(state->mem_ctx,
1856 state->request->extra_data.data,
1857 state->request->data.auth_crap.nt_resp_len);
1858 } else {
1859 nt_resp = data_blob_talloc(state->mem_ctx,
1860 state->request->data.auth_crap.nt_resp,
1861 state->request->data.auth_crap.nt_resp_len);
1864 /* what domain should we contact? */
1866 if ( IS_DC ) {
1867 if (!(contact_domain = find_domain_from_name(name_domain))) {
1868 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1869 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1870 result = NT_STATUS_NO_SUCH_USER;
1871 goto done;
1873 } else {
1874 if (is_myname(name_domain)) {
1875 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1876 result = NT_STATUS_NO_SUCH_USER;
1877 goto done;
1879 contact_domain = find_our_domain();
1882 do {
1883 netlogon_fn_t logon_fn;
1885 retry = false;
1887 netlogon_pipe = NULL;
1888 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1890 if (!NT_STATUS_IS_OK(result)) {
1891 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1892 nt_errstr(result)));
1893 goto done;
1896 logon_fn = contact_domain->can_do_samlogon_ex
1897 ? rpccli_netlogon_sam_network_logon_ex
1898 : rpccli_netlogon_sam_network_logon;
1900 result = logon_fn(netlogon_pipe,
1901 state->mem_ctx,
1902 state->request->data.auth_crap.logon_parameters,
1903 contact_domain->dcname,
1904 name_user,
1905 name_domain,
1906 /* Bug #3248 - found by Stefan Burkei. */
1907 workstation, /* We carefully set this above so use it... */
1908 state->request->data.auth_crap.chal,
1909 lm_resp,
1910 nt_resp,
1911 &info3);
1913 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1914 && contact_domain->can_do_samlogon_ex) {
1915 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1916 "retrying with NetSamLogon\n"));
1917 contact_domain->can_do_samlogon_ex = false;
1918 retry = true;
1919 continue;
1922 attempts += 1;
1924 /* We have to try a second time as cm_connect_netlogon
1925 might not yet have noticed that the DC has killed
1926 our connection. */
1928 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1929 retry = true;
1930 continue;
1933 /* if we get access denied, a possible cause was that we had and open
1934 connection to the DC, but someone changed our machine account password
1935 out from underneath us using 'net rpc changetrustpw' */
1937 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1938 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1939 "ACCESS_DENIED. Maybe the trust account "
1940 "password was changed and we didn't know it. "
1941 "Killing connections to domain %s\n",
1942 name_domain));
1943 invalidate_cm_connection(&contact_domain->conn);
1944 retry = true;
1947 } while ( (attempts < 2) && retry );
1949 if (NT_STATUS_IS_OK(result)) {
1951 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1952 netsamlogon_cache_store(name_user, info3);
1954 /* Check if the user is in the right group */
1956 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1957 state->request->data.auth_crap.require_membership_of_sid))) {
1958 DEBUG(3, ("User %s is not in the required group (%s), so "
1959 "crap authentication is rejected\n",
1960 state->request->data.auth_crap.user,
1961 state->request->data.auth_crap.require_membership_of_sid));
1962 goto done;
1965 result = append_data(state, info3, name_domain, name_user);
1966 if (!NT_STATUS_IS_OK(result)) {
1967 goto done;
1971 done:
1973 /* give us a more useful (more correct?) error code */
1974 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1975 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1976 result = NT_STATUS_NO_LOGON_SERVERS;
1979 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1980 result = nt_status_squash(result);
1983 set_auth_errors(state->response, result);
1985 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1986 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1987 name_domain,
1988 name_user,
1989 state->response->data.auth.nt_status_string,
1990 state->response->data.auth.pam_error));
1992 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1995 /* Change a user password */
1997 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1999 fstring domain, user;
2000 char *mapped_user;
2001 struct winbindd_domain *contact_domain;
2002 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2004 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2005 state->request->data.chauthtok.user));
2007 /* Setup crap */
2009 nt_status = normalize_name_unmap(state->mem_ctx,
2010 state->request->data.chauthtok.user,
2011 &mapped_user);
2013 /* Update the chauthtok name if we did any mapping */
2015 if (NT_STATUS_IS_OK(nt_status) ||
2016 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2018 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2021 /* Must pass in state->...chauthtok.user because
2022 canonicalize_username() assumes an fstring(). Since
2023 we have already copied it (if necessary), this is ok. */
2025 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2026 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2027 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2028 "(PAM: %d)\n",
2029 state->request->data.auth.user,
2030 state->response->data.auth.nt_status_string,
2031 state->response->data.auth.pam_error));
2032 request_error(state);
2033 return;
2036 contact_domain = find_domain_from_name(domain);
2037 if (!contact_domain) {
2038 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2039 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2040 state->request->data.chauthtok.user, domain, user, domain));
2041 request_error(state);
2042 return;
2045 sendto_domain(state, contact_domain);
2048 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2049 struct winbindd_cli_state *state)
2051 char *oldpass;
2052 char *newpass = NULL;
2053 struct policy_handle dom_pol;
2054 struct rpc_pipe_client *cli;
2055 bool got_info = false;
2056 struct samr_DomInfo1 *info = NULL;
2057 struct samr_ChangeReject *reject = NULL;
2058 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2059 fstring domain, user;
2061 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2062 state->request->data.auth.user));
2064 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2065 goto done;
2068 /* Change password */
2070 oldpass = state->request->data.chauthtok.oldpass;
2071 newpass = state->request->data.chauthtok.newpass;
2073 /* Initialize reject reason */
2074 state->response->data.auth.reject_reason = Undefined;
2076 /* Get sam handle */
2078 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2079 &dom_pol);
2080 if (!NT_STATUS_IS_OK(result)) {
2081 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2082 goto done;
2085 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2086 user,
2087 newpass,
2088 oldpass,
2089 &info,
2090 &reject);
2092 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2094 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2096 fill_in_password_policy(state->response, info);
2098 state->response->data.auth.reject_reason =
2099 reject->reason;
2101 got_info = true;
2104 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2105 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2106 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2107 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2109 /* only fallback when the chgpasswd_user3 call is not supported */
2110 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2111 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2112 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2113 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2115 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2116 nt_errstr(result)));
2118 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2120 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2121 Map to the same status code as Windows 2003. */
2123 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2124 result = NT_STATUS_PASSWORD_RESTRICTION;
2128 done:
2130 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2132 /* Update the single sign-on memory creds. */
2133 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2134 newpass);
2136 /* When we login from gdm or xdm and password expires,
2137 * we change password, but there are no memory crendentials
2138 * So, winbindd_replace_memory_creds() returns
2139 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2140 * --- BoYang
2141 * */
2142 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2143 result = NT_STATUS_OK;
2146 if (!NT_STATUS_IS_OK(result)) {
2147 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2148 goto process_result;
2151 if (lp_winbind_offline_logon()) {
2152 result = winbindd_update_creds_by_name(contact_domain,
2153 state->mem_ctx, user,
2154 newpass);
2155 /* Again, this happens when we login from gdm or xdm
2156 * and the password expires, *BUT* cached crendentials
2157 * doesn't exist. winbindd_update_creds_by_name()
2158 * returns NT_STATUS_NO_SUCH_USER.
2159 * This is not a failure.
2160 * --- BoYang
2161 * */
2162 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2163 result = NT_STATUS_OK;
2166 if (!NT_STATUS_IS_OK(result)) {
2167 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2168 goto process_result;
2173 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2175 NTSTATUS policy_ret;
2177 policy_ret = fillup_password_policy(contact_domain, state);
2179 /* failure of this is non critical, it will just provide no
2180 * additional information to the client why the change has
2181 * failed - Guenther */
2183 if (!NT_STATUS_IS_OK(policy_ret)) {
2184 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2185 goto process_result;
2189 process_result:
2191 set_auth_errors(state->response, result);
2193 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2194 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2195 domain,
2196 user,
2197 state->response->data.auth.nt_status_string,
2198 state->response->data.auth.pam_error));
2200 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2203 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2205 struct winbindd_domain *domain;
2206 fstring name_domain, user;
2207 uid_t caller_uid = (uid_t)-1;
2208 uid_t request_uid = state->request->data.logoff.uid;
2210 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2211 state->request->data.logoff.user));
2213 /* Ensure null termination */
2214 state->request->data.logoff.user
2215 [sizeof(state->request->data.logoff.user)-1]='\0';
2217 state->request->data.logoff.krb5ccname
2218 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2220 if (request_uid == (gid_t)-1) {
2221 goto failed;
2224 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2225 goto failed;
2228 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2229 goto failed;
2232 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2233 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2234 strerror(errno)));
2235 goto failed;
2238 switch (caller_uid) {
2239 case -1:
2240 goto failed;
2241 case 0:
2242 /* root must be able to logoff any user - gd */
2243 state->request->data.logoff.uid = request_uid;
2244 break;
2245 default:
2246 if (caller_uid != request_uid) {
2247 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2248 goto failed;
2250 state->request->data.logoff.uid = caller_uid;
2251 break;
2254 sendto_domain(state, domain);
2255 return;
2257 failed:
2258 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2259 DEBUG(5, ("Pam Logoff for %s returned %s "
2260 "(PAM: %d)\n",
2261 state->request->data.logoff.user,
2262 state->response->data.auth.nt_status_string,
2263 state->response->data.auth.pam_error));
2264 request_error(state);
2265 return;
2268 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2269 struct winbindd_cli_state *state)
2271 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2273 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2274 state->request->data.logoff.user));
2276 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2277 result = NT_STATUS_OK;
2278 goto process_result;
2281 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2282 result = NT_STATUS_OK;
2283 goto process_result;
2286 #ifdef HAVE_KRB5
2288 if (state->request->data.logoff.uid < 0) {
2289 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2290 goto process_result;
2293 /* what we need here is to find the corresponding krb5 ccache name *we*
2294 * created for a given username and destroy it */
2296 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2297 result = NT_STATUS_OK;
2298 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2299 goto process_result;
2302 if (!ccache_entry_identical(state->request->data.logoff.user,
2303 state->request->data.logoff.uid,
2304 state->request->data.logoff.krb5ccname)) {
2305 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2306 goto process_result;
2309 result = remove_ccache(state->request->data.logoff.user);
2310 if (!NT_STATUS_IS_OK(result)) {
2311 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2312 nt_errstr(result)));
2313 goto process_result;
2316 #else
2317 result = NT_STATUS_NOT_SUPPORTED;
2318 #endif
2320 process_result:
2322 winbindd_delete_memory_creds(state->request->data.logoff.user);
2324 set_auth_errors(state->response, result);
2326 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2329 /* Change user password with auth crap*/
2331 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2333 struct winbindd_domain *domain = NULL;
2334 const char *domain_name = NULL;
2336 /* Ensure null termination */
2337 state->request->data.chng_pswd_auth_crap.user[
2338 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2339 state->request->data.chng_pswd_auth_crap.domain[
2340 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2342 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2343 (unsigned long)state->pid,
2344 state->request->data.chng_pswd_auth_crap.domain,
2345 state->request->data.chng_pswd_auth_crap.user));
2347 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2348 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2349 } else if (lp_winbind_use_default_domain()) {
2350 domain_name = lp_workgroup();
2353 if (domain_name != NULL)
2354 domain = find_domain_from_name(domain_name);
2356 if (domain != NULL) {
2357 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2358 "%s\n", (unsigned long)state->pid,domain->name));
2359 sendto_domain(state, domain);
2360 return;
2363 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2364 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2365 state->request->data.chng_pswd_auth_crap.domain,
2366 state->request->data.chng_pswd_auth_crap.user,
2367 state->response->data.auth.nt_status_string,
2368 state->response->data.auth.pam_error));
2369 request_error(state);
2370 return;
2373 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2375 NTSTATUS result;
2376 DATA_BLOB new_nt_password;
2377 DATA_BLOB old_nt_hash_enc;
2378 DATA_BLOB new_lm_password;
2379 DATA_BLOB old_lm_hash_enc;
2380 fstring domain,user;
2381 struct policy_handle dom_pol;
2382 struct winbindd_domain *contact_domain = domainSt;
2383 struct rpc_pipe_client *cli;
2385 /* Ensure null termination */
2386 state->request->data.chng_pswd_auth_crap.user[
2387 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2388 state->request->data.chng_pswd_auth_crap.domain[
2389 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2390 *domain = 0;
2391 *user = 0;
2393 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2394 (unsigned long)state->pid,
2395 state->request->data.chng_pswd_auth_crap.domain,
2396 state->request->data.chng_pswd_auth_crap.user));
2398 if (lp_winbind_offline_logon()) {
2399 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2400 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2401 result = NT_STATUS_ACCESS_DENIED;
2402 goto done;
2405 if (*state->request->data.chng_pswd_auth_crap.domain) {
2406 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2407 } else {
2408 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2409 domain, user);
2411 if(!*domain) {
2412 DEBUG(3,("no domain specified with username (%s) - "
2413 "failing auth\n",
2414 state->request->data.chng_pswd_auth_crap.user));
2415 result = NT_STATUS_NO_SUCH_USER;
2416 goto done;
2420 if (!*domain && lp_winbind_use_default_domain()) {
2421 fstrcpy(domain,(char *)lp_workgroup());
2424 if(!*user) {
2425 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2428 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2429 (unsigned long)state->pid, domain, user));
2431 /* Change password */
2432 new_nt_password = data_blob_talloc(
2433 state->mem_ctx,
2434 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2435 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2437 old_nt_hash_enc = data_blob_talloc(
2438 state->mem_ctx,
2439 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2440 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2442 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2443 new_lm_password = data_blob_talloc(
2444 state->mem_ctx,
2445 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2446 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2448 old_lm_hash_enc = data_blob_talloc(
2449 state->mem_ctx,
2450 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2451 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2452 } else {
2453 new_lm_password.length = 0;
2454 old_lm_hash_enc.length = 0;
2457 /* Get sam handle */
2459 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2460 if (!NT_STATUS_IS_OK(result)) {
2461 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2462 goto done;
2465 result = rpccli_samr_chng_pswd_auth_crap(
2466 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2467 new_lm_password, old_lm_hash_enc);
2469 done:
2471 set_auth_errors(state->response, result);
2473 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2474 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2475 domain, user,
2476 state->response->data.auth.nt_status_string,
2477 state->response->data.auth.pam_error));
2479 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;