s4-auth Move libcli/security/session.c to the top level
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob68fa01f6d70c3a610b7c63f276a48fec234e1924
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/ndr_samr_c.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_response *response)
393 TALLOC_CTX *frame = talloc_stackframe();
394 struct winbindd_methods *methods;
395 NTSTATUS status;
396 struct samr_DomInfo1 password_policy;
398 if ( !winbindd_can_contact_domain( domain ) ) {
399 DEBUG(5,("fillup_password_policy: No inbound trust to "
400 "contact domain %s\n", domain->name));
401 status = NT_STATUS_NOT_SUPPORTED;
402 goto done;
405 methods = domain->methods;
407 status = methods->password_policy(domain, talloc_tos(), &password_policy);
408 if (NT_STATUS_IS_ERR(status)) {
409 goto done;
412 fill_in_password_policy(response, &password_policy);
414 done:
415 TALLOC_FREE(frame);
416 return NT_STATUS_OK;
419 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
420 TALLOC_CTX *mem_ctx,
421 uint16 *lockout_threshold)
423 struct winbindd_methods *methods;
424 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
425 struct samr_DomInfo12 lockout_policy;
427 *lockout_threshold = 0;
429 methods = domain->methods;
431 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
432 if (NT_STATUS_IS_ERR(status)) {
433 return status;
436 *lockout_threshold = lockout_policy.lockout_threshold;
438 return NT_STATUS_OK;
441 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
442 TALLOC_CTX *mem_ctx,
443 uint32 *password_properties)
445 struct winbindd_methods *methods;
446 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
447 struct samr_DomInfo1 password_policy;
449 *password_properties = 0;
451 methods = domain->methods;
453 status = methods->password_policy(domain, mem_ctx, &password_policy);
454 if (NT_STATUS_IS_ERR(status)) {
455 return status;
458 *password_properties = password_policy.password_properties;
460 return NT_STATUS_OK;
463 #ifdef HAVE_KRB5
465 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
466 const char *type,
467 uid_t uid,
468 const char **user_ccache_file)
470 /* accept FILE and WRFILE as krb5_cc_type from the client and then
471 * build the full ccname string based on the user's uid here -
472 * Guenther*/
474 const char *gen_cc = NULL;
476 if (uid != -1) {
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(
479 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
481 if (strequal(type, "WRFILE")) {
482 gen_cc = talloc_asprintf(
483 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
487 *user_ccache_file = gen_cc;
489 if (gen_cc == NULL) {
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
492 if (gen_cc == NULL) {
493 DEBUG(0,("out of memory\n"));
494 return NULL;
497 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
498 (*user_ccache_file == NULL) ? " (internal)":""));
500 return gen_cc;
503 #endif
505 uid_t get_uid_from_request(struct winbindd_request *request)
507 uid_t uid;
509 uid = request->data.auth.uid;
511 if (uid < 0) {
512 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
513 return -1;
515 return uid;
518 /**********************************************************************
519 Authenticate a user with a clear text password using Kerberos and fill up
520 ccache if required
521 **********************************************************************/
523 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
524 struct winbindd_domain *domain,
525 const char *user,
526 const char *pass,
527 const char *krb5_cc_type,
528 uid_t uid,
529 struct netr_SamInfo3 **info3,
530 fstring krb5ccname)
532 #ifdef HAVE_KRB5
533 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
534 krb5_error_code krb5_ret;
535 const char *cc = NULL;
536 const char *principal_s = NULL;
537 const char *service = NULL;
538 char *realm = NULL;
539 fstring name_domain, name_user;
540 time_t ticket_lifetime = 0;
541 time_t renewal_until = 0;
542 ADS_STRUCT *ads;
543 time_t time_offset = 0;
544 const char *user_ccache_file;
545 struct PAC_LOGON_INFO *logon_info = NULL;
547 *info3 = NULL;
549 /* 1st step:
550 * prepare a krb5_cc_cache string for the user */
552 if (uid == -1) {
553 DEBUG(0,("no valid uid\n"));
556 cc = generate_krb5_ccache(mem_ctx,
557 krb5_cc_type,
558 uid,
559 &user_ccache_file);
560 if (cc == NULL) {
561 return NT_STATUS_NO_MEMORY;
565 /* 2nd step:
566 * get kerberos properties */
568 if (domain->private_data) {
569 ads = (ADS_STRUCT *)domain->private_data;
570 time_offset = ads->auth.time_offset;
574 /* 3rd step:
575 * do kerberos auth and setup ccache as the user */
577 parse_domain_user(user, name_domain, name_user);
579 realm = domain->alt_name;
580 strupper_m(realm);
582 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
583 if (principal_s == NULL) {
584 return NT_STATUS_NO_MEMORY;
587 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
588 if (service == NULL) {
589 return NT_STATUS_NO_MEMORY;
592 /* if this is a user ccache, we need to act as the user to let the krb5
593 * library handle the chown, etc. */
595 /************************ ENTERING NON-ROOT **********************/
597 if (user_ccache_file != NULL) {
598 set_effective_uid(uid);
599 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
602 result = kerberos_return_pac(mem_ctx,
603 principal_s,
604 pass,
605 time_offset,
606 &ticket_lifetime,
607 &renewal_until,
609 true,
610 true,
611 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
612 NULL,
613 &logon_info);
614 if (user_ccache_file != NULL) {
615 gain_root_privilege();
618 /************************ RETURNED TO ROOT **********************/
620 if (!NT_STATUS_IS_OK(result)) {
621 goto failed;
624 *info3 = &logon_info->info3;
626 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
627 principal_s));
629 /* if we had a user's ccache then return that string for the pam
630 * environment */
632 if (user_ccache_file != NULL) {
634 fstrcpy(krb5ccname, user_ccache_file);
636 result = add_ccache_to_list(principal_s,
638 service,
639 user,
640 realm,
641 uid,
642 time(NULL),
643 ticket_lifetime,
644 renewal_until,
645 false);
647 if (!NT_STATUS_IS_OK(result)) {
648 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
649 nt_errstr(result)));
651 } else {
653 /* need to delete the memory cred cache, it is not used anymore */
655 krb5_ret = ads_kdestroy(cc);
656 if (krb5_ret) {
657 DEBUG(3,("winbindd_raw_kerberos_login: "
658 "could not destroy krb5 credential cache: "
659 "%s\n", error_message(krb5_ret)));
664 return NT_STATUS_OK;
666 failed:
668 /* we could have created a new credential cache with a valid tgt in it
669 * but we werent able to get or verify the service ticket for this
670 * local host and therefor didn't get the PAC, we need to remove that
671 * cache entirely now */
673 krb5_ret = ads_kdestroy(cc);
674 if (krb5_ret) {
675 DEBUG(3,("winbindd_raw_kerberos_login: "
676 "could not destroy krb5 credential cache: "
677 "%s\n", error_message(krb5_ret)));
680 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
681 DEBUG(3,("winbindd_raw_kerberos_login: "
682 "could not remove ccache for user %s\n",
683 user));
686 return result;
687 #else
688 return NT_STATUS_NOT_SUPPORTED;
689 #endif /* HAVE_KRB5 */
692 /****************************************************************
693 ****************************************************************/
695 bool check_request_flags(uint32_t flags)
697 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
698 WBFLAG_PAM_INFO3_TEXT |
699 WBFLAG_PAM_INFO3_NDR;
701 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
702 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
703 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
704 !(flags & flags_edata) ) {
705 return true;
708 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
709 flags));
711 return false;
714 /****************************************************************
715 ****************************************************************/
717 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
718 struct winbindd_response *resp,
719 uint32_t request_flags,
720 struct netr_SamInfo3 *info3,
721 const char *name_domain,
722 const char *name_user)
724 NTSTATUS result;
726 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
727 memcpy(resp->data.auth.user_session_key,
728 info3->base.key.key,
729 sizeof(resp->data.auth.user_session_key)
730 /* 16 */);
733 if (request_flags & WBFLAG_PAM_LMKEY) {
734 memcpy(resp->data.auth.first_8_lm_hash,
735 info3->base.LMSessKey.key,
736 sizeof(resp->data.auth.first_8_lm_hash)
737 /* 8 */);
740 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
741 result = append_unix_username(mem_ctx, resp,
742 info3, name_domain, name_user);
743 if (!NT_STATUS_IS_OK(result)) {
744 DEBUG(10,("Failed to append Unix Username: %s\n",
745 nt_errstr(result)));
746 return result;
750 /* currently, anything from here on potentially overwrites extra_data. */
752 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
753 result = append_info3_as_ndr(mem_ctx, resp, info3);
754 if (!NT_STATUS_IS_OK(result)) {
755 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
756 nt_errstr(result)));
757 return result;
761 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
762 result = append_info3_as_txt(mem_ctx, resp, info3);
763 if (!NT_STATUS_IS_OK(result)) {
764 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
765 nt_errstr(result)));
766 return result;
770 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
771 result = append_afs_token(mem_ctx, resp,
772 info3, name_domain, name_user);
773 if (!NT_STATUS_IS_OK(result)) {
774 DEBUG(10,("Failed to append AFS token: %s\n",
775 nt_errstr(result)));
776 return result;
780 return NT_STATUS_OK;
783 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
784 struct winbindd_cli_state *state,
785 struct netr_SamInfo3 **info3)
787 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
788 uint16 max_allowed_bad_attempts;
789 fstring name_domain, name_user;
790 struct dom_sid sid;
791 enum lsa_SidType type;
792 uchar new_nt_pass[NT_HASH_LEN];
793 const uint8 *cached_nt_pass;
794 const uint8 *cached_salt;
795 struct netr_SamInfo3 *my_info3;
796 time_t kickoff_time, must_change_time;
797 bool password_good = false;
798 #ifdef HAVE_KRB5
799 struct winbindd_tdc_domain *tdc_domain = NULL;
800 #endif
802 *info3 = NULL;
804 ZERO_STRUCTP(info3);
806 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
808 /* Parse domain and username */
810 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
813 if (!lookup_cached_name(name_domain,
814 name_user,
815 &sid,
816 &type)) {
817 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
818 return NT_STATUS_NO_SUCH_USER;
821 if (type != SID_NAME_USER) {
822 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
823 return NT_STATUS_LOGON_FAILURE;
826 result = winbindd_get_creds(domain,
827 state->mem_ctx,
828 &sid,
829 &my_info3,
830 &cached_nt_pass,
831 &cached_salt);
832 if (!NT_STATUS_IS_OK(result)) {
833 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
834 return result;
837 *info3 = my_info3;
839 E_md4hash(state->request->data.auth.pass, new_nt_pass);
841 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
842 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
843 if (cached_salt) {
844 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
847 if (cached_salt) {
848 /* In this case we didn't store the nt_hash itself,
849 but the MD5 combination of salt + nt_hash. */
850 uchar salted_hash[NT_HASH_LEN];
851 E_md5hash(cached_salt, new_nt_pass, salted_hash);
853 password_good = (memcmp(cached_nt_pass, salted_hash,
854 NT_HASH_LEN) == 0);
855 } else {
856 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
857 password_good = (memcmp(cached_nt_pass, new_nt_pass,
858 NT_HASH_LEN) == 0);
861 if (password_good) {
863 /* User *DOES* know the password, update logon_time and reset
864 * bad_pw_count */
866 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
868 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
869 return NT_STATUS_ACCOUNT_LOCKED_OUT;
872 if (my_info3->base.acct_flags & ACB_DISABLED) {
873 return NT_STATUS_ACCOUNT_DISABLED;
876 if (my_info3->base.acct_flags & ACB_WSTRUST) {
877 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
880 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
881 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
884 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
885 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
888 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
889 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
890 my_info3->base.acct_flags));
891 return NT_STATUS_LOGON_FAILURE;
894 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
895 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
896 return NT_STATUS_ACCOUNT_EXPIRED;
899 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
900 if (must_change_time != 0 && must_change_time < time(NULL)) {
901 /* we allow grace logons when the password has expired */
902 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
903 /* return NT_STATUS_PASSWORD_EXPIRED; */
904 goto success;
907 #ifdef HAVE_KRB5
908 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
909 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
910 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
911 /* used to cope with the case winbindd starting without network. */
912 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
914 uid_t uid = -1;
915 const char *cc = NULL;
916 char *realm = NULL;
917 const char *principal_s = NULL;
918 const char *service = NULL;
919 const char *user_ccache_file;
921 uid = get_uid_from_request(state->request);
922 if (uid == -1) {
923 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
924 return NT_STATUS_INVALID_PARAMETER;
927 cc = generate_krb5_ccache(state->mem_ctx,
928 state->request->data.auth.krb5_cc_type,
929 state->request->data.auth.uid,
930 &user_ccache_file);
931 if (cc == NULL) {
932 return NT_STATUS_NO_MEMORY;
935 realm = domain->alt_name;
936 strupper_m(realm);
938 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
939 if (principal_s == NULL) {
940 return NT_STATUS_NO_MEMORY;
943 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
944 if (service == NULL) {
945 return NT_STATUS_NO_MEMORY;
948 if (user_ccache_file != NULL) {
950 fstrcpy(state->response->data.auth.krb5ccname,
951 user_ccache_file);
953 result = add_ccache_to_list(principal_s,
955 service,
956 state->request->data.auth.user,
957 domain->alt_name,
958 uid,
959 time(NULL),
960 time(NULL) + lp_winbind_cache_time(),
961 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
962 true);
964 if (!NT_STATUS_IS_OK(result)) {
965 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
966 "to add ccache to list: %s\n",
967 nt_errstr(result)));
971 #endif /* HAVE_KRB5 */
972 success:
973 /* FIXME: we possibly should handle logon hours as well (does xp when
974 * offline?) see auth/auth_sam.c:sam_account_ok for details */
976 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
977 my_info3->base.bad_password_count = 0;
979 result = winbindd_update_creds_by_info3(domain,
980 state->request->data.auth.user,
981 state->request->data.auth.pass,
982 my_info3);
983 if (!NT_STATUS_IS_OK(result)) {
984 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
985 nt_errstr(result)));
986 return result;
989 return NT_STATUS_OK;
993 /* User does *NOT* know the correct password, modify info3 accordingly */
995 /* failure of this is not critical */
996 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
997 if (!NT_STATUS_IS_OK(result)) {
998 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
999 "Won't be able to honour account lockout policies\n"));
1002 /* increase counter */
1003 my_info3->base.bad_password_count++;
1005 if (max_allowed_bad_attempts == 0) {
1006 goto failed;
1009 /* lockout user */
1010 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1012 uint32 password_properties;
1014 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1015 if (!NT_STATUS_IS_OK(result)) {
1016 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1019 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1020 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1021 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1025 failed:
1026 result = winbindd_update_creds_by_info3(domain,
1027 state->request->data.auth.user,
1028 NULL,
1029 my_info3);
1031 if (!NT_STATUS_IS_OK(result)) {
1032 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1033 nt_errstr(result)));
1036 return NT_STATUS_LOGON_FAILURE;
1039 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1040 struct winbindd_cli_state *state,
1041 struct netr_SamInfo3 **info3)
1043 struct winbindd_domain *contact_domain;
1044 fstring name_domain, name_user;
1045 NTSTATUS result;
1047 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1049 /* Parse domain and username */
1051 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1053 /* what domain should we contact? */
1055 if ( IS_DC ) {
1056 if (!(contact_domain = find_domain_from_name(name_domain))) {
1057 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1058 state->request->data.auth.user, name_domain, name_user, name_domain));
1059 result = NT_STATUS_NO_SUCH_USER;
1060 goto done;
1063 } else {
1064 if (is_myname(name_domain)) {
1065 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1066 result = NT_STATUS_NO_SUCH_USER;
1067 goto done;
1070 contact_domain = find_domain_from_name(name_domain);
1071 if (contact_domain == NULL) {
1072 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1073 state->request->data.auth.user, name_domain, name_user, name_domain));
1075 contact_domain = find_our_domain();
1079 if (contact_domain->initialized &&
1080 contact_domain->active_directory) {
1081 goto try_login;
1084 if (!contact_domain->initialized) {
1085 init_dc_connection(contact_domain);
1088 if (!contact_domain->active_directory) {
1089 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1090 return NT_STATUS_INVALID_LOGON_TYPE;
1092 try_login:
1093 result = winbindd_raw_kerberos_login(
1094 state->mem_ctx, contact_domain,
1095 state->request->data.auth.user,
1096 state->request->data.auth.pass,
1097 state->request->data.auth.krb5_cc_type,
1098 get_uid_from_request(state->request),
1099 info3, state->response->data.auth.krb5ccname);
1100 done:
1101 return result;
1104 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1105 const char *domain, const char *user,
1106 const DATA_BLOB *challenge,
1107 const DATA_BLOB *lm_resp,
1108 const DATA_BLOB *nt_resp,
1109 struct netr_SamInfo3 **pinfo3)
1111 struct auth_usersupplied_info *user_info = NULL;
1112 NTSTATUS status;
1114 status = make_user_info(&user_info, user, user, domain, domain,
1115 global_myname(), lm_resp, nt_resp, NULL, NULL,
1116 NULL, AUTH_PASSWORD_RESPONSE);
1117 if (!NT_STATUS_IS_OK(status)) {
1118 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1119 return status;
1122 /* We don't want any more mapping of the username */
1123 user_info->mapped_state = True;
1125 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1126 pinfo3);
1127 free_user_info(&user_info);
1128 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1129 user, nt_errstr(status)));
1130 return status;
1133 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1134 TALLOC_CTX *mem_ctx,
1135 uint32_t logon_parameters,
1136 const char *server,
1137 const char *username,
1138 const char *domainname,
1139 const char *workstation,
1140 const uint8_t chal[8],
1141 DATA_BLOB lm_response,
1142 DATA_BLOB nt_response,
1143 struct netr_SamInfo3 **info3)
1145 int attempts = 0;
1146 bool retry = false;
1147 NTSTATUS result;
1149 do {
1150 struct rpc_pipe_client *netlogon_pipe;
1151 const struct pipe_auth_data *auth;
1152 uint32_t neg_flags = 0;
1154 ZERO_STRUCTP(info3);
1155 retry = false;
1157 result = cm_connect_netlogon(domain, &netlogon_pipe);
1159 if (!NT_STATUS_IS_OK(result)) {
1160 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1161 nt_errstr(result)));
1162 return result;
1164 auth = netlogon_pipe->auth;
1165 if (netlogon_pipe->dc) {
1166 neg_flags = netlogon_pipe->dc->negotiate_flags;
1169 /* It is really important to try SamLogonEx here,
1170 * because in a clustered environment, we want to use
1171 * one machine account from multiple physical
1172 * computers.
1174 * With a normal SamLogon call, we must keep the
1175 * credentials chain updated and intact between all
1176 * users of the machine account (which would imply
1177 * cross-node communication for every NTLM logon).
1179 * (The credentials chain is not per NETLOGON pipe
1180 * connection, but globally on the server/client pair
1181 * by machine name).
1183 * When using SamLogonEx, the credentials are not
1184 * supplied, but the session key is implied by the
1185 * wrapping SamLogon context.
1187 * -- abartlet 21 April 2008
1189 * It's also important to use NetlogonValidationSamInfo4 (6),
1190 * because it relies on the rpc transport encryption
1191 * and avoids using the global netlogon schannel
1192 * session key to en/decrypt secret information
1193 * like the user_session_key for network logons.
1195 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1196 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1197 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1198 * are the indication that the server supports
1199 * NetlogonValidationSamInfo4 (6). And it must only
1200 * be used if "SealSecureChannel" is used.
1202 * -- metze 4 February 2011
1205 if (auth == NULL) {
1206 domain->can_do_validation6 = false;
1207 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1208 domain->can_do_validation6 = false;
1209 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1210 domain->can_do_validation6 = false;
1211 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1212 domain->can_do_validation6 = false;
1213 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1214 domain->can_do_validation6 = false;
1217 if (domain->can_do_samlogon_ex) {
1218 result = rpccli_netlogon_sam_network_logon_ex(
1219 netlogon_pipe,
1220 mem_ctx,
1222 server, /* server name */
1223 username, /* user name */
1224 domainname, /* target domain */
1225 workstation, /* workstation */
1226 chal,
1227 domain->can_do_validation6 ? 6 : 3,
1228 lm_response,
1229 nt_response,
1230 info3);
1231 } else {
1232 result = rpccli_netlogon_sam_network_logon(
1233 netlogon_pipe,
1234 mem_ctx,
1236 server, /* server name */
1237 username, /* user name */
1238 domainname, /* target domain */
1239 workstation, /* workstation */
1240 chal,
1241 domain->can_do_validation6 ? 6 : 3,
1242 lm_response,
1243 nt_response,
1244 info3);
1247 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1248 && domain->can_do_samlogon_ex) {
1249 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1250 "retrying with NetSamLogon\n"));
1251 domain->can_do_samlogon_ex = false;
1253 * It's likely that the server also does not support
1254 * validation level 6
1256 domain->can_do_validation6 = false;
1257 retry = true;
1258 continue;
1261 if (domain->can_do_validation6 &&
1262 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1263 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1264 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1265 DEBUG(3,("Got a DC that can not do validation level 6, "
1266 "retrying with level 3\n"));
1267 domain->can_do_validation6 = false;
1268 retry = true;
1269 continue;
1273 * we increment this after the "feature negotiation"
1274 * for can_do_samlogon_ex and can_do_validation6
1276 attempts += 1;
1278 /* We have to try a second time as cm_connect_netlogon
1279 might not yet have noticed that the DC has killed
1280 our connection. */
1282 if (!rpccli_is_connected(netlogon_pipe)) {
1283 retry = true;
1284 continue;
1287 /* if we get access denied, a possible cause was that we had
1288 and open connection to the DC, but someone changed our
1289 machine account password out from underneath us using 'net
1290 rpc changetrustpw' */
1292 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1293 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1294 "ACCESS_DENIED. Maybe the trust account "
1295 "password was changed and we didn't know it. "
1296 "Killing connections to domain %s\n",
1297 domainname));
1298 invalidate_cm_connection(&domain->conn);
1299 retry = true;
1302 } while ( (attempts < 2) && retry );
1304 return result;
1307 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1308 struct winbindd_domain *domain,
1309 const char *user,
1310 const char *pass,
1311 uint32_t request_flags,
1312 struct netr_SamInfo3 **info3)
1315 uchar chal[8];
1316 DATA_BLOB lm_resp;
1317 DATA_BLOB nt_resp;
1318 unsigned char local_nt_response[24];
1319 fstring name_domain, name_user;
1320 NTSTATUS result;
1321 struct netr_SamInfo3 *my_info3 = NULL;
1323 *info3 = NULL;
1325 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1327 /* Parse domain and username */
1329 parse_domain_user(user, name_domain, name_user);
1331 /* do password magic */
1333 generate_random_buffer(chal, sizeof(chal));
1335 if (lp_client_ntlmv2_auth()) {
1336 DATA_BLOB server_chal;
1337 DATA_BLOB names_blob;
1338 server_chal = data_blob_const(chal, 8);
1340 /* note that the 'workgroup' here is for the local
1341 machine. The 'server name' must match the
1342 'workstation' passed to the actual SamLogon call.
1344 names_blob = NTLMv2_generate_names_blob(
1345 mem_ctx, global_myname(), lp_workgroup());
1347 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1348 pass,
1349 &server_chal,
1350 &names_blob,
1351 &lm_resp, &nt_resp, NULL, NULL)) {
1352 data_blob_free(&names_blob);
1353 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1354 result = NT_STATUS_NO_MEMORY;
1355 goto done;
1357 data_blob_free(&names_blob);
1358 } else {
1359 lm_resp = data_blob_null;
1360 SMBNTencrypt(pass, chal, local_nt_response);
1362 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1363 sizeof(local_nt_response));
1366 if (strequal(name_domain, get_global_sam_name())) {
1367 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1369 result = winbindd_dual_auth_passdb(
1370 mem_ctx, name_domain, name_user,
1371 &chal_blob, &lm_resp, &nt_resp, info3);
1372 goto done;
1375 /* check authentication loop */
1377 result = winbind_samlogon_retry_loop(domain,
1378 mem_ctx,
1380 domain->dcname,
1381 name_user,
1382 name_domain,
1383 global_myname(),
1384 chal,
1385 lm_resp,
1386 nt_resp,
1387 &my_info3);
1388 if (!NT_STATUS_IS_OK(result)) {
1389 goto done;
1392 /* handle the case where a NT4 DC does not fill in the acct_flags in
1393 * the samlogon reply info3. When accurate info3 is required by the
1394 * caller, we look up the account flags ourselve - gd */
1396 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1397 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1399 struct rpc_pipe_client *samr_pipe;
1400 struct policy_handle samr_domain_handle, user_pol;
1401 union samr_UserInfo *info = NULL;
1402 NTSTATUS status_tmp, result_tmp;
1403 uint32 acct_flags;
1404 struct dcerpc_binding_handle *b;
1406 status_tmp = cm_connect_sam(domain, mem_ctx,
1407 &samr_pipe, &samr_domain_handle);
1409 if (!NT_STATUS_IS_OK(status_tmp)) {
1410 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1411 nt_errstr(status_tmp)));
1412 goto done;
1415 b = samr_pipe->binding_handle;
1417 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1418 &samr_domain_handle,
1419 MAXIMUM_ALLOWED_ACCESS,
1420 my_info3->base.rid,
1421 &user_pol,
1422 &result_tmp);
1424 if (!NT_STATUS_IS_OK(status_tmp)) {
1425 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1426 nt_errstr(status_tmp)));
1427 goto done;
1429 if (!NT_STATUS_IS_OK(result_tmp)) {
1430 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1431 nt_errstr(result_tmp)));
1432 goto done;
1435 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1436 &user_pol,
1438 &info,
1439 &result_tmp);
1441 if (!NT_STATUS_IS_OK(status_tmp)) {
1442 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1443 nt_errstr(status_tmp)));
1444 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1445 goto done;
1447 if (!NT_STATUS_IS_OK(result_tmp)) {
1448 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1449 nt_errstr(result_tmp)));
1450 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1451 goto done;
1454 acct_flags = info->info16.acct_flags;
1456 if (acct_flags == 0) {
1457 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1458 goto done;
1461 my_info3->base.acct_flags = acct_flags;
1463 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1465 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1468 *info3 = my_info3;
1469 done:
1470 return result;
1473 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1474 struct winbindd_cli_state *state)
1476 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1477 NTSTATUS krb5_result = NT_STATUS_OK;
1478 fstring name_domain, name_user;
1479 char *mapped_user;
1480 fstring domain_user;
1481 struct netr_SamInfo3 *info3 = NULL;
1482 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1484 /* Ensure null termination */
1485 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1487 /* Ensure null termination */
1488 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1490 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1491 state->request->data.auth.user));
1493 /* Parse domain and username */
1495 name_map_status = normalize_name_unmap(state->mem_ctx,
1496 state->request->data.auth.user,
1497 &mapped_user);
1499 /* If the name normalization didnt' actually do anything,
1500 just use the original name */
1502 if (!NT_STATUS_IS_OK(name_map_status) &&
1503 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1505 mapped_user = state->request->data.auth.user;
1508 parse_domain_user(mapped_user, name_domain, name_user);
1510 if ( mapped_user != state->request->data.auth.user ) {
1511 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1512 *lp_winbind_separator(),
1513 name_user );
1514 safe_strcpy( state->request->data.auth.user, domain_user,
1515 sizeof(state->request->data.auth.user)-1 );
1518 if (!domain->online) {
1519 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1520 if (domain->startup) {
1521 /* Logons are very important to users. If we're offline and
1522 we get a request within the first 30 seconds of startup,
1523 try very hard to find a DC and go online. */
1525 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1526 "request in startup mode.\n", domain->name ));
1528 winbindd_flush_negative_conn_cache(domain);
1529 result = init_dc_connection(domain);
1533 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1535 /* Check for Kerberos authentication */
1536 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1538 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1539 /* save for later */
1540 krb5_result = result;
1543 if (NT_STATUS_IS_OK(result)) {
1544 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1545 goto process_result;
1546 } else {
1547 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1550 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1551 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1552 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1553 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1554 set_domain_offline( domain );
1555 goto cached_logon;
1558 /* there are quite some NT_STATUS errors where there is no
1559 * point in retrying with a samlogon, we explictly have to take
1560 * care not to increase the bad logon counter on the DC */
1562 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1563 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1564 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1565 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1566 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1567 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1568 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1569 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1570 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1571 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1572 goto done;
1575 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1576 DEBUG(3,("falling back to samlogon\n"));
1577 goto sam_logon;
1578 } else {
1579 goto cached_logon;
1583 sam_logon:
1584 /* Check for Samlogon authentication */
1585 if (domain->online) {
1586 result = winbindd_dual_pam_auth_samlogon(
1587 state->mem_ctx, domain,
1588 state->request->data.auth.user,
1589 state->request->data.auth.pass,
1590 state->request->flags,
1591 &info3);
1593 if (NT_STATUS_IS_OK(result)) {
1594 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1595 /* add the Krb5 err if we have one */
1596 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1597 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1599 goto process_result;
1602 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1603 nt_errstr(result)));
1605 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1606 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1607 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1609 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1610 set_domain_offline( domain );
1611 goto cached_logon;
1614 if (domain->online) {
1615 /* We're still online - fail. */
1616 goto done;
1620 cached_logon:
1621 /* Check for Cached logons */
1622 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1623 lp_winbind_offline_logon()) {
1625 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1627 if (NT_STATUS_IS_OK(result)) {
1628 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1629 goto process_result;
1630 } else {
1631 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1632 goto done;
1636 process_result:
1638 if (NT_STATUS_IS_OK(result)) {
1640 struct dom_sid user_sid;
1642 /* In all codepaths where result == NT_STATUS_OK info3 must have
1643 been initialized. */
1644 if (!info3) {
1645 result = NT_STATUS_INTERNAL_ERROR;
1646 goto done;
1649 sid_compose(&user_sid, info3->base.domain_sid,
1650 info3->base.rid);
1652 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1653 &user_sid);
1654 netsamlogon_cache_store(name_user, info3);
1656 /* save name_to_sid info as early as possible (only if
1657 this is our primary domain so we don't invalidate
1658 the cache entry by storing the seq_num for the wrong
1659 domain). */
1660 if ( domain->primary ) {
1661 cache_name2sid(domain, name_domain, name_user,
1662 SID_NAME_USER, &user_sid);
1665 /* Check if the user is in the right group */
1667 result = check_info3_in_group(
1668 info3,
1669 state->request->data.auth.require_membership_of_sid);
1670 if (!NT_STATUS_IS_OK(result)) {
1671 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1672 state->request->data.auth.user,
1673 state->request->data.auth.require_membership_of_sid));
1674 goto done;
1677 result = append_auth_data(state->mem_ctx, state->response,
1678 state->request->flags, info3,
1679 name_domain, name_user);
1680 if (!NT_STATUS_IS_OK(result)) {
1681 goto done;
1684 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1685 && lp_winbind_offline_logon()) {
1687 result = winbindd_store_creds(domain,
1688 state->request->data.auth.user,
1689 state->request->data.auth.pass,
1690 info3);
1693 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1694 struct winbindd_domain *our_domain = find_our_domain();
1696 /* This is not entirely correct I believe, but it is
1697 consistent. Only apply the password policy settings
1698 too warn users for our own domain. Cannot obtain these
1699 from trusted DCs all the time so don't do it at all.
1700 -- jerry */
1702 result = NT_STATUS_NOT_SUPPORTED;
1703 if (our_domain == domain ) {
1704 result = fillup_password_policy(
1705 our_domain, state->response);
1708 if (!NT_STATUS_IS_OK(result)
1709 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1711 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1712 domain->name, nt_errstr(result)));
1713 goto done;
1717 result = NT_STATUS_OK;
1720 done:
1721 /* give us a more useful (more correct?) error code */
1722 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1723 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1724 result = NT_STATUS_NO_LOGON_SERVERS;
1727 set_auth_errors(state->response, result);
1729 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1730 state->request->data.auth.user,
1731 state->response->data.auth.nt_status_string,
1732 state->response->data.auth.pam_error));
1734 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1737 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1738 struct winbindd_cli_state *state)
1740 NTSTATUS result;
1741 struct netr_SamInfo3 *info3 = NULL;
1742 const char *name_user = NULL;
1743 const char *name_domain = NULL;
1744 const char *workstation;
1746 DATA_BLOB lm_resp, nt_resp;
1748 /* This is child-only, so no check for privileged access is needed
1749 anymore */
1751 /* Ensure null termination */
1752 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1753 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1755 name_user = state->request->data.auth_crap.user;
1756 name_domain = state->request->data.auth_crap.domain;
1757 workstation = state->request->data.auth_crap.workstation;
1759 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1760 name_domain, name_user));
1762 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1763 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1764 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1765 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1766 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1767 state->request->data.auth_crap.lm_resp_len,
1768 state->request->data.auth_crap.nt_resp_len));
1769 result = NT_STATUS_INVALID_PARAMETER;
1770 goto done;
1774 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1775 state->request->data.auth_crap.lm_resp_len);
1777 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1778 nt_resp = data_blob_talloc(state->mem_ctx,
1779 state->request->extra_data.data,
1780 state->request->data.auth_crap.nt_resp_len);
1781 } else {
1782 nt_resp = data_blob_talloc(state->mem_ctx,
1783 state->request->data.auth_crap.nt_resp,
1784 state->request->data.auth_crap.nt_resp_len);
1787 if (strequal(name_domain, get_global_sam_name())) {
1788 DATA_BLOB chal_blob = data_blob_const(
1789 state->request->data.auth_crap.chal,
1790 sizeof(state->request->data.auth_crap.chal));
1792 result = winbindd_dual_auth_passdb(
1793 state->mem_ctx, name_domain, name_user,
1794 &chal_blob, &lm_resp, &nt_resp, &info3);
1795 goto process_result;
1798 result = winbind_samlogon_retry_loop(domain,
1799 state->mem_ctx,
1800 state->request->data.auth_crap.logon_parameters,
1801 domain->dcname,
1802 name_user,
1803 name_domain,
1804 /* Bug #3248 - found by Stefan Burkei. */
1805 workstation, /* We carefully set this above so use it... */
1806 state->request->data.auth_crap.chal,
1807 lm_resp,
1808 nt_resp,
1809 &info3);
1810 if (!NT_STATUS_IS_OK(result)) {
1811 goto done;
1814 process_result:
1816 if (NT_STATUS_IS_OK(result)) {
1817 struct dom_sid user_sid;
1819 sid_compose(&user_sid, info3->base.domain_sid,
1820 info3->base.rid);
1821 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1822 &user_sid);
1823 netsamlogon_cache_store(name_user, info3);
1825 /* Check if the user is in the right group */
1827 result = check_info3_in_group(
1828 info3,
1829 state->request->data.auth_crap.require_membership_of_sid);
1830 if (!NT_STATUS_IS_OK(result)) {
1831 DEBUG(3, ("User %s is not in the required group (%s), so "
1832 "crap authentication is rejected\n",
1833 state->request->data.auth_crap.user,
1834 state->request->data.auth_crap.require_membership_of_sid));
1835 goto done;
1838 result = append_auth_data(state->mem_ctx, state->response,
1839 state->request->flags, info3,
1840 name_domain, name_user);
1841 if (!NT_STATUS_IS_OK(result)) {
1842 goto done;
1846 done:
1848 /* give us a more useful (more correct?) error code */
1849 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1850 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1851 result = NT_STATUS_NO_LOGON_SERVERS;
1854 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1855 result = nt_status_squash(result);
1858 set_auth_errors(state->response, result);
1860 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1861 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1862 name_domain,
1863 name_user,
1864 state->response->data.auth.nt_status_string,
1865 state->response->data.auth.pam_error));
1867 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1870 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1871 struct winbindd_cli_state *state)
1873 char *oldpass;
1874 char *newpass = NULL;
1875 struct policy_handle dom_pol;
1876 struct rpc_pipe_client *cli = NULL;
1877 bool got_info = false;
1878 struct samr_DomInfo1 *info = NULL;
1879 struct userPwdChangeFailureInformation *reject = NULL;
1880 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1881 fstring domain, user;
1882 struct dcerpc_binding_handle *b = NULL;
1884 ZERO_STRUCT(dom_pol);
1886 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1887 state->request->data.auth.user));
1889 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1890 goto done;
1893 /* Change password */
1895 oldpass = state->request->data.chauthtok.oldpass;
1896 newpass = state->request->data.chauthtok.newpass;
1898 /* Initialize reject reason */
1899 state->response->data.auth.reject_reason = Undefined;
1901 /* Get sam handle */
1903 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1904 &dom_pol);
1905 if (!NT_STATUS_IS_OK(result)) {
1906 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1907 goto done;
1910 b = cli->binding_handle;
1912 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1913 user,
1914 newpass,
1915 oldpass,
1916 &info,
1917 &reject);
1919 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1921 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1923 fill_in_password_policy(state->response, info);
1925 state->response->data.auth.reject_reason =
1926 reject->extendedFailureReason;
1928 got_info = true;
1931 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1932 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1933 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1934 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1936 /* only fallback when the chgpasswd_user3 call is not supported */
1937 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1938 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1939 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
1940 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1942 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1943 nt_errstr(result)));
1945 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1947 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1948 Map to the same status code as Windows 2003. */
1950 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1951 result = NT_STATUS_PASSWORD_RESTRICTION;
1955 done:
1957 if (NT_STATUS_IS_OK(result)
1958 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1959 && lp_winbind_offline_logon()) {
1960 result = winbindd_update_creds_by_name(contact_domain, user,
1961 newpass);
1962 /* Again, this happens when we login from gdm or xdm
1963 * and the password expires, *BUT* cached crendentials
1964 * doesn't exist. winbindd_update_creds_by_name()
1965 * returns NT_STATUS_NO_SUCH_USER.
1966 * This is not a failure.
1967 * --- BoYang
1968 * */
1969 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1970 result = NT_STATUS_OK;
1973 if (!NT_STATUS_IS_OK(result)) {
1974 DEBUG(10, ("Failed to store creds: %s\n",
1975 nt_errstr(result)));
1976 goto process_result;
1980 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
1982 NTSTATUS policy_ret;
1984 policy_ret = fillup_password_policy(
1985 contact_domain, state->response);
1987 /* failure of this is non critical, it will just provide no
1988 * additional information to the client why the change has
1989 * failed - Guenther */
1991 if (!NT_STATUS_IS_OK(policy_ret)) {
1992 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
1993 goto process_result;
1997 process_result:
1999 if (strequal(contact_domain->name, get_global_sam_name())) {
2000 /* FIXME: internal rpc pipe does not cache handles yet */
2001 if (b) {
2002 if (is_valid_policy_hnd(&dom_pol)) {
2003 NTSTATUS _result;
2004 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2006 TALLOC_FREE(cli);
2010 set_auth_errors(state->response, result);
2012 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2013 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2014 domain,
2015 user,
2016 state->response->data.auth.nt_status_string,
2017 state->response->data.auth.pam_error));
2019 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2022 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2023 struct winbindd_cli_state *state)
2025 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2027 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2028 state->request->data.logoff.user));
2030 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2031 result = NT_STATUS_OK;
2032 goto process_result;
2035 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2036 result = NT_STATUS_OK;
2037 goto process_result;
2040 #ifdef HAVE_KRB5
2042 if (state->request->data.logoff.uid < 0) {
2043 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2044 goto process_result;
2047 /* what we need here is to find the corresponding krb5 ccache name *we*
2048 * created for a given username and destroy it */
2050 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2051 result = NT_STATUS_OK;
2052 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2053 goto process_result;
2056 if (!ccache_entry_identical(state->request->data.logoff.user,
2057 state->request->data.logoff.uid,
2058 state->request->data.logoff.krb5ccname)) {
2059 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2060 goto process_result;
2063 result = remove_ccache(state->request->data.logoff.user);
2064 if (!NT_STATUS_IS_OK(result)) {
2065 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2066 nt_errstr(result)));
2067 goto process_result;
2070 #else
2071 result = NT_STATUS_NOT_SUPPORTED;
2072 #endif
2074 process_result:
2077 set_auth_errors(state->response, result);
2079 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2082 /* Change user password with auth crap*/
2084 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2086 NTSTATUS result;
2087 DATA_BLOB new_nt_password;
2088 DATA_BLOB old_nt_hash_enc;
2089 DATA_BLOB new_lm_password;
2090 DATA_BLOB old_lm_hash_enc;
2091 fstring domain,user;
2092 struct policy_handle dom_pol;
2093 struct winbindd_domain *contact_domain = domainSt;
2094 struct rpc_pipe_client *cli = NULL;
2095 struct dcerpc_binding_handle *b = NULL;
2097 ZERO_STRUCT(dom_pol);
2099 /* Ensure null termination */
2100 state->request->data.chng_pswd_auth_crap.user[
2101 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2102 state->request->data.chng_pswd_auth_crap.domain[
2103 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2104 *domain = 0;
2105 *user = 0;
2107 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2108 (unsigned long)state->pid,
2109 state->request->data.chng_pswd_auth_crap.domain,
2110 state->request->data.chng_pswd_auth_crap.user));
2112 if (lp_winbind_offline_logon()) {
2113 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2114 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2115 result = NT_STATUS_ACCESS_DENIED;
2116 goto done;
2119 if (*state->request->data.chng_pswd_auth_crap.domain) {
2120 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2121 } else {
2122 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2123 domain, user);
2125 if(!*domain) {
2126 DEBUG(3,("no domain specified with username (%s) - "
2127 "failing auth\n",
2128 state->request->data.chng_pswd_auth_crap.user));
2129 result = NT_STATUS_NO_SUCH_USER;
2130 goto done;
2134 if (!*domain && lp_winbind_use_default_domain()) {
2135 fstrcpy(domain,(char *)lp_workgroup());
2138 if(!*user) {
2139 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2142 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2143 (unsigned long)state->pid, domain, user));
2145 /* Change password */
2146 new_nt_password = data_blob_const(
2147 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2148 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2150 old_nt_hash_enc = data_blob_const(
2151 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2152 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2154 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2155 new_lm_password = data_blob_const(
2156 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2157 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2159 old_lm_hash_enc = data_blob_const(
2160 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2161 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2162 } else {
2163 new_lm_password.length = 0;
2164 old_lm_hash_enc.length = 0;
2167 /* Get sam handle */
2169 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2170 if (!NT_STATUS_IS_OK(result)) {
2171 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2172 goto done;
2175 b = cli->binding_handle;
2177 result = rpccli_samr_chng_pswd_auth_crap(
2178 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2179 new_lm_password, old_lm_hash_enc);
2181 done:
2183 if (strequal(contact_domain->name, get_global_sam_name())) {
2184 /* FIXME: internal rpc pipe does not cache handles yet */
2185 if (b) {
2186 if (is_valid_policy_hnd(&dom_pol)) {
2187 NTSTATUS _result;
2188 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2190 TALLOC_FREE(cli);
2194 set_auth_errors(state->response, result);
2196 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2197 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2198 domain, user,
2199 state->response->data.auth.nt_status_string,
2200 state->response->data.auth.pam_error));
2202 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;