s3-winbindd: pass logon parmeters down to check_sam_security()
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob7163af2596bf792ff07866706161f591f183ce97
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_pipe.h"
30 #include "rpc_client/cli_samr.h"
31 #include "../librpc/gen_ndr/ndr_netlogon.h"
32 #include "rpc_client/cli_netlogon.h"
33 #include "smb_krb5.h"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
36 #include "ads.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.h"
39 #include "auth.h"
40 #include "../lib/tsocket/tsocket.h"
42 #undef DBGC_CLASS
43 #define DBGC_CLASS DBGC_WINBIND
45 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
47 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
48 struct winbindd_response *resp,
49 struct netr_SamInfo3 *info3)
51 char *ex;
52 uint32_t i;
54 resp->data.auth.info3.logon_time =
55 nt_time_to_unix(info3->base.logon_time);
56 resp->data.auth.info3.logoff_time =
57 nt_time_to_unix(info3->base.logoff_time);
58 resp->data.auth.info3.kickoff_time =
59 nt_time_to_unix(info3->base.kickoff_time);
60 resp->data.auth.info3.pass_last_set_time =
61 nt_time_to_unix(info3->base.last_password_change);
62 resp->data.auth.info3.pass_can_change_time =
63 nt_time_to_unix(info3->base.allow_password_change);
64 resp->data.auth.info3.pass_must_change_time =
65 nt_time_to_unix(info3->base.force_password_change);
67 resp->data.auth.info3.logon_count = info3->base.logon_count;
68 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
70 resp->data.auth.info3.user_rid = info3->base.rid;
71 resp->data.auth.info3.group_rid = info3->base.primary_gid;
72 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
74 resp->data.auth.info3.num_groups = info3->base.groups.count;
75 resp->data.auth.info3.user_flgs = info3->base.user_flags;
77 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
78 resp->data.auth.info3.num_other_sids = info3->sidcount;
80 fstrcpy(resp->data.auth.info3.user_name,
81 info3->base.account_name.string);
82 fstrcpy(resp->data.auth.info3.full_name,
83 info3->base.full_name.string);
84 fstrcpy(resp->data.auth.info3.logon_script,
85 info3->base.logon_script.string);
86 fstrcpy(resp->data.auth.info3.profile_path,
87 info3->base.profile_path.string);
88 fstrcpy(resp->data.auth.info3.home_dir,
89 info3->base.home_directory.string);
90 fstrcpy(resp->data.auth.info3.dir_drive,
91 info3->base.home_drive.string);
93 fstrcpy(resp->data.auth.info3.logon_srv,
94 info3->base.logon_server.string);
95 fstrcpy(resp->data.auth.info3.logon_dom,
96 info3->base.logon_domain.string);
98 ex = talloc_strdup(mem_ctx, "");
99 NT_STATUS_HAVE_NO_MEMORY(ex);
101 for (i=0; i < info3->base.groups.count; i++) {
102 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
103 info3->base.groups.rids[i].rid,
104 info3->base.groups.rids[i].attributes);
105 NT_STATUS_HAVE_NO_MEMORY(ex);
108 for (i=0; i < info3->sidcount; i++) {
109 char *sid;
111 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
112 NT_STATUS_HAVE_NO_MEMORY(sid);
114 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
115 sid,
116 info3->sids[i].attributes);
117 NT_STATUS_HAVE_NO_MEMORY(ex);
119 talloc_free(sid);
122 resp->extra_data.data = ex;
123 resp->length += talloc_get_size(ex);
125 return NT_STATUS_OK;
128 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
129 struct winbindd_response *resp,
130 struct netr_SamInfo3 *info3)
132 DATA_BLOB blob;
133 enum ndr_err_code ndr_err;
135 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
136 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
137 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
138 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
139 return ndr_map_error2ntstatus(ndr_err);
142 resp->extra_data.data = blob.data;
143 resp->length += blob.length;
145 return NT_STATUS_OK;
148 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
149 struct winbindd_response *resp,
150 const struct netr_SamInfo3 *info3,
151 const char *name_domain,
152 const char *name_user)
154 /* We've been asked to return the unix username, per
155 'winbind use default domain' settings and the like */
157 const char *nt_username, *nt_domain;
159 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
160 if (!nt_domain) {
161 /* If the server didn't give us one, just use the one
162 * we sent them */
163 nt_domain = name_domain;
166 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
167 if (!nt_username) {
168 /* If the server didn't give us one, just use the one
169 * we sent them */
170 nt_username = name_user;
173 fill_domain_username(resp->data.auth.unix_username,
174 nt_domain, nt_username, true);
176 DEBUG(5, ("Setting unix username to [%s]\n",
177 resp->data.auth.unix_username));
179 return NT_STATUS_OK;
182 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
183 struct winbindd_response *resp,
184 const struct netr_SamInfo3 *info3,
185 const char *name_domain,
186 const char *name_user)
188 char *afsname = NULL;
189 char *cell;
190 char *token;
192 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
193 if (afsname == NULL) {
194 return NT_STATUS_NO_MEMORY;
197 afsname = talloc_string_sub(mem_ctx,
198 lp_afs_username_map(),
199 "%D", name_domain);
200 afsname = talloc_string_sub(mem_ctx, afsname,
201 "%u", name_user);
202 afsname = talloc_string_sub(mem_ctx, afsname,
203 "%U", name_user);
206 struct dom_sid user_sid;
207 fstring sidstr;
209 sid_compose(&user_sid, info3->base.domain_sid,
210 info3->base.rid);
211 sid_to_fstring(sidstr, &user_sid);
212 afsname = talloc_string_sub(mem_ctx, afsname,
213 "%s", sidstr);
216 if (afsname == NULL) {
217 return NT_STATUS_NO_MEMORY;
220 strlower_m(afsname);
222 DEBUG(10, ("Generating token for user %s\n", afsname));
224 cell = strchr(afsname, '@');
226 if (cell == NULL) {
227 return NT_STATUS_NO_MEMORY;
230 *cell = '\0';
231 cell += 1;
233 token = afs_createtoken_str(afsname, cell);
234 if (token == NULL) {
235 return NT_STATUS_OK;
237 resp->extra_data.data = talloc_strdup(mem_ctx, token);
238 if (resp->extra_data.data == NULL) {
239 return NT_STATUS_NO_MEMORY;
241 resp->length += strlen((const char *)resp->extra_data.data)+1;
243 return NT_STATUS_OK;
246 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
247 const char *group_sid)
249 * Check whether a user belongs to a group or list of groups.
251 * @param mem_ctx talloc memory context.
252 * @param info3 user information, including group membership info.
253 * @param group_sid One or more groups , separated by commas.
255 * @return NT_STATUS_OK on success,
256 * NT_STATUS_LOGON_FAILURE if the user does not belong,
257 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
260 struct dom_sid *require_membership_of_sid;
261 uint32_t num_require_membership_of_sid;
262 char *req_sid;
263 const char *p;
264 struct dom_sid sid;
265 size_t i;
266 struct security_token *token;
267 TALLOC_CTX *frame = talloc_stackframe();
268 NTSTATUS status;
270 /* Parse the 'required group' SID */
272 if (!group_sid || !group_sid[0]) {
273 /* NO sid supplied, all users may access */
274 return NT_STATUS_OK;
277 token = talloc_zero(talloc_tos(), struct security_token);
278 if (token == NULL) {
279 DEBUG(0, ("talloc failed\n"));
280 TALLOC_FREE(frame);
281 return NT_STATUS_NO_MEMORY;
284 num_require_membership_of_sid = 0;
285 require_membership_of_sid = NULL;
287 p = group_sid;
289 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
290 if (!string_to_sid(&sid, req_sid)) {
291 DEBUG(0, ("check_info3_in_group: could not parse %s "
292 "as a SID!", req_sid));
293 TALLOC_FREE(frame);
294 return NT_STATUS_INVALID_PARAMETER;
297 status = add_sid_to_array(talloc_tos(), &sid,
298 &require_membership_of_sid,
299 &num_require_membership_of_sid);
300 if (!NT_STATUS_IS_OK(status)) {
301 DEBUG(0, ("add_sid_to_array failed\n"));
302 TALLOC_FREE(frame);
303 return status;
307 status = sid_array_from_info3(talloc_tos(), info3,
308 &token->sids,
309 &token->num_sids,
310 true, false);
311 if (!NT_STATUS_IS_OK(status)) {
312 TALLOC_FREE(frame);
313 return status;
316 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
317 token))
318 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
319 token))) {
320 DEBUG(3, ("could not add aliases: %s\n",
321 nt_errstr(status)));
322 TALLOC_FREE(frame);
323 return status;
326 security_token_debug(DBGC_CLASS, 10, token);
328 for (i=0; i<num_require_membership_of_sid; i++) {
329 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
330 &require_membership_of_sid[i])));
331 if (nt_token_check_sid(&require_membership_of_sid[i],
332 token)) {
333 DEBUG(10, ("Access ok\n"));
334 TALLOC_FREE(frame);
335 return NT_STATUS_OK;
339 /* Do not distinguish this error from a wrong username/pw */
341 TALLOC_FREE(frame);
342 return NT_STATUS_LOGON_FAILURE;
345 struct winbindd_domain *find_auth_domain(uint8_t flags,
346 const char *domain_name)
348 struct winbindd_domain *domain;
350 if (IS_DC) {
351 domain = find_domain_from_name_noinit(domain_name);
352 if (domain == NULL) {
353 DEBUG(3, ("Authentication for domain [%s] refused "
354 "as it is not a trusted domain\n",
355 domain_name));
357 return domain;
360 if (strequal(domain_name, get_global_sam_name())) {
361 return find_domain_from_name_noinit(domain_name);
364 /* we can auth against trusted domains */
365 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
366 domain = find_domain_from_name_noinit(domain_name);
367 if (domain == NULL) {
368 DEBUG(3, ("Authentication for domain [%s] skipped "
369 "as it is not a trusted domain\n",
370 domain_name));
371 } else {
372 return domain;
376 return find_our_domain();
379 static void fill_in_password_policy(struct winbindd_response *r,
380 const struct samr_DomInfo1 *p)
382 r->data.auth.policy.min_length_password =
383 p->min_password_length;
384 r->data.auth.policy.password_history =
385 p->password_history_length;
386 r->data.auth.policy.password_properties =
387 p->password_properties;
388 r->data.auth.policy.expire =
389 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
390 r->data.auth.policy.min_passwordage =
391 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
394 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
395 struct winbindd_response *response)
397 TALLOC_CTX *frame = talloc_stackframe();
398 struct winbindd_methods *methods;
399 NTSTATUS status;
400 struct samr_DomInfo1 password_policy;
402 if ( !winbindd_can_contact_domain( domain ) ) {
403 DEBUG(5,("fillup_password_policy: No inbound trust to "
404 "contact domain %s\n", domain->name));
405 status = NT_STATUS_NOT_SUPPORTED;
406 goto done;
409 methods = domain->methods;
411 status = methods->password_policy(domain, talloc_tos(), &password_policy);
412 if (NT_STATUS_IS_ERR(status)) {
413 goto done;
416 fill_in_password_policy(response, &password_policy);
418 done:
419 TALLOC_FREE(frame);
420 return NT_STATUS_OK;
423 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
424 TALLOC_CTX *mem_ctx,
425 uint16 *lockout_threshold)
427 struct winbindd_methods *methods;
428 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
429 struct samr_DomInfo12 lockout_policy;
431 *lockout_threshold = 0;
433 methods = domain->methods;
435 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
436 if (NT_STATUS_IS_ERR(status)) {
437 return status;
440 *lockout_threshold = lockout_policy.lockout_threshold;
442 return NT_STATUS_OK;
445 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
446 TALLOC_CTX *mem_ctx,
447 uint32 *password_properties)
449 struct winbindd_methods *methods;
450 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
451 struct samr_DomInfo1 password_policy;
453 *password_properties = 0;
455 methods = domain->methods;
457 status = methods->password_policy(domain, mem_ctx, &password_policy);
458 if (NT_STATUS_IS_ERR(status)) {
459 return status;
462 *password_properties = password_policy.password_properties;
464 return NT_STATUS_OK;
467 #ifdef HAVE_KRB5
469 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
470 const char *type,
471 uid_t uid,
472 const char **user_ccache_file)
474 /* accept FILE and WRFILE as krb5_cc_type from the client and then
475 * build the full ccname string based on the user's uid here -
476 * Guenther*/
478 const char *gen_cc = NULL;
480 if (uid != -1) {
481 if (strequal(type, "FILE")) {
482 gen_cc = talloc_asprintf(
483 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
485 if (strequal(type, "WRFILE")) {
486 gen_cc = talloc_asprintf(
487 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
491 *user_ccache_file = gen_cc;
493 if (gen_cc == NULL) {
494 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
496 if (gen_cc == NULL) {
497 DEBUG(0,("out of memory\n"));
498 return NULL;
501 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
502 (*user_ccache_file == NULL) ? " (internal)":""));
504 return gen_cc;
507 #endif
509 uid_t get_uid_from_request(struct winbindd_request *request)
511 uid_t uid;
513 uid = request->data.auth.uid;
515 if (uid < 0) {
516 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
517 return -1;
519 return uid;
522 /**********************************************************************
523 Authenticate a user with a clear text password using Kerberos and fill up
524 ccache if required
525 **********************************************************************/
527 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
528 struct winbindd_domain *domain,
529 const char *user,
530 const char *pass,
531 const char *krb5_cc_type,
532 uid_t uid,
533 struct netr_SamInfo3 **info3,
534 fstring krb5ccname)
536 #ifdef HAVE_KRB5
537 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
538 krb5_error_code krb5_ret;
539 const char *cc = NULL;
540 const char *principal_s = NULL;
541 const char *service = NULL;
542 char *realm = NULL;
543 fstring name_domain, name_user;
544 time_t ticket_lifetime = 0;
545 time_t renewal_until = 0;
546 ADS_STRUCT *ads;
547 time_t time_offset = 0;
548 const char *user_ccache_file;
549 struct PAC_LOGON_INFO *logon_info = NULL;
551 *info3 = NULL;
553 /* 1st step:
554 * prepare a krb5_cc_cache string for the user */
556 if (uid == -1) {
557 DEBUG(0,("no valid uid\n"));
560 cc = generate_krb5_ccache(mem_ctx,
561 krb5_cc_type,
562 uid,
563 &user_ccache_file);
564 if (cc == NULL) {
565 return NT_STATUS_NO_MEMORY;
569 /* 2nd step:
570 * get kerberos properties */
572 if (domain->private_data) {
573 ads = (ADS_STRUCT *)domain->private_data;
574 time_offset = ads->auth.time_offset;
578 /* 3rd step:
579 * do kerberos auth and setup ccache as the user */
581 parse_domain_user(user, name_domain, name_user);
583 realm = domain->alt_name;
584 strupper_m(realm);
586 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
587 if (principal_s == NULL) {
588 return NT_STATUS_NO_MEMORY;
591 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
592 if (service == NULL) {
593 return NT_STATUS_NO_MEMORY;
596 /* if this is a user ccache, we need to act as the user to let the krb5
597 * library handle the chown, etc. */
599 /************************ ENTERING NON-ROOT **********************/
601 if (user_ccache_file != NULL) {
602 set_effective_uid(uid);
603 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
606 result = kerberos_return_pac(mem_ctx,
607 principal_s,
608 pass,
609 time_offset,
610 &ticket_lifetime,
611 &renewal_until,
613 true,
614 true,
615 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
616 NULL,
617 &logon_info);
618 if (user_ccache_file != NULL) {
619 gain_root_privilege();
622 /************************ RETURNED TO ROOT **********************/
624 if (!NT_STATUS_IS_OK(result)) {
625 goto failed;
628 *info3 = &logon_info->info3;
630 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
631 principal_s));
633 /* if we had a user's ccache then return that string for the pam
634 * environment */
636 if (user_ccache_file != NULL) {
638 fstrcpy(krb5ccname, user_ccache_file);
640 result = add_ccache_to_list(principal_s,
642 service,
643 user,
644 realm,
645 uid,
646 time(NULL),
647 ticket_lifetime,
648 renewal_until,
649 false);
651 if (!NT_STATUS_IS_OK(result)) {
652 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
653 nt_errstr(result)));
655 } else {
657 /* need to delete the memory cred cache, it is not used anymore */
659 krb5_ret = ads_kdestroy(cc);
660 if (krb5_ret) {
661 DEBUG(3,("winbindd_raw_kerberos_login: "
662 "could not destroy krb5 credential cache: "
663 "%s\n", error_message(krb5_ret)));
668 return NT_STATUS_OK;
670 failed:
672 /* we could have created a new credential cache with a valid tgt in it
673 * but we werent able to get or verify the service ticket for this
674 * local host and therefor didn't get the PAC, we need to remove that
675 * cache entirely now */
677 krb5_ret = ads_kdestroy(cc);
678 if (krb5_ret) {
679 DEBUG(3,("winbindd_raw_kerberos_login: "
680 "could not destroy krb5 credential cache: "
681 "%s\n", error_message(krb5_ret)));
684 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
685 DEBUG(3,("winbindd_raw_kerberos_login: "
686 "could not remove ccache for user %s\n",
687 user));
690 return result;
691 #else
692 return NT_STATUS_NOT_SUPPORTED;
693 #endif /* HAVE_KRB5 */
696 /****************************************************************
697 ****************************************************************/
699 bool check_request_flags(uint32_t flags)
701 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
702 WBFLAG_PAM_INFO3_TEXT |
703 WBFLAG_PAM_INFO3_NDR;
705 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
706 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
707 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
708 !(flags & flags_edata) ) {
709 return true;
712 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
713 flags));
715 return false;
718 /****************************************************************
719 ****************************************************************/
721 static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
722 struct winbindd_response *resp,
723 uint32_t request_flags,
724 struct netr_SamInfo3 *info3,
725 const char *name_domain,
726 const char *name_user)
728 NTSTATUS result;
730 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
731 memcpy(resp->data.auth.user_session_key,
732 info3->base.key.key,
733 sizeof(resp->data.auth.user_session_key)
734 /* 16 */);
737 if (request_flags & WBFLAG_PAM_LMKEY) {
738 memcpy(resp->data.auth.first_8_lm_hash,
739 info3->base.LMSessKey.key,
740 sizeof(resp->data.auth.first_8_lm_hash)
741 /* 8 */);
744 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
745 result = append_unix_username(mem_ctx, resp,
746 info3, name_domain, name_user);
747 if (!NT_STATUS_IS_OK(result)) {
748 DEBUG(10,("Failed to append Unix Username: %s\n",
749 nt_errstr(result)));
750 return result;
754 /* currently, anything from here on potentially overwrites extra_data. */
756 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
757 result = append_info3_as_ndr(mem_ctx, resp, info3);
758 if (!NT_STATUS_IS_OK(result)) {
759 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
760 nt_errstr(result)));
761 return result;
765 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
766 result = append_info3_as_txt(mem_ctx, resp, info3);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
769 nt_errstr(result)));
770 return result;
774 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
775 result = append_afs_token(mem_ctx, resp,
776 info3, name_domain, name_user);
777 if (!NT_STATUS_IS_OK(result)) {
778 DEBUG(10,("Failed to append AFS token: %s\n",
779 nt_errstr(result)));
780 return result;
784 return NT_STATUS_OK;
787 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
788 struct winbindd_cli_state *state,
789 struct netr_SamInfo3 **info3)
791 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
792 uint16 max_allowed_bad_attempts;
793 fstring name_domain, name_user;
794 struct dom_sid sid;
795 enum lsa_SidType type;
796 uchar new_nt_pass[NT_HASH_LEN];
797 const uint8 *cached_nt_pass;
798 const uint8 *cached_salt;
799 struct netr_SamInfo3 *my_info3;
800 time_t kickoff_time, must_change_time;
801 bool password_good = false;
802 #ifdef HAVE_KRB5
803 struct winbindd_tdc_domain *tdc_domain = NULL;
804 #endif
806 *info3 = NULL;
808 ZERO_STRUCTP(info3);
810 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
812 /* Parse domain and username */
814 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
817 if (!lookup_cached_name(name_domain,
818 name_user,
819 &sid,
820 &type)) {
821 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
822 return NT_STATUS_NO_SUCH_USER;
825 if (type != SID_NAME_USER) {
826 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
827 return NT_STATUS_LOGON_FAILURE;
830 result = winbindd_get_creds(domain,
831 state->mem_ctx,
832 &sid,
833 &my_info3,
834 &cached_nt_pass,
835 &cached_salt);
836 if (!NT_STATUS_IS_OK(result)) {
837 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
838 return result;
841 *info3 = my_info3;
843 E_md4hash(state->request->data.auth.pass, new_nt_pass);
845 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
846 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
847 if (cached_salt) {
848 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
851 if (cached_salt) {
852 /* In this case we didn't store the nt_hash itself,
853 but the MD5 combination of salt + nt_hash. */
854 uchar salted_hash[NT_HASH_LEN];
855 E_md5hash(cached_salt, new_nt_pass, salted_hash);
857 password_good = (memcmp(cached_nt_pass, salted_hash,
858 NT_HASH_LEN) == 0);
859 } else {
860 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
861 password_good = (memcmp(cached_nt_pass, new_nt_pass,
862 NT_HASH_LEN) == 0);
865 if (password_good) {
867 /* User *DOES* know the password, update logon_time and reset
868 * bad_pw_count */
870 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
872 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
873 return NT_STATUS_ACCOUNT_LOCKED_OUT;
876 if (my_info3->base.acct_flags & ACB_DISABLED) {
877 return NT_STATUS_ACCOUNT_DISABLED;
880 if (my_info3->base.acct_flags & ACB_WSTRUST) {
881 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
884 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
885 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
888 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
889 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
892 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
893 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
894 my_info3->base.acct_flags));
895 return NT_STATUS_LOGON_FAILURE;
898 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
899 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
900 return NT_STATUS_ACCOUNT_EXPIRED;
903 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
904 if (must_change_time != 0 && must_change_time < time(NULL)) {
905 /* we allow grace logons when the password has expired */
906 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
907 /* return NT_STATUS_PASSWORD_EXPIRED; */
908 goto success;
911 #ifdef HAVE_KRB5
912 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
913 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
914 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
915 /* used to cope with the case winbindd starting without network. */
916 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
918 uid_t uid = -1;
919 const char *cc = NULL;
920 char *realm = NULL;
921 const char *principal_s = NULL;
922 const char *service = NULL;
923 const char *user_ccache_file;
925 uid = get_uid_from_request(state->request);
926 if (uid == -1) {
927 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
928 return NT_STATUS_INVALID_PARAMETER;
931 cc = generate_krb5_ccache(state->mem_ctx,
932 state->request->data.auth.krb5_cc_type,
933 state->request->data.auth.uid,
934 &user_ccache_file);
935 if (cc == NULL) {
936 return NT_STATUS_NO_MEMORY;
939 realm = domain->alt_name;
940 strupper_m(realm);
942 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
943 if (principal_s == NULL) {
944 return NT_STATUS_NO_MEMORY;
947 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
948 if (service == NULL) {
949 return NT_STATUS_NO_MEMORY;
952 if (user_ccache_file != NULL) {
954 fstrcpy(state->response->data.auth.krb5ccname,
955 user_ccache_file);
957 result = add_ccache_to_list(principal_s,
959 service,
960 state->request->data.auth.user,
961 domain->alt_name,
962 uid,
963 time(NULL),
964 time(NULL) + lp_winbind_cache_time(),
965 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
966 true);
968 if (!NT_STATUS_IS_OK(result)) {
969 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
970 "to add ccache to list: %s\n",
971 nt_errstr(result)));
975 #endif /* HAVE_KRB5 */
976 success:
977 /* FIXME: we possibly should handle logon hours as well (does xp when
978 * offline?) see auth/auth_sam.c:sam_account_ok for details */
980 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
981 my_info3->base.bad_password_count = 0;
983 result = winbindd_update_creds_by_info3(domain,
984 state->request->data.auth.user,
985 state->request->data.auth.pass,
986 my_info3);
987 if (!NT_STATUS_IS_OK(result)) {
988 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
989 nt_errstr(result)));
990 return result;
993 return NT_STATUS_OK;
997 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
998 if (domain->online == false) {
999 goto failed;
1002 /* failure of this is not critical */
1003 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1004 if (!NT_STATUS_IS_OK(result)) {
1005 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1006 "Won't be able to honour account lockout policies\n"));
1009 /* increase counter */
1010 my_info3->base.bad_password_count++;
1012 if (max_allowed_bad_attempts == 0) {
1013 goto failed;
1016 /* lockout user */
1017 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1019 uint32 password_properties;
1021 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1022 if (!NT_STATUS_IS_OK(result)) {
1023 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1026 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1027 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1028 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1032 failed:
1033 result = winbindd_update_creds_by_info3(domain,
1034 state->request->data.auth.user,
1035 NULL,
1036 my_info3);
1038 if (!NT_STATUS_IS_OK(result)) {
1039 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1040 nt_errstr(result)));
1043 return NT_STATUS_LOGON_FAILURE;
1046 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1047 struct winbindd_cli_state *state,
1048 struct netr_SamInfo3 **info3)
1050 struct winbindd_domain *contact_domain;
1051 fstring name_domain, name_user;
1052 NTSTATUS result;
1054 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1056 /* Parse domain and username */
1058 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1060 /* what domain should we contact? */
1062 if ( IS_DC ) {
1063 if (!(contact_domain = find_domain_from_name(name_domain))) {
1064 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1065 state->request->data.auth.user, name_domain, name_user, name_domain));
1066 result = NT_STATUS_NO_SUCH_USER;
1067 goto done;
1070 } else {
1071 if (is_myname(name_domain)) {
1072 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1073 result = NT_STATUS_NO_SUCH_USER;
1074 goto done;
1077 contact_domain = find_domain_from_name(name_domain);
1078 if (contact_domain == NULL) {
1079 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1080 state->request->data.auth.user, name_domain, name_user, name_domain));
1082 result = NT_STATUS_NO_SUCH_USER;
1083 goto done;
1087 if (contact_domain->initialized &&
1088 contact_domain->active_directory) {
1089 goto try_login;
1092 if (!contact_domain->initialized) {
1093 init_dc_connection(contact_domain);
1096 if (!contact_domain->active_directory) {
1097 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1098 return NT_STATUS_INVALID_LOGON_TYPE;
1100 try_login:
1101 result = winbindd_raw_kerberos_login(
1102 state->mem_ctx, contact_domain,
1103 state->request->data.auth.user,
1104 state->request->data.auth.pass,
1105 state->request->data.auth.krb5_cc_type,
1106 get_uid_from_request(state->request),
1107 info3, state->response->data.auth.krb5ccname);
1108 done:
1109 return result;
1112 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1113 uint32_t logon_parameters,
1114 const char *domain, const char *user,
1115 const DATA_BLOB *challenge,
1116 const DATA_BLOB *lm_resp,
1117 const DATA_BLOB *nt_resp,
1118 struct netr_SamInfo3 **pinfo3)
1120 struct auth_usersupplied_info *user_info = NULL;
1121 struct tsocket_address *local;
1122 NTSTATUS status;
1123 int rc;
1125 rc = tsocket_address_inet_from_strings(mem_ctx,
1126 "ip",
1127 "127.0.0.1",
1129 &local);
1130 if (rc < 0) {
1131 return NT_STATUS_NO_MEMORY;
1133 status = make_user_info(&user_info, user, user, domain, domain,
1134 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1135 NULL, AUTH_PASSWORD_RESPONSE);
1136 if (!NT_STATUS_IS_OK(status)) {
1137 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1138 return status;
1140 user_info->logon_parameters = logon_parameters;
1142 /* We don't want any more mapping of the username */
1143 user_info->mapped_state = True;
1145 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1146 pinfo3);
1147 free_user_info(&user_info);
1148 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1149 user, nt_errstr(status)));
1150 return status;
1153 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1154 TALLOC_CTX *mem_ctx,
1155 uint32_t logon_parameters,
1156 const char *server,
1157 const char *username,
1158 const char *domainname,
1159 const char *workstation,
1160 const uint8_t chal[8],
1161 DATA_BLOB lm_response,
1162 DATA_BLOB nt_response,
1163 struct netr_SamInfo3 **info3)
1165 int attempts = 0;
1166 bool retry = false;
1167 NTSTATUS result;
1169 do {
1170 struct rpc_pipe_client *netlogon_pipe;
1171 const struct pipe_auth_data *auth;
1172 uint32_t neg_flags = 0;
1174 ZERO_STRUCTP(info3);
1175 retry = false;
1177 result = cm_connect_netlogon(domain, &netlogon_pipe);
1179 if (!NT_STATUS_IS_OK(result)) {
1180 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1181 nt_errstr(result)));
1182 return result;
1184 auth = netlogon_pipe->auth;
1185 if (netlogon_pipe->dc) {
1186 neg_flags = netlogon_pipe->dc->negotiate_flags;
1189 /* It is really important to try SamLogonEx here,
1190 * because in a clustered environment, we want to use
1191 * one machine account from multiple physical
1192 * computers.
1194 * With a normal SamLogon call, we must keep the
1195 * credentials chain updated and intact between all
1196 * users of the machine account (which would imply
1197 * cross-node communication for every NTLM logon).
1199 * (The credentials chain is not per NETLOGON pipe
1200 * connection, but globally on the server/client pair
1201 * by machine name).
1203 * When using SamLogonEx, the credentials are not
1204 * supplied, but the session key is implied by the
1205 * wrapping SamLogon context.
1207 * -- abartlet 21 April 2008
1209 * It's also important to use NetlogonValidationSamInfo4 (6),
1210 * because it relies on the rpc transport encryption
1211 * and avoids using the global netlogon schannel
1212 * session key to en/decrypt secret information
1213 * like the user_session_key for network logons.
1215 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1216 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1217 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1218 * are the indication that the server supports
1219 * NetlogonValidationSamInfo4 (6). And it must only
1220 * be used if "SealSecureChannel" is used.
1222 * -- metze 4 February 2011
1225 if (auth == NULL) {
1226 domain->can_do_validation6 = false;
1227 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1228 domain->can_do_validation6 = false;
1229 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1230 domain->can_do_validation6 = false;
1231 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1232 domain->can_do_validation6 = false;
1233 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1234 domain->can_do_validation6 = false;
1237 if (domain->can_do_samlogon_ex) {
1238 result = rpccli_netlogon_sam_network_logon_ex(
1239 netlogon_pipe,
1240 mem_ctx,
1241 logon_parameters,
1242 server, /* server name */
1243 username, /* user name */
1244 domainname, /* target domain */
1245 workstation, /* workstation */
1246 chal,
1247 domain->can_do_validation6 ? 6 : 3,
1248 lm_response,
1249 nt_response,
1250 info3);
1251 } else {
1252 result = rpccli_netlogon_sam_network_logon(
1253 netlogon_pipe,
1254 mem_ctx,
1255 logon_parameters,
1256 server, /* server name */
1257 username, /* user name */
1258 domainname, /* target domain */
1259 workstation, /* workstation */
1260 chal,
1261 domain->can_do_validation6 ? 6 : 3,
1262 lm_response,
1263 nt_response,
1264 info3);
1267 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1270 * It's likely that the server also does not support
1271 * validation level 6
1273 domain->can_do_validation6 = false;
1275 if (domain->can_do_samlogon_ex) {
1276 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1277 "retrying with NetSamLogon\n"));
1278 domain->can_do_samlogon_ex = false;
1279 retry = true;
1280 continue;
1284 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1285 * (no Ex). This happens against old Samba
1286 * DCs. Drop the connection.
1288 invalidate_cm_connection(&domain->conn);
1289 result = NT_STATUS_LOGON_FAILURE;
1290 break;
1293 if (domain->can_do_validation6 &&
1294 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1295 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1296 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1297 DEBUG(3,("Got a DC that can not do validation level 6, "
1298 "retrying with level 3\n"));
1299 domain->can_do_validation6 = false;
1300 retry = true;
1301 continue;
1305 * we increment this after the "feature negotiation"
1306 * for can_do_samlogon_ex and can_do_validation6
1308 attempts += 1;
1310 /* We have to try a second time as cm_connect_netlogon
1311 might not yet have noticed that the DC has killed
1312 our connection. */
1314 if (!rpccli_is_connected(netlogon_pipe)) {
1315 retry = true;
1316 continue;
1319 /* if we get access denied, a possible cause was that we had
1320 and open connection to the DC, but someone changed our
1321 machine account password out from underneath us using 'net
1322 rpc changetrustpw' */
1324 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1325 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1326 "ACCESS_DENIED. Maybe the trust account "
1327 "password was changed and we didn't know it. "
1328 "Killing connections to domain %s\n",
1329 domainname));
1330 invalidate_cm_connection(&domain->conn);
1331 retry = true;
1334 } while ( (attempts < 2) && retry );
1336 return result;
1339 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1340 struct winbindd_domain *domain,
1341 const char *user,
1342 const char *pass,
1343 uint32_t request_flags,
1344 struct netr_SamInfo3 **info3)
1347 uchar chal[8];
1348 DATA_BLOB lm_resp;
1349 DATA_BLOB nt_resp;
1350 unsigned char local_nt_response[24];
1351 fstring name_domain, name_user;
1352 NTSTATUS result;
1353 struct netr_SamInfo3 *my_info3 = NULL;
1355 *info3 = NULL;
1357 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1359 /* Parse domain and username */
1361 parse_domain_user(user, name_domain, name_user);
1363 /* do password magic */
1365 generate_random_buffer(chal, sizeof(chal));
1367 if (lp_client_ntlmv2_auth()) {
1368 DATA_BLOB server_chal;
1369 DATA_BLOB names_blob;
1370 server_chal = data_blob_const(chal, 8);
1372 /* note that the 'workgroup' here is for the local
1373 machine. The 'server name' must match the
1374 'workstation' passed to the actual SamLogon call.
1376 names_blob = NTLMv2_generate_names_blob(
1377 mem_ctx, lp_netbios_name(), lp_workgroup());
1379 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1380 pass,
1381 &server_chal,
1382 &names_blob,
1383 &lm_resp, &nt_resp, NULL, NULL)) {
1384 data_blob_free(&names_blob);
1385 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1386 result = NT_STATUS_NO_MEMORY;
1387 goto done;
1389 data_blob_free(&names_blob);
1390 } else {
1391 lm_resp = data_blob_null;
1392 SMBNTencrypt(pass, chal, local_nt_response);
1394 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1395 sizeof(local_nt_response));
1398 if (strequal(name_domain, get_global_sam_name())) {
1399 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1401 result = winbindd_dual_auth_passdb(
1402 mem_ctx, 0, name_domain, name_user,
1403 &chal_blob, &lm_resp, &nt_resp, info3);
1404 goto done;
1407 /* check authentication loop */
1409 result = winbind_samlogon_retry_loop(domain,
1410 mem_ctx,
1412 domain->dcname,
1413 name_user,
1414 name_domain,
1415 lp_netbios_name(),
1416 chal,
1417 lm_resp,
1418 nt_resp,
1419 &my_info3);
1420 if (!NT_STATUS_IS_OK(result)) {
1421 goto done;
1424 /* handle the case where a NT4 DC does not fill in the acct_flags in
1425 * the samlogon reply info3. When accurate info3 is required by the
1426 * caller, we look up the account flags ourselve - gd */
1428 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1429 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1431 struct rpc_pipe_client *samr_pipe;
1432 struct policy_handle samr_domain_handle, user_pol;
1433 union samr_UserInfo *info = NULL;
1434 NTSTATUS status_tmp, result_tmp;
1435 uint32 acct_flags;
1436 struct dcerpc_binding_handle *b;
1438 status_tmp = cm_connect_sam(domain, mem_ctx,
1439 &samr_pipe, &samr_domain_handle);
1441 if (!NT_STATUS_IS_OK(status_tmp)) {
1442 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1443 nt_errstr(status_tmp)));
1444 goto done;
1447 b = samr_pipe->binding_handle;
1449 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1450 &samr_domain_handle,
1451 MAXIMUM_ALLOWED_ACCESS,
1452 my_info3->base.rid,
1453 &user_pol,
1454 &result_tmp);
1456 if (!NT_STATUS_IS_OK(status_tmp)) {
1457 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1458 nt_errstr(status_tmp)));
1459 goto done;
1461 if (!NT_STATUS_IS_OK(result_tmp)) {
1462 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1463 nt_errstr(result_tmp)));
1464 goto done;
1467 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1468 &user_pol,
1470 &info,
1471 &result_tmp);
1473 if (!NT_STATUS_IS_OK(status_tmp)) {
1474 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1475 nt_errstr(status_tmp)));
1476 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1477 goto done;
1479 if (!NT_STATUS_IS_OK(result_tmp)) {
1480 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1481 nt_errstr(result_tmp)));
1482 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1483 goto done;
1486 acct_flags = info->info16.acct_flags;
1488 if (acct_flags == 0) {
1489 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1490 goto done;
1493 my_info3->base.acct_flags = acct_flags;
1495 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1497 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1500 *info3 = my_info3;
1501 done:
1502 return result;
1505 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1506 struct winbindd_cli_state *state)
1508 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1509 NTSTATUS krb5_result = NT_STATUS_OK;
1510 fstring name_domain, name_user;
1511 char *mapped_user;
1512 fstring domain_user;
1513 struct netr_SamInfo3 *info3 = NULL;
1514 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1516 /* Ensure null termination */
1517 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1519 /* Ensure null termination */
1520 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1522 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1523 state->request->data.auth.user));
1525 /* Parse domain and username */
1527 name_map_status = normalize_name_unmap(state->mem_ctx,
1528 state->request->data.auth.user,
1529 &mapped_user);
1531 /* If the name normalization didnt' actually do anything,
1532 just use the original name */
1534 if (!NT_STATUS_IS_OK(name_map_status) &&
1535 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1537 mapped_user = state->request->data.auth.user;
1540 parse_domain_user(mapped_user, name_domain, name_user);
1542 if ( mapped_user != state->request->data.auth.user ) {
1543 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1544 *lp_winbind_separator(),
1545 name_user );
1546 strlcpy( state->request->data.auth.user, domain_user,
1547 sizeof(state->request->data.auth.user));
1550 if (!domain->online) {
1551 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1552 if (domain->startup) {
1553 /* Logons are very important to users. If we're offline and
1554 we get a request within the first 30 seconds of startup,
1555 try very hard to find a DC and go online. */
1557 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1558 "request in startup mode.\n", domain->name ));
1560 winbindd_flush_negative_conn_cache(domain);
1561 result = init_dc_connection(domain);
1565 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1567 /* Check for Kerberos authentication */
1568 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1570 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1571 /* save for later */
1572 krb5_result = result;
1575 if (NT_STATUS_IS_OK(result)) {
1576 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1577 goto process_result;
1578 } else {
1579 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1582 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1583 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1584 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1585 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1586 set_domain_offline( domain );
1587 goto cached_logon;
1590 /* there are quite some NT_STATUS errors where there is no
1591 * point in retrying with a samlogon, we explictly have to take
1592 * care not to increase the bad logon counter on the DC */
1594 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1595 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1596 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1597 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1598 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1599 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1600 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1601 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1602 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1603 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1604 goto done;
1607 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1608 DEBUG(3,("falling back to samlogon\n"));
1609 goto sam_logon;
1610 } else {
1611 goto cached_logon;
1615 sam_logon:
1616 /* Check for Samlogon authentication */
1617 if (domain->online) {
1618 result = winbindd_dual_pam_auth_samlogon(
1619 state->mem_ctx, domain,
1620 state->request->data.auth.user,
1621 state->request->data.auth.pass,
1622 state->request->flags,
1623 &info3);
1625 if (NT_STATUS_IS_OK(result)) {
1626 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1627 /* add the Krb5 err if we have one */
1628 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1629 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1631 goto process_result;
1634 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1635 nt_errstr(result)));
1637 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1638 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1639 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1641 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1642 set_domain_offline( domain );
1643 goto cached_logon;
1646 if (domain->online) {
1647 /* We're still online - fail. */
1648 goto done;
1652 cached_logon:
1653 /* Check for Cached logons */
1654 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1655 lp_winbind_offline_logon()) {
1657 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1659 if (NT_STATUS_IS_OK(result)) {
1660 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1661 goto process_result;
1662 } else {
1663 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1664 goto done;
1668 process_result:
1670 if (NT_STATUS_IS_OK(result)) {
1672 struct dom_sid user_sid;
1674 /* In all codepaths where result == NT_STATUS_OK info3 must have
1675 been initialized. */
1676 if (!info3) {
1677 result = NT_STATUS_INTERNAL_ERROR;
1678 goto done;
1681 sid_compose(&user_sid, info3->base.domain_sid,
1682 info3->base.rid);
1684 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1685 &user_sid);
1686 netsamlogon_cache_store(name_user, info3);
1688 /* save name_to_sid info as early as possible (only if
1689 this is our primary domain so we don't invalidate
1690 the cache entry by storing the seq_num for the wrong
1691 domain). */
1692 if ( domain->primary ) {
1693 cache_name2sid(domain, name_domain, name_user,
1694 SID_NAME_USER, &user_sid);
1697 /* Check if the user is in the right group */
1699 result = check_info3_in_group(
1700 info3,
1701 state->request->data.auth.require_membership_of_sid);
1702 if (!NT_STATUS_IS_OK(result)) {
1703 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1704 state->request->data.auth.user,
1705 state->request->data.auth.require_membership_of_sid));
1706 goto done;
1709 result = append_auth_data(state->mem_ctx, state->response,
1710 state->request->flags, info3,
1711 name_domain, name_user);
1712 if (!NT_STATUS_IS_OK(result)) {
1713 goto done;
1716 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1717 && lp_winbind_offline_logon()) {
1719 result = winbindd_store_creds(domain,
1720 state->request->data.auth.user,
1721 state->request->data.auth.pass,
1722 info3);
1725 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1726 struct winbindd_domain *our_domain = find_our_domain();
1728 /* This is not entirely correct I believe, but it is
1729 consistent. Only apply the password policy settings
1730 too warn users for our own domain. Cannot obtain these
1731 from trusted DCs all the time so don't do it at all.
1732 -- jerry */
1734 result = NT_STATUS_NOT_SUPPORTED;
1735 if (our_domain == domain ) {
1736 result = fillup_password_policy(
1737 our_domain, state->response);
1740 if (!NT_STATUS_IS_OK(result)
1741 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1743 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1744 domain->name, nt_errstr(result)));
1745 goto done;
1749 result = NT_STATUS_OK;
1752 done:
1753 /* give us a more useful (more correct?) error code */
1754 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1755 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1756 result = NT_STATUS_NO_LOGON_SERVERS;
1759 set_auth_errors(state->response, result);
1761 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1762 state->request->data.auth.user,
1763 state->response->data.auth.nt_status_string,
1764 state->response->data.auth.pam_error));
1766 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1769 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1770 struct winbindd_cli_state *state)
1772 NTSTATUS result;
1773 struct netr_SamInfo3 *info3 = NULL;
1774 const char *name_user = NULL;
1775 const char *name_domain = NULL;
1776 const char *workstation;
1778 DATA_BLOB lm_resp, nt_resp;
1780 /* This is child-only, so no check for privileged access is needed
1781 anymore */
1783 /* Ensure null termination */
1784 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1785 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1787 name_user = state->request->data.auth_crap.user;
1788 name_domain = state->request->data.auth_crap.domain;
1789 workstation = state->request->data.auth_crap.workstation;
1791 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1792 name_domain, name_user));
1794 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1795 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1796 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1797 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1798 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1799 state->request->data.auth_crap.lm_resp_len,
1800 state->request->data.auth_crap.nt_resp_len));
1801 result = NT_STATUS_INVALID_PARAMETER;
1802 goto done;
1806 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1807 state->request->data.auth_crap.lm_resp_len);
1809 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1810 nt_resp = data_blob_talloc(state->mem_ctx,
1811 state->request->extra_data.data,
1812 state->request->data.auth_crap.nt_resp_len);
1813 } else {
1814 nt_resp = data_blob_talloc(state->mem_ctx,
1815 state->request->data.auth_crap.nt_resp,
1816 state->request->data.auth_crap.nt_resp_len);
1819 if (strequal(name_domain, get_global_sam_name())) {
1820 DATA_BLOB chal_blob = data_blob_const(
1821 state->request->data.auth_crap.chal,
1822 sizeof(state->request->data.auth_crap.chal));
1824 result = winbindd_dual_auth_passdb(
1825 state->mem_ctx,
1826 state->request->data.auth_crap.logon_parameters,
1827 name_domain, name_user,
1828 &chal_blob, &lm_resp, &nt_resp, &info3);
1829 goto process_result;
1832 result = winbind_samlogon_retry_loop(domain,
1833 state->mem_ctx,
1834 state->request->data.auth_crap.logon_parameters,
1835 domain->dcname,
1836 name_user,
1837 name_domain,
1838 /* Bug #3248 - found by Stefan Burkei. */
1839 workstation, /* We carefully set this above so use it... */
1840 state->request->data.auth_crap.chal,
1841 lm_resp,
1842 nt_resp,
1843 &info3);
1844 if (!NT_STATUS_IS_OK(result)) {
1845 goto done;
1848 process_result:
1850 if (NT_STATUS_IS_OK(result)) {
1851 struct dom_sid user_sid;
1853 sid_compose(&user_sid, info3->base.domain_sid,
1854 info3->base.rid);
1855 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1856 &user_sid);
1857 netsamlogon_cache_store(name_user, info3);
1859 /* Check if the user is in the right group */
1861 result = check_info3_in_group(
1862 info3,
1863 state->request->data.auth_crap.require_membership_of_sid);
1864 if (!NT_STATUS_IS_OK(result)) {
1865 DEBUG(3, ("User %s is not in the required group (%s), so "
1866 "crap authentication is rejected\n",
1867 state->request->data.auth_crap.user,
1868 state->request->data.auth_crap.require_membership_of_sid));
1869 goto done;
1872 result = append_auth_data(state->mem_ctx, state->response,
1873 state->request->flags, info3,
1874 name_domain, name_user);
1875 if (!NT_STATUS_IS_OK(result)) {
1876 goto done;
1880 done:
1882 /* give us a more useful (more correct?) error code */
1883 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1884 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1885 result = NT_STATUS_NO_LOGON_SERVERS;
1888 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1889 result = nt_status_squash(result);
1892 set_auth_errors(state->response, result);
1894 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1895 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1896 name_domain,
1897 name_user,
1898 state->response->data.auth.nt_status_string,
1899 state->response->data.auth.pam_error));
1901 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1904 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1905 struct winbindd_cli_state *state)
1907 char *oldpass;
1908 char *newpass = NULL;
1909 struct policy_handle dom_pol;
1910 struct rpc_pipe_client *cli = NULL;
1911 bool got_info = false;
1912 struct samr_DomInfo1 *info = NULL;
1913 struct userPwdChangeFailureInformation *reject = NULL;
1914 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1915 fstring domain, user;
1916 struct dcerpc_binding_handle *b = NULL;
1918 ZERO_STRUCT(dom_pol);
1920 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1921 state->request->data.auth.user));
1923 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1924 goto done;
1927 /* Change password */
1929 oldpass = state->request->data.chauthtok.oldpass;
1930 newpass = state->request->data.chauthtok.newpass;
1932 /* Initialize reject reason */
1933 state->response->data.auth.reject_reason = Undefined;
1935 /* Get sam handle */
1937 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1938 &dom_pol);
1939 if (!NT_STATUS_IS_OK(result)) {
1940 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1941 goto done;
1944 b = cli->binding_handle;
1946 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1947 user,
1948 newpass,
1949 oldpass,
1950 &info,
1951 &reject);
1953 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1955 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1957 fill_in_password_policy(state->response, info);
1959 state->response->data.auth.reject_reason =
1960 reject->extendedFailureReason;
1962 got_info = true;
1965 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1966 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1967 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1968 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1970 /* only fallback when the chgpasswd_user3 call is not supported */
1971 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1972 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1973 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1974 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1976 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1977 nt_errstr(result)));
1979 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1981 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1982 Map to the same status code as Windows 2003. */
1984 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1985 result = NT_STATUS_PASSWORD_RESTRICTION;
1989 done:
1991 if (NT_STATUS_IS_OK(result)
1992 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1993 && lp_winbind_offline_logon()) {
1994 result = winbindd_update_creds_by_name(contact_domain, user,
1995 newpass);
1996 /* Again, this happens when we login from gdm or xdm
1997 * and the password expires, *BUT* cached crendentials
1998 * doesn't exist. winbindd_update_creds_by_name()
1999 * returns NT_STATUS_NO_SUCH_USER.
2000 * This is not a failure.
2001 * --- BoYang
2002 * */
2003 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2004 result = NT_STATUS_OK;
2007 if (!NT_STATUS_IS_OK(result)) {
2008 DEBUG(10, ("Failed to store creds: %s\n",
2009 nt_errstr(result)));
2010 goto process_result;
2014 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2016 NTSTATUS policy_ret;
2018 policy_ret = fillup_password_policy(
2019 contact_domain, state->response);
2021 /* failure of this is non critical, it will just provide no
2022 * additional information to the client why the change has
2023 * failed - Guenther */
2025 if (!NT_STATUS_IS_OK(policy_ret)) {
2026 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2027 goto process_result;
2031 process_result:
2033 if (strequal(contact_domain->name, get_global_sam_name())) {
2034 /* FIXME: internal rpc pipe does not cache handles yet */
2035 if (b) {
2036 if (is_valid_policy_hnd(&dom_pol)) {
2037 NTSTATUS _result;
2038 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2040 TALLOC_FREE(cli);
2044 set_auth_errors(state->response, result);
2046 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2047 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2048 domain,
2049 user,
2050 state->response->data.auth.nt_status_string,
2051 state->response->data.auth.pam_error));
2053 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2056 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2057 struct winbindd_cli_state *state)
2059 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2061 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2062 state->request->data.logoff.user));
2064 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2065 result = NT_STATUS_OK;
2066 goto process_result;
2069 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2070 result = NT_STATUS_OK;
2071 goto process_result;
2074 #ifdef HAVE_KRB5
2076 if (state->request->data.logoff.uid < 0) {
2077 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2078 goto process_result;
2081 /* what we need here is to find the corresponding krb5 ccache name *we*
2082 * created for a given username and destroy it */
2084 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2085 result = NT_STATUS_OK;
2086 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2087 goto process_result;
2090 if (!ccache_entry_identical(state->request->data.logoff.user,
2091 state->request->data.logoff.uid,
2092 state->request->data.logoff.krb5ccname)) {
2093 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2094 goto process_result;
2097 result = remove_ccache(state->request->data.logoff.user);
2098 if (!NT_STATUS_IS_OK(result)) {
2099 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2100 nt_errstr(result)));
2101 goto process_result;
2104 #else
2105 result = NT_STATUS_NOT_SUPPORTED;
2106 #endif
2108 process_result:
2111 set_auth_errors(state->response, result);
2113 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2116 /* Change user password with auth crap*/
2118 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2120 NTSTATUS result;
2121 DATA_BLOB new_nt_password;
2122 DATA_BLOB old_nt_hash_enc;
2123 DATA_BLOB new_lm_password;
2124 DATA_BLOB old_lm_hash_enc;
2125 fstring domain,user;
2126 struct policy_handle dom_pol;
2127 struct winbindd_domain *contact_domain = domainSt;
2128 struct rpc_pipe_client *cli = NULL;
2129 struct dcerpc_binding_handle *b = NULL;
2131 ZERO_STRUCT(dom_pol);
2133 /* Ensure null termination */
2134 state->request->data.chng_pswd_auth_crap.user[
2135 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2136 state->request->data.chng_pswd_auth_crap.domain[
2137 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2138 *domain = 0;
2139 *user = 0;
2141 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2142 (unsigned long)state->pid,
2143 state->request->data.chng_pswd_auth_crap.domain,
2144 state->request->data.chng_pswd_auth_crap.user));
2146 if (lp_winbind_offline_logon()) {
2147 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2148 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2149 result = NT_STATUS_ACCESS_DENIED;
2150 goto done;
2153 if (*state->request->data.chng_pswd_auth_crap.domain) {
2154 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2155 } else {
2156 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2157 domain, user);
2159 if(!*domain) {
2160 DEBUG(3,("no domain specified with username (%s) - "
2161 "failing auth\n",
2162 state->request->data.chng_pswd_auth_crap.user));
2163 result = NT_STATUS_NO_SUCH_USER;
2164 goto done;
2168 if (!*domain && lp_winbind_use_default_domain()) {
2169 fstrcpy(domain,lp_workgroup());
2172 if(!*user) {
2173 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2176 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2177 (unsigned long)state->pid, domain, user));
2179 /* Change password */
2180 new_nt_password = data_blob_const(
2181 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2182 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2184 old_nt_hash_enc = data_blob_const(
2185 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2186 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2188 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2189 new_lm_password = data_blob_const(
2190 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2191 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2193 old_lm_hash_enc = data_blob_const(
2194 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2195 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2196 } else {
2197 new_lm_password.length = 0;
2198 old_lm_hash_enc.length = 0;
2201 /* Get sam handle */
2203 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2204 if (!NT_STATUS_IS_OK(result)) {
2205 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2206 goto done;
2209 b = cli->binding_handle;
2211 result = rpccli_samr_chng_pswd_auth_crap(
2212 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2213 new_lm_password, old_lm_hash_enc);
2215 done:
2217 if (strequal(contact_domain->name, get_global_sam_name())) {
2218 /* FIXME: internal rpc pipe does not cache handles yet */
2219 if (b) {
2220 if (is_valid_policy_hnd(&dom_pol)) {
2221 NTSTATUS _result;
2222 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2224 TALLOC_FREE(cli);
2228 set_auth_errors(state->response, result);
2230 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2231 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2232 domain, user,
2233 state->response->data.auth.nt_status_string,
2234 state->response->data.auth.pam_error));
2236 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;