s3-winbind: Implemented samr backend function sam_trusted_domains.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blobef8d464008d2bc02571ef738816e08555fa5668c
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - pam auth funcions
6 Copyright (C) Andrew Tridgell 2000
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett 2001-2002
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "../libcli/auth/libcli_auth.h"
28 #include "../librpc/gen_ndr/cli_samr.h"
29 #include "rpc_client/cli_samr.h"
30 #include "../librpc/gen_ndr/ndr_netlogon.h"
31 #include "rpc_client/cli_netlogon.h"
32 #include "smb_krb5.h"
33 #include "../lib/crypto/arcfour.h"
34 #include "rpc_server/srv_samr_util.h"
35 #include "../libcli/security/dom_sid.h"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_WINBIND
40 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
42 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
43 struct winbindd_cli_state *state,
44 struct netr_SamInfo3 *info3)
46 char *ex;
47 uint32_t i;
49 state->response->data.auth.info3.logon_time =
50 nt_time_to_unix(info3->base.last_logon);
51 state->response->data.auth.info3.logoff_time =
52 nt_time_to_unix(info3->base.last_logoff);
53 state->response->data.auth.info3.kickoff_time =
54 nt_time_to_unix(info3->base.acct_expiry);
55 state->response->data.auth.info3.pass_last_set_time =
56 nt_time_to_unix(info3->base.last_password_change);
57 state->response->data.auth.info3.pass_can_change_time =
58 nt_time_to_unix(info3->base.allow_password_change);
59 state->response->data.auth.info3.pass_must_change_time =
60 nt_time_to_unix(info3->base.force_password_change);
62 state->response->data.auth.info3.logon_count = info3->base.logon_count;
63 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
65 state->response->data.auth.info3.user_rid = info3->base.rid;
66 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
67 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
69 state->response->data.auth.info3.num_groups = info3->base.groups.count;
70 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
72 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
73 state->response->data.auth.info3.num_other_sids = info3->sidcount;
75 fstrcpy(state->response->data.auth.info3.user_name,
76 info3->base.account_name.string);
77 fstrcpy(state->response->data.auth.info3.full_name,
78 info3->base.full_name.string);
79 fstrcpy(state->response->data.auth.info3.logon_script,
80 info3->base.logon_script.string);
81 fstrcpy(state->response->data.auth.info3.profile_path,
82 info3->base.profile_path.string);
83 fstrcpy(state->response->data.auth.info3.home_dir,
84 info3->base.home_directory.string);
85 fstrcpy(state->response->data.auth.info3.dir_drive,
86 info3->base.home_drive.string);
88 fstrcpy(state->response->data.auth.info3.logon_srv,
89 info3->base.logon_server.string);
90 fstrcpy(state->response->data.auth.info3.logon_dom,
91 info3->base.domain.string);
93 ex = talloc_strdup(state->mem_ctx, "");
94 NT_STATUS_HAVE_NO_MEMORY(ex);
96 for (i=0; i < info3->base.groups.count; i++) {
97 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
98 info3->base.groups.rids[i].rid,
99 info3->base.groups.rids[i].attributes);
100 NT_STATUS_HAVE_NO_MEMORY(ex);
103 for (i=0; i < info3->sidcount; i++) {
104 char *sid;
106 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
107 NT_STATUS_HAVE_NO_MEMORY(sid);
109 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
110 sid,
111 info3->sids[i].attributes);
112 NT_STATUS_HAVE_NO_MEMORY(ex);
114 talloc_free(sid);
117 state->response->extra_data.data = ex;
118 state->response->length += talloc_get_size(ex);
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 state->response->extra_data.data = blob.data;
138 state->response->length += blob.length;
140 return NT_STATUS_OK;
143 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
144 struct winbindd_cli_state *state,
145 const struct netr_SamInfo3 *info3,
146 const char *name_domain,
147 const char *name_user)
149 /* We've been asked to return the unix username, per
150 'winbind use default domain' settings and the like */
152 const char *nt_username, *nt_domain;
154 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
155 if (!nt_domain) {
156 /* If the server didn't give us one, just use the one
157 * we sent them */
158 nt_domain = name_domain;
161 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
162 if (!nt_username) {
163 /* If the server didn't give us one, just use the one
164 * we sent them */
165 nt_username = name_user;
168 fill_domain_username(state->response->data.auth.unix_username,
169 nt_domain, nt_username, true);
171 DEBUG(5,("Setting unix username to [%s]\n",
172 state->response->data.auth.unix_username));
174 return NT_STATUS_OK;
177 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
178 struct winbindd_cli_state *state,
179 const struct netr_SamInfo3 *info3,
180 const char *name_domain,
181 const char *name_user)
183 char *afsname = NULL;
184 char *cell;
185 char *token;
187 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
188 if (afsname == NULL) {
189 return NT_STATUS_NO_MEMORY;
192 afsname = talloc_string_sub(mem_ctx,
193 lp_afs_username_map(),
194 "%D", name_domain);
195 afsname = talloc_string_sub(mem_ctx, afsname,
196 "%u", name_user);
197 afsname = talloc_string_sub(mem_ctx, afsname,
198 "%U", name_user);
201 struct dom_sid user_sid;
202 fstring sidstr;
204 sid_compose(&user_sid, info3->base.domain_sid,
205 info3->base.rid);
206 sid_to_fstring(sidstr, &user_sid);
207 afsname = talloc_string_sub(mem_ctx, afsname,
208 "%s", sidstr);
211 if (afsname == NULL) {
212 return NT_STATUS_NO_MEMORY;
215 strlower_m(afsname);
217 DEBUG(10, ("Generating token for user %s\n", afsname));
219 cell = strchr(afsname, '@');
221 if (cell == NULL) {
222 return NT_STATUS_NO_MEMORY;
225 *cell = '\0';
226 cell += 1;
228 token = afs_createtoken_str(afsname, cell);
229 if (token == NULL) {
230 return NT_STATUS_OK;
232 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
233 token);
234 if (state->response->extra_data.data == NULL) {
235 return NT_STATUS_NO_MEMORY;
237 state->response->length +=
238 strlen((const char *)state->response->extra_data.data)+1;
240 return NT_STATUS_OK;
243 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
244 const char *group_sid)
246 * Check whether a user belongs to a group or list of groups.
248 * @param mem_ctx talloc memory context.
249 * @param info3 user information, including group membership info.
250 * @param group_sid One or more groups , separated by commas.
252 * @return NT_STATUS_OK on success,
253 * NT_STATUS_LOGON_FAILURE if the user does not belong,
254 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
257 struct dom_sid *require_membership_of_sid;
258 size_t num_require_membership_of_sid;
259 char *req_sid;
260 const char *p;
261 struct dom_sid sid;
262 size_t i;
263 struct nt_user_token *token;
264 TALLOC_CTX *frame = talloc_stackframe();
265 NTSTATUS status;
267 /* Parse the 'required group' SID */
269 if (!group_sid || !group_sid[0]) {
270 /* NO sid supplied, all users may access */
271 return NT_STATUS_OK;
274 token = talloc_zero(talloc_tos(), struct nt_user_token);
275 if (token == NULL) {
276 DEBUG(0, ("talloc failed\n"));
277 TALLOC_FREE(frame);
278 return NT_STATUS_NO_MEMORY;
281 num_require_membership_of_sid = 0;
282 require_membership_of_sid = NULL;
284 p = group_sid;
286 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
287 if (!string_to_sid(&sid, req_sid)) {
288 DEBUG(0, ("check_info3_in_group: could not parse %s "
289 "as a SID!", req_sid));
290 TALLOC_FREE(frame);
291 return NT_STATUS_INVALID_PARAMETER;
294 status = add_sid_to_array(talloc_tos(), &sid,
295 &require_membership_of_sid,
296 &num_require_membership_of_sid);
297 if (!NT_STATUS_IS_OK(status)) {
298 DEBUG(0, ("add_sid_to_array failed\n"));
299 TALLOC_FREE(frame);
300 return status;
304 status = sid_array_from_info3(talloc_tos(), info3,
305 &token->user_sids,
306 &token->num_sids,
307 true, false);
308 if (!NT_STATUS_IS_OK(status)) {
309 TALLOC_FREE(frame);
310 return status;
313 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
314 token))
315 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
316 token))) {
317 DEBUG(3, ("could not add aliases: %s\n",
318 nt_errstr(status)));
319 TALLOC_FREE(frame);
320 return status;
323 debug_nt_user_token(DBGC_CLASS, 10, token);
325 for (i=0; i<num_require_membership_of_sid; i++) {
326 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
327 &require_membership_of_sid[i])));
328 if (nt_token_check_sid(&require_membership_of_sid[i],
329 token)) {
330 DEBUG(10, ("Access ok\n"));
331 TALLOC_FREE(frame);
332 return NT_STATUS_OK;
336 /* Do not distinguish this error from a wrong username/pw */
338 TALLOC_FREE(frame);
339 return NT_STATUS_LOGON_FAILURE;
342 struct winbindd_domain *find_auth_domain(uint8_t flags,
343 const char *domain_name)
345 struct winbindd_domain *domain;
347 if (IS_DC) {
348 domain = find_domain_from_name_noinit(domain_name);
349 if (domain == NULL) {
350 DEBUG(3, ("Authentication for domain [%s] refused "
351 "as it is not a trusted domain\n",
352 domain_name));
354 return domain;
357 if (strequal(domain_name, get_global_sam_name())) {
358 return find_domain_from_name_noinit(domain_name);
361 /* we can auth against trusted domains */
362 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
363 domain = find_domain_from_name_noinit(domain_name);
364 if (domain == NULL) {
365 DEBUG(3, ("Authentication for domain [%s] skipped "
366 "as it is not a trusted domain\n",
367 domain_name));
368 } else {
369 return domain;
373 return find_our_domain();
376 static void fill_in_password_policy(struct winbindd_response *r,
377 const struct samr_DomInfo1 *p)
379 r->data.auth.policy.min_length_password =
380 p->min_password_length;
381 r->data.auth.policy.password_history =
382 p->password_history_length;
383 r->data.auth.policy.password_properties =
384 p->password_properties;
385 r->data.auth.policy.expire =
386 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
387 r->data.auth.policy.min_passwordage =
388 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
391 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
392 struct winbindd_cli_state *state)
394 struct winbindd_methods *methods;
395 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
396 struct samr_DomInfo1 password_policy;
398 if ( !winbindd_can_contact_domain( domain ) ) {
399 DEBUG(5,("fillup_password_policy: No inbound trust to "
400 "contact domain %s\n", domain->name));
401 return NT_STATUS_NOT_SUPPORTED;
404 methods = domain->methods;
406 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
407 if (NT_STATUS_IS_ERR(status)) {
408 return status;
411 fill_in_password_policy(state->response, &password_policy);
413 return NT_STATUS_OK;
416 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
417 TALLOC_CTX *mem_ctx,
418 uint16 *lockout_threshold)
420 struct winbindd_methods *methods;
421 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
422 struct samr_DomInfo12 lockout_policy;
424 *lockout_threshold = 0;
426 methods = domain->methods;
428 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
429 if (NT_STATUS_IS_ERR(status)) {
430 return status;
433 *lockout_threshold = lockout_policy.lockout_threshold;
435 return NT_STATUS_OK;
438 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
439 TALLOC_CTX *mem_ctx,
440 uint32 *password_properties)
442 struct winbindd_methods *methods;
443 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
444 struct samr_DomInfo1 password_policy;
446 *password_properties = 0;
448 methods = domain->methods;
450 status = methods->password_policy(domain, mem_ctx, &password_policy);
451 if (NT_STATUS_IS_ERR(status)) {
452 return status;
455 *password_properties = password_policy.password_properties;
457 return NT_STATUS_OK;
460 #ifdef HAVE_KRB5
462 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
463 const char *type,
464 uid_t uid,
465 bool *internal_ccache)
467 /* accept FILE and WRFILE as krb5_cc_type from the client and then
468 * build the full ccname string based on the user's uid here -
469 * Guenther*/
471 const char *gen_cc = NULL;
473 *internal_ccache = true;
475 if (uid == -1) {
476 goto memory_ccache;
479 if (!type || type[0] == '\0') {
480 goto memory_ccache;
483 if (strequal(type, "FILE")) {
484 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
485 } else if (strequal(type, "WRFILE")) {
486 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
487 } else {
488 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
489 goto memory_ccache;
492 *internal_ccache = false;
493 goto done;
495 memory_ccache:
496 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
498 done:
499 if (gen_cc == NULL) {
500 DEBUG(0,("out of memory\n"));
501 return NULL;
504 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
506 return gen_cc;
509 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
511 const char *type = state->request->data.auth.krb5_cc_type;
513 state->response->data.auth.krb5ccname[0] = '\0';
515 if (type[0] == '\0') {
516 return;
519 if (!strequal(type, "FILE") &&
520 !strequal(type, "WRFILE")) {
521 DEBUG(10,("won't return krbccname for a %s type ccache\n",
522 type));
523 return;
526 fstrcpy(state->response->data.auth.krb5ccname, cc);
529 #endif
531 uid_t get_uid_from_request(struct winbindd_request *request)
533 uid_t uid;
535 uid = request->data.auth.uid;
537 if (uid < 0) {
538 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
539 return -1;
541 return uid;
544 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
546 return get_uid_from_request(state->request);
549 /**********************************************************************
550 Authenticate a user with a clear text password using Kerberos and fill up
551 ccache if required
552 **********************************************************************/
554 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
555 struct winbindd_cli_state *state,
556 struct netr_SamInfo3 **info3)
558 #ifdef HAVE_KRB5
559 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
560 krb5_error_code krb5_ret;
561 const char *cc = NULL;
562 const char *principal_s = NULL;
563 const char *service = NULL;
564 char *realm = NULL;
565 fstring name_domain, name_user;
566 time_t ticket_lifetime = 0;
567 time_t renewal_until = 0;
568 uid_t uid = -1;
569 ADS_STRUCT *ads;
570 time_t time_offset = 0;
571 bool internal_ccache = true;
572 struct PAC_LOGON_INFO *logon_info = NULL;
574 *info3 = NULL;
576 /* 1st step:
577 * prepare a krb5_cc_cache string for the user */
579 uid = get_uid_from_state(state);
580 if (uid == -1) {
581 DEBUG(0,("no valid uid\n"));
584 cc = generate_krb5_ccache(state->mem_ctx,
585 state->request->data.auth.krb5_cc_type,
586 state->request->data.auth.uid,
587 &internal_ccache);
588 if (cc == NULL) {
589 return NT_STATUS_NO_MEMORY;
593 /* 2nd step:
594 * get kerberos properties */
596 if (domain->private_data) {
597 ads = (ADS_STRUCT *)domain->private_data;
598 time_offset = ads->auth.time_offset;
602 /* 3rd step:
603 * do kerberos auth and setup ccache as the user */
605 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
607 realm = domain->alt_name;
608 strupper_m(realm);
610 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
611 if (principal_s == NULL) {
612 return NT_STATUS_NO_MEMORY;
615 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
616 if (service == NULL) {
617 return NT_STATUS_NO_MEMORY;
620 /* if this is a user ccache, we need to act as the user to let the krb5
621 * library handle the chown, etc. */
623 /************************ ENTERING NON-ROOT **********************/
625 if (!internal_ccache) {
626 set_effective_uid(uid);
627 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
630 result = kerberos_return_pac(state->mem_ctx,
631 principal_s,
632 state->request->data.auth.pass,
633 time_offset,
634 &ticket_lifetime,
635 &renewal_until,
637 true,
638 true,
639 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
640 NULL,
641 &logon_info);
642 if (!internal_ccache) {
643 gain_root_privilege();
646 /************************ RETURNED TO ROOT **********************/
648 if (!NT_STATUS_IS_OK(result)) {
649 goto failed;
652 *info3 = &logon_info->info3;
654 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
655 principal_s));
657 /* if we had a user's ccache then return that string for the pam
658 * environment */
660 if (!internal_ccache) {
662 setup_return_cc_name(state, cc);
664 result = add_ccache_to_list(principal_s,
666 service,
667 state->request->data.auth.user,
668 realm,
669 uid,
670 time(NULL),
671 ticket_lifetime,
672 renewal_until,
673 false);
675 if (!NT_STATUS_IS_OK(result)) {
676 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
677 nt_errstr(result)));
679 } else {
681 /* need to delete the memory cred cache, it is not used anymore */
683 krb5_ret = ads_kdestroy(cc);
684 if (krb5_ret) {
685 DEBUG(3,("winbindd_raw_kerberos_login: "
686 "could not destroy krb5 credential cache: "
687 "%s\n", error_message(krb5_ret)));
692 return NT_STATUS_OK;
694 failed:
696 /* we could have created a new credential cache with a valid tgt in it
697 * but we werent able to get or verify the service ticket for this
698 * local host and therefor didn't get the PAC, we need to remove that
699 * cache entirely now */
701 krb5_ret = ads_kdestroy(cc);
702 if (krb5_ret) {
703 DEBUG(3,("winbindd_raw_kerberos_login: "
704 "could not destroy krb5 credential cache: "
705 "%s\n", error_message(krb5_ret)));
708 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
709 DEBUG(3,("winbindd_raw_kerberos_login: "
710 "could not remove ccache for user %s\n",
711 state->request->data.auth.user));
714 return result;
715 #else
716 return NT_STATUS_NOT_SUPPORTED;
717 #endif /* HAVE_KRB5 */
720 /****************************************************************
721 ****************************************************************/
723 bool check_request_flags(uint32_t flags)
725 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
726 WBFLAG_PAM_INFO3_TEXT |
727 WBFLAG_PAM_INFO3_NDR;
729 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
730 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
731 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
732 !(flags & flags_edata) ) {
733 return true;
736 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
737 flags));
739 return false;
742 /****************************************************************
743 ****************************************************************/
745 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
746 struct netr_SamInfo3 *info3,
747 const char *name_domain,
748 const char *name_user)
750 NTSTATUS result;
751 uint32_t flags = state->request->flags;
753 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
754 memcpy(state->response->data.auth.user_session_key,
755 info3->base.key.key,
756 sizeof(state->response->data.auth.user_session_key)
757 /* 16 */);
760 if (flags & WBFLAG_PAM_LMKEY) {
761 memcpy(state->response->data.auth.first_8_lm_hash,
762 info3->base.LMSessKey.key,
763 sizeof(state->response->data.auth.first_8_lm_hash)
764 /* 8 */);
767 if (flags & WBFLAG_PAM_UNIX_NAME) {
768 result = append_unix_username(state->mem_ctx, state, info3,
769 name_domain, name_user);
770 if (!NT_STATUS_IS_OK(result)) {
771 DEBUG(10,("Failed to append Unix Username: %s\n",
772 nt_errstr(result)));
773 return result;
777 /* currently, anything from here on potentially overwrites extra_data. */
779 if (flags & WBFLAG_PAM_INFO3_NDR) {
780 result = append_info3_as_ndr(state->mem_ctx, state, info3);
781 if (!NT_STATUS_IS_OK(result)) {
782 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
783 nt_errstr(result)));
784 return result;
788 if (flags & WBFLAG_PAM_INFO3_TEXT) {
789 result = append_info3_as_txt(state->mem_ctx, state, info3);
790 if (!NT_STATUS_IS_OK(result)) {
791 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
792 nt_errstr(result)));
793 return result;
797 if (flags & WBFLAG_PAM_AFS_TOKEN) {
798 result = append_afs_token(state->mem_ctx, state, info3,
799 name_domain, name_user);
800 if (!NT_STATUS_IS_OK(result)) {
801 DEBUG(10,("Failed to append AFS token: %s\n",
802 nt_errstr(result)));
803 return result;
807 return NT_STATUS_OK;
810 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
811 struct winbindd_cli_state *state,
812 struct netr_SamInfo3 **info3)
814 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
815 uint16 max_allowed_bad_attempts;
816 fstring name_domain, name_user;
817 struct dom_sid sid;
818 enum lsa_SidType type;
819 uchar new_nt_pass[NT_HASH_LEN];
820 const uint8 *cached_nt_pass;
821 const uint8 *cached_salt;
822 struct netr_SamInfo3 *my_info3;
823 time_t kickoff_time, must_change_time;
824 bool password_good = false;
825 #ifdef HAVE_KRB5
826 struct winbindd_tdc_domain *tdc_domain = NULL;
827 #endif
829 *info3 = NULL;
831 ZERO_STRUCTP(info3);
833 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
835 /* Parse domain and username */
837 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
840 if (!lookup_cached_name(state->mem_ctx,
841 name_domain,
842 name_user,
843 &sid,
844 &type)) {
845 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
846 return NT_STATUS_NO_SUCH_USER;
849 if (type != SID_NAME_USER) {
850 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
851 return NT_STATUS_LOGON_FAILURE;
854 result = winbindd_get_creds(domain,
855 state->mem_ctx,
856 &sid,
857 &my_info3,
858 &cached_nt_pass,
859 &cached_salt);
860 if (!NT_STATUS_IS_OK(result)) {
861 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
862 return result;
865 *info3 = my_info3;
867 E_md4hash(state->request->data.auth.pass, new_nt_pass);
869 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
870 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
871 if (cached_salt) {
872 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
875 if (cached_salt) {
876 /* In this case we didn't store the nt_hash itself,
877 but the MD5 combination of salt + nt_hash. */
878 uchar salted_hash[NT_HASH_LEN];
879 E_md5hash(cached_salt, new_nt_pass, salted_hash);
881 password_good = (memcmp(cached_nt_pass, salted_hash,
882 NT_HASH_LEN) == 0);
883 } else {
884 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
885 password_good = (memcmp(cached_nt_pass, new_nt_pass,
886 NT_HASH_LEN) == 0);
889 if (password_good) {
891 /* User *DOES* know the password, update logon_time and reset
892 * bad_pw_count */
894 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
896 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
897 return NT_STATUS_ACCOUNT_LOCKED_OUT;
900 if (my_info3->base.acct_flags & ACB_DISABLED) {
901 return NT_STATUS_ACCOUNT_DISABLED;
904 if (my_info3->base.acct_flags & ACB_WSTRUST) {
905 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
908 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
909 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
912 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
913 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
916 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
917 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
918 my_info3->base.acct_flags));
919 return NT_STATUS_LOGON_FAILURE;
922 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
923 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
924 return NT_STATUS_ACCOUNT_EXPIRED;
927 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
928 if (must_change_time != 0 && must_change_time < time(NULL)) {
929 /* we allow grace logons when the password has expired */
930 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
931 /* return NT_STATUS_PASSWORD_EXPIRED; */
932 goto success;
935 #ifdef HAVE_KRB5
936 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
937 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
938 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
939 /* used to cope with the case winbindd starting without network. */
940 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
942 uid_t uid = -1;
943 const char *cc = NULL;
944 char *realm = NULL;
945 const char *principal_s = NULL;
946 const char *service = NULL;
947 bool internal_ccache = false;
949 uid = get_uid_from_state(state);
950 if (uid == -1) {
951 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
952 return NT_STATUS_INVALID_PARAMETER;
955 cc = generate_krb5_ccache(state->mem_ctx,
956 state->request->data.auth.krb5_cc_type,
957 state->request->data.auth.uid,
958 &internal_ccache);
959 if (cc == NULL) {
960 return NT_STATUS_NO_MEMORY;
963 realm = domain->alt_name;
964 strupper_m(realm);
966 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
967 if (principal_s == NULL) {
968 return NT_STATUS_NO_MEMORY;
971 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
972 if (service == NULL) {
973 return NT_STATUS_NO_MEMORY;
976 if (!internal_ccache) {
978 setup_return_cc_name(state, cc);
980 result = add_ccache_to_list(principal_s,
982 service,
983 state->request->data.auth.user,
984 domain->alt_name,
985 uid,
986 time(NULL),
987 time(NULL) + lp_winbind_cache_time(),
988 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
989 true);
991 if (!NT_STATUS_IS_OK(result)) {
992 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
993 "to add ccache to list: %s\n",
994 nt_errstr(result)));
998 #endif /* HAVE_KRB5 */
999 success:
1000 /* FIXME: we possibly should handle logon hours as well (does xp when
1001 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1003 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1004 my_info3->base.bad_password_count = 0;
1006 result = winbindd_update_creds_by_info3(domain,
1007 state->mem_ctx,
1008 state->request->data.auth.user,
1009 state->request->data.auth.pass,
1010 my_info3);
1011 if (!NT_STATUS_IS_OK(result)) {
1012 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1013 nt_errstr(result)));
1014 return result;
1017 return NT_STATUS_OK;
1021 /* User does *NOT* know the correct password, modify info3 accordingly */
1023 /* failure of this is not critical */
1024 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1025 if (!NT_STATUS_IS_OK(result)) {
1026 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1027 "Won't be able to honour account lockout policies\n"));
1030 /* increase counter */
1031 my_info3->base.bad_password_count++;
1033 if (max_allowed_bad_attempts == 0) {
1034 goto failed;
1037 /* lockout user */
1038 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1040 uint32 password_properties;
1042 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1043 if (!NT_STATUS_IS_OK(result)) {
1044 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1047 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1048 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1049 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1053 failed:
1054 result = winbindd_update_creds_by_info3(domain,
1055 state->mem_ctx,
1056 state->request->data.auth.user,
1057 NULL,
1058 my_info3);
1060 if (!NT_STATUS_IS_OK(result)) {
1061 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1062 nt_errstr(result)));
1065 return NT_STATUS_LOGON_FAILURE;
1068 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1069 struct winbindd_cli_state *state,
1070 struct netr_SamInfo3 **info3)
1072 struct winbindd_domain *contact_domain;
1073 fstring name_domain, name_user;
1074 NTSTATUS result;
1076 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1078 /* Parse domain and username */
1080 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1082 /* what domain should we contact? */
1084 if ( IS_DC ) {
1085 if (!(contact_domain = find_domain_from_name(name_domain))) {
1086 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1087 state->request->data.auth.user, name_domain, name_user, name_domain));
1088 result = NT_STATUS_NO_SUCH_USER;
1089 goto done;
1092 } else {
1093 if (is_myname(name_domain)) {
1094 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1095 result = NT_STATUS_NO_SUCH_USER;
1096 goto done;
1099 contact_domain = find_domain_from_name(name_domain);
1100 if (contact_domain == NULL) {
1101 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1102 state->request->data.auth.user, name_domain, name_user, name_domain));
1104 contact_domain = find_our_domain();
1108 if (contact_domain->initialized &&
1109 contact_domain->active_directory) {
1110 goto try_login;
1113 if (!contact_domain->initialized) {
1114 init_dc_connection(contact_domain);
1117 if (!contact_domain->active_directory) {
1118 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1119 return NT_STATUS_INVALID_LOGON_TYPE;
1121 try_login:
1122 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1123 done:
1124 return result;
1127 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1128 const char *domain, const char *user,
1129 const DATA_BLOB *challenge,
1130 const DATA_BLOB *lm_resp,
1131 const DATA_BLOB *nt_resp,
1132 struct netr_SamInfo3 **pinfo3)
1134 struct auth_usersupplied_info *user_info = NULL;
1135 struct auth_serversupplied_info *server_info = NULL;
1136 struct netr_SamInfo3 *info3;
1137 NTSTATUS status;
1139 status = make_user_info(&user_info, user, user, domain, domain,
1140 global_myname(), lm_resp, nt_resp, NULL, NULL,
1141 NULL, True);
1142 if (!NT_STATUS_IS_OK(status)) {
1143 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1144 return status;
1147 status = check_sam_security(challenge, talloc_tos(), user_info,
1148 &server_info);
1149 free_user_info(&user_info);
1151 if (!NT_STATUS_IS_OK(status)) {
1152 DEBUG(10, ("check_ntlm_password failed: %s\n",
1153 nt_errstr(status)));
1154 return status;
1157 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1158 if (info3 == NULL) {
1159 return NT_STATUS_NO_MEMORY;
1162 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1163 if (!NT_STATUS_IS_OK(status)) {
1164 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1165 nt_errstr(status)));
1166 return status;
1169 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1170 *pinfo3 = info3;
1171 return NT_STATUS_OK;
1174 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1175 TALLOC_CTX *mem_ctx,
1176 uint32 logon_parameters,
1177 const char *server,
1178 const char *username,
1179 const char *domain,
1180 const char *workstation,
1181 const uint8 chal[8],
1182 DATA_BLOB lm_response,
1183 DATA_BLOB nt_response,
1184 struct netr_SamInfo3 **info3);
1186 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1187 struct winbindd_cli_state *state,
1188 struct netr_SamInfo3 **info3)
1191 struct rpc_pipe_client *netlogon_pipe;
1192 uchar chal[8];
1193 DATA_BLOB lm_resp;
1194 DATA_BLOB nt_resp;
1195 int attempts = 0;
1196 unsigned char local_lm_response[24];
1197 unsigned char local_nt_response[24];
1198 fstring name_domain, name_user;
1199 bool retry;
1200 NTSTATUS result;
1201 struct netr_SamInfo3 *my_info3 = NULL;
1203 *info3 = NULL;
1205 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1207 /* Parse domain and username */
1209 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1211 /* do password magic */
1213 generate_random_buffer(chal, sizeof(chal));
1215 if (lp_client_ntlmv2_auth()) {
1216 DATA_BLOB server_chal;
1217 DATA_BLOB names_blob;
1218 DATA_BLOB nt_response;
1219 DATA_BLOB lm_response;
1220 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1222 /* note that the 'workgroup' here is a best guess - we don't know
1223 the server's domain at this point. The 'server name' is also
1224 dodgy...
1226 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1228 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1229 state->request->data.auth.pass,
1230 &server_chal,
1231 &names_blob,
1232 &lm_response, &nt_response, NULL, NULL)) {
1233 data_blob_free(&names_blob);
1234 data_blob_free(&server_chal);
1235 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1236 result = NT_STATUS_NO_MEMORY;
1237 goto done;
1239 data_blob_free(&names_blob);
1240 data_blob_free(&server_chal);
1241 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1242 lm_response.length);
1243 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1244 nt_response.length);
1245 data_blob_free(&lm_response);
1246 data_blob_free(&nt_response);
1248 } else {
1249 if (lp_client_lanman_auth()
1250 && SMBencrypt(state->request->data.auth.pass,
1251 chal,
1252 local_lm_response)) {
1253 lm_resp = data_blob_talloc(state->mem_ctx,
1254 local_lm_response,
1255 sizeof(local_lm_response));
1256 } else {
1257 lm_resp = data_blob_null;
1259 SMBNTencrypt(state->request->data.auth.pass,
1260 chal,
1261 local_nt_response);
1263 nt_resp = data_blob_talloc(state->mem_ctx,
1264 local_nt_response,
1265 sizeof(local_nt_response));
1268 if (strequal(name_domain, get_global_sam_name())) {
1269 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1271 result = winbindd_dual_auth_passdb(
1272 state->mem_ctx, name_domain, name_user,
1273 &chal_blob, &lm_resp, &nt_resp, info3);
1274 goto done;
1277 /* check authentication loop */
1279 do {
1280 netlogon_fn_t logon_fn;
1282 ZERO_STRUCTP(my_info3);
1283 retry = false;
1285 result = cm_connect_netlogon(domain, &netlogon_pipe);
1287 if (!NT_STATUS_IS_OK(result)) {
1288 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1289 goto done;
1292 /* It is really important to try SamLogonEx here,
1293 * because in a clustered environment, we want to use
1294 * one machine account from multiple physical
1295 * computers.
1297 * With a normal SamLogon call, we must keep the
1298 * credentials chain updated and intact between all
1299 * users of the machine account (which would imply
1300 * cross-node communication for every NTLM logon).
1302 * (The credentials chain is not per NETLOGON pipe
1303 * connection, but globally on the server/client pair
1304 * by machine name).
1306 * When using SamLogonEx, the credentials are not
1307 * supplied, but the session key is implied by the
1308 * wrapping SamLogon context.
1310 * -- abartlet 21 April 2008
1313 logon_fn = domain->can_do_samlogon_ex
1314 ? rpccli_netlogon_sam_network_logon_ex
1315 : rpccli_netlogon_sam_network_logon;
1317 result = logon_fn(netlogon_pipe,
1318 state->mem_ctx,
1320 domain->dcname, /* server name */
1321 name_user, /* user name */
1322 name_domain, /* target domain */
1323 global_myname(), /* workstation */
1324 chal,
1325 lm_resp,
1326 nt_resp,
1327 &my_info3);
1328 attempts += 1;
1330 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1331 && domain->can_do_samlogon_ex) {
1332 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1333 "retrying with NetSamLogon\n"));
1334 domain->can_do_samlogon_ex = false;
1335 retry = true;
1336 continue;
1339 /* We have to try a second time as cm_connect_netlogon
1340 might not yet have noticed that the DC has killed
1341 our connection. */
1343 if (!rpccli_is_connected(netlogon_pipe)) {
1344 retry = true;
1345 continue;
1348 /* if we get access denied, a possible cause was that we had
1349 and open connection to the DC, but someone changed our
1350 machine account password out from underneath us using 'net
1351 rpc changetrustpw' */
1353 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1354 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1355 "ACCESS_DENIED. Maybe the trust account "
1356 "password was changed and we didn't know it. "
1357 "Killing connections to domain %s\n",
1358 name_domain));
1359 invalidate_cm_connection(&domain->conn);
1360 retry = true;
1363 } while ( (attempts < 2) && retry );
1365 /* handle the case where a NT4 DC does not fill in the acct_flags in
1366 * the samlogon reply info3. When accurate info3 is required by the
1367 * caller, we look up the account flags ourselve - gd */
1369 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1370 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1372 struct rpc_pipe_client *samr_pipe;
1373 struct policy_handle samr_domain_handle, user_pol;
1374 union samr_UserInfo *info = NULL;
1375 NTSTATUS status_tmp;
1376 uint32 acct_flags;
1378 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1379 &samr_pipe, &samr_domain_handle);
1381 if (!NT_STATUS_IS_OK(status_tmp)) {
1382 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1383 nt_errstr(status_tmp)));
1384 goto done;
1387 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1388 &samr_domain_handle,
1389 MAXIMUM_ALLOWED_ACCESS,
1390 my_info3->base.rid,
1391 &user_pol);
1393 if (!NT_STATUS_IS_OK(status_tmp)) {
1394 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1395 nt_errstr(status_tmp)));
1396 goto done;
1399 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1400 &user_pol,
1402 &info);
1404 if (!NT_STATUS_IS_OK(status_tmp)) {
1405 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1406 nt_errstr(status_tmp)));
1407 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1408 goto done;
1411 acct_flags = info->info16.acct_flags;
1413 if (acct_flags == 0) {
1414 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1415 goto done;
1418 my_info3->base.acct_flags = acct_flags;
1420 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1422 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1425 *info3 = my_info3;
1426 done:
1427 return result;
1430 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1431 struct winbindd_cli_state *state)
1433 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1434 NTSTATUS krb5_result = NT_STATUS_OK;
1435 fstring name_domain, name_user;
1436 char *mapped_user;
1437 fstring domain_user;
1438 struct netr_SamInfo3 *info3 = NULL;
1439 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1441 /* Ensure null termination */
1442 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1444 /* Ensure null termination */
1445 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1447 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1448 state->request->data.auth.user));
1450 if (!check_request_flags(state->request->flags)) {
1451 result = NT_STATUS_INVALID_PARAMETER_MIX;
1452 goto done;
1455 /* Parse domain and username */
1457 name_map_status = normalize_name_unmap(state->mem_ctx,
1458 state->request->data.auth.user,
1459 &mapped_user);
1461 /* If the name normalization didnt' actually do anything,
1462 just use the original name */
1464 if (!NT_STATUS_IS_OK(name_map_status) &&
1465 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1467 mapped_user = state->request->data.auth.user;
1470 parse_domain_user(mapped_user, name_domain, name_user);
1472 if ( mapped_user != state->request->data.auth.user ) {
1473 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1474 safe_strcpy( state->request->data.auth.user, domain_user,
1475 sizeof(state->request->data.auth.user)-1 );
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 done;
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 struct 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 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1605 netsamlogon_cache_store(name_user, 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 result = check_info3_in_group(
1621 info3,
1622 state->request->data.auth.require_membership_of_sid);
1623 if (!NT_STATUS_IS_OK(result)) {
1624 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1625 state->request->data.auth.user,
1626 state->request->data.auth.require_membership_of_sid));
1627 goto done;
1630 result = append_auth_data(state, info3, name_domain,
1631 name_user);
1632 if (!NT_STATUS_IS_OK(result)) {
1633 goto done;
1636 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1638 if (lp_winbind_offline_logon()) {
1639 result = winbindd_store_creds(domain,
1640 state->mem_ctx,
1641 state->request->data.auth.user,
1642 state->request->data.auth.pass,
1643 info3, NULL);
1648 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1649 struct winbindd_domain *our_domain = find_our_domain();
1651 /* This is not entirely correct I believe, but it is
1652 consistent. Only apply the password policy settings
1653 too warn users for our own domain. Cannot obtain these
1654 from trusted DCs all the time so don't do it at all.
1655 -- jerry */
1657 result = NT_STATUS_NOT_SUPPORTED;
1658 if (our_domain == domain ) {
1659 result = fillup_password_policy(our_domain, state);
1662 if (!NT_STATUS_IS_OK(result)
1663 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1665 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1666 domain->name, nt_errstr(result)));
1667 goto done;
1671 result = NT_STATUS_OK;
1674 done:
1675 /* give us a more useful (more correct?) error code */
1676 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1677 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1678 result = NT_STATUS_NO_LOGON_SERVERS;
1681 set_auth_errors(state->response, result);
1683 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1684 state->request->data.auth.user,
1685 state->response->data.auth.nt_status_string,
1686 state->response->data.auth.pam_error));
1688 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1691 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1692 struct winbindd_cli_state *state)
1694 NTSTATUS result;
1695 struct netr_SamInfo3 *info3 = NULL;
1696 struct rpc_pipe_client *netlogon_pipe;
1697 const char *name_user = NULL;
1698 const char *name_domain = NULL;
1699 const char *workstation;
1700 int attempts = 0;
1701 bool retry;
1703 DATA_BLOB lm_resp, nt_resp;
1705 /* This is child-only, so no check for privileged access is needed
1706 anymore */
1708 /* Ensure null termination */
1709 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1710 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1712 if (!check_request_flags(state->request->flags)) {
1713 result = NT_STATUS_INVALID_PARAMETER_MIX;
1714 goto done;
1717 name_user = state->request->data.auth_crap.user;
1719 if (*state->request->data.auth_crap.domain) {
1720 name_domain = state->request->data.auth_crap.domain;
1721 } else if (lp_winbind_use_default_domain()) {
1722 name_domain = lp_workgroup();
1723 } else {
1724 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1725 name_user));
1726 result = NT_STATUS_NO_SUCH_USER;
1727 goto done;
1730 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1731 name_domain, name_user));
1733 if (*state->request->data.auth_crap.workstation) {
1734 workstation = state->request->data.auth_crap.workstation;
1735 } else {
1736 workstation = global_myname();
1739 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1740 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1741 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1742 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1743 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1744 state->request->data.auth_crap.lm_resp_len,
1745 state->request->data.auth_crap.nt_resp_len));
1746 result = NT_STATUS_INVALID_PARAMETER;
1747 goto done;
1751 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1752 state->request->data.auth_crap.lm_resp_len);
1754 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1755 nt_resp = data_blob_talloc(state->mem_ctx,
1756 state->request->extra_data.data,
1757 state->request->data.auth_crap.nt_resp_len);
1758 } else {
1759 nt_resp = data_blob_talloc(state->mem_ctx,
1760 state->request->data.auth_crap.nt_resp,
1761 state->request->data.auth_crap.nt_resp_len);
1764 if (strequal(name_domain, get_global_sam_name())) {
1765 DATA_BLOB chal_blob = data_blob_const(
1766 state->request->data.auth_crap.chal,
1767 sizeof(state->request->data.auth_crap.chal));
1769 result = winbindd_dual_auth_passdb(
1770 state->mem_ctx, name_domain, name_user,
1771 &chal_blob, &lm_resp, &nt_resp, &info3);
1772 goto process_result;
1775 do {
1776 netlogon_fn_t logon_fn;
1778 retry = false;
1780 netlogon_pipe = NULL;
1781 result = cm_connect_netlogon(domain, &netlogon_pipe);
1783 if (!NT_STATUS_IS_OK(result)) {
1784 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1785 nt_errstr(result)));
1786 goto done;
1789 logon_fn = domain->can_do_samlogon_ex
1790 ? rpccli_netlogon_sam_network_logon_ex
1791 : rpccli_netlogon_sam_network_logon;
1793 result = logon_fn(netlogon_pipe,
1794 state->mem_ctx,
1795 state->request->data.auth_crap.logon_parameters,
1796 domain->dcname,
1797 name_user,
1798 name_domain,
1799 /* Bug #3248 - found by Stefan Burkei. */
1800 workstation, /* We carefully set this above so use it... */
1801 state->request->data.auth_crap.chal,
1802 lm_resp,
1803 nt_resp,
1804 &info3);
1806 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1807 && domain->can_do_samlogon_ex) {
1808 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1809 "retrying with NetSamLogon\n"));
1810 domain->can_do_samlogon_ex = false;
1811 retry = true;
1812 continue;
1815 attempts += 1;
1817 /* We have to try a second time as cm_connect_netlogon
1818 might not yet have noticed that the DC has killed
1819 our connection. */
1821 if (!rpccli_is_connected(netlogon_pipe)) {
1822 retry = true;
1823 continue;
1826 /* if we get access denied, a possible cause was that we had and open
1827 connection to the DC, but someone changed our machine account password
1828 out from underneath us using 'net rpc changetrustpw' */
1830 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1831 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1832 "ACCESS_DENIED. Maybe the trust account "
1833 "password was changed and we didn't know it. "
1834 "Killing connections to domain %s\n",
1835 name_domain));
1836 invalidate_cm_connection(&domain->conn);
1837 retry = true;
1840 } while ( (attempts < 2) && retry );
1842 process_result:
1844 if (NT_STATUS_IS_OK(result)) {
1846 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1847 netsamlogon_cache_store(name_user, info3);
1849 /* Check if the user is in the right group */
1851 result = check_info3_in_group(
1852 info3,
1853 state->request->data.auth_crap.require_membership_of_sid);
1854 if (!NT_STATUS_IS_OK(result)) {
1855 DEBUG(3, ("User %s is not in the required group (%s), so "
1856 "crap authentication is rejected\n",
1857 state->request->data.auth_crap.user,
1858 state->request->data.auth_crap.require_membership_of_sid));
1859 goto done;
1862 result = append_auth_data(state, info3, name_domain,
1863 name_user);
1864 if (!NT_STATUS_IS_OK(result)) {
1865 goto done;
1869 done:
1871 /* give us a more useful (more correct?) error code */
1872 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1873 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1874 result = NT_STATUS_NO_LOGON_SERVERS;
1877 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1878 result = nt_status_squash(result);
1881 set_auth_errors(state->response, result);
1883 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1884 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1885 name_domain,
1886 name_user,
1887 state->response->data.auth.nt_status_string,
1888 state->response->data.auth.pam_error));
1890 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1893 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1894 struct winbindd_cli_state *state)
1896 char *oldpass;
1897 char *newpass = NULL;
1898 struct policy_handle dom_pol;
1899 struct rpc_pipe_client *cli;
1900 bool got_info = false;
1901 struct samr_DomInfo1 *info = NULL;
1902 struct userPwdChangeFailureInformation *reject = NULL;
1903 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1904 fstring domain, user;
1906 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1907 state->request->data.auth.user));
1909 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1910 goto done;
1913 /* Change password */
1915 oldpass = state->request->data.chauthtok.oldpass;
1916 newpass = state->request->data.chauthtok.newpass;
1918 /* Initialize reject reason */
1919 state->response->data.auth.reject_reason = Undefined;
1921 if (strequal(domain, get_global_sam_name())) {
1922 struct samr_CryptPassword new_nt_password;
1923 struct samr_CryptPassword new_lm_password;
1924 struct samr_Password old_nt_hash_enc;
1925 struct samr_Password old_lanman_hash_enc;
1926 enum samPwdChangeReason rejectReason;
1928 uchar old_nt_hash[16];
1929 uchar old_lanman_hash[16];
1930 uchar new_nt_hash[16];
1931 uchar new_lanman_hash[16];
1933 contact_domain = NULL;
1935 E_md4hash(oldpass, old_nt_hash);
1936 E_md4hash(newpass, new_nt_hash);
1938 if (lp_client_lanman_auth() &&
1939 E_deshash(newpass, new_lanman_hash) &&
1940 E_deshash(oldpass, old_lanman_hash)) {
1942 /* E_deshash returns false for 'long' passwords (> 14
1943 DOS chars). This allows us to match Win2k, which
1944 does not store a LM hash for these passwords (which
1945 would reduce the effective password length to 14) */
1947 encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
1948 arcfour_crypt(new_lm_password.data, old_nt_hash, 516);
1949 E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
1950 } else {
1951 ZERO_STRUCT(new_lm_password);
1952 ZERO_STRUCT(old_lanman_hash_enc);
1955 encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
1957 arcfour_crypt(new_nt_password.data, old_nt_hash, 516);
1958 E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
1960 result = pass_oem_change(
1961 user,
1962 new_lm_password.data, old_lanman_hash_enc.hash,
1963 new_nt_password.data, old_nt_hash_enc.hash,
1964 &rejectReason);
1965 goto done;
1968 /* Get sam handle */
1970 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1971 &dom_pol);
1972 if (!NT_STATUS_IS_OK(result)) {
1973 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1974 goto done;
1977 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1978 user,
1979 newpass,
1980 oldpass,
1981 &info,
1982 &reject);
1984 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1986 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1988 fill_in_password_policy(state->response, info);
1990 state->response->data.auth.reject_reason =
1991 reject->extendedFailureReason;
1993 got_info = true;
1996 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1997 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1998 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1999 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2001 /* only fallback when the chgpasswd_user3 call is not supported */
2002 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2003 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2004 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2005 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2007 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2008 nt_errstr(result)));
2010 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2012 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2013 Map to the same status code as Windows 2003. */
2015 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2016 result = NT_STATUS_PASSWORD_RESTRICTION;
2020 done:
2022 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2023 if (lp_winbind_offline_logon()) {
2024 result = winbindd_update_creds_by_name(contact_domain,
2025 state->mem_ctx, user,
2026 newpass);
2027 /* Again, this happens when we login from gdm or xdm
2028 * and the password expires, *BUT* cached crendentials
2029 * doesn't exist. winbindd_update_creds_by_name()
2030 * returns NT_STATUS_NO_SUCH_USER.
2031 * This is not a failure.
2032 * --- BoYang
2033 * */
2034 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2035 result = NT_STATUS_OK;
2038 if (!NT_STATUS_IS_OK(result)) {
2039 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2040 goto process_result;
2045 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2047 NTSTATUS policy_ret;
2049 policy_ret = fillup_password_policy(contact_domain, state);
2051 /* failure of this is non critical, it will just provide no
2052 * additional information to the client why the change has
2053 * failed - Guenther */
2055 if (!NT_STATUS_IS_OK(policy_ret)) {
2056 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2057 goto process_result;
2061 process_result:
2063 set_auth_errors(state->response, result);
2065 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2066 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2067 domain,
2068 user,
2069 state->response->data.auth.nt_status_string,
2070 state->response->data.auth.pam_error));
2072 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2075 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2076 struct winbindd_cli_state *state)
2078 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2080 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2081 state->request->data.logoff.user));
2083 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2084 result = NT_STATUS_OK;
2085 goto process_result;
2088 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2089 result = NT_STATUS_OK;
2090 goto process_result;
2093 #ifdef HAVE_KRB5
2095 if (state->request->data.logoff.uid < 0) {
2096 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2097 goto process_result;
2100 /* what we need here is to find the corresponding krb5 ccache name *we*
2101 * created for a given username and destroy it */
2103 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2104 result = NT_STATUS_OK;
2105 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2106 goto process_result;
2109 if (!ccache_entry_identical(state->request->data.logoff.user,
2110 state->request->data.logoff.uid,
2111 state->request->data.logoff.krb5ccname)) {
2112 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2113 goto process_result;
2116 result = remove_ccache(state->request->data.logoff.user);
2117 if (!NT_STATUS_IS_OK(result)) {
2118 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2119 nt_errstr(result)));
2120 goto process_result;
2123 #else
2124 result = NT_STATUS_NOT_SUPPORTED;
2125 #endif
2127 process_result:
2130 set_auth_errors(state->response, result);
2132 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2135 /* Change user password with auth crap*/
2137 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2139 NTSTATUS result;
2140 DATA_BLOB new_nt_password;
2141 DATA_BLOB old_nt_hash_enc;
2142 DATA_BLOB new_lm_password;
2143 DATA_BLOB old_lm_hash_enc;
2144 fstring domain,user;
2145 struct policy_handle dom_pol;
2146 struct winbindd_domain *contact_domain = domainSt;
2147 struct rpc_pipe_client *cli;
2149 /* Ensure null termination */
2150 state->request->data.chng_pswd_auth_crap.user[
2151 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2152 state->request->data.chng_pswd_auth_crap.domain[
2153 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2154 *domain = 0;
2155 *user = 0;
2157 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2158 (unsigned long)state->pid,
2159 state->request->data.chng_pswd_auth_crap.domain,
2160 state->request->data.chng_pswd_auth_crap.user));
2162 if (lp_winbind_offline_logon()) {
2163 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2164 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2165 result = NT_STATUS_ACCESS_DENIED;
2166 goto done;
2169 if (*state->request->data.chng_pswd_auth_crap.domain) {
2170 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2171 } else {
2172 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2173 domain, user);
2175 if(!*domain) {
2176 DEBUG(3,("no domain specified with username (%s) - "
2177 "failing auth\n",
2178 state->request->data.chng_pswd_auth_crap.user));
2179 result = NT_STATUS_NO_SUCH_USER;
2180 goto done;
2184 if (!*domain && lp_winbind_use_default_domain()) {
2185 fstrcpy(domain,(char *)lp_workgroup());
2188 if(!*user) {
2189 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2192 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2193 (unsigned long)state->pid, domain, user));
2195 if (strequal(domain, get_global_sam_name())) {
2196 enum samPwdChangeReason reject_reason;
2198 result = pass_oem_change(
2199 user,
2200 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2201 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2202 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2203 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2204 &reject_reason);
2205 DEBUG(10, ("pass_oem_change returned %s\n",
2206 nt_errstr(result)));
2207 goto done;
2210 /* Change password */
2211 new_nt_password = data_blob_const(
2212 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2213 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2215 old_nt_hash_enc = data_blob_const(
2216 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2217 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2219 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2220 new_lm_password = data_blob_const(
2221 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2222 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2224 old_lm_hash_enc = data_blob_const(
2225 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2226 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2227 } else {
2228 new_lm_password.length = 0;
2229 old_lm_hash_enc.length = 0;
2232 /* Get sam handle */
2234 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2235 if (!NT_STATUS_IS_OK(result)) {
2236 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2237 goto done;
2240 result = rpccli_samr_chng_pswd_auth_crap(
2241 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2242 new_lm_password, old_lm_hash_enc);
2244 done:
2246 set_auth_errors(state->response, result);
2248 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2249 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2250 domain, user,
2251 state->response->data.auth.nt_status_string,
2252 state->response->data.auth.pam_error));
2254 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;