s4-netlogon: implement dcesrv_netr_DsRAddressToSitenamesExW
[Samba/aatanasov.git] / source3 / winbindd / winbindd_pam.c
blobb87d2a8a9b11ffa071e0378a29ed61567bfffb56
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 static 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,
929 NT_HASH_LEN) == 0);
930 } else {
931 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
932 password_good = (memcmp(cached_nt_pass, new_nt_pass,
933 NT_HASH_LEN) == 0);
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 static 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 static 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 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1734 "denied. !\n"));
1735 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1736 "on %s are set correctly.\n",
1737 get_winbind_priv_pipe_dir()));
1738 /* send a better message than ACCESS_DENIED */
1739 fstr_sprintf(state->response->data.auth.error_string,
1740 "winbind client not authorized to use "
1741 "winbindd_pam_auth_crap. Ensure permissions on "
1742 "%s are set correctly.",
1743 get_winbind_priv_pipe_dir());
1744 result = NT_STATUS_ACCESS_DENIED;
1745 goto done;
1748 /* Ensure null termination */
1749 state->request->data.auth_crap.user
1750 [sizeof(state->request->data.auth_crap.user)-1]=0;
1751 state->request->data.auth_crap.domain
1752 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1754 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1755 (unsigned long)state->pid,
1756 state->request->data.auth_crap.domain,
1757 state->request->data.auth_crap.user));
1759 if (*state->request->data.auth_crap.domain != '\0') {
1760 domain_name = state->request->data.auth_crap.domain;
1761 } else if (lp_winbind_use_default_domain()) {
1762 domain_name = lp_workgroup();
1765 if (domain_name != NULL)
1766 domain = find_auth_domain(state, domain_name);
1768 if (domain != NULL) {
1769 sendto_domain(state, domain);
1770 return;
1773 result = NT_STATUS_NO_SUCH_USER;
1775 done:
1776 set_auth_errors(state->response, result);
1777 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1778 state->request->data.auth_crap.domain,
1779 state->request->data.auth_crap.user,
1780 state->response->data.auth.nt_status_string,
1781 state->response->data.auth.pam_error));
1782 request_error(state);
1783 return;
1787 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1788 struct winbindd_cli_state *state)
1790 NTSTATUS result;
1791 struct netr_SamInfo3 *info3 = NULL;
1792 struct rpc_pipe_client *netlogon_pipe;
1793 const char *name_user = NULL;
1794 const char *name_domain = NULL;
1795 const char *workstation;
1796 struct winbindd_domain *contact_domain;
1797 int attempts = 0;
1798 bool retry;
1800 DATA_BLOB lm_resp, nt_resp;
1802 /* This is child-only, so no check for privileged access is needed
1803 anymore */
1805 /* Ensure null termination */
1806 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1807 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1809 if (!check_request_flags(state->request->flags)) {
1810 result = NT_STATUS_INVALID_PARAMETER_MIX;
1811 goto done;
1814 name_user = state->request->data.auth_crap.user;
1816 if (*state->request->data.auth_crap.domain) {
1817 name_domain = state->request->data.auth_crap.domain;
1818 } else if (lp_winbind_use_default_domain()) {
1819 name_domain = lp_workgroup();
1820 } else {
1821 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1822 name_user));
1823 result = NT_STATUS_NO_SUCH_USER;
1824 goto done;
1827 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1828 name_domain, name_user));
1830 if (*state->request->data.auth_crap.workstation) {
1831 workstation = state->request->data.auth_crap.workstation;
1832 } else {
1833 workstation = global_myname();
1836 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1837 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1838 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1839 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1840 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1841 state->request->data.auth_crap.lm_resp_len,
1842 state->request->data.auth_crap.nt_resp_len));
1843 result = NT_STATUS_INVALID_PARAMETER;
1844 goto done;
1848 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1849 state->request->data.auth_crap.lm_resp_len);
1851 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1852 nt_resp = data_blob_talloc(state->mem_ctx,
1853 state->request->extra_data.data,
1854 state->request->data.auth_crap.nt_resp_len);
1855 } else {
1856 nt_resp = data_blob_talloc(state->mem_ctx,
1857 state->request->data.auth_crap.nt_resp,
1858 state->request->data.auth_crap.nt_resp_len);
1861 /* what domain should we contact? */
1863 if ( IS_DC ) {
1864 if (!(contact_domain = find_domain_from_name(name_domain))) {
1865 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1866 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1867 result = NT_STATUS_NO_SUCH_USER;
1868 goto done;
1870 } else {
1871 if (is_myname(name_domain)) {
1872 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1873 result = NT_STATUS_NO_SUCH_USER;
1874 goto done;
1876 contact_domain = find_our_domain();
1879 do {
1880 netlogon_fn_t logon_fn;
1882 retry = false;
1884 netlogon_pipe = NULL;
1885 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1887 if (!NT_STATUS_IS_OK(result)) {
1888 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1889 nt_errstr(result)));
1890 goto done;
1893 logon_fn = contact_domain->can_do_samlogon_ex
1894 ? rpccli_netlogon_sam_network_logon_ex
1895 : rpccli_netlogon_sam_network_logon;
1897 result = logon_fn(netlogon_pipe,
1898 state->mem_ctx,
1899 state->request->data.auth_crap.logon_parameters,
1900 contact_domain->dcname,
1901 name_user,
1902 name_domain,
1903 /* Bug #3248 - found by Stefan Burkei. */
1904 workstation, /* We carefully set this above so use it... */
1905 state->request->data.auth_crap.chal,
1906 lm_resp,
1907 nt_resp,
1908 &info3);
1910 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1911 && contact_domain->can_do_samlogon_ex) {
1912 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1913 "retrying with NetSamLogon\n"));
1914 contact_domain->can_do_samlogon_ex = false;
1915 retry = true;
1916 continue;
1919 attempts += 1;
1921 /* We have to try a second time as cm_connect_netlogon
1922 might not yet have noticed that the DC has killed
1923 our connection. */
1925 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1926 retry = true;
1927 continue;
1930 /* if we get access denied, a possible cause was that we had and open
1931 connection to the DC, but someone changed our machine account password
1932 out from underneath us using 'net rpc changetrustpw' */
1934 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1935 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1936 "ACCESS_DENIED. Maybe the trust account "
1937 "password was changed and we didn't know it. "
1938 "Killing connections to domain %s\n",
1939 name_domain));
1940 invalidate_cm_connection(&contact_domain->conn);
1941 retry = true;
1944 } while ( (attempts < 2) && retry );
1946 if (NT_STATUS_IS_OK(result)) {
1948 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1949 netsamlogon_cache_store(name_user, info3);
1951 /* Check if the user is in the right group */
1953 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1954 state->request->data.auth_crap.require_membership_of_sid))) {
1955 DEBUG(3, ("User %s is not in the required group (%s), so "
1956 "crap authentication is rejected\n",
1957 state->request->data.auth_crap.user,
1958 state->request->data.auth_crap.require_membership_of_sid));
1959 goto done;
1962 result = append_data(state, info3, name_domain, name_user);
1963 if (!NT_STATUS_IS_OK(result)) {
1964 goto done;
1968 done:
1970 /* give us a more useful (more correct?) error code */
1971 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1972 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1973 result = NT_STATUS_NO_LOGON_SERVERS;
1976 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1977 result = nt_status_squash(result);
1980 set_auth_errors(state->response, result);
1982 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1983 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1984 name_domain,
1985 name_user,
1986 state->response->data.auth.nt_status_string,
1987 state->response->data.auth.pam_error));
1989 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1992 /* Change a user password */
1994 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1996 fstring domain, user;
1997 char *mapped_user;
1998 struct winbindd_domain *contact_domain;
1999 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2001 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2002 state->request->data.chauthtok.user));
2004 /* Setup crap */
2006 nt_status = normalize_name_unmap(state->mem_ctx,
2007 state->request->data.chauthtok.user,
2008 &mapped_user);
2010 /* Update the chauthtok name if we did any mapping */
2012 if (NT_STATUS_IS_OK(nt_status) ||
2013 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2015 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2018 /* Must pass in state->...chauthtok.user because
2019 canonicalize_username() assumes an fstring(). Since
2020 we have already copied it (if necessary), this is ok. */
2022 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2023 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2024 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2025 "(PAM: %d)\n",
2026 state->request->data.auth.user,
2027 state->response->data.auth.nt_status_string,
2028 state->response->data.auth.pam_error));
2029 request_error(state);
2030 return;
2033 contact_domain = find_domain_from_name(domain);
2034 if (!contact_domain) {
2035 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2036 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2037 state->request->data.chauthtok.user, domain, user, domain));
2038 request_error(state);
2039 return;
2042 sendto_domain(state, contact_domain);
2045 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2046 struct winbindd_cli_state *state)
2048 char *oldpass;
2049 char *newpass = NULL;
2050 struct policy_handle dom_pol;
2051 struct rpc_pipe_client *cli;
2052 bool got_info = false;
2053 struct samr_DomInfo1 *info = NULL;
2054 struct samr_ChangeReject *reject = NULL;
2055 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2056 fstring domain, user;
2058 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2059 state->request->data.auth.user));
2061 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2062 goto done;
2065 /* Change password */
2067 oldpass = state->request->data.chauthtok.oldpass;
2068 newpass = state->request->data.chauthtok.newpass;
2070 /* Initialize reject reason */
2071 state->response->data.auth.reject_reason = Undefined;
2073 /* Get sam handle */
2075 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2076 &dom_pol);
2077 if (!NT_STATUS_IS_OK(result)) {
2078 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2079 goto done;
2082 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2083 user,
2084 newpass,
2085 oldpass,
2086 &info,
2087 &reject);
2089 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2091 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2093 fill_in_password_policy(state->response, info);
2095 state->response->data.auth.reject_reason =
2096 reject->reason;
2098 got_info = true;
2101 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2102 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2103 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2104 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2106 /* only fallback when the chgpasswd_user3 call is not supported */
2107 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2108 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2109 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2110 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2112 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2113 nt_errstr(result)));
2115 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2117 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2118 Map to the same status code as Windows 2003. */
2120 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2121 result = NT_STATUS_PASSWORD_RESTRICTION;
2125 done:
2127 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2129 /* Update the single sign-on memory creds. */
2130 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2131 newpass);
2133 /* When we login from gdm or xdm and password expires,
2134 * we change password, but there are no memory crendentials
2135 * So, winbindd_replace_memory_creds() returns
2136 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2137 * --- BoYang
2138 * */
2139 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2140 result = NT_STATUS_OK;
2143 if (!NT_STATUS_IS_OK(result)) {
2144 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2145 goto process_result;
2148 if (lp_winbind_offline_logon()) {
2149 result = winbindd_update_creds_by_name(contact_domain,
2150 state->mem_ctx, user,
2151 newpass);
2152 /* Again, this happens when we login from gdm or xdm
2153 * and the password expires, *BUT* cached crendentials
2154 * doesn't exist. winbindd_update_creds_by_name()
2155 * returns NT_STATUS_NO_SUCH_USER.
2156 * This is not a failure.
2157 * --- BoYang
2158 * */
2159 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2160 result = NT_STATUS_OK;
2163 if (!NT_STATUS_IS_OK(result)) {
2164 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2165 goto process_result;
2170 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2172 NTSTATUS policy_ret;
2174 policy_ret = fillup_password_policy(contact_domain, state);
2176 /* failure of this is non critical, it will just provide no
2177 * additional information to the client why the change has
2178 * failed - Guenther */
2180 if (!NT_STATUS_IS_OK(policy_ret)) {
2181 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2182 goto process_result;
2186 process_result:
2188 set_auth_errors(state->response, result);
2190 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2191 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2192 domain,
2193 user,
2194 state->response->data.auth.nt_status_string,
2195 state->response->data.auth.pam_error));
2197 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2200 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2202 struct winbindd_domain *domain;
2203 fstring name_domain, user;
2204 uid_t caller_uid = (uid_t)-1;
2205 uid_t request_uid = state->request->data.logoff.uid;
2207 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2208 state->request->data.logoff.user));
2210 /* Ensure null termination */
2211 state->request->data.logoff.user
2212 [sizeof(state->request->data.logoff.user)-1]='\0';
2214 state->request->data.logoff.krb5ccname
2215 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2217 if (request_uid == (gid_t)-1) {
2218 goto failed;
2221 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2222 goto failed;
2225 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2226 goto failed;
2229 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2230 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2231 strerror(errno)));
2232 goto failed;
2235 switch (caller_uid) {
2236 case -1:
2237 goto failed;
2238 case 0:
2239 /* root must be able to logoff any user - gd */
2240 state->request->data.logoff.uid = request_uid;
2241 break;
2242 default:
2243 if (caller_uid != request_uid) {
2244 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2245 goto failed;
2247 state->request->data.logoff.uid = caller_uid;
2248 break;
2251 sendto_domain(state, domain);
2252 return;
2254 failed:
2255 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2256 DEBUG(5, ("Pam Logoff for %s returned %s "
2257 "(PAM: %d)\n",
2258 state->request->data.logoff.user,
2259 state->response->data.auth.nt_status_string,
2260 state->response->data.auth.pam_error));
2261 request_error(state);
2262 return;
2265 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2266 struct winbindd_cli_state *state)
2268 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2270 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2271 state->request->data.logoff.user));
2273 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2274 result = NT_STATUS_OK;
2275 goto process_result;
2278 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2279 result = NT_STATUS_OK;
2280 goto process_result;
2283 #ifdef HAVE_KRB5
2285 if (state->request->data.logoff.uid < 0) {
2286 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2287 goto process_result;
2290 /* what we need here is to find the corresponding krb5 ccache name *we*
2291 * created for a given username and destroy it */
2293 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2294 result = NT_STATUS_OK;
2295 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2296 goto process_result;
2299 if (!ccache_entry_identical(state->request->data.logoff.user,
2300 state->request->data.logoff.uid,
2301 state->request->data.logoff.krb5ccname)) {
2302 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2303 goto process_result;
2306 result = remove_ccache(state->request->data.logoff.user);
2307 if (!NT_STATUS_IS_OK(result)) {
2308 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2309 nt_errstr(result)));
2310 goto process_result;
2313 #else
2314 result = NT_STATUS_NOT_SUPPORTED;
2315 #endif
2317 process_result:
2319 winbindd_delete_memory_creds(state->request->data.logoff.user);
2321 set_auth_errors(state->response, result);
2323 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2326 /* Change user password with auth crap*/
2328 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2330 struct winbindd_domain *domain = NULL;
2331 const char *domain_name = NULL;
2333 /* Ensure null termination */
2334 state->request->data.chng_pswd_auth_crap.user[
2335 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2336 state->request->data.chng_pswd_auth_crap.domain[
2337 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2339 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2340 (unsigned long)state->pid,
2341 state->request->data.chng_pswd_auth_crap.domain,
2342 state->request->data.chng_pswd_auth_crap.user));
2344 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2345 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2346 } else if (lp_winbind_use_default_domain()) {
2347 domain_name = lp_workgroup();
2350 if (domain_name != NULL)
2351 domain = find_domain_from_name(domain_name);
2353 if (domain != NULL) {
2354 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2355 "%s\n", (unsigned long)state->pid,domain->name));
2356 sendto_domain(state, domain);
2357 return;
2360 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2361 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2362 state->request->data.chng_pswd_auth_crap.domain,
2363 state->request->data.chng_pswd_auth_crap.user,
2364 state->response->data.auth.nt_status_string,
2365 state->response->data.auth.pam_error));
2366 request_error(state);
2367 return;
2370 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2372 NTSTATUS result;
2373 DATA_BLOB new_nt_password;
2374 DATA_BLOB old_nt_hash_enc;
2375 DATA_BLOB new_lm_password;
2376 DATA_BLOB old_lm_hash_enc;
2377 fstring domain,user;
2378 struct policy_handle dom_pol;
2379 struct winbindd_domain *contact_domain = domainSt;
2380 struct rpc_pipe_client *cli;
2382 /* Ensure null termination */
2383 state->request->data.chng_pswd_auth_crap.user[
2384 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2385 state->request->data.chng_pswd_auth_crap.domain[
2386 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2387 *domain = 0;
2388 *user = 0;
2390 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2391 (unsigned long)state->pid,
2392 state->request->data.chng_pswd_auth_crap.domain,
2393 state->request->data.chng_pswd_auth_crap.user));
2395 if (lp_winbind_offline_logon()) {
2396 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2397 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2398 result = NT_STATUS_ACCESS_DENIED;
2399 goto done;
2402 if (*state->request->data.chng_pswd_auth_crap.domain) {
2403 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2404 } else {
2405 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2406 domain, user);
2408 if(!*domain) {
2409 DEBUG(3,("no domain specified with username (%s) - "
2410 "failing auth\n",
2411 state->request->data.chng_pswd_auth_crap.user));
2412 result = NT_STATUS_NO_SUCH_USER;
2413 goto done;
2417 if (!*domain && lp_winbind_use_default_domain()) {
2418 fstrcpy(domain,(char *)lp_workgroup());
2421 if(!*user) {
2422 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2425 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2426 (unsigned long)state->pid, domain, user));
2428 /* Change password */
2429 new_nt_password = data_blob_talloc(
2430 state->mem_ctx,
2431 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2432 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2434 old_nt_hash_enc = data_blob_talloc(
2435 state->mem_ctx,
2436 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2437 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2439 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2440 new_lm_password = data_blob_talloc(
2441 state->mem_ctx,
2442 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2443 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2445 old_lm_hash_enc = data_blob_talloc(
2446 state->mem_ctx,
2447 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2448 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2449 } else {
2450 new_lm_password.length = 0;
2451 old_lm_hash_enc.length = 0;
2454 /* Get sam handle */
2456 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2457 if (!NT_STATUS_IS_OK(result)) {
2458 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2459 goto done;
2462 result = rpccli_samr_chng_pswd_auth_crap(
2463 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2464 new_lm_password, old_lm_hash_enc);
2466 done:
2468 set_auth_errors(state->response, result);
2470 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2471 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2472 domain, user,
2473 state->response->data.auth.nt_status_string,
2474 state->response->data.auth.pam_error));
2476 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;