idmap rewrite
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blob0f9f1e1621009cecce5f4810186a07dab37b4f37
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
32 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
33 struct winbindd_cli_state *state,
34 struct netr_SamInfo3 *info3)
36 char *ex;
37 size_t size;
38 uint32_t i;
40 state->response.data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response.data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response.data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response.data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response.data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response.data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response.data.auth.info3.logon_count = info3->base.logon_count;
54 state->response.data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response.data.auth.info3.user_rid = info3->base.rid;
57 state->response.data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response.data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response.data.auth.info3.num_groups = info3->base.groups.count;
61 state->response.data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response.data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response.data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response.data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response.data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response.data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response.data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response.data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response.data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response.data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response.data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
95 char *sid;
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
101 sid,
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 talloc_free(sid);
108 size = talloc_get_size(ex);
110 SAFE_FREE(state->response.extra_data.data);
111 state->response.extra_data.data = SMB_MALLOC(size);
112 if (!state->response.extra_data.data) {
113 return NT_STATUS_NO_MEMORY;
115 memcpy(state->response.extra_data.data, ex, size);
116 talloc_free(ex);
118 state->response.length += size;
120 return NT_STATUS_OK;
123 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
124 struct winbindd_cli_state *state,
125 struct netr_SamInfo3 *info3)
127 DATA_BLOB blob;
128 enum ndr_err_code ndr_err;
130 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
131 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
132 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
134 return ndr_map_error2ntstatus(ndr_err);
137 SAFE_FREE(state->response.extra_data.data);
138 state->response.extra_data.data = SMB_MALLOC(blob.length);
139 if (!state->response.extra_data.data) {
140 data_blob_free(&blob);
141 return NT_STATUS_NO_MEMORY;
144 memset(state->response.extra_data.data, '\0', blob.length);
145 memcpy(state->response.extra_data.data, blob.data, blob.length);
146 state->response.length += blob.length;
148 data_blob_free(&blob);
150 return NT_STATUS_OK;
153 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
154 struct winbindd_cli_state *state,
155 const struct netr_SamInfo3 *info3,
156 const char *name_domain,
157 const char *name_user)
159 /* We've been asked to return the unix username, per
160 'winbind use default domain' settings and the like */
162 const char *nt_username, *nt_domain;
164 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
165 if (!nt_domain) {
166 /* If the server didn't give us one, just use the one
167 * we sent them */
168 nt_domain = name_domain;
171 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
172 if (!nt_username) {
173 /* If the server didn't give us one, just use the one
174 * we sent them */
175 nt_username = name_user;
178 fill_domain_username(state->response.data.auth.unix_username,
179 nt_domain, nt_username, True);
181 DEBUG(5,("Setting unix username to [%s]\n",
182 state->response.data.auth.unix_username));
184 return NT_STATUS_OK;
187 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
188 struct winbindd_cli_state *state,
189 const struct netr_SamInfo3 *info3,
190 const char *name_domain,
191 const char *name_user)
193 char *afsname = NULL;
194 char *cell;
196 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197 if (afsname == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 afsname = talloc_string_sub(mem_ctx,
202 lp_afs_username_map(),
203 "%D", name_domain);
204 afsname = talloc_string_sub(mem_ctx, afsname,
205 "%u", name_user);
206 afsname = talloc_string_sub(mem_ctx, afsname,
207 "%U", name_user);
210 DOM_SID user_sid;
211 fstring sidstr;
213 sid_copy(&user_sid, info3->base.domain_sid);
214 sid_append_rid(&user_sid, info3->base.rid);
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
217 "%s", sidstr);
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 strlower_m(afsname);
226 DEBUG(10, ("Generating token for user %s\n", afsname));
228 cell = strchr(afsname, '@');
230 if (cell == NULL) {
231 return NT_STATUS_NO_MEMORY;
234 *cell = '\0';
235 cell += 1;
237 /* Append an AFS token string */
238 SAFE_FREE(state->response.extra_data.data);
239 state->response.extra_data.data =
240 afs_createtoken_str(afsname, cell);
242 if (state->response.extra_data.data != NULL) {
243 state->response.length +=
244 strlen((const char *)state->response.extra_data.data)+1;
247 return NT_STATUS_OK;
250 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
251 struct netr_SamInfo3 *info3,
252 const char *group_sid)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 DOM_SID *require_membership_of_sid;
266 size_t num_require_membership_of_sid;
267 char *req_sid;
268 const char *p;
269 DOM_SID sid;
270 size_t i;
271 struct nt_user_token *token;
272 TALLOC_CTX *frame = NULL;
273 NTSTATUS status;
275 /* Parse the 'required group' SID */
277 if (!group_sid || !group_sid[0]) {
278 /* NO sid supplied, all users may access */
279 return NT_STATUS_OK;
282 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
283 DEBUG(0, ("talloc failed\n"));
284 return NT_STATUS_NO_MEMORY;
287 num_require_membership_of_sid = 0;
288 require_membership_of_sid = NULL;
290 p = group_sid;
292 frame = talloc_stackframe();
293 while (next_token_talloc(frame, &p, &req_sid, ",")) {
294 if (!string_to_sid(&sid, req_sid)) {
295 DEBUG(0, ("check_info3_in_group: could not parse %s "
296 "as a SID!", req_sid));
297 TALLOC_FREE(frame);
298 return NT_STATUS_INVALID_PARAMETER;
301 status = add_sid_to_array(mem_ctx, &sid,
302 &require_membership_of_sid,
303 &num_require_membership_of_sid);
304 if (!NT_STATUS_IS_OK(status)) {
305 DEBUG(0, ("add_sid_to_array failed\n"));
306 TALLOC_FREE(frame);
307 return status;
311 TALLOC_FREE(frame);
313 status = sid_array_from_info3(mem_ctx, info3,
314 &token->user_sids,
315 &token->num_sids,
316 true, false);
317 if (!NT_STATUS_IS_OK(status)) {
318 return status;
321 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
322 token))
323 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
324 token))) {
325 DEBUG(3, ("could not add aliases: %s\n",
326 nt_errstr(status)));
327 return status;
330 debug_nt_user_token(DBGC_CLASS, 10, token);
332 for (i=0; i<num_require_membership_of_sid; i++) {
333 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
334 &require_membership_of_sid[i])));
335 if (nt_token_check_sid(&require_membership_of_sid[i],
336 token)) {
337 DEBUG(10, ("Access ok\n"));
338 return NT_STATUS_OK;
342 /* Do not distinguish this error from a wrong username/pw */
344 return NT_STATUS_LOGON_FAILURE;
347 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
348 const char *domain_name)
350 struct winbindd_domain *domain;
352 if (IS_DC) {
353 domain = find_domain_from_name_noinit(domain_name);
354 if (domain == NULL) {
355 DEBUG(3, ("Authentication for domain [%s] refused "
356 "as it is not a trusted domain\n",
357 domain_name));
359 return domain;
362 if (is_myname(domain_name)) {
363 DEBUG(3, ("Authentication for domain %s (local domain "
364 "to this server) not supported at this "
365 "stage\n", domain_name));
366 return NULL;
369 /* we can auth against trusted domains */
370 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
371 domain = find_domain_from_name_noinit(domain_name);
372 if (domain == NULL) {
373 DEBUG(3, ("Authentication for domain [%s] skipped "
374 "as it is not a trusted domain\n",
375 domain_name));
376 } else {
377 return domain;
381 return find_our_domain();
384 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
386 resp->data.auth.nt_status = NT_STATUS_V(result);
387 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
389 /* we might have given a more useful error above */
390 if (*resp->data.auth.error_string == '\0')
391 fstrcpy(resp->data.auth.error_string,
392 get_friendly_nt_error_msg(result));
393 resp->data.auth.pam_error = nt_status_to_pam(result);
396 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
397 struct winbindd_cli_state *state)
399 struct winbindd_methods *methods;
400 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
401 struct samr_DomInfo1 password_policy;
403 if ( !winbindd_can_contact_domain( domain ) ) {
404 DEBUG(5,("fillup_password_policy: No inbound trust to "
405 "contact domain %s\n", domain->name));
406 return NT_STATUS_NOT_SUPPORTED;
409 methods = domain->methods;
411 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
412 if (NT_STATUS_IS_ERR(status)) {
413 return status;
416 state->response.data.auth.policy.min_length_password =
417 password_policy.min_password_length;
418 state->response.data.auth.policy.password_history =
419 password_policy.password_history_length;
420 state->response.data.auth.policy.password_properties =
421 password_policy.password_properties;
422 state->response.data.auth.policy.expire =
423 nt_time_to_unix_abs((NTTIME *)&(password_policy.max_password_age));
424 state->response.data.auth.policy.min_passwordage =
425 nt_time_to_unix_abs((NTTIME *)&(password_policy.min_password_age));
427 return NT_STATUS_OK;
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
431 TALLOC_CTX *mem_ctx,
432 uint16 *lockout_threshold)
434 struct winbindd_methods *methods;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 struct samr_DomInfo12 lockout_policy;
438 *lockout_threshold = 0;
440 methods = domain->methods;
442 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
443 if (NT_STATUS_IS_ERR(status)) {
444 return status;
447 *lockout_threshold = lockout_policy.lockout_threshold;
449 return NT_STATUS_OK;
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
453 TALLOC_CTX *mem_ctx,
454 uint32 *password_properties)
456 struct winbindd_methods *methods;
457 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458 struct samr_DomInfo1 password_policy;
460 *password_properties = 0;
462 methods = domain->methods;
464 status = methods->password_policy(domain, mem_ctx, &password_policy);
465 if (NT_STATUS_IS_ERR(status)) {
466 return status;
469 *password_properties = password_policy.password_properties;
471 return NT_STATUS_OK;
474 #ifdef HAVE_KRB5
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
477 const char *type,
478 uid_t uid,
479 bool *internal_ccache)
481 /* accept FILE and WRFILE as krb5_cc_type from the client and then
482 * build the full ccname string based on the user's uid here -
483 * Guenther*/
485 const char *gen_cc = NULL;
487 *internal_ccache = True;
489 if (uid == -1) {
490 goto memory_ccache;
493 if (!type || type[0] == '\0') {
494 goto memory_ccache;
497 if (strequal(type, "FILE")) {
498 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
499 } else if (strequal(type, "WRFILE")) {
500 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
501 } else {
502 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
503 goto memory_ccache;
506 *internal_ccache = False;
507 goto done;
509 memory_ccache:
510 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
512 done:
513 if (gen_cc == NULL) {
514 DEBUG(0,("out of memory\n"));
515 return NULL;
518 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
520 return gen_cc;
523 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
525 const char *type = state->request.data.auth.krb5_cc_type;
527 state->response.data.auth.krb5ccname[0] = '\0';
529 if (type[0] == '\0') {
530 return;
533 if (!strequal(type, "FILE") &&
534 !strequal(type, "WRFILE")) {
535 DEBUG(10,("won't return krbccname for a %s type ccache\n",
536 type));
537 return;
540 fstrcpy(state->response.data.auth.krb5ccname, cc);
543 #endif
545 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
547 uid_t uid = -1;
549 uid = state->request.data.auth.uid;
551 if (uid < 0) {
552 DEBUG(1,("invalid uid: '%d'\n", uid));
553 return -1;
555 return uid;
558 /**********************************************************************
559 Authenticate a user with a clear text password using Kerberos and fill up
560 ccache if required
561 **********************************************************************/
563 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
564 struct winbindd_cli_state *state,
565 struct netr_SamInfo3 **info3)
567 #ifdef HAVE_KRB5
568 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
569 krb5_error_code krb5_ret;
570 const char *cc = NULL;
571 const char *principal_s = NULL;
572 const char *service = NULL;
573 char *realm = NULL;
574 fstring name_domain, name_user;
575 time_t ticket_lifetime = 0;
576 time_t renewal_until = 0;
577 uid_t uid = -1;
578 ADS_STRUCT *ads;
579 time_t time_offset = 0;
580 bool internal_ccache = True;
582 ZERO_STRUCTP(info3);
584 *info3 = NULL;
586 /* 1st step:
587 * prepare a krb5_cc_cache string for the user */
589 uid = get_uid_from_state(state);
590 if (uid == -1) {
591 DEBUG(0,("no valid uid\n"));
594 cc = generate_krb5_ccache(state->mem_ctx,
595 state->request.data.auth.krb5_cc_type,
596 state->request.data.auth.uid,
597 &internal_ccache);
598 if (cc == NULL) {
599 return NT_STATUS_NO_MEMORY;
603 /* 2nd step:
604 * get kerberos properties */
606 if (domain->private_data) {
607 ads = (ADS_STRUCT *)domain->private_data;
608 time_offset = ads->auth.time_offset;
612 /* 3rd step:
613 * do kerberos auth and setup ccache as the user */
615 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
617 realm = domain->alt_name;
618 strupper_m(realm);
620 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
621 if (principal_s == NULL) {
622 return NT_STATUS_NO_MEMORY;
625 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
626 if (service == NULL) {
627 return NT_STATUS_NO_MEMORY;
630 /* if this is a user ccache, we need to act as the user to let the krb5
631 * library handle the chown, etc. */
633 /************************ ENTERING NON-ROOT **********************/
635 if (!internal_ccache) {
636 set_effective_uid(uid);
637 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
640 result = kerberos_return_info3_from_pac(state->mem_ctx,
641 principal_s,
642 state->request.data.auth.pass,
643 time_offset,
644 &ticket_lifetime,
645 &renewal_until,
647 True,
648 True,
649 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
650 info3);
651 if (!internal_ccache) {
652 gain_root_privilege();
655 /************************ RETURNED TO ROOT **********************/
657 if (!NT_STATUS_IS_OK(result)) {
658 goto failed;
661 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
662 principal_s));
664 /* if we had a user's ccache then return that string for the pam
665 * environment */
667 if (!internal_ccache) {
669 setup_return_cc_name(state, cc);
671 result = add_ccache_to_list(principal_s,
673 service,
674 state->request.data.auth.user,
675 realm,
676 uid,
677 time(NULL),
678 ticket_lifetime,
679 renewal_until,
680 False);
682 if (!NT_STATUS_IS_OK(result)) {
683 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
684 nt_errstr(result)));
686 } else {
688 /* need to delete the memory cred cache, it is not used anymore */
690 krb5_ret = ads_kdestroy(cc);
691 if (krb5_ret) {
692 DEBUG(3,("winbindd_raw_kerberos_login: "
693 "could not destroy krb5 credential cache: "
694 "%s\n", error_message(krb5_ret)));
699 return NT_STATUS_OK;
701 failed:
703 /* we could have created a new credential cache with a valid tgt in it
704 * but we werent able to get or verify the service ticket for this
705 * local host and therefor didn't get the PAC, we need to remove that
706 * cache entirely now */
708 krb5_ret = ads_kdestroy(cc);
709 if (krb5_ret) {
710 DEBUG(3,("winbindd_raw_kerberos_login: "
711 "could not destroy krb5 credential cache: "
712 "%s\n", error_message(krb5_ret)));
715 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
716 DEBUG(3,("winbindd_raw_kerberos_login: "
717 "could not remove ccache for user %s\n",
718 state->request.data.auth.user));
721 return result;
722 #else
723 return NT_STATUS_NOT_SUPPORTED;
724 #endif /* HAVE_KRB5 */
727 /****************************************************************
728 ****************************************************************/
730 static bool check_request_flags(uint32_t flags)
732 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
733 WBFLAG_PAM_INFO3_TEXT |
734 WBFLAG_PAM_INFO3_NDR;
736 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
737 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
738 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
739 !(flags & flags_edata) ) {
740 return True;
743 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
745 return False;
748 /****************************************************************
749 ****************************************************************/
751 static NTSTATUS append_data(struct winbindd_cli_state *state,
752 struct netr_SamInfo3 *info3,
753 const char *name_domain,
754 const char *name_user)
756 NTSTATUS result;
757 uint32_t flags = state->request.flags;
759 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
760 memcpy(state->response.data.auth.user_session_key,
761 info3->base.key.key,
762 sizeof(state->response.data.auth.user_session_key)
763 /* 16 */);
766 if (flags & WBFLAG_PAM_LMKEY) {
767 memcpy(state->response.data.auth.first_8_lm_hash,
768 info3->base.LMSessKey.key,
769 sizeof(state->response.data.auth.first_8_lm_hash)
770 /* 8 */);
773 if (flags & WBFLAG_PAM_INFO3_TEXT) {
774 result = append_info3_as_txt(state->mem_ctx, state, info3);
775 if (!NT_STATUS_IS_OK(result)) {
776 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
777 nt_errstr(result)));
778 return result;
782 /* currently, anything from here on potentially overwrites extra_data. */
784 if (flags & WBFLAG_PAM_INFO3_NDR) {
785 result = append_info3_as_ndr(state->mem_ctx, state, info3);
786 if (!NT_STATUS_IS_OK(result)) {
787 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
788 nt_errstr(result)));
789 return result;
793 if (flags & WBFLAG_PAM_UNIX_NAME) {
794 result = append_unix_username(state->mem_ctx, state, info3,
795 name_domain, name_user);
796 if (!NT_STATUS_IS_OK(result)) {
797 DEBUG(10,("Failed to append Unix Username: %s\n",
798 nt_errstr(result)));
799 return result;
803 if (flags & WBFLAG_PAM_AFS_TOKEN) {
804 result = append_afs_token(state->mem_ctx, state, info3,
805 name_domain, name_user);
806 if (!NT_STATUS_IS_OK(result)) {
807 DEBUG(10,("Failed to append AFS token: %s\n",
808 nt_errstr(result)));
809 return result;
813 return NT_STATUS_OK;
816 void winbindd_pam_auth(struct winbindd_cli_state *state)
818 struct winbindd_domain *domain;
819 fstring name_domain, name_user;
820 NTSTATUS result;
822 /* Ensure null termination */
823 state->request.data.auth.user
824 [sizeof(state->request.data.auth.user)-1]='\0';
826 /* Ensure null termination */
827 state->request.data.auth.pass
828 [sizeof(state->request.data.auth.pass)-1]='\0';
830 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
831 state->request.data.auth.user));
833 if (!check_request_flags(state->request.flags)) {
834 result = NT_STATUS_INVALID_PARAMETER_MIX;
835 goto done;
838 /* Parse domain and username */
840 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
842 if (!canonicalize_username(state->request.data.auth.user,
843 name_domain, name_user)) {
844 result = NT_STATUS_NO_SUCH_USER;
845 goto done;
848 domain = find_auth_domain(state, name_domain);
850 if (domain == NULL) {
851 result = NT_STATUS_NO_SUCH_USER;
852 goto done;
855 sendto_domain(state, domain);
856 return;
857 done:
858 set_auth_errors(&state->response, result);
859 DEBUG(5, ("Plain text authentication for %s returned %s "
860 "(PAM: %d)\n",
861 state->request.data.auth.user,
862 state->response.data.auth.nt_status_string,
863 state->response.data.auth.pam_error));
864 request_error(state);
867 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
868 struct winbindd_cli_state *state,
869 struct netr_SamInfo3 **info3)
871 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
872 uint16 max_allowed_bad_attempts;
873 fstring name_domain, name_user;
874 DOM_SID sid;
875 enum lsa_SidType type;
876 uchar new_nt_pass[NT_HASH_LEN];
877 const uint8 *cached_nt_pass;
878 const uint8 *cached_salt;
879 struct netr_SamInfo3 *my_info3;
880 time_t kickoff_time, must_change_time;
881 bool password_good = False;
882 #ifdef HAVE_KRB5
883 struct winbindd_tdc_domain *tdc_domain = NULL;
884 #endif
886 *info3 = NULL;
888 ZERO_STRUCTP(info3);
890 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
892 /* Parse domain and username */
894 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
897 if (!lookup_cached_name(state->mem_ctx,
898 name_domain,
899 name_user,
900 &sid,
901 &type)) {
902 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
903 return NT_STATUS_NO_SUCH_USER;
906 if (type != SID_NAME_USER) {
907 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
908 return NT_STATUS_LOGON_FAILURE;
911 result = winbindd_get_creds(domain,
912 state->mem_ctx,
913 &sid,
914 &my_info3,
915 &cached_nt_pass,
916 &cached_salt);
917 if (!NT_STATUS_IS_OK(result)) {
918 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
919 return result;
922 *info3 = my_info3;
924 E_md4hash(state->request.data.auth.pass, new_nt_pass);
926 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
927 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
928 if (cached_salt) {
929 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
932 if (cached_salt) {
933 /* In this case we didn't store the nt_hash itself,
934 but the MD5 combination of salt + nt_hash. */
935 uchar salted_hash[NT_HASH_LEN];
936 E_md5hash(cached_salt, new_nt_pass, salted_hash);
938 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
939 True : False;
940 } else {
941 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
942 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
943 True : False;
946 if (password_good) {
948 /* User *DOES* know the password, update logon_time and reset
949 * bad_pw_count */
951 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
953 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
954 return NT_STATUS_ACCOUNT_LOCKED_OUT;
957 if (my_info3->base.acct_flags & ACB_DISABLED) {
958 return NT_STATUS_ACCOUNT_DISABLED;
961 if (my_info3->base.acct_flags & ACB_WSTRUST) {
962 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
965 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
966 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
969 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
970 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
973 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
974 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
975 my_info3->base.acct_flags));
976 return NT_STATUS_LOGON_FAILURE;
979 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
980 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
981 return NT_STATUS_ACCOUNT_EXPIRED;
984 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
985 if (must_change_time != 0 && must_change_time < time(NULL)) {
986 /* we allow grace logons when the password has expired */
987 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
988 /* return NT_STATUS_PASSWORD_EXPIRED; */
989 goto success;
992 #ifdef HAVE_KRB5
993 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
994 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
995 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
997 uid_t uid = -1;
998 const char *cc = NULL;
999 char *realm = NULL;
1000 const char *principal_s = NULL;
1001 const char *service = NULL;
1002 bool internal_ccache = False;
1004 uid = get_uid_from_state(state);
1005 if (uid == -1) {
1006 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1007 return NT_STATUS_INVALID_PARAMETER;
1010 cc = generate_krb5_ccache(state->mem_ctx,
1011 state->request.data.auth.krb5_cc_type,
1012 state->request.data.auth.uid,
1013 &internal_ccache);
1014 if (cc == NULL) {
1015 return NT_STATUS_NO_MEMORY;
1018 realm = domain->alt_name;
1019 strupper_m(realm);
1021 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1022 if (principal_s == NULL) {
1023 return NT_STATUS_NO_MEMORY;
1026 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1027 if (service == NULL) {
1028 return NT_STATUS_NO_MEMORY;
1031 if (!internal_ccache) {
1033 setup_return_cc_name(state, cc);
1035 result = add_ccache_to_list(principal_s,
1037 service,
1038 state->request.data.auth.user,
1039 domain->alt_name,
1040 uid,
1041 time(NULL),
1042 time(NULL) + lp_winbind_cache_time(),
1043 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1044 True);
1046 if (!NT_STATUS_IS_OK(result)) {
1047 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1048 "to add ccache to list: %s\n",
1049 nt_errstr(result)));
1053 #endif /* HAVE_KRB5 */
1054 success:
1055 /* FIXME: we possibly should handle logon hours as well (does xp when
1056 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1058 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1059 my_info3->base.bad_password_count = 0;
1061 result = winbindd_update_creds_by_info3(domain,
1062 state->mem_ctx,
1063 state->request.data.auth.user,
1064 state->request.data.auth.pass,
1065 my_info3);
1066 if (!NT_STATUS_IS_OK(result)) {
1067 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1068 nt_errstr(result)));
1069 return result;
1072 return NT_STATUS_OK;
1076 /* User does *NOT* know the correct password, modify info3 accordingly */
1078 /* failure of this is not critical */
1079 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1080 if (!NT_STATUS_IS_OK(result)) {
1081 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1082 "Won't be able to honour account lockout policies\n"));
1085 /* increase counter */
1086 my_info3->base.bad_password_count++;
1088 if (max_allowed_bad_attempts == 0) {
1089 goto failed;
1092 /* lockout user */
1093 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1095 uint32 password_properties;
1097 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1098 if (!NT_STATUS_IS_OK(result)) {
1099 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1102 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1103 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1104 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1108 failed:
1109 result = winbindd_update_creds_by_info3(domain,
1110 state->mem_ctx,
1111 state->request.data.auth.user,
1112 NULL,
1113 my_info3);
1115 if (!NT_STATUS_IS_OK(result)) {
1116 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1117 nt_errstr(result)));
1120 return NT_STATUS_LOGON_FAILURE;
1123 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1124 struct winbindd_cli_state *state,
1125 struct netr_SamInfo3 **info3)
1127 struct winbindd_domain *contact_domain;
1128 fstring name_domain, name_user;
1129 NTSTATUS result;
1131 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1133 /* Parse domain and username */
1135 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1137 /* what domain should we contact? */
1139 if ( IS_DC ) {
1140 if (!(contact_domain = find_domain_from_name(name_domain))) {
1141 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1142 state->request.data.auth.user, name_domain, name_user, name_domain));
1143 result = NT_STATUS_NO_SUCH_USER;
1144 goto done;
1147 } else {
1148 if (is_myname(name_domain)) {
1149 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1150 result = NT_STATUS_NO_SUCH_USER;
1151 goto done;
1154 contact_domain = find_domain_from_name(name_domain);
1155 if (contact_domain == NULL) {
1156 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1157 state->request.data.auth.user, name_domain, name_user, name_domain));
1159 contact_domain = find_our_domain();
1163 if (contact_domain->initialized &&
1164 contact_domain->active_directory) {
1165 goto try_login;
1168 if (!contact_domain->initialized) {
1169 init_dc_connection(contact_domain);
1172 if (!contact_domain->active_directory) {
1173 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1174 return NT_STATUS_INVALID_LOGON_TYPE;
1176 try_login:
1177 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1178 done:
1179 return result;
1182 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1183 TALLOC_CTX *mem_ctx,
1184 uint32 logon_parameters,
1185 const char *server,
1186 const char *username,
1187 const char *domain,
1188 const char *workstation,
1189 const uint8 chal[8],
1190 DATA_BLOB lm_response,
1191 DATA_BLOB nt_response,
1192 struct netr_SamInfo3 **info3);
1194 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1195 struct winbindd_cli_state *state,
1196 struct netr_SamInfo3 **info3)
1199 struct rpc_pipe_client *netlogon_pipe;
1200 uchar chal[8];
1201 DATA_BLOB lm_resp;
1202 DATA_BLOB nt_resp;
1203 int attempts = 0;
1204 unsigned char local_lm_response[24];
1205 unsigned char local_nt_response[24];
1206 struct winbindd_domain *contact_domain;
1207 fstring name_domain, name_user;
1208 bool retry;
1209 NTSTATUS result;
1210 struct netr_SamInfo3 *my_info3 = NULL;
1212 *info3 = NULL;
1214 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1216 /* Parse domain and username */
1218 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1220 /* do password magic */
1223 generate_random_buffer(chal, 8);
1224 if (lp_client_ntlmv2_auth()) {
1225 DATA_BLOB server_chal;
1226 DATA_BLOB names_blob;
1227 DATA_BLOB nt_response;
1228 DATA_BLOB lm_response;
1229 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1231 /* note that the 'workgroup' here is a best guess - we don't know
1232 the server's domain at this point. The 'server name' is also
1233 dodgy...
1235 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1237 if (!SMBNTLMv2encrypt(name_user, name_domain,
1238 state->request.data.auth.pass,
1239 &server_chal,
1240 &names_blob,
1241 &lm_response, &nt_response, NULL)) {
1242 data_blob_free(&names_blob);
1243 data_blob_free(&server_chal);
1244 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1245 result = NT_STATUS_NO_MEMORY;
1246 goto done;
1248 data_blob_free(&names_blob);
1249 data_blob_free(&server_chal);
1250 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1251 lm_response.length);
1252 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1253 nt_response.length);
1254 data_blob_free(&lm_response);
1255 data_blob_free(&nt_response);
1257 } else {
1258 if (lp_client_lanman_auth()
1259 && SMBencrypt(state->request.data.auth.pass,
1260 chal,
1261 local_lm_response)) {
1262 lm_resp = data_blob_talloc(state->mem_ctx,
1263 local_lm_response,
1264 sizeof(local_lm_response));
1265 } else {
1266 lm_resp = data_blob_null;
1268 SMBNTencrypt(state->request.data.auth.pass,
1269 chal,
1270 local_nt_response);
1272 nt_resp = data_blob_talloc(state->mem_ctx,
1273 local_nt_response,
1274 sizeof(local_nt_response));
1277 /* what domain should we contact? */
1279 if ( IS_DC ) {
1280 if (!(contact_domain = find_domain_from_name(name_domain))) {
1281 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1282 state->request.data.auth.user, name_domain, name_user, name_domain));
1283 result = NT_STATUS_NO_SUCH_USER;
1284 goto done;
1287 } else {
1288 if (is_myname(name_domain)) {
1289 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1290 result = NT_STATUS_NO_SUCH_USER;
1291 goto done;
1294 contact_domain = find_our_domain();
1297 /* check authentication loop */
1299 do {
1300 netlogon_fn_t logon_fn;
1302 ZERO_STRUCTP(my_info3);
1303 retry = False;
1305 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1307 if (!NT_STATUS_IS_OK(result)) {
1308 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1309 goto done;
1312 /* It is really important to try SamLogonEx here,
1313 * because in a clustered environment, we want to use
1314 * one machine account from multiple physical
1315 * computers.
1317 * With a normal SamLogon call, we must keep the
1318 * credentials chain updated and intact between all
1319 * users of the machine account (which would imply
1320 * cross-node communication for every NTLM logon).
1322 * (The credentials chain is not per NETLOGON pipe
1323 * connection, but globally on the server/client pair
1324 * by machine name).
1326 * When using SamLogonEx, the credentials are not
1327 * supplied, but the session key is implied by the
1328 * wrapping SamLogon context.
1330 * -- abartlet 21 April 2008
1333 logon_fn = contact_domain->can_do_samlogon_ex
1334 ? rpccli_netlogon_sam_network_logon_ex
1335 : rpccli_netlogon_sam_network_logon;
1337 result = logon_fn(netlogon_pipe,
1338 state->mem_ctx,
1340 contact_domain->dcname, /* server name */
1341 name_user, /* user name */
1342 name_domain, /* target domain */
1343 global_myname(), /* workstation */
1344 chal,
1345 lm_resp,
1346 nt_resp,
1347 &my_info3);
1348 attempts += 1;
1350 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1351 && contact_domain->can_do_samlogon_ex) {
1352 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1353 "retrying with NetSamLogon\n"));
1354 contact_domain->can_do_samlogon_ex = False;
1355 retry = True;
1356 continue;
1359 /* We have to try a second time as cm_connect_netlogon
1360 might not yet have noticed that the DC has killed
1361 our connection. */
1363 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1364 retry = True;
1365 continue;
1368 /* if we get access denied, a possible cause was that we had
1369 and open connection to the DC, but someone changed our
1370 machine account password out from underneath us using 'net
1371 rpc changetrustpw' */
1373 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1374 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1375 "ACCESS_DENIED. Maybe the trust account "
1376 "password was changed and we didn't know it. "
1377 "Killing connections to domain %s\n",
1378 name_domain));
1379 invalidate_cm_connection(&contact_domain->conn);
1380 retry = True;
1383 } while ( (attempts < 2) && retry );
1385 /* handle the case where a NT4 DC does not fill in the acct_flags in
1386 * the samlogon reply info3. When accurate info3 is required by the
1387 * caller, we look up the account flags ourselve - gd */
1389 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1390 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1392 struct rpc_pipe_client *samr_pipe;
1393 POLICY_HND samr_domain_handle, user_pol;
1394 union samr_UserInfo *info = NULL;
1395 NTSTATUS status_tmp;
1396 uint32 acct_flags;
1398 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1399 &samr_pipe, &samr_domain_handle);
1401 if (!NT_STATUS_IS_OK(status_tmp)) {
1402 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1403 nt_errstr(status_tmp)));
1404 goto done;
1407 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1408 &samr_domain_handle,
1409 MAXIMUM_ALLOWED_ACCESS,
1410 my_info3->base.rid,
1411 &user_pol);
1413 if (!NT_STATUS_IS_OK(status_tmp)) {
1414 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1415 nt_errstr(status_tmp)));
1416 goto done;
1419 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1420 &user_pol,
1422 &info);
1424 if (!NT_STATUS_IS_OK(status_tmp)) {
1425 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1426 nt_errstr(status_tmp)));
1427 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1428 goto done;
1431 acct_flags = info->info16.acct_flags;
1433 if (acct_flags == 0) {
1434 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1435 goto done;
1438 my_info3->base.acct_flags = acct_flags;
1440 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1442 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1445 *info3 = my_info3;
1446 done:
1447 return result;
1450 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1451 struct winbindd_cli_state *state)
1453 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1454 NTSTATUS krb5_result = NT_STATUS_OK;
1455 fstring name_domain, name_user;
1456 struct netr_SamInfo3 *info3 = NULL;
1458 /* Ensure null termination */
1459 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1461 /* Ensure null termination */
1462 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1464 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1465 state->request.data.auth.user));
1467 if (!check_request_flags(state->request.flags)) {
1468 result = NT_STATUS_INVALID_PARAMETER_MIX;
1469 goto done;
1472 /* Parse domain and username */
1474 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1476 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1478 if (domain->online == False) {
1479 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1480 if (domain->startup) {
1481 /* Logons are very important to users. If we're offline and
1482 we get a request within the first 30 seconds of startup,
1483 try very hard to find a DC and go online. */
1485 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1486 "request in startup mode.\n", domain->name ));
1488 winbindd_flush_negative_conn_cache(domain);
1489 result = init_dc_connection(domain);
1493 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1495 /* Check for Kerberos authentication */
1496 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1498 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1499 /* save for later */
1500 krb5_result = result;
1503 if (NT_STATUS_IS_OK(result)) {
1504 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1505 goto process_result;
1506 } else {
1507 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1510 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1511 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1512 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1513 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1514 set_domain_offline( domain );
1515 goto cached_logon;
1518 /* there are quite some NT_STATUS errors where there is no
1519 * point in retrying with a samlogon, we explictly have to take
1520 * care not to increase the bad logon counter on the DC */
1522 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1527 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1528 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1529 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1530 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1531 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1532 goto process_result;
1535 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1536 DEBUG(3,("falling back to samlogon\n"));
1537 goto sam_logon;
1538 } else {
1539 goto cached_logon;
1543 sam_logon:
1544 /* Check for Samlogon authentication */
1545 if (domain->online) {
1546 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1548 if (NT_STATUS_IS_OK(result)) {
1549 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1550 /* add the Krb5 err if we have one */
1551 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1552 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1554 goto process_result;
1557 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1558 nt_errstr(result)));
1560 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1561 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1562 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1564 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1565 set_domain_offline( domain );
1566 goto cached_logon;
1569 if (domain->online) {
1570 /* We're still online - fail. */
1571 goto done;
1575 cached_logon:
1576 /* Check for Cached logons */
1577 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1578 lp_winbind_offline_logon()) {
1580 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1582 if (NT_STATUS_IS_OK(result)) {
1583 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1584 goto process_result;
1585 } else {
1586 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1587 goto done;
1591 process_result:
1593 if (NT_STATUS_IS_OK(result)) {
1595 DOM_SID user_sid;
1597 /* In all codepaths where result == NT_STATUS_OK info3 must have
1598 been initialized. */
1599 if (!info3) {
1600 result = NT_STATUS_INTERNAL_ERROR;
1601 goto done;
1604 netsamlogon_cache_store(name_user, info3);
1605 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1607 /* save name_to_sid info as early as possible (only if
1608 this is our primary domain so we don't invalidate
1609 the cache entry by storing the seq_num for the wrong
1610 domain). */
1611 if ( domain->primary ) {
1612 sid_compose(&user_sid, info3->base.domain_sid,
1613 info3->base.rid);
1614 cache_name2sid(domain, name_domain, name_user,
1615 SID_NAME_USER, &user_sid);
1618 /* Check if the user is in the right group */
1620 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1621 state->request.data.auth.require_membership_of_sid))) {
1622 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1623 state->request.data.auth.user,
1624 state->request.data.auth.require_membership_of_sid));
1625 goto done;
1628 result = append_data(state, info3, name_domain, name_user);
1629 if (!NT_STATUS_IS_OK(result)) {
1630 goto done;
1633 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1635 /* Store in-memory creds for single-signon using ntlm_auth. */
1636 result = winbindd_add_memory_creds(state->request.data.auth.user,
1637 get_uid_from_state(state),
1638 state->request.data.auth.pass);
1640 if (!NT_STATUS_IS_OK(result)) {
1641 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1642 goto done;
1645 if (lp_winbind_offline_logon()) {
1646 result = winbindd_store_creds(domain,
1647 state->mem_ctx,
1648 state->request.data.auth.user,
1649 state->request.data.auth.pass,
1650 info3, NULL);
1651 if (!NT_STATUS_IS_OK(result)) {
1653 /* Release refcount. */
1654 winbindd_delete_memory_creds(state->request.data.auth.user);
1656 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1657 goto done;
1663 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1664 struct winbindd_domain *our_domain = find_our_domain();
1666 /* This is not entirely correct I believe, but it is
1667 consistent. Only apply the password policy settings
1668 too warn users for our own domain. Cannot obtain these
1669 from trusted DCs all the time so don't do it at all.
1670 -- jerry */
1672 result = NT_STATUS_NOT_SUPPORTED;
1673 if (our_domain == domain ) {
1674 result = fillup_password_policy(our_domain, state);
1677 if (!NT_STATUS_IS_OK(result)
1678 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1680 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1681 domain->name, nt_errstr(result)));
1682 goto done;
1686 result = NT_STATUS_OK;
1689 done:
1690 /* give us a more useful (more correct?) error code */
1691 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1692 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1693 result = NT_STATUS_NO_LOGON_SERVERS;
1696 state->response.data.auth.nt_status = NT_STATUS_V(result);
1697 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1699 /* we might have given a more useful error above */
1700 if (!*state->response.data.auth.error_string)
1701 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1702 state->response.data.auth.pam_error = nt_status_to_pam(result);
1704 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1705 state->request.data.auth.user,
1706 state->response.data.auth.nt_status_string,
1707 state->response.data.auth.pam_error));
1709 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1713 /**********************************************************************
1714 Challenge Response Authentication Protocol
1715 **********************************************************************/
1717 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1719 struct winbindd_domain *domain = NULL;
1720 const char *domain_name = NULL;
1721 NTSTATUS result;
1723 if (!check_request_flags(state->request.flags)) {
1724 result = NT_STATUS_INVALID_PARAMETER_MIX;
1725 goto done;
1728 if (!state->privileged) {
1729 char *error_string = NULL;
1730 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1731 "denied. !\n"));
1732 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1733 "on %s are set correctly.\n",
1734 get_winbind_priv_pipe_dir()));
1735 /* send a better message than ACCESS_DENIED */
1736 error_string = talloc_asprintf(state->mem_ctx,
1737 "winbind client not authorized "
1738 "to use winbindd_pam_auth_crap."
1739 " Ensure permissions on %s "
1740 "are set correctly.",
1741 get_winbind_priv_pipe_dir());
1742 fstrcpy(state->response.data.auth.error_string, error_string);
1743 result = NT_STATUS_ACCESS_DENIED;
1744 goto done;
1747 /* Ensure null termination */
1748 state->request.data.auth_crap.user
1749 [sizeof(state->request.data.auth_crap.user)-1]=0;
1750 state->request.data.auth_crap.domain
1751 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1753 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1754 (unsigned long)state->pid,
1755 state->request.data.auth_crap.domain,
1756 state->request.data.auth_crap.user));
1758 if (*state->request.data.auth_crap.domain != '\0') {
1759 domain_name = state->request.data.auth_crap.domain;
1760 } else if (lp_winbind_use_default_domain()) {
1761 domain_name = lp_workgroup();
1764 if (domain_name != NULL)
1765 domain = find_auth_domain(state, domain_name);
1767 if (domain != NULL) {
1768 sendto_domain(state, domain);
1769 return;
1772 result = NT_STATUS_NO_SUCH_USER;
1774 done:
1775 set_auth_errors(&state->response, result);
1776 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1777 state->request.data.auth_crap.domain,
1778 state->request.data.auth_crap.user,
1779 state->response.data.auth.nt_status_string,
1780 state->response.data.auth.pam_error));
1781 request_error(state);
1782 return;
1786 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1787 struct winbindd_cli_state *state)
1789 NTSTATUS result;
1790 struct netr_SamInfo3 *info3 = NULL;
1791 struct rpc_pipe_client *netlogon_pipe;
1792 const char *name_user = NULL;
1793 const char *name_domain = NULL;
1794 const char *workstation;
1795 struct winbindd_domain *contact_domain;
1796 int attempts = 0;
1797 bool retry;
1799 DATA_BLOB lm_resp, nt_resp;
1801 /* This is child-only, so no check for privileged access is needed
1802 anymore */
1804 /* Ensure null termination */
1805 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1806 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1808 if (!check_request_flags(state->request.flags)) {
1809 result = NT_STATUS_INVALID_PARAMETER_MIX;
1810 goto done;
1813 name_user = state->request.data.auth_crap.user;
1815 if (*state->request.data.auth_crap.domain) {
1816 name_domain = state->request.data.auth_crap.domain;
1817 } else if (lp_winbind_use_default_domain()) {
1818 name_domain = lp_workgroup();
1819 } else {
1820 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1821 name_user));
1822 result = NT_STATUS_NO_SUCH_USER;
1823 goto done;
1826 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1827 name_domain, name_user));
1829 if (*state->request.data.auth_crap.workstation) {
1830 workstation = state->request.data.auth_crap.workstation;
1831 } else {
1832 workstation = global_myname();
1835 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1836 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1837 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1838 state->request.data.auth_crap.lm_resp_len,
1839 state->request.data.auth_crap.nt_resp_len));
1840 result = NT_STATUS_INVALID_PARAMETER;
1841 goto done;
1844 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1845 state->request.data.auth_crap.lm_resp_len);
1846 nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1847 state->request.data.auth_crap.nt_resp_len);
1849 /* what domain should we contact? */
1851 if ( IS_DC ) {
1852 if (!(contact_domain = find_domain_from_name(name_domain))) {
1853 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1854 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1855 result = NT_STATUS_NO_SUCH_USER;
1856 goto done;
1858 } else {
1859 if (is_myname(name_domain)) {
1860 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1861 result = NT_STATUS_NO_SUCH_USER;
1862 goto done;
1864 contact_domain = find_our_domain();
1867 do {
1868 netlogon_fn_t logon_fn;
1870 retry = False;
1872 netlogon_pipe = NULL;
1873 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1875 if (!NT_STATUS_IS_OK(result)) {
1876 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1877 nt_errstr(result)));
1878 goto done;
1881 logon_fn = contact_domain->can_do_samlogon_ex
1882 ? rpccli_netlogon_sam_network_logon_ex
1883 : rpccli_netlogon_sam_network_logon;
1885 result = logon_fn(netlogon_pipe,
1886 state->mem_ctx,
1887 state->request.data.auth_crap.logon_parameters,
1888 contact_domain->dcname,
1889 name_user,
1890 name_domain,
1891 /* Bug #3248 - found by Stefan Burkei. */
1892 workstation, /* We carefully set this above so use it... */
1893 state->request.data.auth_crap.chal,
1894 lm_resp,
1895 nt_resp,
1896 &info3);
1898 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1899 && contact_domain->can_do_samlogon_ex) {
1900 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1901 "retrying with NetSamLogon\n"));
1902 contact_domain->can_do_samlogon_ex = False;
1903 retry = True;
1904 continue;
1907 attempts += 1;
1909 /* We have to try a second time as cm_connect_netlogon
1910 might not yet have noticed that the DC has killed
1911 our connection. */
1913 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1914 retry = True;
1915 continue;
1918 /* if we get access denied, a possible cause was that we had and open
1919 connection to the DC, but someone changed our machine account password
1920 out from underneath us using 'net rpc changetrustpw' */
1922 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1923 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1924 "ACCESS_DENIED. Maybe the trust account "
1925 "password was changed and we didn't know it. "
1926 "Killing connections to domain %s\n",
1927 name_domain));
1928 invalidate_cm_connection(&contact_domain->conn);
1929 retry = True;
1932 } while ( (attempts < 2) && retry );
1934 if (NT_STATUS_IS_OK(result)) {
1936 netsamlogon_cache_store(name_user, info3);
1937 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1939 /* Check if the user is in the right group */
1941 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1942 state->request.data.auth_crap.require_membership_of_sid))) {
1943 DEBUG(3, ("User %s is not in the required group (%s), so "
1944 "crap authentication is rejected\n",
1945 state->request.data.auth_crap.user,
1946 state->request.data.auth_crap.require_membership_of_sid));
1947 goto done;
1950 result = append_data(state, info3, name_domain, name_user);
1951 if (!NT_STATUS_IS_OK(result)) {
1952 goto done;
1956 done:
1958 /* give us a more useful (more correct?) error code */
1959 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1960 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1961 result = NT_STATUS_NO_LOGON_SERVERS;
1964 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1965 result = nt_status_squash(result);
1968 state->response.data.auth.nt_status = NT_STATUS_V(result);
1969 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1971 /* we might have given a more useful error above */
1972 if (!*state->response.data.auth.error_string) {
1973 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1975 state->response.data.auth.pam_error = nt_status_to_pam(result);
1977 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1978 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1979 name_domain,
1980 name_user,
1981 state->response.data.auth.nt_status_string,
1982 state->response.data.auth.pam_error));
1984 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1987 /* Change a user password */
1989 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1991 fstring domain, user;
1992 struct winbindd_domain *contact_domain;
1994 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1995 state->request.data.chauthtok.user));
1997 /* Setup crap */
1999 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
2001 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
2002 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2003 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2004 "(PAM: %d)\n",
2005 state->request.data.auth.user,
2006 state->response.data.auth.nt_status_string,
2007 state->response.data.auth.pam_error));
2008 request_error(state);
2009 return;
2012 contact_domain = find_domain_from_name(domain);
2013 if (!contact_domain) {
2014 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2015 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2016 state->request.data.chauthtok.user, domain, user, domain));
2017 request_error(state);
2018 return;
2021 sendto_domain(state, contact_domain);
2024 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2025 struct winbindd_cli_state *state)
2027 char *oldpass;
2028 char *newpass = NULL;
2029 POLICY_HND dom_pol;
2030 struct rpc_pipe_client *cli;
2031 bool got_info = False;
2032 struct samr_DomInfo1 *info = NULL;
2033 struct samr_ChangeReject *reject = NULL;
2034 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2035 fstring domain, user;
2037 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2038 state->request.data.auth.user));
2040 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2041 goto done;
2044 /* Change password */
2046 oldpass = state->request.data.chauthtok.oldpass;
2047 newpass = state->request.data.chauthtok.newpass;
2049 /* Initialize reject reason */
2050 state->response.data.auth.reject_reason = Undefined;
2052 /* Get sam handle */
2054 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2055 &dom_pol);
2056 if (!NT_STATUS_IS_OK(result)) {
2057 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2058 goto done;
2061 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2062 user,
2063 newpass,
2064 oldpass,
2065 &info,
2066 &reject);
2068 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2070 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2071 state->response.data.auth.policy.min_length_password =
2072 info->min_password_length;
2073 state->response.data.auth.policy.password_history =
2074 info->password_history_length;
2075 state->response.data.auth.policy.password_properties =
2076 info->password_properties;
2077 state->response.data.auth.policy.expire =
2078 nt_time_to_unix_abs((NTTIME *)&info->max_password_age);
2079 state->response.data.auth.policy.min_passwordage =
2080 nt_time_to_unix_abs((NTTIME *)&info->min_password_age);
2082 state->response.data.auth.reject_reason =
2083 reject->reason;
2085 got_info = True;
2088 /* only fallback when the chgpasswd_user3 call is not supported */
2089 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2090 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2091 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2093 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2094 nt_errstr(result)));
2096 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2098 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2099 Map to the same status code as Windows 2003. */
2101 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2102 result = NT_STATUS_PASSWORD_RESTRICTION;
2106 done:
2108 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2110 /* Update the single sign-on memory creds. */
2111 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2112 newpass);
2114 /* When we login from gdm or xdm and password expires,
2115 * we change password, but there are no memory crendentials
2116 * So, winbindd_replace_memory_creds() returns
2117 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2118 * --- BoYang
2119 * */
2120 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2121 result = NT_STATUS_OK;
2124 if (!NT_STATUS_IS_OK(result)) {
2125 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2126 goto process_result;
2129 if (lp_winbind_offline_logon()) {
2130 result = winbindd_update_creds_by_name(contact_domain,
2131 state->mem_ctx, user,
2132 newpass);
2133 /* Again, this happens when we login from gdm or xdm
2134 * and the password expires, *BUT* cached crendentials
2135 * doesn't exist. winbindd_update_creds_by_name()
2136 * returns NT_STATUS_NO_SUCH_USER.
2137 * This is not a failure.
2138 * --- BoYang
2139 * */
2140 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2141 result = NT_STATUS_OK;
2144 if (!NT_STATUS_IS_OK(result)) {
2145 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2146 goto process_result;
2151 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2153 NTSTATUS policy_ret;
2155 policy_ret = fillup_password_policy(contact_domain, state);
2157 /* failure of this is non critical, it will just provide no
2158 * additional information to the client why the change has
2159 * failed - Guenther */
2161 if (!NT_STATUS_IS_OK(policy_ret)) {
2162 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2163 goto process_result;
2167 process_result:
2169 state->response.data.auth.nt_status = NT_STATUS_V(result);
2170 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2171 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2172 state->response.data.auth.pam_error = nt_status_to_pam(result);
2174 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2175 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2176 domain,
2177 user,
2178 state->response.data.auth.nt_status_string,
2179 state->response.data.auth.pam_error));
2181 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2184 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2186 struct winbindd_domain *domain;
2187 fstring name_domain, user;
2188 uid_t caller_uid = (uid_t)-1;
2189 uid_t request_uid = state->request.data.logoff.uid;
2191 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2192 state->request.data.logoff.user));
2194 /* Ensure null termination */
2195 state->request.data.logoff.user
2196 [sizeof(state->request.data.logoff.user)-1]='\0';
2198 state->request.data.logoff.krb5ccname
2199 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2201 if (request_uid == (gid_t)-1) {
2202 goto failed;
2205 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2206 goto failed;
2209 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2210 goto failed;
2213 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2214 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2215 strerror(errno)));
2216 goto failed;
2219 switch (caller_uid) {
2220 case -1:
2221 goto failed;
2222 case 0:
2223 /* root must be able to logoff any user - gd */
2224 state->request.data.logoff.uid = request_uid;
2225 break;
2226 default:
2227 if (caller_uid != request_uid) {
2228 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2229 goto failed;
2231 state->request.data.logoff.uid = caller_uid;
2232 break;
2235 sendto_domain(state, domain);
2236 return;
2238 failed:
2239 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2240 DEBUG(5, ("Pam Logoff for %s returned %s "
2241 "(PAM: %d)\n",
2242 state->request.data.logoff.user,
2243 state->response.data.auth.nt_status_string,
2244 state->response.data.auth.pam_error));
2245 request_error(state);
2246 return;
2249 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2250 struct winbindd_cli_state *state)
2252 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2254 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2255 state->request.data.logoff.user));
2257 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2258 result = NT_STATUS_OK;
2259 goto process_result;
2262 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2263 result = NT_STATUS_OK;
2264 goto process_result;
2267 #ifdef HAVE_KRB5
2269 if (state->request.data.logoff.uid < 0) {
2270 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2271 goto process_result;
2274 /* what we need here is to find the corresponding krb5 ccache name *we*
2275 * created for a given username and destroy it */
2277 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2278 result = NT_STATUS_OK;
2279 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2280 goto process_result;
2283 if (!ccache_entry_identical(state->request.data.logoff.user,
2284 state->request.data.logoff.uid,
2285 state->request.data.logoff.krb5ccname)) {
2286 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2287 goto process_result;
2290 result = remove_ccache(state->request.data.logoff.user);
2291 if (!NT_STATUS_IS_OK(result)) {
2292 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2293 nt_errstr(result)));
2294 goto process_result;
2297 #else
2298 result = NT_STATUS_NOT_SUPPORTED;
2299 #endif
2301 process_result:
2303 winbindd_delete_memory_creds(state->request.data.logoff.user);
2305 state->response.data.auth.nt_status = NT_STATUS_V(result);
2306 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2307 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2308 state->response.data.auth.pam_error = nt_status_to_pam(result);
2310 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2313 /* Change user password with auth crap*/
2315 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2317 struct winbindd_domain *domain = NULL;
2318 const char *domain_name = NULL;
2320 /* Ensure null termination */
2321 state->request.data.chng_pswd_auth_crap.user[
2322 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2323 state->request.data.chng_pswd_auth_crap.domain[
2324 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2326 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2327 (unsigned long)state->pid,
2328 state->request.data.chng_pswd_auth_crap.domain,
2329 state->request.data.chng_pswd_auth_crap.user));
2331 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2332 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2333 } else if (lp_winbind_use_default_domain()) {
2334 domain_name = lp_workgroup();
2337 if (domain_name != NULL)
2338 domain = find_domain_from_name(domain_name);
2340 if (domain != NULL) {
2341 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2342 "%s\n", (unsigned long)state->pid,domain->name));
2343 sendto_domain(state, domain);
2344 return;
2347 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2348 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2349 state->request.data.chng_pswd_auth_crap.domain,
2350 state->request.data.chng_pswd_auth_crap.user,
2351 state->response.data.auth.nt_status_string,
2352 state->response.data.auth.pam_error));
2353 request_error(state);
2354 return;
2357 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2359 NTSTATUS result;
2360 DATA_BLOB new_nt_password;
2361 DATA_BLOB old_nt_hash_enc;
2362 DATA_BLOB new_lm_password;
2363 DATA_BLOB old_lm_hash_enc;
2364 fstring domain,user;
2365 POLICY_HND dom_pol;
2366 struct winbindd_domain *contact_domain = domainSt;
2367 struct rpc_pipe_client *cli;
2369 /* Ensure null termination */
2370 state->request.data.chng_pswd_auth_crap.user[
2371 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2372 state->request.data.chng_pswd_auth_crap.domain[
2373 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2374 *domain = 0;
2375 *user = 0;
2377 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2378 (unsigned long)state->pid,
2379 state->request.data.chng_pswd_auth_crap.domain,
2380 state->request.data.chng_pswd_auth_crap.user));
2382 if (lp_winbind_offline_logon()) {
2383 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2384 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2385 result = NT_STATUS_ACCESS_DENIED;
2386 goto done;
2389 if (*state->request.data.chng_pswd_auth_crap.domain) {
2390 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2391 } else {
2392 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2393 domain, user);
2395 if(!*domain) {
2396 DEBUG(3,("no domain specified with username (%s) - "
2397 "failing auth\n",
2398 state->request.data.chng_pswd_auth_crap.user));
2399 result = NT_STATUS_NO_SUCH_USER;
2400 goto done;
2404 if (!*domain && lp_winbind_use_default_domain()) {
2405 fstrcpy(domain,(char *)lp_workgroup());
2408 if(!*user) {
2409 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2412 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2413 (unsigned long)state->pid, domain, user));
2415 /* Change password */
2416 new_nt_password = data_blob_talloc(
2417 state->mem_ctx,
2418 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2419 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2421 old_nt_hash_enc = data_blob_talloc(
2422 state->mem_ctx,
2423 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2424 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2426 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2427 new_lm_password = data_blob_talloc(
2428 state->mem_ctx,
2429 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2430 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2432 old_lm_hash_enc = data_blob_talloc(
2433 state->mem_ctx,
2434 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2435 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2436 } else {
2437 new_lm_password.length = 0;
2438 old_lm_hash_enc.length = 0;
2441 /* Get sam handle */
2443 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2444 if (!NT_STATUS_IS_OK(result)) {
2445 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2446 goto done;
2449 result = rpccli_samr_chng_pswd_auth_crap(
2450 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2451 new_lm_password, old_lm_hash_enc);
2453 done:
2454 state->response.data.auth.nt_status = NT_STATUS_V(result);
2455 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2456 fstrcpy(state->response.data.auth.error_string,
2457 get_friendly_nt_error_msg(result));
2458 state->response.data.auth.pam_error = nt_status_to_pam(result);
2460 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2461 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2462 domain, user,
2463 state->response.data.auth.nt_status_string,
2464 state->response.data.auth.pam_error));
2466 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;