s3-utils: net - Fix one error/usage message
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_pam.c
blob41f38a421d71d25f5cd97077521c60ef73a3e73f
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 contact_domain = find_our_domain();
1086 if (contact_domain->initialized &&
1087 contact_domain->active_directory) {
1088 goto try_login;
1091 if (!contact_domain->initialized) {
1092 init_dc_connection(contact_domain);
1095 if (!contact_domain->active_directory) {
1096 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1097 return NT_STATUS_INVALID_LOGON_TYPE;
1099 try_login:
1100 result = winbindd_raw_kerberos_login(
1101 state->mem_ctx, contact_domain,
1102 state->request->data.auth.user,
1103 state->request->data.auth.pass,
1104 state->request->data.auth.krb5_cc_type,
1105 get_uid_from_request(state->request),
1106 info3, state->response->data.auth.krb5ccname);
1107 done:
1108 return result;
1111 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1112 const char *domain, const char *user,
1113 const DATA_BLOB *challenge,
1114 const DATA_BLOB *lm_resp,
1115 const DATA_BLOB *nt_resp,
1116 struct netr_SamInfo3 **pinfo3)
1118 struct auth_usersupplied_info *user_info = NULL;
1119 struct tsocket_address *local;
1120 NTSTATUS status;
1121 int rc;
1123 rc = tsocket_address_inet_from_strings(mem_ctx,
1124 "ip",
1125 "127.0.0.1",
1127 &local);
1128 if (rc < 0) {
1129 return NT_STATUS_NO_MEMORY;
1131 status = make_user_info(&user_info, user, user, domain, domain,
1132 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1133 NULL, AUTH_PASSWORD_RESPONSE);
1134 if (!NT_STATUS_IS_OK(status)) {
1135 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1136 return status;
1139 /* We don't want any more mapping of the username */
1140 user_info->mapped_state = True;
1142 status = check_sam_security_info3(challenge, talloc_tos(), user_info,
1143 pinfo3);
1144 free_user_info(&user_info);
1145 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1146 user, nt_errstr(status)));
1147 return status;
1150 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1151 TALLOC_CTX *mem_ctx,
1152 uint32_t logon_parameters,
1153 const char *server,
1154 const char *username,
1155 const char *domainname,
1156 const char *workstation,
1157 const uint8_t chal[8],
1158 DATA_BLOB lm_response,
1159 DATA_BLOB nt_response,
1160 struct netr_SamInfo3 **info3)
1162 int attempts = 0;
1163 bool retry = false;
1164 NTSTATUS result;
1166 do {
1167 struct rpc_pipe_client *netlogon_pipe;
1168 const struct pipe_auth_data *auth;
1169 uint32_t neg_flags = 0;
1171 ZERO_STRUCTP(info3);
1172 retry = false;
1174 result = cm_connect_netlogon(domain, &netlogon_pipe);
1176 if (!NT_STATUS_IS_OK(result)) {
1177 DEBUG(3,("could not open handle to NETLOGON pipe (error: %s)\n",
1178 nt_errstr(result)));
1179 return result;
1181 auth = netlogon_pipe->auth;
1182 if (netlogon_pipe->dc) {
1183 neg_flags = netlogon_pipe->dc->negotiate_flags;
1186 /* It is really important to try SamLogonEx here,
1187 * because in a clustered environment, we want to use
1188 * one machine account from multiple physical
1189 * computers.
1191 * With a normal SamLogon call, we must keep the
1192 * credentials chain updated and intact between all
1193 * users of the machine account (which would imply
1194 * cross-node communication for every NTLM logon).
1196 * (The credentials chain is not per NETLOGON pipe
1197 * connection, but globally on the server/client pair
1198 * by machine name).
1200 * When using SamLogonEx, the credentials are not
1201 * supplied, but the session key is implied by the
1202 * wrapping SamLogon context.
1204 * -- abartlet 21 April 2008
1206 * It's also important to use NetlogonValidationSamInfo4 (6),
1207 * because it relies on the rpc transport encryption
1208 * and avoids using the global netlogon schannel
1209 * session key to en/decrypt secret information
1210 * like the user_session_key for network logons.
1212 * [MS-APDS] 3.1.5.2 NTLM Network Logon
1213 * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
1214 * NETLOGON_NEG_AUTHENTICATED_RPC set together
1215 * are the indication that the server supports
1216 * NetlogonValidationSamInfo4 (6). And it must only
1217 * be used if "SealSecureChannel" is used.
1219 * -- metze 4 February 2011
1222 if (auth == NULL) {
1223 domain->can_do_validation6 = false;
1224 } else if (auth->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
1225 domain->can_do_validation6 = false;
1226 } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
1227 domain->can_do_validation6 = false;
1228 } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
1229 domain->can_do_validation6 = false;
1230 } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
1231 domain->can_do_validation6 = false;
1234 if (domain->can_do_samlogon_ex) {
1235 result = rpccli_netlogon_sam_network_logon_ex(
1236 netlogon_pipe,
1237 mem_ctx,
1238 logon_parameters,
1239 server, /* server name */
1240 username, /* user name */
1241 domainname, /* target domain */
1242 workstation, /* workstation */
1243 chal,
1244 domain->can_do_validation6 ? 6 : 3,
1245 lm_response,
1246 nt_response,
1247 info3);
1248 } else {
1249 result = rpccli_netlogon_sam_network_logon(
1250 netlogon_pipe,
1251 mem_ctx,
1252 logon_parameters,
1253 server, /* server name */
1254 username, /* user name */
1255 domainname, /* target domain */
1256 workstation, /* workstation */
1257 chal,
1258 domain->can_do_validation6 ? 6 : 3,
1259 lm_response,
1260 nt_response,
1261 info3);
1264 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1267 * It's likely that the server also does not support
1268 * validation level 6
1270 domain->can_do_validation6 = false;
1272 if (domain->can_do_samlogon_ex) {
1273 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1274 "retrying with NetSamLogon\n"));
1275 domain->can_do_samlogon_ex = false;
1276 retry = true;
1277 continue;
1281 /* Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1282 * (no Ex). This happens against old Samba
1283 * DCs. Drop the connection.
1285 invalidate_cm_connection(&domain->conn);
1286 result = NT_STATUS_LOGON_FAILURE;
1287 break;
1290 if (domain->can_do_validation6 &&
1291 (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
1292 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
1293 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) {
1294 DEBUG(3,("Got a DC that can not do validation level 6, "
1295 "retrying with level 3\n"));
1296 domain->can_do_validation6 = false;
1297 retry = true;
1298 continue;
1302 * we increment this after the "feature negotiation"
1303 * for can_do_samlogon_ex and can_do_validation6
1305 attempts += 1;
1307 /* We have to try a second time as cm_connect_netlogon
1308 might not yet have noticed that the DC has killed
1309 our connection. */
1311 if (!rpccli_is_connected(netlogon_pipe)) {
1312 retry = true;
1313 continue;
1316 /* if we get access denied, a possible cause was that we had
1317 and open connection to the DC, but someone changed our
1318 machine account password out from underneath us using 'net
1319 rpc changetrustpw' */
1321 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1322 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1323 "ACCESS_DENIED. Maybe the trust account "
1324 "password was changed and we didn't know it. "
1325 "Killing connections to domain %s\n",
1326 domainname));
1327 invalidate_cm_connection(&domain->conn);
1328 retry = true;
1331 } while ( (attempts < 2) && retry );
1333 return result;
1336 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1337 struct winbindd_domain *domain,
1338 const char *user,
1339 const char *pass,
1340 uint32_t request_flags,
1341 struct netr_SamInfo3 **info3)
1344 uchar chal[8];
1345 DATA_BLOB lm_resp;
1346 DATA_BLOB nt_resp;
1347 unsigned char local_nt_response[24];
1348 fstring name_domain, name_user;
1349 NTSTATUS result;
1350 struct netr_SamInfo3 *my_info3 = NULL;
1352 *info3 = NULL;
1354 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1356 /* Parse domain and username */
1358 parse_domain_user(user, name_domain, name_user);
1360 /* do password magic */
1362 generate_random_buffer(chal, sizeof(chal));
1364 if (lp_client_ntlmv2_auth()) {
1365 DATA_BLOB server_chal;
1366 DATA_BLOB names_blob;
1367 server_chal = data_blob_const(chal, 8);
1369 /* note that the 'workgroup' here is for the local
1370 machine. The 'server name' must match the
1371 'workstation' passed to the actual SamLogon call.
1373 names_blob = NTLMv2_generate_names_blob(
1374 mem_ctx, lp_netbios_name(), lp_workgroup());
1376 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1377 pass,
1378 &server_chal,
1379 &names_blob,
1380 &lm_resp, &nt_resp, NULL, NULL)) {
1381 data_blob_free(&names_blob);
1382 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1383 result = NT_STATUS_NO_MEMORY;
1384 goto done;
1386 data_blob_free(&names_blob);
1387 } else {
1388 lm_resp = data_blob_null;
1389 SMBNTencrypt(pass, chal, local_nt_response);
1391 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1392 sizeof(local_nt_response));
1395 if (strequal(name_domain, get_global_sam_name())) {
1396 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1398 result = winbindd_dual_auth_passdb(
1399 mem_ctx, name_domain, name_user,
1400 &chal_blob, &lm_resp, &nt_resp, info3);
1401 goto done;
1404 /* check authentication loop */
1406 result = winbind_samlogon_retry_loop(domain,
1407 mem_ctx,
1409 domain->dcname,
1410 name_user,
1411 name_domain,
1412 lp_netbios_name(),
1413 chal,
1414 lm_resp,
1415 nt_resp,
1416 &my_info3);
1417 if (!NT_STATUS_IS_OK(result)) {
1418 goto done;
1421 /* handle the case where a NT4 DC does not fill in the acct_flags in
1422 * the samlogon reply info3. When accurate info3 is required by the
1423 * caller, we look up the account flags ourselve - gd */
1425 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1426 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1428 struct rpc_pipe_client *samr_pipe;
1429 struct policy_handle samr_domain_handle, user_pol;
1430 union samr_UserInfo *info = NULL;
1431 NTSTATUS status_tmp, result_tmp;
1432 uint32 acct_flags;
1433 struct dcerpc_binding_handle *b;
1435 status_tmp = cm_connect_sam(domain, mem_ctx,
1436 &samr_pipe, &samr_domain_handle);
1438 if (!NT_STATUS_IS_OK(status_tmp)) {
1439 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1440 nt_errstr(status_tmp)));
1441 goto done;
1444 b = samr_pipe->binding_handle;
1446 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1447 &samr_domain_handle,
1448 MAXIMUM_ALLOWED_ACCESS,
1449 my_info3->base.rid,
1450 &user_pol,
1451 &result_tmp);
1453 if (!NT_STATUS_IS_OK(status_tmp)) {
1454 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1455 nt_errstr(status_tmp)));
1456 goto done;
1458 if (!NT_STATUS_IS_OK(result_tmp)) {
1459 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1460 nt_errstr(result_tmp)));
1461 goto done;
1464 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1465 &user_pol,
1467 &info,
1468 &result_tmp);
1470 if (!NT_STATUS_IS_OK(status_tmp)) {
1471 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1472 nt_errstr(status_tmp)));
1473 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1474 goto done;
1476 if (!NT_STATUS_IS_OK(result_tmp)) {
1477 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1478 nt_errstr(result_tmp)));
1479 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1480 goto done;
1483 acct_flags = info->info16.acct_flags;
1485 if (acct_flags == 0) {
1486 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1487 goto done;
1490 my_info3->base.acct_flags = acct_flags;
1492 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1494 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1497 *info3 = my_info3;
1498 done:
1499 return result;
1502 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1503 struct winbindd_cli_state *state)
1505 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1506 NTSTATUS krb5_result = NT_STATUS_OK;
1507 fstring name_domain, name_user;
1508 char *mapped_user;
1509 fstring domain_user;
1510 struct netr_SamInfo3 *info3 = NULL;
1511 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1513 /* Ensure null termination */
1514 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1516 /* Ensure null termination */
1517 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1519 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1520 state->request->data.auth.user));
1522 /* Parse domain and username */
1524 name_map_status = normalize_name_unmap(state->mem_ctx,
1525 state->request->data.auth.user,
1526 &mapped_user);
1528 /* If the name normalization didnt' actually do anything,
1529 just use the original name */
1531 if (!NT_STATUS_IS_OK(name_map_status) &&
1532 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1534 mapped_user = state->request->data.auth.user;
1537 parse_domain_user(mapped_user, name_domain, name_user);
1539 if ( mapped_user != state->request->data.auth.user ) {
1540 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1541 *lp_winbind_separator(),
1542 name_user );
1543 strlcpy( state->request->data.auth.user, domain_user,
1544 sizeof(state->request->data.auth.user));
1547 if (!domain->online) {
1548 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1549 if (domain->startup) {
1550 /* Logons are very important to users. If we're offline and
1551 we get a request within the first 30 seconds of startup,
1552 try very hard to find a DC and go online. */
1554 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1555 "request in startup mode.\n", domain->name ));
1557 winbindd_flush_negative_conn_cache(domain);
1558 result = init_dc_connection(domain);
1562 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1564 /* Check for Kerberos authentication */
1565 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1567 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1568 /* save for later */
1569 krb5_result = result;
1572 if (NT_STATUS_IS_OK(result)) {
1573 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1574 goto process_result;
1575 } else {
1576 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1579 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1580 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1581 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1582 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1583 set_domain_offline( domain );
1584 goto cached_logon;
1587 /* there are quite some NT_STATUS errors where there is no
1588 * point in retrying with a samlogon, we explictly have to take
1589 * care not to increase the bad logon counter on the DC */
1591 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1592 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1593 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1594 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1595 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1596 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1597 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1598 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1599 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1600 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1601 goto done;
1604 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1605 DEBUG(3,("falling back to samlogon\n"));
1606 goto sam_logon;
1607 } else {
1608 goto cached_logon;
1612 sam_logon:
1613 /* Check for Samlogon authentication */
1614 if (domain->online) {
1615 result = winbindd_dual_pam_auth_samlogon(
1616 state->mem_ctx, domain,
1617 state->request->data.auth.user,
1618 state->request->data.auth.pass,
1619 state->request->flags,
1620 &info3);
1622 if (NT_STATUS_IS_OK(result)) {
1623 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1624 /* add the Krb5 err if we have one */
1625 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1626 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1628 goto process_result;
1631 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1632 nt_errstr(result)));
1634 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1635 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1636 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1638 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1639 set_domain_offline( domain );
1640 goto cached_logon;
1643 if (domain->online) {
1644 /* We're still online - fail. */
1645 goto done;
1649 cached_logon:
1650 /* Check for Cached logons */
1651 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1652 lp_winbind_offline_logon()) {
1654 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1656 if (NT_STATUS_IS_OK(result)) {
1657 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1658 goto process_result;
1659 } else {
1660 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1661 goto done;
1665 process_result:
1667 if (NT_STATUS_IS_OK(result)) {
1669 struct dom_sid user_sid;
1671 /* In all codepaths where result == NT_STATUS_OK info3 must have
1672 been initialized. */
1673 if (!info3) {
1674 result = NT_STATUS_INTERNAL_ERROR;
1675 goto done;
1678 sid_compose(&user_sid, info3->base.domain_sid,
1679 info3->base.rid);
1681 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1682 &user_sid);
1683 netsamlogon_cache_store(name_user, info3);
1685 /* save name_to_sid info as early as possible (only if
1686 this is our primary domain so we don't invalidate
1687 the cache entry by storing the seq_num for the wrong
1688 domain). */
1689 if ( domain->primary ) {
1690 cache_name2sid(domain, name_domain, name_user,
1691 SID_NAME_USER, &user_sid);
1694 /* Check if the user is in the right group */
1696 result = check_info3_in_group(
1697 info3,
1698 state->request->data.auth.require_membership_of_sid);
1699 if (!NT_STATUS_IS_OK(result)) {
1700 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1701 state->request->data.auth.user,
1702 state->request->data.auth.require_membership_of_sid));
1703 goto done;
1706 result = append_auth_data(state->mem_ctx, state->response,
1707 state->request->flags, info3,
1708 name_domain, name_user);
1709 if (!NT_STATUS_IS_OK(result)) {
1710 goto done;
1713 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1714 && lp_winbind_offline_logon()) {
1716 result = winbindd_store_creds(domain,
1717 state->request->data.auth.user,
1718 state->request->data.auth.pass,
1719 info3);
1722 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1723 struct winbindd_domain *our_domain = find_our_domain();
1725 /* This is not entirely correct I believe, but it is
1726 consistent. Only apply the password policy settings
1727 too warn users for our own domain. Cannot obtain these
1728 from trusted DCs all the time so don't do it at all.
1729 -- jerry */
1731 result = NT_STATUS_NOT_SUPPORTED;
1732 if (our_domain == domain ) {
1733 result = fillup_password_policy(
1734 our_domain, state->response);
1737 if (!NT_STATUS_IS_OK(result)
1738 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1740 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1741 domain->name, nt_errstr(result)));
1742 goto done;
1746 result = NT_STATUS_OK;
1749 done:
1750 /* give us a more useful (more correct?) error code */
1751 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1752 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1753 result = NT_STATUS_NO_LOGON_SERVERS;
1756 set_auth_errors(state->response, result);
1758 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1759 state->request->data.auth.user,
1760 state->response->data.auth.nt_status_string,
1761 state->response->data.auth.pam_error));
1763 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1766 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1767 struct winbindd_cli_state *state)
1769 NTSTATUS result;
1770 struct netr_SamInfo3 *info3 = NULL;
1771 const char *name_user = NULL;
1772 const char *name_domain = NULL;
1773 const char *workstation;
1775 DATA_BLOB lm_resp, nt_resp;
1777 /* This is child-only, so no check for privileged access is needed
1778 anymore */
1780 /* Ensure null termination */
1781 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1782 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1784 name_user = state->request->data.auth_crap.user;
1785 name_domain = state->request->data.auth_crap.domain;
1786 workstation = state->request->data.auth_crap.workstation;
1788 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1789 name_domain, name_user));
1791 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1792 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1793 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1794 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1795 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1796 state->request->data.auth_crap.lm_resp_len,
1797 state->request->data.auth_crap.nt_resp_len));
1798 result = NT_STATUS_INVALID_PARAMETER;
1799 goto done;
1803 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1804 state->request->data.auth_crap.lm_resp_len);
1806 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1807 nt_resp = data_blob_talloc(state->mem_ctx,
1808 state->request->extra_data.data,
1809 state->request->data.auth_crap.nt_resp_len);
1810 } else {
1811 nt_resp = data_blob_talloc(state->mem_ctx,
1812 state->request->data.auth_crap.nt_resp,
1813 state->request->data.auth_crap.nt_resp_len);
1816 if (strequal(name_domain, get_global_sam_name())) {
1817 DATA_BLOB chal_blob = data_blob_const(
1818 state->request->data.auth_crap.chal,
1819 sizeof(state->request->data.auth_crap.chal));
1821 result = winbindd_dual_auth_passdb(
1822 state->mem_ctx, name_domain, name_user,
1823 &chal_blob, &lm_resp, &nt_resp, &info3);
1824 goto process_result;
1827 result = winbind_samlogon_retry_loop(domain,
1828 state->mem_ctx,
1829 state->request->data.auth_crap.logon_parameters,
1830 domain->dcname,
1831 name_user,
1832 name_domain,
1833 /* Bug #3248 - found by Stefan Burkei. */
1834 workstation, /* We carefully set this above so use it... */
1835 state->request->data.auth_crap.chal,
1836 lm_resp,
1837 nt_resp,
1838 &info3);
1839 if (!NT_STATUS_IS_OK(result)) {
1840 goto done;
1843 process_result:
1845 if (NT_STATUS_IS_OK(result)) {
1846 struct dom_sid user_sid;
1848 sid_compose(&user_sid, info3->base.domain_sid,
1849 info3->base.rid);
1850 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1851 &user_sid);
1852 netsamlogon_cache_store(name_user, info3);
1854 /* Check if the user is in the right group */
1856 result = check_info3_in_group(
1857 info3,
1858 state->request->data.auth_crap.require_membership_of_sid);
1859 if (!NT_STATUS_IS_OK(result)) {
1860 DEBUG(3, ("User %s is not in the required group (%s), so "
1861 "crap authentication is rejected\n",
1862 state->request->data.auth_crap.user,
1863 state->request->data.auth_crap.require_membership_of_sid));
1864 goto done;
1867 result = append_auth_data(state->mem_ctx, state->response,
1868 state->request->flags, info3,
1869 name_domain, name_user);
1870 if (!NT_STATUS_IS_OK(result)) {
1871 goto done;
1875 done:
1877 /* give us a more useful (more correct?) error code */
1878 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1879 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1880 result = NT_STATUS_NO_LOGON_SERVERS;
1883 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1884 result = nt_status_squash(result);
1887 set_auth_errors(state->response, result);
1889 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1890 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1891 name_domain,
1892 name_user,
1893 state->response->data.auth.nt_status_string,
1894 state->response->data.auth.pam_error));
1896 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1899 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1900 struct winbindd_cli_state *state)
1902 char *oldpass;
1903 char *newpass = NULL;
1904 struct policy_handle dom_pol;
1905 struct rpc_pipe_client *cli = NULL;
1906 bool got_info = false;
1907 struct samr_DomInfo1 *info = NULL;
1908 struct userPwdChangeFailureInformation *reject = NULL;
1909 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1910 fstring domain, user;
1911 struct dcerpc_binding_handle *b = NULL;
1913 ZERO_STRUCT(dom_pol);
1915 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1916 state->request->data.auth.user));
1918 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
1919 goto done;
1922 /* Change password */
1924 oldpass = state->request->data.chauthtok.oldpass;
1925 newpass = state->request->data.chauthtok.newpass;
1927 /* Initialize reject reason */
1928 state->response->data.auth.reject_reason = Undefined;
1930 /* Get sam handle */
1932 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1933 &dom_pol);
1934 if (!NT_STATUS_IS_OK(result)) {
1935 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1936 goto done;
1939 b = cli->binding_handle;
1941 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
1942 user,
1943 newpass,
1944 oldpass,
1945 &info,
1946 &reject);
1948 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1950 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1952 fill_in_password_policy(state->response, info);
1954 state->response->data.auth.reject_reason =
1955 reject->extendedFailureReason;
1957 got_info = true;
1960 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
1961 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
1962 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
1963 * short to comply with the samr_ChangePasswordUser3 idl - gd */
1965 /* only fallback when the chgpasswd_user3 call is not supported */
1966 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
1967 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
1968 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
1969 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1971 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
1972 nt_errstr(result)));
1974 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
1976 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1977 Map to the same status code as Windows 2003. */
1979 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
1980 result = NT_STATUS_PASSWORD_RESTRICTION;
1984 done:
1986 if (NT_STATUS_IS_OK(result)
1987 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1988 && lp_winbind_offline_logon()) {
1989 result = winbindd_update_creds_by_name(contact_domain, user,
1990 newpass);
1991 /* Again, this happens when we login from gdm or xdm
1992 * and the password expires, *BUT* cached crendentials
1993 * doesn't exist. winbindd_update_creds_by_name()
1994 * returns NT_STATUS_NO_SUCH_USER.
1995 * This is not a failure.
1996 * --- BoYang
1997 * */
1998 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
1999 result = NT_STATUS_OK;
2002 if (!NT_STATUS_IS_OK(result)) {
2003 DEBUG(10, ("Failed to store creds: %s\n",
2004 nt_errstr(result)));
2005 goto process_result;
2009 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2011 NTSTATUS policy_ret;
2013 policy_ret = fillup_password_policy(
2014 contact_domain, state->response);
2016 /* failure of this is non critical, it will just provide no
2017 * additional information to the client why the change has
2018 * failed - Guenther */
2020 if (!NT_STATUS_IS_OK(policy_ret)) {
2021 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2022 goto process_result;
2026 process_result:
2028 if (strequal(contact_domain->name, get_global_sam_name())) {
2029 /* FIXME: internal rpc pipe does not cache handles yet */
2030 if (b) {
2031 if (is_valid_policy_hnd(&dom_pol)) {
2032 NTSTATUS _result;
2033 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2035 TALLOC_FREE(cli);
2039 set_auth_errors(state->response, result);
2041 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2042 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2043 domain,
2044 user,
2045 state->response->data.auth.nt_status_string,
2046 state->response->data.auth.pam_error));
2048 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2051 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2052 struct winbindd_cli_state *state)
2054 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2056 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2057 state->request->data.logoff.user));
2059 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2060 result = NT_STATUS_OK;
2061 goto process_result;
2064 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2065 result = NT_STATUS_OK;
2066 goto process_result;
2069 #ifdef HAVE_KRB5
2071 if (state->request->data.logoff.uid < 0) {
2072 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2073 goto process_result;
2076 /* what we need here is to find the corresponding krb5 ccache name *we*
2077 * created for a given username and destroy it */
2079 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2080 result = NT_STATUS_OK;
2081 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2082 goto process_result;
2085 if (!ccache_entry_identical(state->request->data.logoff.user,
2086 state->request->data.logoff.uid,
2087 state->request->data.logoff.krb5ccname)) {
2088 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2089 goto process_result;
2092 result = remove_ccache(state->request->data.logoff.user);
2093 if (!NT_STATUS_IS_OK(result)) {
2094 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2095 nt_errstr(result)));
2096 goto process_result;
2099 #else
2100 result = NT_STATUS_NOT_SUPPORTED;
2101 #endif
2103 process_result:
2106 set_auth_errors(state->response, result);
2108 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2111 /* Change user password with auth crap*/
2113 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2115 NTSTATUS result;
2116 DATA_BLOB new_nt_password;
2117 DATA_BLOB old_nt_hash_enc;
2118 DATA_BLOB new_lm_password;
2119 DATA_BLOB old_lm_hash_enc;
2120 fstring domain,user;
2121 struct policy_handle dom_pol;
2122 struct winbindd_domain *contact_domain = domainSt;
2123 struct rpc_pipe_client *cli = NULL;
2124 struct dcerpc_binding_handle *b = NULL;
2126 ZERO_STRUCT(dom_pol);
2128 /* Ensure null termination */
2129 state->request->data.chng_pswd_auth_crap.user[
2130 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2131 state->request->data.chng_pswd_auth_crap.domain[
2132 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2133 *domain = 0;
2134 *user = 0;
2136 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2137 (unsigned long)state->pid,
2138 state->request->data.chng_pswd_auth_crap.domain,
2139 state->request->data.chng_pswd_auth_crap.user));
2141 if (lp_winbind_offline_logon()) {
2142 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2143 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2144 result = NT_STATUS_ACCESS_DENIED;
2145 goto done;
2148 if (*state->request->data.chng_pswd_auth_crap.domain) {
2149 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2150 } else {
2151 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2152 domain, user);
2154 if(!*domain) {
2155 DEBUG(3,("no domain specified with username (%s) - "
2156 "failing auth\n",
2157 state->request->data.chng_pswd_auth_crap.user));
2158 result = NT_STATUS_NO_SUCH_USER;
2159 goto done;
2163 if (!*domain && lp_winbind_use_default_domain()) {
2164 fstrcpy(domain,lp_workgroup());
2167 if(!*user) {
2168 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2171 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2172 (unsigned long)state->pid, domain, user));
2174 /* Change password */
2175 new_nt_password = data_blob_const(
2176 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2177 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2179 old_nt_hash_enc = data_blob_const(
2180 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2181 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2183 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2184 new_lm_password = data_blob_const(
2185 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2186 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2188 old_lm_hash_enc = data_blob_const(
2189 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2190 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2191 } else {
2192 new_lm_password.length = 0;
2193 old_lm_hash_enc.length = 0;
2196 /* Get sam handle */
2198 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2199 if (!NT_STATUS_IS_OK(result)) {
2200 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2201 goto done;
2204 b = cli->binding_handle;
2206 result = rpccli_samr_chng_pswd_auth_crap(
2207 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2208 new_lm_password, old_lm_hash_enc);
2210 done:
2212 if (strequal(contact_domain->name, get_global_sam_name())) {
2213 /* FIXME: internal rpc pipe does not cache handles yet */
2214 if (b) {
2215 if (is_valid_policy_hnd(&dom_pol)) {
2216 NTSTATUS _result;
2217 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2219 TALLOC_FREE(cli);
2223 set_auth_errors(state->response, result);
2225 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2226 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2227 domain, user,
2228 state->response->data.auth.nt_status_string,
2229 state->response->data.auth.pam_error));
2231 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;