s3-rpc_client: move protos to cli_lsarpc.h
[Samba/ekacnet.git] / source3 / winbindd / winbindd_pam.c
blobf2e3829361985b1f92e0e1006938b32052d501b8
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"
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_WINBIND
38 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
40 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
41 struct winbindd_cli_state *state,
42 struct netr_SamInfo3 *info3)
44 char *ex;
45 uint32_t i;
47 state->response->data.auth.info3.logon_time =
48 nt_time_to_unix(info3->base.last_logon);
49 state->response->data.auth.info3.logoff_time =
50 nt_time_to_unix(info3->base.last_logoff);
51 state->response->data.auth.info3.kickoff_time =
52 nt_time_to_unix(info3->base.acct_expiry);
53 state->response->data.auth.info3.pass_last_set_time =
54 nt_time_to_unix(info3->base.last_password_change);
55 state->response->data.auth.info3.pass_can_change_time =
56 nt_time_to_unix(info3->base.allow_password_change);
57 state->response->data.auth.info3.pass_must_change_time =
58 nt_time_to_unix(info3->base.force_password_change);
60 state->response->data.auth.info3.logon_count = info3->base.logon_count;
61 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
63 state->response->data.auth.info3.user_rid = info3->base.rid;
64 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
65 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
67 state->response->data.auth.info3.num_groups = info3->base.groups.count;
68 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
70 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
71 state->response->data.auth.info3.num_other_sids = info3->sidcount;
73 fstrcpy(state->response->data.auth.info3.user_name,
74 info3->base.account_name.string);
75 fstrcpy(state->response->data.auth.info3.full_name,
76 info3->base.full_name.string);
77 fstrcpy(state->response->data.auth.info3.logon_script,
78 info3->base.logon_script.string);
79 fstrcpy(state->response->data.auth.info3.profile_path,
80 info3->base.profile_path.string);
81 fstrcpy(state->response->data.auth.info3.home_dir,
82 info3->base.home_directory.string);
83 fstrcpy(state->response->data.auth.info3.dir_drive,
84 info3->base.home_drive.string);
86 fstrcpy(state->response->data.auth.info3.logon_srv,
87 info3->base.logon_server.string);
88 fstrcpy(state->response->data.auth.info3.logon_dom,
89 info3->base.domain.string);
91 ex = talloc_strdup(state->mem_ctx, "");
92 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->base.groups.count; i++) {
95 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
96 info3->base.groups.rids[i].rid,
97 info3->base.groups.rids[i].attributes);
98 NT_STATUS_HAVE_NO_MEMORY(ex);
101 for (i=0; i < info3->sidcount; i++) {
102 char *sid;
104 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
105 NT_STATUS_HAVE_NO_MEMORY(sid);
107 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
108 sid,
109 info3->sids[i].attributes);
110 NT_STATUS_HAVE_NO_MEMORY(ex);
112 talloc_free(sid);
115 state->response->extra_data.data = ex;
116 state->response->length += talloc_get_size(ex);
118 return NT_STATUS_OK;
121 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
122 struct winbindd_cli_state *state,
123 struct netr_SamInfo3 *info3)
125 DATA_BLOB blob;
126 enum ndr_err_code ndr_err;
128 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
129 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
130 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
131 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
132 return ndr_map_error2ntstatus(ndr_err);
135 state->response->extra_data.data = blob.data;
136 state->response->length += blob.length;
138 return NT_STATUS_OK;
141 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
142 struct winbindd_cli_state *state,
143 const struct netr_SamInfo3 *info3,
144 const char *name_domain,
145 const char *name_user)
147 /* We've been asked to return the unix username, per
148 'winbind use default domain' settings and the like */
150 const char *nt_username, *nt_domain;
152 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
153 if (!nt_domain) {
154 /* If the server didn't give us one, just use the one
155 * we sent them */
156 nt_domain = name_domain;
159 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
160 if (!nt_username) {
161 /* If the server didn't give us one, just use the one
162 * we sent them */
163 nt_username = name_user;
166 fill_domain_username(state->response->data.auth.unix_username,
167 nt_domain, nt_username, true);
169 DEBUG(5,("Setting unix username to [%s]\n",
170 state->response->data.auth.unix_username));
172 return NT_STATUS_OK;
175 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
176 struct winbindd_cli_state *state,
177 const struct netr_SamInfo3 *info3,
178 const char *name_domain,
179 const char *name_user)
181 char *afsname = NULL;
182 char *cell;
183 char *token;
185 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
186 if (afsname == NULL) {
187 return NT_STATUS_NO_MEMORY;
190 afsname = talloc_string_sub(mem_ctx,
191 lp_afs_username_map(),
192 "%D", name_domain);
193 afsname = talloc_string_sub(mem_ctx, afsname,
194 "%u", name_user);
195 afsname = talloc_string_sub(mem_ctx, afsname,
196 "%U", name_user);
199 DOM_SID user_sid;
200 fstring sidstr;
202 sid_compose(&user_sid, info3->base.domain_sid,
203 info3->base.rid);
204 sid_to_fstring(sidstr, &user_sid);
205 afsname = talloc_string_sub(mem_ctx, afsname,
206 "%s", sidstr);
209 if (afsname == NULL) {
210 return NT_STATUS_NO_MEMORY;
213 strlower_m(afsname);
215 DEBUG(10, ("Generating token for user %s\n", afsname));
217 cell = strchr(afsname, '@');
219 if (cell == NULL) {
220 return NT_STATUS_NO_MEMORY;
223 *cell = '\0';
224 cell += 1;
226 token = afs_createtoken_str(afsname, cell);
227 if (token == NULL) {
228 return NT_STATUS_OK;
230 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
231 token);
232 if (state->response->extra_data.data == NULL) {
233 return NT_STATUS_NO_MEMORY;
235 state->response->length +=
236 strlen((const char *)state->response->extra_data.data)+1;
238 return NT_STATUS_OK;
241 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
242 const char *group_sid)
244 * Check whether a user belongs to a group or list of groups.
246 * @param mem_ctx talloc memory context.
247 * @param info3 user information, including group membership info.
248 * @param group_sid One or more groups , separated by commas.
250 * @return NT_STATUS_OK on success,
251 * NT_STATUS_LOGON_FAILURE if the user does not belong,
252 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
255 DOM_SID *require_membership_of_sid;
256 size_t num_require_membership_of_sid;
257 char *req_sid;
258 const char *p;
259 DOM_SID sid;
260 size_t i;
261 struct nt_user_token *token;
262 TALLOC_CTX *frame = talloc_stackframe();
263 NTSTATUS status;
265 /* Parse the 'required group' SID */
267 if (!group_sid || !group_sid[0]) {
268 /* NO sid supplied, all users may access */
269 return NT_STATUS_OK;
272 token = talloc_zero(talloc_tos(), struct nt_user_token);
273 if (token == NULL) {
274 DEBUG(0, ("talloc failed\n"));
275 TALLOC_FREE(frame);
276 return NT_STATUS_NO_MEMORY;
279 num_require_membership_of_sid = 0;
280 require_membership_of_sid = NULL;
282 p = group_sid;
284 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
285 if (!string_to_sid(&sid, req_sid)) {
286 DEBUG(0, ("check_info3_in_group: could not parse %s "
287 "as a SID!", req_sid));
288 TALLOC_FREE(frame);
289 return NT_STATUS_INVALID_PARAMETER;
292 status = add_sid_to_array(talloc_tos(), &sid,
293 &require_membership_of_sid,
294 &num_require_membership_of_sid);
295 if (!NT_STATUS_IS_OK(status)) {
296 DEBUG(0, ("add_sid_to_array failed\n"));
297 TALLOC_FREE(frame);
298 return status;
302 status = sid_array_from_info3(talloc_tos(), info3,
303 &token->user_sids,
304 &token->num_sids,
305 true, false);
306 if (!NT_STATUS_IS_OK(status)) {
307 TALLOC_FREE(frame);
308 return status;
311 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
312 token))
313 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
314 token))) {
315 DEBUG(3, ("could not add aliases: %s\n",
316 nt_errstr(status)));
317 TALLOC_FREE(frame);
318 return status;
321 debug_nt_user_token(DBGC_CLASS, 10, token);
323 for (i=0; i<num_require_membership_of_sid; i++) {
324 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
325 &require_membership_of_sid[i])));
326 if (nt_token_check_sid(&require_membership_of_sid[i],
327 token)) {
328 DEBUG(10, ("Access ok\n"));
329 TALLOC_FREE(frame);
330 return NT_STATUS_OK;
334 /* Do not distinguish this error from a wrong username/pw */
336 TALLOC_FREE(frame);
337 return NT_STATUS_LOGON_FAILURE;
340 struct winbindd_domain *find_auth_domain(uint8_t flags,
341 const char *domain_name)
343 struct winbindd_domain *domain;
345 if (IS_DC) {
346 domain = find_domain_from_name_noinit(domain_name);
347 if (domain == NULL) {
348 DEBUG(3, ("Authentication for domain [%s] refused "
349 "as it is not a trusted domain\n",
350 domain_name));
352 return domain;
355 if (strequal(domain_name, get_global_sam_name())) {
356 return find_domain_from_name_noinit(domain_name);
359 /* we can auth against trusted domains */
360 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
361 domain = find_domain_from_name_noinit(domain_name);
362 if (domain == NULL) {
363 DEBUG(3, ("Authentication for domain [%s] skipped "
364 "as it is not a trusted domain\n",
365 domain_name));
366 } else {
367 return domain;
371 return find_our_domain();
374 static void fill_in_password_policy(struct winbindd_response *r,
375 const struct samr_DomInfo1 *p)
377 r->data.auth.policy.min_length_password =
378 p->min_password_length;
379 r->data.auth.policy.password_history =
380 p->password_history_length;
381 r->data.auth.policy.password_properties =
382 p->password_properties;
383 r->data.auth.policy.expire =
384 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
385 r->data.auth.policy.min_passwordage =
386 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
389 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
390 struct winbindd_cli_state *state)
392 struct winbindd_methods *methods;
393 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
394 struct samr_DomInfo1 password_policy;
396 if ( !winbindd_can_contact_domain( domain ) ) {
397 DEBUG(5,("fillup_password_policy: No inbound trust to "
398 "contact domain %s\n", domain->name));
399 return NT_STATUS_NOT_SUPPORTED;
402 methods = domain->methods;
404 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
405 if (NT_STATUS_IS_ERR(status)) {
406 return status;
409 fill_in_password_policy(state->response, &password_policy);
411 return NT_STATUS_OK;
414 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
415 TALLOC_CTX *mem_ctx,
416 uint16 *lockout_threshold)
418 struct winbindd_methods *methods;
419 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
420 struct samr_DomInfo12 lockout_policy;
422 *lockout_threshold = 0;
424 methods = domain->methods;
426 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
427 if (NT_STATUS_IS_ERR(status)) {
428 return status;
431 *lockout_threshold = lockout_policy.lockout_threshold;
433 return NT_STATUS_OK;
436 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
437 TALLOC_CTX *mem_ctx,
438 uint32 *password_properties)
440 struct winbindd_methods *methods;
441 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
442 struct samr_DomInfo1 password_policy;
444 *password_properties = 0;
446 methods = domain->methods;
448 status = methods->password_policy(domain, mem_ctx, &password_policy);
449 if (NT_STATUS_IS_ERR(status)) {
450 return status;
453 *password_properties = password_policy.password_properties;
455 return NT_STATUS_OK;
458 #ifdef HAVE_KRB5
460 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
461 const char *type,
462 uid_t uid,
463 bool *internal_ccache)
465 /* accept FILE and WRFILE as krb5_cc_type from the client and then
466 * build the full ccname string based on the user's uid here -
467 * Guenther*/
469 const char *gen_cc = NULL;
471 *internal_ccache = true;
473 if (uid == -1) {
474 goto memory_ccache;
477 if (!type || type[0] == '\0') {
478 goto memory_ccache;
481 if (strequal(type, "FILE")) {
482 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
483 } else if (strequal(type, "WRFILE")) {
484 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
485 } else {
486 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
487 goto memory_ccache;
490 *internal_ccache = false;
491 goto done;
493 memory_ccache:
494 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
496 done:
497 if (gen_cc == NULL) {
498 DEBUG(0,("out of memory\n"));
499 return NULL;
502 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
504 return gen_cc;
507 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
509 const char *type = state->request->data.auth.krb5_cc_type;
511 state->response->data.auth.krb5ccname[0] = '\0';
513 if (type[0] == '\0') {
514 return;
517 if (!strequal(type, "FILE") &&
518 !strequal(type, "WRFILE")) {
519 DEBUG(10,("won't return krbccname for a %s type ccache\n",
520 type));
521 return;
524 fstrcpy(state->response->data.auth.krb5ccname, cc);
527 #endif
529 uid_t get_uid_from_request(struct winbindd_request *request)
531 uid_t uid;
533 uid = request->data.auth.uid;
535 if (uid < 0) {
536 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
537 return -1;
539 return uid;
542 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
544 return get_uid_from_request(state->request);
547 /**********************************************************************
548 Authenticate a user with a clear text password using Kerberos and fill up
549 ccache if required
550 **********************************************************************/
552 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
553 struct winbindd_cli_state *state,
554 struct netr_SamInfo3 **info3)
556 #ifdef HAVE_KRB5
557 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
558 krb5_error_code krb5_ret;
559 const char *cc = NULL;
560 const char *principal_s = NULL;
561 const char *service = NULL;
562 char *realm = NULL;
563 fstring name_domain, name_user;
564 time_t ticket_lifetime = 0;
565 time_t renewal_until = 0;
566 uid_t uid = -1;
567 ADS_STRUCT *ads;
568 time_t time_offset = 0;
569 bool internal_ccache = true;
570 struct PAC_LOGON_INFO *logon_info = NULL;
572 *info3 = NULL;
574 /* 1st step:
575 * prepare a krb5_cc_cache string for the user */
577 uid = get_uid_from_state(state);
578 if (uid == -1) {
579 DEBUG(0,("no valid uid\n"));
582 cc = generate_krb5_ccache(state->mem_ctx,
583 state->request->data.auth.krb5_cc_type,
584 state->request->data.auth.uid,
585 &internal_ccache);
586 if (cc == NULL) {
587 return NT_STATUS_NO_MEMORY;
591 /* 2nd step:
592 * get kerberos properties */
594 if (domain->private_data) {
595 ads = (ADS_STRUCT *)domain->private_data;
596 time_offset = ads->auth.time_offset;
600 /* 3rd step:
601 * do kerberos auth and setup ccache as the user */
603 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
605 realm = domain->alt_name;
606 strupper_m(realm);
608 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
609 if (principal_s == NULL) {
610 return NT_STATUS_NO_MEMORY;
613 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
614 if (service == NULL) {
615 return NT_STATUS_NO_MEMORY;
618 /* if this is a user ccache, we need to act as the user to let the krb5
619 * library handle the chown, etc. */
621 /************************ ENTERING NON-ROOT **********************/
623 if (!internal_ccache) {
624 set_effective_uid(uid);
625 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
628 result = kerberos_return_pac(state->mem_ctx,
629 principal_s,
630 state->request->data.auth.pass,
631 time_offset,
632 &ticket_lifetime,
633 &renewal_until,
635 true,
636 true,
637 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
638 NULL,
639 &logon_info);
640 if (!internal_ccache) {
641 gain_root_privilege();
644 /************************ RETURNED TO ROOT **********************/
646 if (!NT_STATUS_IS_OK(result)) {
647 goto failed;
650 *info3 = &logon_info->info3;
652 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
653 principal_s));
655 /* if we had a user's ccache then return that string for the pam
656 * environment */
658 if (!internal_ccache) {
660 setup_return_cc_name(state, cc);
662 result = add_ccache_to_list(principal_s,
664 service,
665 state->request->data.auth.user,
666 realm,
667 uid,
668 time(NULL),
669 ticket_lifetime,
670 renewal_until,
671 false);
673 if (!NT_STATUS_IS_OK(result)) {
674 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
675 nt_errstr(result)));
677 } else {
679 /* need to delete the memory cred cache, it is not used anymore */
681 krb5_ret = ads_kdestroy(cc);
682 if (krb5_ret) {
683 DEBUG(3,("winbindd_raw_kerberos_login: "
684 "could not destroy krb5 credential cache: "
685 "%s\n", error_message(krb5_ret)));
690 return NT_STATUS_OK;
692 failed:
694 /* we could have created a new credential cache with a valid tgt in it
695 * but we werent able to get or verify the service ticket for this
696 * local host and therefor didn't get the PAC, we need to remove that
697 * cache entirely now */
699 krb5_ret = ads_kdestroy(cc);
700 if (krb5_ret) {
701 DEBUG(3,("winbindd_raw_kerberos_login: "
702 "could not destroy krb5 credential cache: "
703 "%s\n", error_message(krb5_ret)));
706 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
707 DEBUG(3,("winbindd_raw_kerberos_login: "
708 "could not remove ccache for user %s\n",
709 state->request->data.auth.user));
712 return result;
713 #else
714 return NT_STATUS_NOT_SUPPORTED;
715 #endif /* HAVE_KRB5 */
718 /****************************************************************
719 ****************************************************************/
721 bool check_request_flags(uint32_t flags)
723 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
724 WBFLAG_PAM_INFO3_TEXT |
725 WBFLAG_PAM_INFO3_NDR;
727 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
728 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
729 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
730 !(flags & flags_edata) ) {
731 return true;
734 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
735 flags));
737 return false;
740 /****************************************************************
741 ****************************************************************/
743 static NTSTATUS append_auth_data(struct winbindd_cli_state *state,
744 struct netr_SamInfo3 *info3,
745 const char *name_domain,
746 const char *name_user)
748 NTSTATUS result;
749 uint32_t flags = state->request->flags;
751 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
752 memcpy(state->response->data.auth.user_session_key,
753 info3->base.key.key,
754 sizeof(state->response->data.auth.user_session_key)
755 /* 16 */);
758 if (flags & WBFLAG_PAM_LMKEY) {
759 memcpy(state->response->data.auth.first_8_lm_hash,
760 info3->base.LMSessKey.key,
761 sizeof(state->response->data.auth.first_8_lm_hash)
762 /* 8 */);
765 if (flags & WBFLAG_PAM_UNIX_NAME) {
766 result = append_unix_username(state->mem_ctx, state, info3,
767 name_domain, name_user);
768 if (!NT_STATUS_IS_OK(result)) {
769 DEBUG(10,("Failed to append Unix Username: %s\n",
770 nt_errstr(result)));
771 return result;
775 /* currently, anything from here on potentially overwrites extra_data. */
777 if (flags & WBFLAG_PAM_INFO3_NDR) {
778 result = append_info3_as_ndr(state->mem_ctx, state, info3);
779 if (!NT_STATUS_IS_OK(result)) {
780 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
781 nt_errstr(result)));
782 return result;
786 if (flags & WBFLAG_PAM_INFO3_TEXT) {
787 result = append_info3_as_txt(state->mem_ctx, state, info3);
788 if (!NT_STATUS_IS_OK(result)) {
789 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
790 nt_errstr(result)));
791 return result;
795 if (flags & WBFLAG_PAM_AFS_TOKEN) {
796 result = append_afs_token(state->mem_ctx, state, info3,
797 name_domain, name_user);
798 if (!NT_STATUS_IS_OK(result)) {
799 DEBUG(10,("Failed to append AFS token: %s\n",
800 nt_errstr(result)));
801 return result;
805 return NT_STATUS_OK;
808 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
809 struct winbindd_cli_state *state,
810 struct netr_SamInfo3 **info3)
812 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
813 uint16 max_allowed_bad_attempts;
814 fstring name_domain, name_user;
815 DOM_SID sid;
816 enum lsa_SidType type;
817 uchar new_nt_pass[NT_HASH_LEN];
818 const uint8 *cached_nt_pass;
819 const uint8 *cached_salt;
820 struct netr_SamInfo3 *my_info3;
821 time_t kickoff_time, must_change_time;
822 bool password_good = false;
823 #ifdef HAVE_KRB5
824 struct winbindd_tdc_domain *tdc_domain = NULL;
825 #endif
827 *info3 = NULL;
829 ZERO_STRUCTP(info3);
831 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
833 /* Parse domain and username */
835 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
838 if (!lookup_cached_name(state->mem_ctx,
839 name_domain,
840 name_user,
841 &sid,
842 &type)) {
843 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
844 return NT_STATUS_NO_SUCH_USER;
847 if (type != SID_NAME_USER) {
848 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
849 return NT_STATUS_LOGON_FAILURE;
852 result = winbindd_get_creds(domain,
853 state->mem_ctx,
854 &sid,
855 &my_info3,
856 &cached_nt_pass,
857 &cached_salt);
858 if (!NT_STATUS_IS_OK(result)) {
859 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
860 return result;
863 *info3 = my_info3;
865 E_md4hash(state->request->data.auth.pass, new_nt_pass);
867 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
868 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
869 if (cached_salt) {
870 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
873 if (cached_salt) {
874 /* In this case we didn't store the nt_hash itself,
875 but the MD5 combination of salt + nt_hash. */
876 uchar salted_hash[NT_HASH_LEN];
877 E_md5hash(cached_salt, new_nt_pass, salted_hash);
879 password_good = (memcmp(cached_nt_pass, salted_hash,
880 NT_HASH_LEN) == 0);
881 } else {
882 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
883 password_good = (memcmp(cached_nt_pass, new_nt_pass,
884 NT_HASH_LEN) == 0);
887 if (password_good) {
889 /* User *DOES* know the password, update logon_time and reset
890 * bad_pw_count */
892 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
894 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
895 return NT_STATUS_ACCOUNT_LOCKED_OUT;
898 if (my_info3->base.acct_flags & ACB_DISABLED) {
899 return NT_STATUS_ACCOUNT_DISABLED;
902 if (my_info3->base.acct_flags & ACB_WSTRUST) {
903 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
906 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
907 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
910 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
911 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
914 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
915 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
916 my_info3->base.acct_flags));
917 return NT_STATUS_LOGON_FAILURE;
920 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
921 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
922 return NT_STATUS_ACCOUNT_EXPIRED;
925 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
926 if (must_change_time != 0 && must_change_time < time(NULL)) {
927 /* we allow grace logons when the password has expired */
928 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
929 /* return NT_STATUS_PASSWORD_EXPIRED; */
930 goto success;
933 #ifdef HAVE_KRB5
934 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
935 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
936 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
937 /* used to cope with the case winbindd starting without network. */
938 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
940 uid_t uid = -1;
941 const char *cc = NULL;
942 char *realm = NULL;
943 const char *principal_s = NULL;
944 const char *service = NULL;
945 bool internal_ccache = false;
947 uid = get_uid_from_state(state);
948 if (uid == -1) {
949 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
950 return NT_STATUS_INVALID_PARAMETER;
953 cc = generate_krb5_ccache(state->mem_ctx,
954 state->request->data.auth.krb5_cc_type,
955 state->request->data.auth.uid,
956 &internal_ccache);
957 if (cc == NULL) {
958 return NT_STATUS_NO_MEMORY;
961 realm = domain->alt_name;
962 strupper_m(realm);
964 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
965 if (principal_s == NULL) {
966 return NT_STATUS_NO_MEMORY;
969 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
970 if (service == NULL) {
971 return NT_STATUS_NO_MEMORY;
974 if (!internal_ccache) {
976 setup_return_cc_name(state, cc);
978 result = add_ccache_to_list(principal_s,
980 service,
981 state->request->data.auth.user,
982 domain->alt_name,
983 uid,
984 time(NULL),
985 time(NULL) + lp_winbind_cache_time(),
986 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
987 true);
989 if (!NT_STATUS_IS_OK(result)) {
990 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
991 "to add ccache to list: %s\n",
992 nt_errstr(result)));
996 #endif /* HAVE_KRB5 */
997 success:
998 /* FIXME: we possibly should handle logon hours as well (does xp when
999 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1001 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1002 my_info3->base.bad_password_count = 0;
1004 result = winbindd_update_creds_by_info3(domain,
1005 state->mem_ctx,
1006 state->request->data.auth.user,
1007 state->request->data.auth.pass,
1008 my_info3);
1009 if (!NT_STATUS_IS_OK(result)) {
1010 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1011 nt_errstr(result)));
1012 return result;
1015 return NT_STATUS_OK;
1019 /* User does *NOT* know the correct password, modify info3 accordingly */
1021 /* failure of this is not critical */
1022 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1023 if (!NT_STATUS_IS_OK(result)) {
1024 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1025 "Won't be able to honour account lockout policies\n"));
1028 /* increase counter */
1029 my_info3->base.bad_password_count++;
1031 if (max_allowed_bad_attempts == 0) {
1032 goto failed;
1035 /* lockout user */
1036 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1038 uint32 password_properties;
1040 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1041 if (!NT_STATUS_IS_OK(result)) {
1042 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1045 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1046 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1047 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1051 failed:
1052 result = winbindd_update_creds_by_info3(domain,
1053 state->mem_ctx,
1054 state->request->data.auth.user,
1055 NULL,
1056 my_info3);
1058 if (!NT_STATUS_IS_OK(result)) {
1059 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1060 nt_errstr(result)));
1063 return NT_STATUS_LOGON_FAILURE;
1066 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1067 struct winbindd_cli_state *state,
1068 struct netr_SamInfo3 **info3)
1070 struct winbindd_domain *contact_domain;
1071 fstring name_domain, name_user;
1072 NTSTATUS result;
1074 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1076 /* Parse domain and username */
1078 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1080 /* what domain should we contact? */
1082 if ( IS_DC ) {
1083 if (!(contact_domain = find_domain_from_name(name_domain))) {
1084 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1085 state->request->data.auth.user, name_domain, name_user, name_domain));
1086 result = NT_STATUS_NO_SUCH_USER;
1087 goto done;
1090 } else {
1091 if (is_myname(name_domain)) {
1092 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1093 result = NT_STATUS_NO_SUCH_USER;
1094 goto done;
1097 contact_domain = find_domain_from_name(name_domain);
1098 if (contact_domain == NULL) {
1099 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1100 state->request->data.auth.user, name_domain, name_user, name_domain));
1102 contact_domain = find_our_domain();
1106 if (contact_domain->initialized &&
1107 contact_domain->active_directory) {
1108 goto try_login;
1111 if (!contact_domain->initialized) {
1112 init_dc_connection(contact_domain);
1115 if (!contact_domain->active_directory) {
1116 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1117 return NT_STATUS_INVALID_LOGON_TYPE;
1119 try_login:
1120 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1121 done:
1122 return result;
1125 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1126 const char *domain, const char *user,
1127 const DATA_BLOB *challenge,
1128 const DATA_BLOB *lm_resp,
1129 const DATA_BLOB *nt_resp,
1130 struct netr_SamInfo3 **pinfo3)
1132 struct auth_usersupplied_info *user_info = NULL;
1133 struct auth_serversupplied_info *server_info = NULL;
1134 struct netr_SamInfo3 *info3;
1135 NTSTATUS status;
1137 status = make_user_info(&user_info, user, user, domain, domain,
1138 global_myname(), lm_resp, nt_resp, NULL, NULL,
1139 NULL, True);
1140 if (!NT_STATUS_IS_OK(status)) {
1141 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1142 return status;
1145 status = check_sam_security(challenge, talloc_tos(), user_info,
1146 &server_info);
1147 free_user_info(&user_info);
1149 if (!NT_STATUS_IS_OK(status)) {
1150 DEBUG(10, ("check_ntlm_password failed: %s\n",
1151 nt_errstr(status)));
1152 return status;
1155 info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
1156 if (info3 == NULL) {
1157 return NT_STATUS_NO_MEMORY;
1160 status = serverinfo_to_SamInfo3(server_info, NULL, 0, info3);
1161 if (!NT_STATUS_IS_OK(status)) {
1162 DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n",
1163 nt_errstr(status)));
1164 return status;
1167 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1168 *pinfo3 = info3;
1169 return NT_STATUS_OK;
1172 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1173 TALLOC_CTX *mem_ctx,
1174 uint32 logon_parameters,
1175 const char *server,
1176 const char *username,
1177 const char *domain,
1178 const char *workstation,
1179 const uint8 chal[8],
1180 DATA_BLOB lm_response,
1181 DATA_BLOB nt_response,
1182 struct netr_SamInfo3 **info3);
1184 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1185 struct winbindd_cli_state *state,
1186 struct netr_SamInfo3 **info3)
1189 struct rpc_pipe_client *netlogon_pipe;
1190 uchar chal[8];
1191 DATA_BLOB lm_resp;
1192 DATA_BLOB nt_resp;
1193 int attempts = 0;
1194 unsigned char local_lm_response[24];
1195 unsigned char local_nt_response[24];
1196 fstring name_domain, name_user;
1197 bool retry;
1198 NTSTATUS result;
1199 struct netr_SamInfo3 *my_info3 = NULL;
1201 *info3 = NULL;
1203 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1205 /* Parse domain and username */
1207 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1209 /* do password magic */
1211 generate_random_buffer(chal, sizeof(chal));
1213 if (lp_client_ntlmv2_auth()) {
1214 DATA_BLOB server_chal;
1215 DATA_BLOB names_blob;
1216 DATA_BLOB nt_response;
1217 DATA_BLOB lm_response;
1218 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1220 /* note that the 'workgroup' here is a best guess - we don't know
1221 the server's domain at this point. The 'server name' is also
1222 dodgy...
1224 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1226 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1227 state->request->data.auth.pass,
1228 &server_chal,
1229 &names_blob,
1230 &lm_response, &nt_response, NULL, NULL)) {
1231 data_blob_free(&names_blob);
1232 data_blob_free(&server_chal);
1233 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1234 result = NT_STATUS_NO_MEMORY;
1235 goto done;
1237 data_blob_free(&names_blob);
1238 data_blob_free(&server_chal);
1239 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1240 lm_response.length);
1241 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1242 nt_response.length);
1243 data_blob_free(&lm_response);
1244 data_blob_free(&nt_response);
1246 } else {
1247 if (lp_client_lanman_auth()
1248 && SMBencrypt(state->request->data.auth.pass,
1249 chal,
1250 local_lm_response)) {
1251 lm_resp = data_blob_talloc(state->mem_ctx,
1252 local_lm_response,
1253 sizeof(local_lm_response));
1254 } else {
1255 lm_resp = data_blob_null;
1257 SMBNTencrypt(state->request->data.auth.pass,
1258 chal,
1259 local_nt_response);
1261 nt_resp = data_blob_talloc(state->mem_ctx,
1262 local_nt_response,
1263 sizeof(local_nt_response));
1266 if (strequal(name_domain, get_global_sam_name())) {
1267 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1269 result = winbindd_dual_auth_passdb(
1270 state->mem_ctx, name_domain, name_user,
1271 &chal_blob, &lm_resp, &nt_resp, info3);
1272 goto done;
1275 /* check authentication loop */
1277 do {
1278 netlogon_fn_t logon_fn;
1280 ZERO_STRUCTP(my_info3);
1281 retry = false;
1283 result = cm_connect_netlogon(domain, &netlogon_pipe);
1285 if (!NT_STATUS_IS_OK(result)) {
1286 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1287 goto done;
1290 /* It is really important to try SamLogonEx here,
1291 * because in a clustered environment, we want to use
1292 * one machine account from multiple physical
1293 * computers.
1295 * With a normal SamLogon call, we must keep the
1296 * credentials chain updated and intact between all
1297 * users of the machine account (which would imply
1298 * cross-node communication for every NTLM logon).
1300 * (The credentials chain is not per NETLOGON pipe
1301 * connection, but globally on the server/client pair
1302 * by machine name).
1304 * When using SamLogonEx, the credentials are not
1305 * supplied, but the session key is implied by the
1306 * wrapping SamLogon context.
1308 * -- abartlet 21 April 2008
1311 logon_fn = domain->can_do_samlogon_ex
1312 ? rpccli_netlogon_sam_network_logon_ex
1313 : rpccli_netlogon_sam_network_logon;
1315 result = logon_fn(netlogon_pipe,
1316 state->mem_ctx,
1318 domain->dcname, /* server name */
1319 name_user, /* user name */
1320 name_domain, /* target domain */
1321 global_myname(), /* workstation */
1322 chal,
1323 lm_resp,
1324 nt_resp,
1325 &my_info3);
1326 attempts += 1;
1328 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1329 && domain->can_do_samlogon_ex) {
1330 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1331 "retrying with NetSamLogon\n"));
1332 domain->can_do_samlogon_ex = false;
1333 retry = true;
1334 continue;
1337 /* We have to try a second time as cm_connect_netlogon
1338 might not yet have noticed that the DC has killed
1339 our connection. */
1341 if (!rpccli_is_connected(netlogon_pipe)) {
1342 retry = true;
1343 continue;
1346 /* if we get access denied, a possible cause was that we had
1347 and open connection to the DC, but someone changed our
1348 machine account password out from underneath us using 'net
1349 rpc changetrustpw' */
1351 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1352 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1353 "ACCESS_DENIED. Maybe the trust account "
1354 "password was changed and we didn't know it. "
1355 "Killing connections to domain %s\n",
1356 name_domain));
1357 invalidate_cm_connection(&domain->conn);
1358 retry = true;
1361 } while ( (attempts < 2) && retry );
1363 /* handle the case where a NT4 DC does not fill in the acct_flags in
1364 * the samlogon reply info3. When accurate info3 is required by the
1365 * caller, we look up the account flags ourselve - gd */
1367 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1368 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1370 struct rpc_pipe_client *samr_pipe;
1371 struct policy_handle samr_domain_handle, user_pol;
1372 union samr_UserInfo *info = NULL;
1373 NTSTATUS status_tmp;
1374 uint32 acct_flags;
1376 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1377 &samr_pipe, &samr_domain_handle);
1379 if (!NT_STATUS_IS_OK(status_tmp)) {
1380 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1381 nt_errstr(status_tmp)));
1382 goto done;
1385 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1386 &samr_domain_handle,
1387 MAXIMUM_ALLOWED_ACCESS,
1388 my_info3->base.rid,
1389 &user_pol);
1391 if (!NT_STATUS_IS_OK(status_tmp)) {
1392 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1393 nt_errstr(status_tmp)));
1394 goto done;
1397 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1398 &user_pol,
1400 &info);
1402 if (!NT_STATUS_IS_OK(status_tmp)) {
1403 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1404 nt_errstr(status_tmp)));
1405 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1406 goto done;
1409 acct_flags = info->info16.acct_flags;
1411 if (acct_flags == 0) {
1412 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1413 goto done;
1416 my_info3->base.acct_flags = acct_flags;
1418 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1420 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1423 *info3 = my_info3;
1424 done:
1425 return result;
1428 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1429 struct winbindd_cli_state *state)
1431 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1432 NTSTATUS krb5_result = NT_STATUS_OK;
1433 fstring name_domain, name_user;
1434 char *mapped_user;
1435 fstring domain_user;
1436 struct netr_SamInfo3 *info3 = NULL;
1437 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1439 /* Ensure null termination */
1440 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1442 /* Ensure null termination */
1443 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1445 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1446 state->request->data.auth.user));
1448 if (!check_request_flags(state->request->flags)) {
1449 result = NT_STATUS_INVALID_PARAMETER_MIX;
1450 goto done;
1453 /* Parse domain and username */
1455 name_map_status = normalize_name_unmap(state->mem_ctx,
1456 state->request->data.auth.user,
1457 &mapped_user);
1459 /* If the name normalization didnt' actually do anything,
1460 just use the original name */
1462 if (!NT_STATUS_IS_OK(name_map_status) &&
1463 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1465 mapped_user = state->request->data.auth.user;
1468 parse_domain_user(mapped_user, name_domain, name_user);
1470 if ( mapped_user != state->request->data.auth.user ) {
1471 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1472 safe_strcpy( state->request->data.auth.user, domain_user,
1473 sizeof(state->request->data.auth.user)-1 );
1476 if (domain->online == false) {
1477 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1478 if (domain->startup) {
1479 /* Logons are very important to users. If we're offline and
1480 we get a request within the first 30 seconds of startup,
1481 try very hard to find a DC and go online. */
1483 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1484 "request in startup mode.\n", domain->name ));
1486 winbindd_flush_negative_conn_cache(domain);
1487 result = init_dc_connection(domain);
1491 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1493 /* Check for Kerberos authentication */
1494 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1496 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1497 /* save for later */
1498 krb5_result = result;
1501 if (NT_STATUS_IS_OK(result)) {
1502 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1503 goto process_result;
1504 } else {
1505 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1508 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1509 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1510 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1511 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1512 set_domain_offline( domain );
1513 goto cached_logon;
1516 /* there are quite some NT_STATUS errors where there is no
1517 * point in retrying with a samlogon, we explictly have to take
1518 * care not to increase the bad logon counter on the DC */
1520 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1521 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1522 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1525 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1526 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1527 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1528 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1529 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1530 goto done;
1533 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1534 DEBUG(3,("falling back to samlogon\n"));
1535 goto sam_logon;
1536 } else {
1537 goto cached_logon;
1541 sam_logon:
1542 /* Check for Samlogon authentication */
1543 if (domain->online) {
1544 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1546 if (NT_STATUS_IS_OK(result)) {
1547 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1548 /* add the Krb5 err if we have one */
1549 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1550 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1552 goto process_result;
1555 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1556 nt_errstr(result)));
1558 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1559 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1560 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1562 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1563 set_domain_offline( domain );
1564 goto cached_logon;
1567 if (domain->online) {
1568 /* We're still online - fail. */
1569 goto done;
1573 cached_logon:
1574 /* Check for Cached logons */
1575 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1576 lp_winbind_offline_logon()) {
1578 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1580 if (NT_STATUS_IS_OK(result)) {
1581 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1582 goto process_result;
1583 } else {
1584 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1585 goto done;
1589 process_result:
1591 if (NT_STATUS_IS_OK(result)) {
1593 DOM_SID user_sid;
1595 /* In all codepaths where result == NT_STATUS_OK info3 must have
1596 been initialized. */
1597 if (!info3) {
1598 result = NT_STATUS_INTERNAL_ERROR;
1599 goto done;
1602 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1603 netsamlogon_cache_store(name_user, info3);
1605 /* save name_to_sid info as early as possible (only if
1606 this is our primary domain so we don't invalidate
1607 the cache entry by storing the seq_num for the wrong
1608 domain). */
1609 if ( domain->primary ) {
1610 sid_compose(&user_sid, info3->base.domain_sid,
1611 info3->base.rid);
1612 cache_name2sid(domain, name_domain, name_user,
1613 SID_NAME_USER, &user_sid);
1616 /* Check if the user is in the right group */
1618 result = check_info3_in_group(
1619 info3,
1620 state->request->data.auth.require_membership_of_sid);
1621 if (!NT_STATUS_IS_OK(result)) {
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_auth_data(state, info3, name_domain,
1629 name_user);
1630 if (!NT_STATUS_IS_OK(result)) {
1631 goto done;
1634 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1636 if (lp_winbind_offline_logon()) {
1637 result = winbindd_store_creds(domain,
1638 state->mem_ctx,
1639 state->request->data.auth.user,
1640 state->request->data.auth.pass,
1641 info3, NULL);
1646 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1647 struct winbindd_domain *our_domain = find_our_domain();
1649 /* This is not entirely correct I believe, but it is
1650 consistent. Only apply the password policy settings
1651 too warn users for our own domain. Cannot obtain these
1652 from trusted DCs all the time so don't do it at all.
1653 -- jerry */
1655 result = NT_STATUS_NOT_SUPPORTED;
1656 if (our_domain == domain ) {
1657 result = fillup_password_policy(our_domain, state);
1660 if (!NT_STATUS_IS_OK(result)
1661 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1663 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1664 domain->name, nt_errstr(result)));
1665 goto done;
1669 result = NT_STATUS_OK;
1672 done:
1673 /* give us a more useful (more correct?) error code */
1674 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1675 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1676 result = NT_STATUS_NO_LOGON_SERVERS;
1679 set_auth_errors(state->response, result);
1681 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1682 state->request->data.auth.user,
1683 state->response->data.auth.nt_status_string,
1684 state->response->data.auth.pam_error));
1686 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1689 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1690 struct winbindd_cli_state *state)
1692 NTSTATUS result;
1693 struct netr_SamInfo3 *info3 = NULL;
1694 struct rpc_pipe_client *netlogon_pipe;
1695 const char *name_user = NULL;
1696 const char *name_domain = NULL;
1697 const char *workstation;
1698 int attempts = 0;
1699 bool retry;
1701 DATA_BLOB lm_resp, nt_resp;
1703 /* This is child-only, so no check for privileged access is needed
1704 anymore */
1706 /* Ensure null termination */
1707 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1708 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1710 if (!check_request_flags(state->request->flags)) {
1711 result = NT_STATUS_INVALID_PARAMETER_MIX;
1712 goto done;
1715 name_user = state->request->data.auth_crap.user;
1717 if (*state->request->data.auth_crap.domain) {
1718 name_domain = state->request->data.auth_crap.domain;
1719 } else if (lp_winbind_use_default_domain()) {
1720 name_domain = lp_workgroup();
1721 } else {
1722 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1723 name_user));
1724 result = NT_STATUS_NO_SUCH_USER;
1725 goto done;
1728 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1729 name_domain, name_user));
1731 if (*state->request->data.auth_crap.workstation) {
1732 workstation = state->request->data.auth_crap.workstation;
1733 } else {
1734 workstation = global_myname();
1737 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1738 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1739 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1740 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1741 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1742 state->request->data.auth_crap.lm_resp_len,
1743 state->request->data.auth_crap.nt_resp_len));
1744 result = NT_STATUS_INVALID_PARAMETER;
1745 goto done;
1749 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1750 state->request->data.auth_crap.lm_resp_len);
1752 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1753 nt_resp = data_blob_talloc(state->mem_ctx,
1754 state->request->extra_data.data,
1755 state->request->data.auth_crap.nt_resp_len);
1756 } else {
1757 nt_resp = data_blob_talloc(state->mem_ctx,
1758 state->request->data.auth_crap.nt_resp,
1759 state->request->data.auth_crap.nt_resp_len);
1762 if (strequal(name_domain, get_global_sam_name())) {
1763 DATA_BLOB chal_blob = data_blob_const(
1764 state->request->data.auth_crap.chal,
1765 sizeof(state->request->data.auth_crap.chal));
1767 result = winbindd_dual_auth_passdb(
1768 state->mem_ctx, name_domain, name_user,
1769 &chal_blob, &lm_resp, &nt_resp, &info3);
1770 goto process_result;
1773 do {
1774 netlogon_fn_t logon_fn;
1776 retry = false;
1778 netlogon_pipe = NULL;
1779 result = cm_connect_netlogon(domain, &netlogon_pipe);
1781 if (!NT_STATUS_IS_OK(result)) {
1782 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1783 nt_errstr(result)));
1784 goto done;
1787 logon_fn = domain->can_do_samlogon_ex
1788 ? rpccli_netlogon_sam_network_logon_ex
1789 : rpccli_netlogon_sam_network_logon;
1791 result = logon_fn(netlogon_pipe,
1792 state->mem_ctx,
1793 state->request->data.auth_crap.logon_parameters,
1794 domain->dcname,
1795 name_user,
1796 name_domain,
1797 /* Bug #3248 - found by Stefan Burkei. */
1798 workstation, /* We carefully set this above so use it... */
1799 state->request->data.auth_crap.chal,
1800 lm_resp,
1801 nt_resp,
1802 &info3);
1804 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1805 && domain->can_do_samlogon_ex) {
1806 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1807 "retrying with NetSamLogon\n"));
1808 domain->can_do_samlogon_ex = false;
1809 retry = true;
1810 continue;
1813 attempts += 1;
1815 /* We have to try a second time as cm_connect_netlogon
1816 might not yet have noticed that the DC has killed
1817 our connection. */
1819 if (!rpccli_is_connected(netlogon_pipe)) {
1820 retry = true;
1821 continue;
1824 /* if we get access denied, a possible cause was that we had and open
1825 connection to the DC, but someone changed our machine account password
1826 out from underneath us using 'net rpc changetrustpw' */
1828 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1829 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1830 "ACCESS_DENIED. Maybe the trust account "
1831 "password was changed and we didn't know it. "
1832 "Killing connections to domain %s\n",
1833 name_domain));
1834 invalidate_cm_connection(&domain->conn);
1835 retry = true;
1838 } while ( (attempts < 2) && retry );
1840 process_result:
1842 if (NT_STATUS_IS_OK(result)) {
1844 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1845 netsamlogon_cache_store(name_user, info3);
1847 /* Check if the user is in the right group */
1849 result = check_info3_in_group(
1850 info3,
1851 state->request->data.auth_crap.require_membership_of_sid);
1852 if (!NT_STATUS_IS_OK(result)) {
1853 DEBUG(3, ("User %s is not in the required group (%s), so "
1854 "crap authentication is rejected\n",
1855 state->request->data.auth_crap.user,
1856 state->request->data.auth_crap.require_membership_of_sid));
1857 goto done;
1860 result = append_auth_data(state, info3, name_domain,
1861 name_user);
1862 if (!NT_STATUS_IS_OK(result)) {
1863 goto done;
1867 done:
1869 /* give us a more useful (more correct?) error code */
1870 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1871 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1872 result = NT_STATUS_NO_LOGON_SERVERS;
1875 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1876 result = nt_status_squash(result);
1879 set_auth_errors(state->response, result);
1881 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1882 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1883 name_domain,
1884 name_user,
1885 state->response->data.auth.nt_status_string,
1886 state->response->data.auth.pam_error));
1888 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1891 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1892 struct winbindd_cli_state *state)
1894 char *oldpass;
1895 char *newpass = NULL;
1896 struct policy_handle dom_pol;
1897 struct rpc_pipe_client *cli;
1898 bool got_info = false;
1899 struct samr_DomInfo1 *info = NULL;
1900 struct userPwdChangeFailureInformation *reject = NULL;
1901 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1902 fstring domain, user;
1904 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1905 state->request->data.auth.user));
1907 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1908 goto done;
1911 /* Change password */
1913 oldpass = state->request->data.chauthtok.oldpass;
1914 newpass = state->request->data.chauthtok.newpass;
1916 /* Initialize reject reason */
1917 state->response->data.auth.reject_reason = Undefined;
1919 if (strequal(domain, get_global_sam_name())) {
1920 struct samr_CryptPassword new_nt_password;
1921 struct samr_CryptPassword new_lm_password;
1922 struct samr_Password old_nt_hash_enc;
1923 struct samr_Password old_lanman_hash_enc;
1924 enum samPwdChangeReason rejectReason;
1926 uchar old_nt_hash[16];
1927 uchar old_lanman_hash[16];
1928 uchar new_nt_hash[16];
1929 uchar new_lanman_hash[16];
1931 contact_domain = NULL;
1933 E_md4hash(oldpass, old_nt_hash);
1934 E_md4hash(newpass, new_nt_hash);
1936 if (lp_client_lanman_auth() &&
1937 E_deshash(newpass, new_lanman_hash) &&
1938 E_deshash(oldpass, old_lanman_hash)) {
1940 /* E_deshash returns false for 'long' passwords (> 14
1941 DOS chars). This allows us to match Win2k, which
1942 does not store a LM hash for these passwords (which
1943 would reduce the effective password length to 14) */
1945 encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
1946 arcfour_crypt(new_lm_password.data, old_nt_hash, 516);
1947 E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
1948 } else {
1949 ZERO_STRUCT(new_lm_password);
1950 ZERO_STRUCT(old_lanman_hash_enc);
1953 encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
1955 arcfour_crypt(new_nt_password.data, old_nt_hash, 516);
1956 E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
1958 result = pass_oem_change(
1959 user,
1960 new_lm_password.data, old_lanman_hash_enc.hash,
1961 new_nt_password.data, old_nt_hash_enc.hash,
1962 &rejectReason);
1963 goto done;
1966 /* Get sam handle */
1968 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1969 &dom_pol);
1970 if (!NT_STATUS_IS_OK(result)) {
1971 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1972 goto done;
1975 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1976 user,
1977 newpass,
1978 oldpass,
1979 &info,
1980 &reject);
1982 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1984 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1986 fill_in_password_policy(state->response, info);
1988 state->response->data.auth.reject_reason =
1989 reject->extendedFailureReason;
1991 got_info = true;
1994 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1995 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1996 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1997 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1999 /* only fallback when the chgpasswd_user3 call is not supported */
2000 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2001 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2002 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2003 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2005 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2006 nt_errstr(result)));
2008 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2010 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2011 Map to the same status code as Windows 2003. */
2013 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2014 result = NT_STATUS_PASSWORD_RESTRICTION;
2018 done:
2020 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2021 if (lp_winbind_offline_logon()) {
2022 result = winbindd_update_creds_by_name(contact_domain,
2023 state->mem_ctx, user,
2024 newpass);
2025 /* Again, this happens when we login from gdm or xdm
2026 * and the password expires, *BUT* cached crendentials
2027 * doesn't exist. winbindd_update_creds_by_name()
2028 * returns NT_STATUS_NO_SUCH_USER.
2029 * This is not a failure.
2030 * --- BoYang
2031 * */
2032 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2033 result = NT_STATUS_OK;
2036 if (!NT_STATUS_IS_OK(result)) {
2037 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2038 goto process_result;
2043 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2045 NTSTATUS policy_ret;
2047 policy_ret = fillup_password_policy(contact_domain, state);
2049 /* failure of this is non critical, it will just provide no
2050 * additional information to the client why the change has
2051 * failed - Guenther */
2053 if (!NT_STATUS_IS_OK(policy_ret)) {
2054 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2055 goto process_result;
2059 process_result:
2061 set_auth_errors(state->response, result);
2063 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2064 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2065 domain,
2066 user,
2067 state->response->data.auth.nt_status_string,
2068 state->response->data.auth.pam_error));
2070 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2073 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2074 struct winbindd_cli_state *state)
2076 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2078 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2079 state->request->data.logoff.user));
2081 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2082 result = NT_STATUS_OK;
2083 goto process_result;
2086 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2087 result = NT_STATUS_OK;
2088 goto process_result;
2091 #ifdef HAVE_KRB5
2093 if (state->request->data.logoff.uid < 0) {
2094 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2095 goto process_result;
2098 /* what we need here is to find the corresponding krb5 ccache name *we*
2099 * created for a given username and destroy it */
2101 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2102 result = NT_STATUS_OK;
2103 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2104 goto process_result;
2107 if (!ccache_entry_identical(state->request->data.logoff.user,
2108 state->request->data.logoff.uid,
2109 state->request->data.logoff.krb5ccname)) {
2110 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2111 goto process_result;
2114 result = remove_ccache(state->request->data.logoff.user);
2115 if (!NT_STATUS_IS_OK(result)) {
2116 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2117 nt_errstr(result)));
2118 goto process_result;
2121 #else
2122 result = NT_STATUS_NOT_SUPPORTED;
2123 #endif
2125 process_result:
2128 set_auth_errors(state->response, result);
2130 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2133 /* Change user password with auth crap*/
2135 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2137 NTSTATUS result;
2138 DATA_BLOB new_nt_password;
2139 DATA_BLOB old_nt_hash_enc;
2140 DATA_BLOB new_lm_password;
2141 DATA_BLOB old_lm_hash_enc;
2142 fstring domain,user;
2143 struct policy_handle dom_pol;
2144 struct winbindd_domain *contact_domain = domainSt;
2145 struct rpc_pipe_client *cli;
2147 /* Ensure null termination */
2148 state->request->data.chng_pswd_auth_crap.user[
2149 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2150 state->request->data.chng_pswd_auth_crap.domain[
2151 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2152 *domain = 0;
2153 *user = 0;
2155 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2156 (unsigned long)state->pid,
2157 state->request->data.chng_pswd_auth_crap.domain,
2158 state->request->data.chng_pswd_auth_crap.user));
2160 if (lp_winbind_offline_logon()) {
2161 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2162 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2163 result = NT_STATUS_ACCESS_DENIED;
2164 goto done;
2167 if (*state->request->data.chng_pswd_auth_crap.domain) {
2168 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2169 } else {
2170 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2171 domain, user);
2173 if(!*domain) {
2174 DEBUG(3,("no domain specified with username (%s) - "
2175 "failing auth\n",
2176 state->request->data.chng_pswd_auth_crap.user));
2177 result = NT_STATUS_NO_SUCH_USER;
2178 goto done;
2182 if (!*domain && lp_winbind_use_default_domain()) {
2183 fstrcpy(domain,(char *)lp_workgroup());
2186 if(!*user) {
2187 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2190 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2191 (unsigned long)state->pid, domain, user));
2193 if (strequal(domain, get_global_sam_name())) {
2194 enum samPwdChangeReason reject_reason;
2196 result = pass_oem_change(
2197 user,
2198 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2199 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2200 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2201 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2202 &reject_reason);
2203 DEBUG(10, ("pass_oem_change returned %s\n",
2204 nt_errstr(result)));
2205 goto done;
2208 /* Change password */
2209 new_nt_password = data_blob_const(
2210 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2211 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2213 old_nt_hash_enc = data_blob_const(
2214 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2215 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2217 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2218 new_lm_password = data_blob_const(
2219 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2220 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2222 old_lm_hash_enc = data_blob_const(
2223 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2224 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2225 } else {
2226 new_lm_password.length = 0;
2227 old_lm_hash_enc.length = 0;
2230 /* Get sam handle */
2232 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2233 if (!NT_STATUS_IS_OK(result)) {
2234 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2235 goto done;
2238 result = rpccli_samr_chng_pswd_auth_crap(
2239 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2240 new_lm_password, old_lm_hash_enc);
2242 done:
2244 set_auth_errors(state->response, result);
2246 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2247 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2248 domain, user,
2249 state->response->data.auth.nt_status_string,
2250 state->response->data.auth.pam_error));
2252 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;