s4:role transfer - use always type "enum drepl_role_master" for role specifications
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blob0053b7fba5599541cce968370d359743ac46a08f
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 "../libcli/security/security.h"
35 #include "ads.h"
36 #include "../librpc/gen_ndr/krb5pac.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
43 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
44 struct winbindd_response *resp,
45 struct netr_SamInfo3 *info3)
47 char *ex;
48 uint32_t i;
50 resp->data.auth.info3.logon_time =
51 nt_time_to_unix(info3->base.last_logon);
52 resp->data.auth.info3.logoff_time =
53 nt_time_to_unix(info3->base.last_logoff);
54 resp->data.auth.info3.kickoff_time =
55 nt_time_to_unix(info3->base.acct_expiry);
56 resp->data.auth.info3.pass_last_set_time =
57 nt_time_to_unix(info3->base.last_password_change);
58 resp->data.auth.info3.pass_can_change_time =
59 nt_time_to_unix(info3->base.allow_password_change);
60 resp->data.auth.info3.pass_must_change_time =
61 nt_time_to_unix(info3->base.force_password_change);
63 resp->data.auth.info3.logon_count = info3->base.logon_count;
64 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
66 resp->data.auth.info3.user_rid = info3->base.rid;
67 resp->data.auth.info3.group_rid = info3->base.primary_gid;
68 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
70 resp->data.auth.info3.num_groups = info3->base.groups.count;
71 resp->data.auth.info3.user_flgs = info3->base.user_flags;
73 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
74 resp->data.auth.info3.num_other_sids = info3->sidcount;
76 fstrcpy(resp->data.auth.info3.user_name,
77 info3->base.account_name.string);
78 fstrcpy(resp->data.auth.info3.full_name,
79 info3->base.full_name.string);
80 fstrcpy(resp->data.auth.info3.logon_script,
81 info3->base.logon_script.string);
82 fstrcpy(resp->data.auth.info3.profile_path,
83 info3->base.profile_path.string);
84 fstrcpy(resp->data.auth.info3.home_dir,
85 info3->base.home_directory.string);
86 fstrcpy(resp->data.auth.info3.dir_drive,
87 info3->base.home_drive.string);
89 fstrcpy(resp->data.auth.info3.logon_srv,
90 info3->base.logon_server.string);
91 fstrcpy(resp->data.auth.info3.logon_dom,
92 info3->base.domain.string);
94 ex = talloc_strdup(mem_ctx, "");
95 NT_STATUS_HAVE_NO_MEMORY(ex);
97 for (i=0; i < info3->base.groups.count; i++) {
98 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
99 info3->base.groups.rids[i].rid,
100 info3->base.groups.rids[i].attributes);
101 NT_STATUS_HAVE_NO_MEMORY(ex);
104 for (i=0; i < info3->sidcount; i++) {
105 char *sid;
107 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
108 NT_STATUS_HAVE_NO_MEMORY(sid);
110 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
111 sid,
112 info3->sids[i].attributes);
113 NT_STATUS_HAVE_NO_MEMORY(ex);
115 talloc_free(sid);
118 resp->extra_data.data = ex;
119 resp->length += talloc_get_size(ex);
121 return NT_STATUS_OK;
124 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
125 struct winbindd_response *resp,
126 struct netr_SamInfo3 *info3)
128 DATA_BLOB blob;
129 enum ndr_err_code ndr_err;
131 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
132 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
133 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
134 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
135 return ndr_map_error2ntstatus(ndr_err);
138 resp->extra_data.data = blob.data;
139 resp->length += blob.length;
141 return NT_STATUS_OK;
144 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
145 struct winbindd_response *resp,
146 const struct netr_SamInfo3 *info3,
147 const char *name_domain,
148 const char *name_user)
150 /* We've been asked to return the unix username, per
151 'winbind use default domain' settings and the like */
153 const char *nt_username, *nt_domain;
155 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
156 if (!nt_domain) {
157 /* If the server didn't give us one, just use the one
158 * we sent them */
159 nt_domain = name_domain;
162 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
163 if (!nt_username) {
164 /* If the server didn't give us one, just use the one
165 * we sent them */
166 nt_username = name_user;
169 fill_domain_username(resp->data.auth.unix_username,
170 nt_domain, nt_username, true);
172 DEBUG(5, ("Setting unix username to [%s]\n",
173 resp->data.auth.unix_username));
175 return NT_STATUS_OK;
178 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
179 struct winbindd_response *resp,
180 const struct netr_SamInfo3 *info3,
181 const char *name_domain,
182 const char *name_user)
184 char *afsname = NULL;
185 char *cell;
186 char *token;
188 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
189 if (afsname == NULL) {
190 return NT_STATUS_NO_MEMORY;
193 afsname = talloc_string_sub(mem_ctx,
194 lp_afs_username_map(),
195 "%D", name_domain);
196 afsname = talloc_string_sub(mem_ctx, afsname,
197 "%u", name_user);
198 afsname = talloc_string_sub(mem_ctx, afsname,
199 "%U", name_user);
202 struct dom_sid user_sid;
203 fstring sidstr;
205 sid_compose(&user_sid, info3->base.domain_sid,
206 info3->base.rid);
207 sid_to_fstring(sidstr, &user_sid);
208 afsname = talloc_string_sub(mem_ctx, afsname,
209 "%s", sidstr);
212 if (afsname == NULL) {
213 return NT_STATUS_NO_MEMORY;
216 strlower_m(afsname);
218 DEBUG(10, ("Generating token for user %s\n", afsname));
220 cell = strchr(afsname, '@');
222 if (cell == NULL) {
223 return NT_STATUS_NO_MEMORY;
226 *cell = '\0';
227 cell += 1;
229 token = afs_createtoken_str(afsname, cell);
230 if (token == NULL) {
231 return NT_STATUS_OK;
233 resp->extra_data.data = talloc_strdup(mem_ctx, token);
234 if (resp->extra_data.data == NULL) {
235 return NT_STATUS_NO_MEMORY;
237 resp->length += strlen((const char *)resp->extra_data.data)+1;
239 return NT_STATUS_OK;
242 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
243 const char *group_sid)
245 * Check whether a user belongs to a group or list of groups.
247 * @param mem_ctx talloc memory context.
248 * @param info3 user information, including group membership info.
249 * @param group_sid One or more groups , separated by commas.
251 * @return NT_STATUS_OK on success,
252 * NT_STATUS_LOGON_FAILURE if the user does not belong,
253 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
256 struct dom_sid *require_membership_of_sid;
257 uint32_t num_require_membership_of_sid;
258 char *req_sid;
259 const char *p;
260 struct dom_sid sid;
261 size_t i;
262 struct security_token *token;
263 TALLOC_CTX *frame = talloc_stackframe();
264 NTSTATUS status;
266 /* Parse the 'required group' SID */
268 if (!group_sid || !group_sid[0]) {
269 /* NO sid supplied, all users may access */
270 return NT_STATUS_OK;
273 token = talloc_zero(talloc_tos(), struct security_token);
274 if (token == NULL) {
275 DEBUG(0, ("talloc failed\n"));
276 TALLOC_FREE(frame);
277 return NT_STATUS_NO_MEMORY;
280 num_require_membership_of_sid = 0;
281 require_membership_of_sid = NULL;
283 p = group_sid;
285 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
286 if (!string_to_sid(&sid, req_sid)) {
287 DEBUG(0, ("check_info3_in_group: could not parse %s "
288 "as a SID!", req_sid));
289 TALLOC_FREE(frame);
290 return NT_STATUS_INVALID_PARAMETER;
293 status = add_sid_to_array(talloc_tos(), &sid,
294 &require_membership_of_sid,
295 &num_require_membership_of_sid);
296 if (!NT_STATUS_IS_OK(status)) {
297 DEBUG(0, ("add_sid_to_array failed\n"));
298 TALLOC_FREE(frame);
299 return status;
303 status = sid_array_from_info3(talloc_tos(), info3,
304 &token->sids,
305 &token->num_sids,
306 true, false);
307 if (!NT_STATUS_IS_OK(status)) {
308 TALLOC_FREE(frame);
309 return status;
312 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
313 token))
314 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
315 token))) {
316 DEBUG(3, ("could not add aliases: %s\n",
317 nt_errstr(status)));
318 TALLOC_FREE(frame);
319 return status;
322 security_token_debug(DBGC_CLASS, 10, token);
324 for (i=0; i<num_require_membership_of_sid; i++) {
325 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
326 &require_membership_of_sid[i])));
327 if (nt_token_check_sid(&require_membership_of_sid[i],
328 token)) {
329 DEBUG(10, ("Access ok\n"));
330 TALLOC_FREE(frame);
331 return NT_STATUS_OK;
335 /* Do not distinguish this error from a wrong username/pw */
337 TALLOC_FREE(frame);
338 return NT_STATUS_LOGON_FAILURE;
341 struct winbindd_domain *find_auth_domain(uint8_t flags,
342 const char *domain_name)
344 struct winbindd_domain *domain;
346 if (IS_DC) {
347 domain = find_domain_from_name_noinit(domain_name);
348 if (domain == NULL) {
349 DEBUG(3, ("Authentication for domain [%s] refused "
350 "as it is not a trusted domain\n",
351 domain_name));
353 return domain;
356 if (strequal(domain_name, get_global_sam_name())) {
357 return find_domain_from_name_noinit(domain_name);
360 /* we can auth against trusted domains */
361 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
362 domain = find_domain_from_name_noinit(domain_name);
363 if (domain == NULL) {
364 DEBUG(3, ("Authentication for domain [%s] skipped "
365 "as it is not a trusted domain\n",
366 domain_name));
367 } else {
368 return domain;
372 return find_our_domain();
375 static void fill_in_password_policy(struct winbindd_response *r,
376 const struct samr_DomInfo1 *p)
378 r->data.auth.policy.min_length_password =
379 p->min_password_length;
380 r->data.auth.policy.password_history =
381 p->password_history_length;
382 r->data.auth.policy.password_properties =
383 p->password_properties;
384 r->data.auth.policy.expire =
385 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
386 r->data.auth.policy.min_passwordage =
387 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
390 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
391 struct winbindd_cli_state *state)
393 struct winbindd_methods *methods;
394 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
395 struct samr_DomInfo1 password_policy;
397 if ( !winbindd_can_contact_domain( domain ) ) {
398 DEBUG(5,("fillup_password_policy: No inbound trust to "
399 "contact domain %s\n", domain->name));
400 return NT_STATUS_NOT_SUPPORTED;
403 methods = domain->methods;
405 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
406 if (NT_STATUS_IS_ERR(status)) {
407 return status;
410 fill_in_password_policy(state->response, &password_policy);
412 return NT_STATUS_OK;
415 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
416 TALLOC_CTX *mem_ctx,
417 uint16 *lockout_threshold)
419 struct winbindd_methods *methods;
420 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
421 struct samr_DomInfo12 lockout_policy;
423 *lockout_threshold = 0;
425 methods = domain->methods;
427 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
428 if (NT_STATUS_IS_ERR(status)) {
429 return status;
432 *lockout_threshold = lockout_policy.lockout_threshold;
434 return NT_STATUS_OK;
437 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
438 TALLOC_CTX *mem_ctx,
439 uint32 *password_properties)
441 struct winbindd_methods *methods;
442 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
443 struct samr_DomInfo1 password_policy;
445 *password_properties = 0;
447 methods = domain->methods;
449 status = methods->password_policy(domain, mem_ctx, &password_policy);
450 if (NT_STATUS_IS_ERR(status)) {
451 return status;
454 *password_properties = password_policy.password_properties;
456 return NT_STATUS_OK;
459 #ifdef HAVE_KRB5
461 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
462 const char *type,
463 uid_t uid,
464 const char **user_ccache_file)
466 /* accept FILE and WRFILE as krb5_cc_type from the client and then
467 * build the full ccname string based on the user's uid here -
468 * Guenther*/
470 const char *gen_cc = NULL;
472 if (uid != -1) {
473 if (strequal(type, "FILE")) {
474 gen_cc = talloc_asprintf(
475 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
477 if (strequal(type, "WRFILE")) {
478 gen_cc = talloc_asprintf(
479 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
483 *user_ccache_file = gen_cc;
485 if (gen_cc == NULL) {
486 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
488 if (gen_cc == NULL) {
489 DEBUG(0,("out of memory\n"));
490 return NULL;
493 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
494 (*user_ccache_file == NULL) ? " (internal)":""));
496 return gen_cc;
499 #endif
501 uid_t get_uid_from_request(struct winbindd_request *request)
503 uid_t uid;
505 uid = request->data.auth.uid;
507 if (uid < 0) {
508 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
509 return -1;
511 return uid;
514 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
516 return get_uid_from_request(state->request);
519 /**********************************************************************
520 Authenticate a user with a clear text password using Kerberos and fill up
521 ccache if required
522 **********************************************************************/
524 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
525 struct winbindd_cli_state *state,
526 struct netr_SamInfo3 **info3)
528 #ifdef HAVE_KRB5
529 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
530 krb5_error_code krb5_ret;
531 const char *cc = NULL;
532 const char *principal_s = NULL;
533 const char *service = NULL;
534 char *realm = NULL;
535 fstring name_domain, name_user;
536 time_t ticket_lifetime = 0;
537 time_t renewal_until = 0;
538 uid_t uid = -1;
539 ADS_STRUCT *ads;
540 time_t time_offset = 0;
541 const char *user_ccache_file;
542 struct PAC_LOGON_INFO *logon_info = NULL;
544 *info3 = NULL;
546 /* 1st step:
547 * prepare a krb5_cc_cache string for the user */
549 uid = get_uid_from_state(state);
550 if (uid == -1) {
551 DEBUG(0,("no valid uid\n"));
554 cc = generate_krb5_ccache(state->mem_ctx,
555 state->request->data.auth.krb5_cc_type,
556 state->request->data.auth.uid,
557 &user_ccache_file);
558 if (cc == NULL) {
559 return NT_STATUS_NO_MEMORY;
563 /* 2nd step:
564 * get kerberos properties */
566 if (domain->private_data) {
567 ads = (ADS_STRUCT *)domain->private_data;
568 time_offset = ads->auth.time_offset;
572 /* 3rd step:
573 * do kerberos auth and setup ccache as the user */
575 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
577 realm = domain->alt_name;
578 strupper_m(realm);
580 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
581 if (principal_s == NULL) {
582 return NT_STATUS_NO_MEMORY;
585 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
586 if (service == NULL) {
587 return NT_STATUS_NO_MEMORY;
590 /* if this is a user ccache, we need to act as the user to let the krb5
591 * library handle the chown, etc. */
593 /************************ ENTERING NON-ROOT **********************/
595 if (user_ccache_file != NULL) {
596 set_effective_uid(uid);
597 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
600 result = kerberos_return_pac(state->mem_ctx,
601 principal_s,
602 state->request->data.auth.pass,
603 time_offset,
604 &ticket_lifetime,
605 &renewal_until,
607 true,
608 true,
609 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
610 NULL,
611 &logon_info);
612 if (user_ccache_file != NULL) {
613 gain_root_privilege();
616 /************************ RETURNED TO ROOT **********************/
618 if (!NT_STATUS_IS_OK(result)) {
619 goto failed;
622 *info3 = &logon_info->info3;
624 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
625 principal_s));
627 /* if we had a user's ccache then return that string for the pam
628 * environment */
630 if (user_ccache_file != NULL) {
632 fstrcpy(state->response->data.auth.krb5ccname,
633 user_ccache_file);
635 result = add_ccache_to_list(principal_s,
637 service,
638 state->request->data.auth.user,
639 realm,
640 uid,
641 time(NULL),
642 ticket_lifetime,
643 renewal_until,
644 false);
646 if (!NT_STATUS_IS_OK(result)) {
647 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
648 nt_errstr(result)));
650 } else {
652 /* need to delete the memory cred cache, it is not used anymore */
654 krb5_ret = ads_kdestroy(cc);
655 if (krb5_ret) {
656 DEBUG(3,("winbindd_raw_kerberos_login: "
657 "could not destroy krb5 credential cache: "
658 "%s\n", error_message(krb5_ret)));
663 return NT_STATUS_OK;
665 failed:
667 /* we could have created a new credential cache with a valid tgt in it
668 * but we werent able to get or verify the service ticket for this
669 * local host and therefor didn't get the PAC, we need to remove that
670 * cache entirely now */
672 krb5_ret = ads_kdestroy(cc);
673 if (krb5_ret) {
674 DEBUG(3,("winbindd_raw_kerberos_login: "
675 "could not destroy krb5 credential cache: "
676 "%s\n", error_message(krb5_ret)));
679 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
680 DEBUG(3,("winbindd_raw_kerberos_login: "
681 "could not remove ccache for user %s\n",
682 state->request->data.auth.user));
685 return result;
686 #else
687 return NT_STATUS_NOT_SUPPORTED;
688 #endif /* HAVE_KRB5 */
691 /****************************************************************
692 ****************************************************************/
694 bool check_request_flags(uint32_t flags)
696 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
697 WBFLAG_PAM_INFO3_TEXT |
698 WBFLAG_PAM_INFO3_NDR;
700 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
701 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
702 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
703 !(flags & flags_edata) ) {
704 return true;
707 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
708 flags));
710 return false;
713 /****************************************************************
714 ****************************************************************/
716 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
717 struct winbindd_response *resp,
718 uint32_t request_flags,
719 struct netr_SamInfo3 *info3,
720 const char *name_domain,
721 const char *name_user)
723 NTSTATUS result;
725 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
726 memcpy(resp->data.auth.user_session_key,
727 info3->base.key.key,
728 sizeof(resp->data.auth.user_session_key)
729 /* 16 */);
732 if (request_flags & WBFLAG_PAM_LMKEY) {
733 memcpy(resp->data.auth.first_8_lm_hash,
734 info3->base.LMSessKey.key,
735 sizeof(resp->data.auth.first_8_lm_hash)
736 /* 8 */);
739 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
740 result = append_unix_username(mem_ctx, resp,
741 info3, name_domain, name_user);
742 if (!NT_STATUS_IS_OK(result)) {
743 DEBUG(10,("Failed to append Unix Username: %s\n",
744 nt_errstr(result)));
745 return result;
749 /* currently, anything from here on potentially overwrites extra_data. */
751 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
752 result = append_info3_as_ndr(mem_ctx, resp, info3);
753 if (!NT_STATUS_IS_OK(result)) {
754 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
755 nt_errstr(result)));
756 return result;
760 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
761 result = append_info3_as_txt(mem_ctx, resp, info3);
762 if (!NT_STATUS_IS_OK(result)) {
763 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
764 nt_errstr(result)));
765 return result;
769 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
770 result = append_afs_token(mem_ctx, resp,
771 info3, name_domain, name_user);
772 if (!NT_STATUS_IS_OK(result)) {
773 DEBUG(10,("Failed to append AFS token: %s\n",
774 nt_errstr(result)));
775 return result;
779 return NT_STATUS_OK;
782 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
783 struct winbindd_cli_state *state,
784 struct netr_SamInfo3 **info3)
786 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
787 uint16 max_allowed_bad_attempts;
788 fstring name_domain, name_user;
789 struct dom_sid sid;
790 enum lsa_SidType type;
791 uchar new_nt_pass[NT_HASH_LEN];
792 const uint8 *cached_nt_pass;
793 const uint8 *cached_salt;
794 struct netr_SamInfo3 *my_info3;
795 time_t kickoff_time, must_change_time;
796 bool password_good = false;
797 #ifdef HAVE_KRB5
798 struct winbindd_tdc_domain *tdc_domain = NULL;
799 #endif
801 *info3 = NULL;
803 ZERO_STRUCTP(info3);
805 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
807 /* Parse domain and username */
809 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
812 if (!lookup_cached_name(name_domain,
813 name_user,
814 &sid,
815 &type)) {
816 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
817 return NT_STATUS_NO_SUCH_USER;
820 if (type != SID_NAME_USER) {
821 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
822 return NT_STATUS_LOGON_FAILURE;
825 result = winbindd_get_creds(domain,
826 state->mem_ctx,
827 &sid,
828 &my_info3,
829 &cached_nt_pass,
830 &cached_salt);
831 if (!NT_STATUS_IS_OK(result)) {
832 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
833 return result;
836 *info3 = my_info3;
838 E_md4hash(state->request->data.auth.pass, new_nt_pass);
840 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
841 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
842 if (cached_salt) {
843 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
846 if (cached_salt) {
847 /* In this case we didn't store the nt_hash itself,
848 but the MD5 combination of salt + nt_hash. */
849 uchar salted_hash[NT_HASH_LEN];
850 E_md5hash(cached_salt, new_nt_pass, salted_hash);
852 password_good = (memcmp(cached_nt_pass, salted_hash,
853 NT_HASH_LEN) == 0);
854 } else {
855 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
856 password_good = (memcmp(cached_nt_pass, new_nt_pass,
857 NT_HASH_LEN) == 0);
860 if (password_good) {
862 /* User *DOES* know the password, update logon_time and reset
863 * bad_pw_count */
865 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
867 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
868 return NT_STATUS_ACCOUNT_LOCKED_OUT;
871 if (my_info3->base.acct_flags & ACB_DISABLED) {
872 return NT_STATUS_ACCOUNT_DISABLED;
875 if (my_info3->base.acct_flags & ACB_WSTRUST) {
876 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
879 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
880 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
883 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
884 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
887 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
888 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
889 my_info3->base.acct_flags));
890 return NT_STATUS_LOGON_FAILURE;
893 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
894 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
895 return NT_STATUS_ACCOUNT_EXPIRED;
898 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
899 if (must_change_time != 0 && must_change_time < time(NULL)) {
900 /* we allow grace logons when the password has expired */
901 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
902 /* return NT_STATUS_PASSWORD_EXPIRED; */
903 goto success;
906 #ifdef HAVE_KRB5
907 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
908 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
909 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
910 /* used to cope with the case winbindd starting without network. */
911 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
913 uid_t uid = -1;
914 const char *cc = NULL;
915 char *realm = NULL;
916 const char *principal_s = NULL;
917 const char *service = NULL;
918 const char *user_ccache_file;
920 uid = get_uid_from_state(state);
921 if (uid == -1) {
922 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
923 return NT_STATUS_INVALID_PARAMETER;
926 cc = generate_krb5_ccache(state->mem_ctx,
927 state->request->data.auth.krb5_cc_type,
928 state->request->data.auth.uid,
929 &user_ccache_file);
930 if (cc == NULL) {
931 return NT_STATUS_NO_MEMORY;
934 realm = domain->alt_name;
935 strupper_m(realm);
937 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
938 if (principal_s == NULL) {
939 return NT_STATUS_NO_MEMORY;
942 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
943 if (service == NULL) {
944 return NT_STATUS_NO_MEMORY;
947 if (user_ccache_file != NULL) {
949 fstrcpy(state->response->data.auth.krb5ccname,
950 user_ccache_file);
952 result = add_ccache_to_list(principal_s,
954 service,
955 state->request->data.auth.user,
956 domain->alt_name,
957 uid,
958 time(NULL),
959 time(NULL) + lp_winbind_cache_time(),
960 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
961 true);
963 if (!NT_STATUS_IS_OK(result)) {
964 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
965 "to add ccache to list: %s\n",
966 nt_errstr(result)));
970 #endif /* HAVE_KRB5 */
971 success:
972 /* FIXME: we possibly should handle logon hours as well (does xp when
973 * offline?) see auth/auth_sam.c:sam_account_ok for details */
975 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
976 my_info3->base.bad_password_count = 0;
978 result = winbindd_update_creds_by_info3(domain,
979 state->request->data.auth.user,
980 state->request->data.auth.pass,
981 my_info3);
982 if (!NT_STATUS_IS_OK(result)) {
983 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
984 nt_errstr(result)));
985 return result;
988 return NT_STATUS_OK;
992 /* User does *NOT* know the correct password, modify info3 accordingly */
994 /* failure of this is not critical */
995 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
996 if (!NT_STATUS_IS_OK(result)) {
997 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
998 "Won't be able to honour account lockout policies\n"));
1001 /* increase counter */
1002 my_info3->base.bad_password_count++;
1004 if (max_allowed_bad_attempts == 0) {
1005 goto failed;
1008 /* lockout user */
1009 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1011 uint32 password_properties;
1013 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1014 if (!NT_STATUS_IS_OK(result)) {
1015 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1018 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1019 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1020 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1024 failed:
1025 result = winbindd_update_creds_by_info3(domain,
1026 state->request->data.auth.user,
1027 NULL,
1028 my_info3);
1030 if (!NT_STATUS_IS_OK(result)) {
1031 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1032 nt_errstr(result)));
1035 return NT_STATUS_LOGON_FAILURE;
1038 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1039 struct winbindd_cli_state *state,
1040 struct netr_SamInfo3 **info3)
1042 struct winbindd_domain *contact_domain;
1043 fstring name_domain, name_user;
1044 NTSTATUS result;
1046 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1048 /* Parse domain and username */
1050 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1052 /* what domain should we contact? */
1054 if ( IS_DC ) {
1055 if (!(contact_domain = find_domain_from_name(name_domain))) {
1056 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1057 state->request->data.auth.user, name_domain, name_user, name_domain));
1058 result = NT_STATUS_NO_SUCH_USER;
1059 goto done;
1062 } else {
1063 if (is_myname(name_domain)) {
1064 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1065 result = NT_STATUS_NO_SUCH_USER;
1066 goto done;
1069 contact_domain = find_domain_from_name(name_domain);
1070 if (contact_domain == NULL) {
1071 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1072 state->request->data.auth.user, name_domain, name_user, name_domain));
1074 contact_domain = find_our_domain();
1078 if (contact_domain->initialized &&
1079 contact_domain->active_directory) {
1080 goto try_login;
1083 if (!contact_domain->initialized) {
1084 init_dc_connection(contact_domain);
1087 if (!contact_domain->active_directory) {
1088 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1089 return NT_STATUS_INVALID_LOGON_TYPE;
1091 try_login:
1092 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1093 done:
1094 return result;
1097 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1098 const char *domain, const char *user,
1099 const DATA_BLOB *challenge,
1100 const DATA_BLOB *lm_resp,
1101 const DATA_BLOB *nt_resp,
1102 struct netr_SamInfo3 **pinfo3)
1104 struct auth_usersupplied_info *user_info = NULL;
1105 NTSTATUS status;
1107 status = make_user_info(&user_info, user, user, domain, domain,
1108 global_myname(), lm_resp, nt_resp, NULL, NULL,
1109 NULL, AUTH_PASSWORD_RESPONSE);
1110 if (!NT_STATUS_IS_OK(status)) {
1111 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1112 return status;
1115 /* We don't want any more mapping of the username */
1116 user_info->mapped_state = True;
1118 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1119 pinfo3);
1120 free_user_info(&user_info);
1121 DEBUG(10, ("Authenticated user %s\\%s successfully\n", domain, user));
1122 return NT_STATUS_OK;
1125 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1126 TALLOC_CTX *mem_ctx,
1127 uint32 logon_parameters,
1128 const char *server,
1129 const char *username,
1130 const char *domain,
1131 const char *workstation,
1132 const uint8 chal[8],
1133 DATA_BLOB lm_response,
1134 DATA_BLOB nt_response,
1135 struct netr_SamInfo3 **info3);
1137 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1138 struct winbindd_cli_state *state,
1139 struct netr_SamInfo3 **info3)
1142 struct rpc_pipe_client *netlogon_pipe;
1143 uchar chal[8];
1144 DATA_BLOB lm_resp;
1145 DATA_BLOB nt_resp;
1146 int attempts = 0;
1147 unsigned char local_lm_response[24];
1148 unsigned char local_nt_response[24];
1149 fstring name_domain, name_user;
1150 bool retry;
1151 NTSTATUS result;
1152 struct netr_SamInfo3 *my_info3 = NULL;
1154 *info3 = NULL;
1156 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1158 /* Parse domain and username */
1160 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1162 /* do password magic */
1164 generate_random_buffer(chal, sizeof(chal));
1166 if (lp_client_ntlmv2_auth()) {
1167 DATA_BLOB server_chal;
1168 DATA_BLOB names_blob;
1169 DATA_BLOB nt_response;
1170 DATA_BLOB lm_response;
1171 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1173 /* note that the 'workgroup' here is a best guess - we don't know
1174 the server's domain at this point. The 'server name' is also
1175 dodgy...
1177 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1179 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1180 state->request->data.auth.pass,
1181 &server_chal,
1182 &names_blob,
1183 &lm_response, &nt_response, NULL, NULL)) {
1184 data_blob_free(&names_blob);
1185 data_blob_free(&server_chal);
1186 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1187 result = NT_STATUS_NO_MEMORY;
1188 goto done;
1190 data_blob_free(&names_blob);
1191 data_blob_free(&server_chal);
1192 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1193 lm_response.length);
1194 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1195 nt_response.length);
1196 data_blob_free(&lm_response);
1197 data_blob_free(&nt_response);
1199 } else {
1200 if (lp_client_lanman_auth()
1201 && SMBencrypt(state->request->data.auth.pass,
1202 chal,
1203 local_lm_response)) {
1204 lm_resp = data_blob_talloc(state->mem_ctx,
1205 local_lm_response,
1206 sizeof(local_lm_response));
1207 } else {
1208 lm_resp = data_blob_null;
1210 SMBNTencrypt(state->request->data.auth.pass,
1211 chal,
1212 local_nt_response);
1214 nt_resp = data_blob_talloc(state->mem_ctx,
1215 local_nt_response,
1216 sizeof(local_nt_response));
1219 if (strequal(name_domain, get_global_sam_name())) {
1220 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1222 result = winbindd_dual_auth_passdb(
1223 state->mem_ctx, name_domain, name_user,
1224 &chal_blob, &lm_resp, &nt_resp, info3);
1225 goto done;
1228 /* check authentication loop */
1230 do {
1231 netlogon_fn_t logon_fn;
1233 ZERO_STRUCTP(my_info3);
1234 retry = false;
1236 result = cm_connect_netlogon(domain, &netlogon_pipe);
1238 if (!NT_STATUS_IS_OK(result)) {
1239 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1240 goto done;
1243 /* It is really important to try SamLogonEx here,
1244 * because in a clustered environment, we want to use
1245 * one machine account from multiple physical
1246 * computers.
1248 * With a normal SamLogon call, we must keep the
1249 * credentials chain updated and intact between all
1250 * users of the machine account (which would imply
1251 * cross-node communication for every NTLM logon).
1253 * (The credentials chain is not per NETLOGON pipe
1254 * connection, but globally on the server/client pair
1255 * by machine name).
1257 * When using SamLogonEx, the credentials are not
1258 * supplied, but the session key is implied by the
1259 * wrapping SamLogon context.
1261 * -- abartlet 21 April 2008
1264 logon_fn = domain->can_do_samlogon_ex
1265 ? rpccli_netlogon_sam_network_logon_ex
1266 : rpccli_netlogon_sam_network_logon;
1268 result = logon_fn(netlogon_pipe,
1269 state->mem_ctx,
1271 domain->dcname, /* server name */
1272 name_user, /* user name */
1273 name_domain, /* target domain */
1274 global_myname(), /* workstation */
1275 chal,
1276 lm_resp,
1277 nt_resp,
1278 &my_info3);
1279 attempts += 1;
1281 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1282 && domain->can_do_samlogon_ex) {
1283 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1284 "retrying with NetSamLogon\n"));
1285 domain->can_do_samlogon_ex = false;
1286 continue;
1289 /* We have to try a second time as cm_connect_netlogon
1290 might not yet have noticed that the DC has killed
1291 our connection. */
1293 if (!rpccli_is_connected(netlogon_pipe)) {
1294 continue;
1297 /* if we get access denied, a possible cause was that we had
1298 and open connection to the DC, but someone changed our
1299 machine account password out from underneath us using 'net
1300 rpc changetrustpw' */
1302 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1303 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1304 "ACCESS_DENIED. Maybe the trust account "
1305 "password was changed and we didn't know it. "
1306 "Killing connections to domain %s\n",
1307 name_domain));
1308 invalidate_cm_connection(&domain->conn);
1309 retry = true;
1312 } while ( (attempts < 2) && retry );
1314 /* handle the case where a NT4 DC does not fill in the acct_flags in
1315 * the samlogon reply info3. When accurate info3 is required by the
1316 * caller, we look up the account flags ourselve - gd */
1318 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1319 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1321 struct rpc_pipe_client *samr_pipe;
1322 struct policy_handle samr_domain_handle, user_pol;
1323 union samr_UserInfo *info = NULL;
1324 NTSTATUS status_tmp;
1325 uint32 acct_flags;
1327 status_tmp = cm_connect_sam(domain, state->mem_ctx,
1328 &samr_pipe, &samr_domain_handle);
1330 if (!NT_STATUS_IS_OK(status_tmp)) {
1331 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1332 nt_errstr(status_tmp)));
1333 goto done;
1336 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1337 &samr_domain_handle,
1338 MAXIMUM_ALLOWED_ACCESS,
1339 my_info3->base.rid,
1340 &user_pol);
1342 if (!NT_STATUS_IS_OK(status_tmp)) {
1343 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1344 nt_errstr(status_tmp)));
1345 goto done;
1348 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1349 &user_pol,
1351 &info);
1353 if (!NT_STATUS_IS_OK(status_tmp)) {
1354 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1355 nt_errstr(status_tmp)));
1356 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1357 goto done;
1360 acct_flags = info->info16.acct_flags;
1362 if (acct_flags == 0) {
1363 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1364 goto done;
1367 my_info3->base.acct_flags = acct_flags;
1369 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1371 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1374 *info3 = my_info3;
1375 done:
1376 return result;
1379 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1380 struct winbindd_cli_state *state)
1382 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1383 NTSTATUS krb5_result = NT_STATUS_OK;
1384 fstring name_domain, name_user;
1385 char *mapped_user;
1386 fstring domain_user;
1387 struct netr_SamInfo3 *info3 = NULL;
1388 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1390 /* Ensure null termination */
1391 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1393 /* Ensure null termination */
1394 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1396 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1397 state->request->data.auth.user));
1399 /* Parse domain and username */
1401 name_map_status = normalize_name_unmap(state->mem_ctx,
1402 state->request->data.auth.user,
1403 &mapped_user);
1405 /* If the name normalization didnt' actually do anything,
1406 just use the original name */
1408 if (!NT_STATUS_IS_OK(name_map_status) &&
1409 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1411 mapped_user = state->request->data.auth.user;
1414 parse_domain_user(mapped_user, name_domain, name_user);
1416 if ( mapped_user != state->request->data.auth.user ) {
1417 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1418 *lp_winbind_separator(),
1419 name_user );
1420 safe_strcpy( state->request->data.auth.user, domain_user,
1421 sizeof(state->request->data.auth.user)-1 );
1424 if (!domain->online) {
1425 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1426 if (domain->startup) {
1427 /* Logons are very important to users. If we're offline and
1428 we get a request within the first 30 seconds of startup,
1429 try very hard to find a DC and go online. */
1431 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1432 "request in startup mode.\n", domain->name ));
1434 winbindd_flush_negative_conn_cache(domain);
1435 result = init_dc_connection(domain);
1439 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1441 /* Check for Kerberos authentication */
1442 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1444 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1445 /* save for later */
1446 krb5_result = result;
1449 if (NT_STATUS_IS_OK(result)) {
1450 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1451 goto process_result;
1452 } else {
1453 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1456 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1457 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1458 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1459 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1460 set_domain_offline( domain );
1461 goto cached_logon;
1464 /* there are quite some NT_STATUS errors where there is no
1465 * point in retrying with a samlogon, we explictly have to take
1466 * care not to increase the bad logon counter on the DC */
1468 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1469 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1470 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1471 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1472 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1473 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1474 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1475 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1476 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1477 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1478 goto done;
1481 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1482 DEBUG(3,("falling back to samlogon\n"));
1483 goto sam_logon;
1484 } else {
1485 goto cached_logon;
1489 sam_logon:
1490 /* Check for Samlogon authentication */
1491 if (domain->online) {
1492 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1494 if (NT_STATUS_IS_OK(result)) {
1495 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1496 /* add the Krb5 err if we have one */
1497 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1498 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1500 goto process_result;
1503 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1504 nt_errstr(result)));
1506 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1507 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1510 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1511 set_domain_offline( domain );
1512 goto cached_logon;
1515 if (domain->online) {
1516 /* We're still online - fail. */
1517 goto done;
1521 cached_logon:
1522 /* Check for Cached logons */
1523 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1524 lp_winbind_offline_logon()) {
1526 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1528 if (NT_STATUS_IS_OK(result)) {
1529 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1530 goto process_result;
1531 } else {
1532 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1533 goto done;
1537 process_result:
1539 if (NT_STATUS_IS_OK(result)) {
1541 struct dom_sid user_sid;
1543 /* In all codepaths where result == NT_STATUS_OK info3 must have
1544 been initialized. */
1545 if (!info3) {
1546 result = NT_STATUS_INTERNAL_ERROR;
1547 goto done;
1550 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1551 netsamlogon_cache_store(name_user, info3);
1553 /* save name_to_sid info as early as possible (only if
1554 this is our primary domain so we don't invalidate
1555 the cache entry by storing the seq_num for the wrong
1556 domain). */
1557 if ( domain->primary ) {
1558 sid_compose(&user_sid, info3->base.domain_sid,
1559 info3->base.rid);
1560 cache_name2sid(domain, name_domain, name_user,
1561 SID_NAME_USER, &user_sid);
1564 /* Check if the user is in the right group */
1566 result = check_info3_in_group(
1567 info3,
1568 state->request->data.auth.require_membership_of_sid);
1569 if (!NT_STATUS_IS_OK(result)) {
1570 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1571 state->request->data.auth.user,
1572 state->request->data.auth.require_membership_of_sid));
1573 goto done;
1576 result = append_auth_data(state->mem_ctx, state->response,
1577 state->request->flags, info3,
1578 name_domain, name_user);
1579 if (!NT_STATUS_IS_OK(result)) {
1580 goto done;
1583 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1584 && lp_winbind_offline_logon()) {
1586 result = winbindd_store_creds(domain,
1587 state->request->data.auth.user,
1588 state->request->data.auth.pass,
1589 info3);
1592 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1593 struct winbindd_domain *our_domain = find_our_domain();
1595 /* This is not entirely correct I believe, but it is
1596 consistent. Only apply the password policy settings
1597 too warn users for our own domain. Cannot obtain these
1598 from trusted DCs all the time so don't do it at all.
1599 -- jerry */
1601 result = NT_STATUS_NOT_SUPPORTED;
1602 if (our_domain == domain ) {
1603 result = fillup_password_policy(our_domain, state);
1606 if (!NT_STATUS_IS_OK(result)
1607 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1609 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1610 domain->name, nt_errstr(result)));
1611 goto done;
1615 result = NT_STATUS_OK;
1618 done:
1619 /* give us a more useful (more correct?) error code */
1620 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1621 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1622 result = NT_STATUS_NO_LOGON_SERVERS;
1625 set_auth_errors(state->response, result);
1627 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1628 state->request->data.auth.user,
1629 state->response->data.auth.nt_status_string,
1630 state->response->data.auth.pam_error));
1632 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1635 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1636 struct winbindd_cli_state *state)
1638 NTSTATUS result;
1639 struct netr_SamInfo3 *info3 = NULL;
1640 struct rpc_pipe_client *netlogon_pipe;
1641 const char *name_user = NULL;
1642 const char *name_domain = NULL;
1643 const char *workstation;
1644 int attempts = 0;
1645 bool retry;
1647 DATA_BLOB lm_resp, nt_resp;
1649 /* This is child-only, so no check for privileged access is needed
1650 anymore */
1652 /* Ensure null termination */
1653 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1654 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1656 name_user = state->request->data.auth_crap.user;
1657 name_domain = state->request->data.auth_crap.domain;
1658 workstation = state->request->data.auth_crap.workstation;
1660 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1661 name_domain, name_user));
1663 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1664 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1665 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1666 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1667 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1668 state->request->data.auth_crap.lm_resp_len,
1669 state->request->data.auth_crap.nt_resp_len));
1670 result = NT_STATUS_INVALID_PARAMETER;
1671 goto done;
1675 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1676 state->request->data.auth_crap.lm_resp_len);
1678 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1679 nt_resp = data_blob_talloc(state->mem_ctx,
1680 state->request->extra_data.data,
1681 state->request->data.auth_crap.nt_resp_len);
1682 } else {
1683 nt_resp = data_blob_talloc(state->mem_ctx,
1684 state->request->data.auth_crap.nt_resp,
1685 state->request->data.auth_crap.nt_resp_len);
1688 if (strequal(name_domain, get_global_sam_name())) {
1689 DATA_BLOB chal_blob = data_blob_const(
1690 state->request->data.auth_crap.chal,
1691 sizeof(state->request->data.auth_crap.chal));
1693 result = winbindd_dual_auth_passdb(
1694 state->mem_ctx, name_domain, name_user,
1695 &chal_blob, &lm_resp, &nt_resp, &info3);
1696 goto process_result;
1699 do {
1700 netlogon_fn_t logon_fn;
1702 retry = false;
1704 netlogon_pipe = NULL;
1705 result = cm_connect_netlogon(domain, &netlogon_pipe);
1707 if (!NT_STATUS_IS_OK(result)) {
1708 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1709 nt_errstr(result)));
1710 goto done;
1713 logon_fn = domain->can_do_samlogon_ex
1714 ? rpccli_netlogon_sam_network_logon_ex
1715 : rpccli_netlogon_sam_network_logon;
1717 result = logon_fn(netlogon_pipe,
1718 state->mem_ctx,
1719 state->request->data.auth_crap.logon_parameters,
1720 domain->dcname,
1721 name_user,
1722 name_domain,
1723 /* Bug #3248 - found by Stefan Burkei. */
1724 workstation, /* We carefully set this above so use it... */
1725 state->request->data.auth_crap.chal,
1726 lm_resp,
1727 nt_resp,
1728 &info3);
1730 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1731 && domain->can_do_samlogon_ex) {
1732 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1733 "retrying with NetSamLogon\n"));
1734 domain->can_do_samlogon_ex = false;
1735 continue;
1738 attempts += 1;
1740 /* We have to try a second time as cm_connect_netlogon
1741 might not yet have noticed that the DC has killed
1742 our connection. */
1744 if (!rpccli_is_connected(netlogon_pipe)) {
1745 continue;
1748 /* if we get access denied, a possible cause was that we had and open
1749 connection to the DC, but someone changed our machine account password
1750 out from underneath us using 'net rpc changetrustpw' */
1752 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1753 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1754 "ACCESS_DENIED. Maybe the trust account "
1755 "password was changed and we didn't know it. "
1756 "Killing connections to domain %s\n",
1757 name_domain));
1758 invalidate_cm_connection(&domain->conn);
1759 retry = true;
1762 } while ( (attempts < 2) && retry );
1764 process_result:
1766 if (NT_STATUS_IS_OK(result)) {
1768 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1769 netsamlogon_cache_store(name_user, info3);
1771 /* Check if the user is in the right group */
1773 result = check_info3_in_group(
1774 info3,
1775 state->request->data.auth_crap.require_membership_of_sid);
1776 if (!NT_STATUS_IS_OK(result)) {
1777 DEBUG(3, ("User %s is not in the required group (%s), so "
1778 "crap authentication is rejected\n",
1779 state->request->data.auth_crap.user,
1780 state->request->data.auth_crap.require_membership_of_sid));
1781 goto done;
1784 result = append_auth_data(state->mem_ctx, state->response,
1785 state->request->flags, info3,
1786 name_domain, name_user);
1787 if (!NT_STATUS_IS_OK(result)) {
1788 goto done;
1792 done:
1794 /* give us a more useful (more correct?) error code */
1795 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1796 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1797 result = NT_STATUS_NO_LOGON_SERVERS;
1800 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1801 result = nt_status_squash(result);
1804 set_auth_errors(state->response, result);
1806 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1807 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1808 name_domain,
1809 name_user,
1810 state->response->data.auth.nt_status_string,
1811 state->response->data.auth.pam_error));
1813 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1816 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1817 struct winbindd_cli_state *state)
1819 char *oldpass;
1820 char *newpass = NULL;
1821 struct policy_handle dom_pol;
1822 struct rpc_pipe_client *cli = NULL;
1823 bool got_info = false;
1824 struct samr_DomInfo1 *info = NULL;
1825 struct userPwdChangeFailureInformation *reject = NULL;
1826 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1827 fstring domain, user;
1829 ZERO_STRUCT(dom_pol);
1831 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1832 state->request->data.auth.user));
1834 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1835 goto done;
1838 /* Change password */
1840 oldpass = state->request->data.chauthtok.oldpass;
1841 newpass = state->request->data.chauthtok.newpass;
1843 /* Initialize reject reason */
1844 state->response->data.auth.reject_reason = Undefined;
1846 /* Get sam handle */
1848 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1849 &dom_pol);
1850 if (!NT_STATUS_IS_OK(result)) {
1851 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1852 goto done;
1855 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1856 user,
1857 newpass,
1858 oldpass,
1859 &info,
1860 &reject);
1862 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1864 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1866 fill_in_password_policy(state->response, info);
1868 state->response->data.auth.reject_reason =
1869 reject->extendedFailureReason;
1871 got_info = true;
1874 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1875 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1876 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1877 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1879 /* only fallback when the chgpasswd_user3 call is not supported */
1880 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1881 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1882 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1883 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1885 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1886 nt_errstr(result)));
1888 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1890 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1891 Map to the same status code as Windows 2003. */
1893 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1894 result = NT_STATUS_PASSWORD_RESTRICTION;
1898 done:
1900 if (NT_STATUS_IS_OK(result)
1901 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1902 && lp_winbind_offline_logon()) {
1903 result = winbindd_update_creds_by_name(contact_domain, user,
1904 newpass);
1905 /* Again, this happens when we login from gdm or xdm
1906 * and the password expires, *BUT* cached crendentials
1907 * doesn't exist. winbindd_update_creds_by_name()
1908 * returns NT_STATUS_NO_SUCH_USER.
1909 * This is not a failure.
1910 * --- BoYang
1911 * */
1912 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1913 result = NT_STATUS_OK;
1916 if (!NT_STATUS_IS_OK(result)) {
1917 DEBUG(10, ("Failed to store creds: %s\n",
1918 nt_errstr(result)));
1919 goto process_result;
1923 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1925 NTSTATUS policy_ret;
1927 policy_ret = fillup_password_policy(contact_domain, state);
1929 /* failure of this is non critical, it will just provide no
1930 * additional information to the client why the change has
1931 * failed - Guenther */
1933 if (!NT_STATUS_IS_OK(policy_ret)) {
1934 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1935 goto process_result;
1939 process_result:
1941 if (strequal(contact_domain->name, get_global_sam_name())) {
1942 /* FIXME: internal rpc pipe does not cache handles yet */
1943 if (cli) {
1944 if (is_valid_policy_hnd(&dom_pol)) {
1945 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
1947 TALLOC_FREE(cli);
1951 set_auth_errors(state->response, result);
1953 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1954 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1955 domain,
1956 user,
1957 state->response->data.auth.nt_status_string,
1958 state->response->data.auth.pam_error));
1960 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1963 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
1964 struct winbindd_cli_state *state)
1966 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
1968 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
1969 state->request->data.logoff.user));
1971 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
1972 result = NT_STATUS_OK;
1973 goto process_result;
1976 if (state->request->data.logoff.krb5ccname[0] == '\0') {
1977 result = NT_STATUS_OK;
1978 goto process_result;
1981 #ifdef HAVE_KRB5
1983 if (state->request->data.logoff.uid < 0) {
1984 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1985 goto process_result;
1988 /* what we need here is to find the corresponding krb5 ccache name *we*
1989 * created for a given username and destroy it */
1991 if (!ccache_entry_exists(state->request->data.logoff.user)) {
1992 result = NT_STATUS_OK;
1993 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
1994 goto process_result;
1997 if (!ccache_entry_identical(state->request->data.logoff.user,
1998 state->request->data.logoff.uid,
1999 state->request->data.logoff.krb5ccname)) {
2000 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2001 goto process_result;
2004 result = remove_ccache(state->request->data.logoff.user);
2005 if (!NT_STATUS_IS_OK(result)) {
2006 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2007 nt_errstr(result)));
2008 goto process_result;
2011 #else
2012 result = NT_STATUS_NOT_SUPPORTED;
2013 #endif
2015 process_result:
2018 set_auth_errors(state->response, result);
2020 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2023 /* Change user password with auth crap*/
2025 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2027 NTSTATUS result;
2028 DATA_BLOB new_nt_password;
2029 DATA_BLOB old_nt_hash_enc;
2030 DATA_BLOB new_lm_password;
2031 DATA_BLOB old_lm_hash_enc;
2032 fstring domain,user;
2033 struct policy_handle dom_pol;
2034 struct winbindd_domain *contact_domain = domainSt;
2035 struct rpc_pipe_client *cli = NULL;
2037 ZERO_STRUCT(dom_pol);
2039 /* Ensure null termination */
2040 state->request->data.chng_pswd_auth_crap.user[
2041 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2042 state->request->data.chng_pswd_auth_crap.domain[
2043 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2044 *domain = 0;
2045 *user = 0;
2047 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2048 (unsigned long)state->pid,
2049 state->request->data.chng_pswd_auth_crap.domain,
2050 state->request->data.chng_pswd_auth_crap.user));
2052 if (lp_winbind_offline_logon()) {
2053 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2054 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2055 result = NT_STATUS_ACCESS_DENIED;
2056 goto done;
2059 if (*state->request->data.chng_pswd_auth_crap.domain) {
2060 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2061 } else {
2062 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2063 domain, user);
2065 if(!*domain) {
2066 DEBUG(3,("no domain specified with username (%s) - "
2067 "failing auth\n",
2068 state->request->data.chng_pswd_auth_crap.user));
2069 result = NT_STATUS_NO_SUCH_USER;
2070 goto done;
2074 if (!*domain && lp_winbind_use_default_domain()) {
2075 fstrcpy(domain,(char *)lp_workgroup());
2078 if(!*user) {
2079 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2082 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2083 (unsigned long)state->pid, domain, user));
2085 /* Change password */
2086 new_nt_password = data_blob_const(
2087 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2088 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2090 old_nt_hash_enc = data_blob_const(
2091 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2092 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2094 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2095 new_lm_password = data_blob_const(
2096 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2097 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2099 old_lm_hash_enc = data_blob_const(
2100 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2101 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2102 } else {
2103 new_lm_password.length = 0;
2104 old_lm_hash_enc.length = 0;
2107 /* Get sam handle */
2109 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2110 if (!NT_STATUS_IS_OK(result)) {
2111 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2112 goto done;
2115 result = rpccli_samr_chng_pswd_auth_crap(
2116 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2117 new_lm_password, old_lm_hash_enc);
2119 done:
2121 if (strequal(contact_domain->name, get_global_sam_name())) {
2122 /* FIXME: internal rpc pipe does not cache handles yet */
2123 if (cli) {
2124 if (is_valid_policy_hnd(&dom_pol)) {
2125 rpccli_samr_Close(cli, state->mem_ctx, &dom_pol);
2127 TALLOC_FREE(cli);
2131 set_auth_errors(state->response, result);
2133 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2134 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2135 domain, user,
2136 state->response->data.auth.nt_status_string,
2137 state->response->data.auth.pam_error));
2139 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;