krb5_wrap: Move krb5_princ_component() to the top
[Samba.git] / source3 / winbindd / winbindd_pam.c
blob8ec4fe4a6a155e66c7b876a95eeb45c30400457e
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"
41 #include "auth/kerberos/pac_utils.h"
42 #include "auth/gensec/gensec.h"
43 #include "librpc/crypto/gse_krb5.h"
44 #include "lib/afs/afs_funcs.h"
46 #undef DBGC_CLASS
47 #define DBGC_CLASS DBGC_WINBIND
49 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
51 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
52 struct winbindd_response *resp,
53 struct netr_SamInfo3 *info3)
55 char *ex;
56 uint32_t i;
58 resp->data.auth.info3.logon_time =
59 nt_time_to_unix(info3->base.logon_time);
60 resp->data.auth.info3.logoff_time =
61 nt_time_to_unix(info3->base.logoff_time);
62 resp->data.auth.info3.kickoff_time =
63 nt_time_to_unix(info3->base.kickoff_time);
64 resp->data.auth.info3.pass_last_set_time =
65 nt_time_to_unix(info3->base.last_password_change);
66 resp->data.auth.info3.pass_can_change_time =
67 nt_time_to_unix(info3->base.allow_password_change);
68 resp->data.auth.info3.pass_must_change_time =
69 nt_time_to_unix(info3->base.force_password_change);
71 resp->data.auth.info3.logon_count = info3->base.logon_count;
72 resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
74 resp->data.auth.info3.user_rid = info3->base.rid;
75 resp->data.auth.info3.group_rid = info3->base.primary_gid;
76 sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
78 resp->data.auth.info3.num_groups = info3->base.groups.count;
79 resp->data.auth.info3.user_flgs = info3->base.user_flags;
81 resp->data.auth.info3.acct_flags = info3->base.acct_flags;
82 resp->data.auth.info3.num_other_sids = info3->sidcount;
84 fstrcpy(resp->data.auth.info3.user_name,
85 info3->base.account_name.string);
86 fstrcpy(resp->data.auth.info3.full_name,
87 info3->base.full_name.string);
88 fstrcpy(resp->data.auth.info3.logon_script,
89 info3->base.logon_script.string);
90 fstrcpy(resp->data.auth.info3.profile_path,
91 info3->base.profile_path.string);
92 fstrcpy(resp->data.auth.info3.home_dir,
93 info3->base.home_directory.string);
94 fstrcpy(resp->data.auth.info3.dir_drive,
95 info3->base.home_drive.string);
97 fstrcpy(resp->data.auth.info3.logon_srv,
98 info3->base.logon_server.string);
99 fstrcpy(resp->data.auth.info3.logon_dom,
100 info3->base.logon_domain.string);
102 ex = talloc_strdup(mem_ctx, "");
103 NT_STATUS_HAVE_NO_MEMORY(ex);
105 for (i=0; i < info3->base.groups.count; i++) {
106 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
107 info3->base.groups.rids[i].rid,
108 info3->base.groups.rids[i].attributes);
109 NT_STATUS_HAVE_NO_MEMORY(ex);
112 for (i=0; i < info3->sidcount; i++) {
113 char *sid;
115 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
116 NT_STATUS_HAVE_NO_MEMORY(sid);
118 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
119 sid,
120 info3->sids[i].attributes);
121 NT_STATUS_HAVE_NO_MEMORY(ex);
123 talloc_free(sid);
126 resp->extra_data.data = ex;
127 resp->length += talloc_get_size(ex);
129 return NT_STATUS_OK;
132 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
133 struct winbindd_response *resp,
134 struct netr_SamInfo3 *info3)
136 DATA_BLOB blob;
137 enum ndr_err_code ndr_err;
139 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
140 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
141 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
142 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
143 return ndr_map_error2ntstatus(ndr_err);
146 resp->extra_data.data = blob.data;
147 resp->length += blob.length;
149 return NT_STATUS_OK;
152 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
153 struct winbindd_response *resp,
154 const struct netr_SamInfo3 *info3,
155 const char *name_domain,
156 const char *name_user)
158 /* We've been asked to return the unix username, per
159 'winbind use default domain' settings and the like */
161 const char *nt_username, *nt_domain;
163 nt_domain = talloc_strdup(mem_ctx, info3->base.logon_domain.string);
164 if (!nt_domain) {
165 /* If the server didn't give us one, just use the one
166 * we sent them */
167 nt_domain = name_domain;
170 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
171 if (!nt_username) {
172 /* If the server didn't give us one, just use the one
173 * we sent them */
174 nt_username = name_user;
177 fill_domain_username(resp->data.auth.unix_username,
178 nt_domain, nt_username, true);
180 DEBUG(5, ("Setting unix username to [%s]\n",
181 resp->data.auth.unix_username));
183 return NT_STATUS_OK;
186 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
187 struct winbindd_response *resp,
188 const struct netr_SamInfo3 *info3,
189 const char *name_domain,
190 const char *name_user)
192 char *afsname = NULL;
193 char *cell;
194 char *token;
196 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197 if (afsname == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 afsname = talloc_string_sub(mem_ctx,
202 lp_afs_username_map(),
203 "%D", name_domain);
204 afsname = talloc_string_sub(mem_ctx, afsname,
205 "%u", name_user);
206 afsname = talloc_string_sub(mem_ctx, afsname,
207 "%U", name_user);
210 struct dom_sid user_sid;
211 fstring sidstr;
213 sid_compose(&user_sid, info3->base.domain_sid,
214 info3->base.rid);
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
217 "%s", sidstr);
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 if (!strlower_m(afsname)) {
225 return NT_STATUS_INVALID_PARAMETER;
228 DEBUG(10, ("Generating token for user %s\n", afsname));
230 cell = strchr(afsname, '@');
232 if (cell == NULL) {
233 return NT_STATUS_NO_MEMORY;
236 *cell = '\0';
237 cell += 1;
239 token = afs_createtoken_str(afsname, cell);
240 if (token == NULL) {
241 return NT_STATUS_OK;
243 resp->extra_data.data = talloc_strdup(mem_ctx, token);
244 if (resp->extra_data.data == NULL) {
245 return NT_STATUS_NO_MEMORY;
247 resp->length += strlen((const char *)resp->extra_data.data)+1;
249 return NT_STATUS_OK;
252 static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
253 const char *group_sid)
255 * Check whether a user belongs to a group or list of groups.
257 * @param mem_ctx talloc memory context.
258 * @param info3 user information, including group membership info.
259 * @param group_sid One or more groups , separated by commas.
261 * @return NT_STATUS_OK on success,
262 * NT_STATUS_LOGON_FAILURE if the user does not belong,
263 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
266 struct dom_sid *require_membership_of_sid;
267 uint32_t num_require_membership_of_sid;
268 char *req_sid;
269 const char *p;
270 struct dom_sid sid;
271 size_t i;
272 struct security_token *token;
273 TALLOC_CTX *frame = talloc_stackframe();
274 NTSTATUS status;
276 /* Parse the 'required group' SID */
278 if (!group_sid || !group_sid[0]) {
279 /* NO sid supplied, all users may access */
280 TALLOC_FREE(frame);
281 return NT_STATUS_OK;
284 token = talloc_zero(talloc_tos(), struct security_token);
285 if (token == NULL) {
286 DEBUG(0, ("talloc failed\n"));
287 TALLOC_FREE(frame);
288 return NT_STATUS_NO_MEMORY;
291 num_require_membership_of_sid = 0;
292 require_membership_of_sid = NULL;
294 p = group_sid;
296 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
297 if (!string_to_sid(&sid, req_sid)) {
298 DEBUG(0, ("check_info3_in_group: could not parse %s "
299 "as a SID!", req_sid));
300 TALLOC_FREE(frame);
301 return NT_STATUS_INVALID_PARAMETER;
304 status = add_sid_to_array(talloc_tos(), &sid,
305 &require_membership_of_sid,
306 &num_require_membership_of_sid);
307 if (!NT_STATUS_IS_OK(status)) {
308 DEBUG(0, ("add_sid_to_array failed\n"));
309 TALLOC_FREE(frame);
310 return status;
314 status = sid_array_from_info3(talloc_tos(), info3,
315 &token->sids,
316 &token->num_sids,
317 true);
318 if (!NT_STATUS_IS_OK(status)) {
319 TALLOC_FREE(frame);
320 return status;
323 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
324 token))
325 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
326 token))) {
327 DEBUG(3, ("could not add aliases: %s\n",
328 nt_errstr(status)));
329 TALLOC_FREE(frame);
330 return status;
333 security_token_debug(DBGC_CLASS, 10, token);
335 for (i=0; i<num_require_membership_of_sid; i++) {
336 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
337 &require_membership_of_sid[i])));
338 if (nt_token_check_sid(&require_membership_of_sid[i],
339 token)) {
340 DEBUG(10, ("Access ok\n"));
341 TALLOC_FREE(frame);
342 return NT_STATUS_OK;
346 /* Do not distinguish this error from a wrong username/pw */
348 TALLOC_FREE(frame);
349 return NT_STATUS_LOGON_FAILURE;
352 struct winbindd_domain *find_auth_domain(uint8_t flags,
353 const char *domain_name)
355 struct winbindd_domain *domain;
357 if (IS_DC) {
358 domain = find_domain_from_name_noinit(domain_name);
359 if (domain == NULL) {
360 DEBUG(3, ("Authentication for domain [%s] refused "
361 "as it is not a trusted domain\n",
362 domain_name));
364 return domain;
367 if (strequal(domain_name, get_global_sam_name())) {
368 return find_domain_from_name_noinit(domain_name);
371 /* we can auth against trusted domains */
372 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
373 domain = find_domain_from_name_noinit(domain_name);
374 if (domain == NULL) {
375 DEBUG(3, ("Authentication for domain [%s] skipped "
376 "as it is not a trusted domain\n",
377 domain_name));
378 } else {
379 return domain;
383 return find_our_domain();
386 static void fill_in_password_policy(struct winbindd_response *r,
387 const struct samr_DomInfo1 *p)
389 r->data.auth.policy.min_length_password =
390 p->min_password_length;
391 r->data.auth.policy.password_history =
392 p->password_history_length;
393 r->data.auth.policy.password_properties =
394 p->password_properties;
395 r->data.auth.policy.expire =
396 nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
397 r->data.auth.policy.min_passwordage =
398 nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
401 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
402 struct winbindd_response *response)
404 TALLOC_CTX *frame = talloc_stackframe();
405 struct winbindd_methods *methods;
406 NTSTATUS status;
407 struct samr_DomInfo1 password_policy;
409 if ( !winbindd_can_contact_domain( domain ) ) {
410 DEBUG(5,("fillup_password_policy: No inbound trust to "
411 "contact domain %s\n", domain->name));
412 status = NT_STATUS_NOT_SUPPORTED;
413 goto done;
416 methods = domain->methods;
418 status = methods->password_policy(domain, talloc_tos(), &password_policy);
419 if (NT_STATUS_IS_ERR(status)) {
420 goto done;
423 fill_in_password_policy(response, &password_policy);
425 done:
426 TALLOC_FREE(frame);
427 return NT_STATUS_OK;
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
431 TALLOC_CTX *mem_ctx,
432 uint16_t *lockout_threshold)
434 struct winbindd_methods *methods;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 struct samr_DomInfo12 lockout_policy;
438 *lockout_threshold = 0;
440 methods = domain->methods;
442 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
443 if (NT_STATUS_IS_ERR(status)) {
444 return status;
447 *lockout_threshold = lockout_policy.lockout_threshold;
449 return NT_STATUS_OK;
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
453 TALLOC_CTX *mem_ctx,
454 uint32_t *password_properties)
456 struct winbindd_methods *methods;
457 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458 struct samr_DomInfo1 password_policy;
460 *password_properties = 0;
462 methods = domain->methods;
464 status = methods->password_policy(domain, mem_ctx, &password_policy);
465 if (NT_STATUS_IS_ERR(status)) {
466 return status;
469 *password_properties = password_policy.password_properties;
471 return NT_STATUS_OK;
474 #ifdef HAVE_KRB5
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
477 const char *type,
478 uid_t uid,
479 const char **user_ccache_file)
481 /* accept FILE and WRFILE as krb5_cc_type from the client and then
482 * build the full ccname string based on the user's uid here -
483 * Guenther*/
485 const char *gen_cc = NULL;
487 if (uid != -1) {
488 if (strequal(type, "FILE")) {
489 gen_cc = talloc_asprintf(
490 mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
492 if (strequal(type, "WRFILE")) {
493 gen_cc = talloc_asprintf(
494 mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
496 if (strequal(type, "KEYRING")) {
497 gen_cc = talloc_asprintf(
498 mem_ctx, "KEYRING:persistent:%d", uid);
501 if (strnequal(type, "FILE:/", 6) ||
502 strnequal(type, "WRFILE:/", 8) ||
503 strnequal(type, "DIR:/", 5)) {
505 /* we allow only one "%u" substitution */
507 char *p;
509 p = strchr(type, '%');
510 if (p != NULL) {
512 p++;
514 if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
515 char uid_str[sizeof("18446744073709551615")];
517 snprintf(uid_str, sizeof(uid_str), "%u", uid);
519 gen_cc = talloc_string_sub2(mem_ctx,
520 type,
521 "%u",
522 uid_str,
523 /* remove_unsafe_characters */
524 false,
525 /* replace_once */
526 true,
527 /* allow_trailing_dollar */
528 false);
534 *user_ccache_file = gen_cc;
536 if (gen_cc == NULL) {
537 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
539 if (gen_cc == NULL) {
540 DEBUG(0,("out of memory\n"));
541 return NULL;
544 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
545 (*user_ccache_file == NULL) ? " (internal)":""));
547 return gen_cc;
550 #endif
552 uid_t get_uid_from_request(struct winbindd_request *request)
554 uid_t uid;
556 uid = request->data.auth.uid;
558 if (uid == (uid_t)-1) {
559 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
560 return -1;
562 return uid;
565 /**********************************************************************
566 Authenticate a user with a clear text password using Kerberos and fill up
567 ccache if required
568 **********************************************************************/
570 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
571 struct winbindd_domain *domain,
572 const char *user,
573 const char *pass,
574 const char *krb5_cc_type,
575 uid_t uid,
576 struct netr_SamInfo3 **info3,
577 fstring krb5ccname)
579 #ifdef HAVE_KRB5
580 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
581 krb5_error_code krb5_ret;
582 const char *cc = NULL;
583 const char *principal_s = NULL;
584 const char *service = NULL;
585 char *realm = NULL;
586 fstring name_domain, name_user;
587 time_t ticket_lifetime = 0;
588 time_t renewal_until = 0;
589 ADS_STRUCT *ads;
590 time_t time_offset = 0;
591 const char *user_ccache_file;
592 struct PAC_LOGON_INFO *logon_info = NULL;
593 struct PAC_DATA *pac_data = NULL;
594 struct PAC_DATA_CTR *pac_data_ctr = NULL;
595 const char *local_service;
596 int i;
597 struct netr_SamInfo3 *info3_copy = NULL;
599 *info3 = NULL;
601 if (domain->alt_name == NULL) {
602 return NT_STATUS_INVALID_PARAMETER;
605 /* 1st step:
606 * prepare a krb5_cc_cache string for the user */
608 if (uid == -1) {
609 DEBUG(0,("no valid uid\n"));
612 cc = generate_krb5_ccache(mem_ctx,
613 krb5_cc_type,
614 uid,
615 &user_ccache_file);
616 if (cc == NULL) {
617 return NT_STATUS_NO_MEMORY;
621 /* 2nd step:
622 * get kerberos properties */
624 if (domain->private_data) {
625 ads = (ADS_STRUCT *)domain->private_data;
626 time_offset = ads->auth.time_offset;
630 /* 3rd step:
631 * do kerberos auth and setup ccache as the user */
633 parse_domain_user(user, name_domain, name_user);
635 realm = talloc_strdup(mem_ctx, domain->alt_name);
636 if (realm == NULL) {
637 return NT_STATUS_NO_MEMORY;
640 if (!strupper_m(realm)) {
641 return NT_STATUS_INVALID_PARAMETER;
644 principal_s = talloc_asprintf(mem_ctx, "%s@%s", name_user, realm);
645 if (principal_s == NULL) {
646 return NT_STATUS_NO_MEMORY;
649 service = talloc_asprintf(mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
650 if (service == NULL) {
651 return NT_STATUS_NO_MEMORY;
654 local_service = talloc_asprintf(mem_ctx, "%s$@%s",
655 lp_netbios_name(), lp_realm());
656 if (local_service == NULL) {
657 return NT_STATUS_NO_MEMORY;
661 /* if this is a user ccache, we need to act as the user to let the krb5
662 * library handle the chown, etc. */
664 /************************ ENTERING NON-ROOT **********************/
666 if (user_ccache_file != NULL) {
667 set_effective_uid(uid);
668 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
671 result = kerberos_return_pac(mem_ctx,
672 principal_s,
673 pass,
674 time_offset,
675 &ticket_lifetime,
676 &renewal_until,
678 true,
679 true,
680 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
681 NULL,
682 local_service,
683 &pac_data_ctr);
684 if (user_ccache_file != NULL) {
685 gain_root_privilege();
688 /************************ RETURNED TO ROOT **********************/
690 if (!NT_STATUS_IS_OK(result)) {
691 goto failed;
694 if (pac_data_ctr == NULL) {
695 goto failed;
698 pac_data = pac_data_ctr->pac_data;
699 if (pac_data == NULL) {
700 goto failed;
703 for (i=0; i < pac_data->num_buffers; i++) {
705 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
706 continue;
709 logon_info = pac_data->buffers[i].info->logon_info.info;
710 if (!logon_info) {
711 return NT_STATUS_INVALID_PARAMETER;
714 break;
717 if (logon_info == NULL) {
718 DEBUG(10,("Missing logon_info in ticket of %s\n",
719 principal_s));
720 return NT_STATUS_INVALID_PARAMETER;
723 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
724 principal_s));
726 result = create_info3_from_pac_logon_info(mem_ctx, logon_info, &info3_copy);
727 if (!NT_STATUS_IS_OK(result)) {
728 goto failed;
731 /* if we had a user's ccache then return that string for the pam
732 * environment */
734 if (user_ccache_file != NULL) {
736 fstrcpy(krb5ccname, user_ccache_file);
738 result = add_ccache_to_list(principal_s,
740 service,
741 user,
742 pass,
743 realm,
744 uid,
745 time(NULL),
746 ticket_lifetime,
747 renewal_until,
748 false);
750 if (!NT_STATUS_IS_OK(result)) {
751 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
752 nt_errstr(result)));
754 } else {
756 /* need to delete the memory cred cache, it is not used anymore */
758 krb5_ret = ads_kdestroy(cc);
759 if (krb5_ret) {
760 DEBUG(3,("winbindd_raw_kerberos_login: "
761 "could not destroy krb5 credential cache: "
762 "%s\n", error_message(krb5_ret)));
766 *info3 = info3_copy;
767 return NT_STATUS_OK;
769 failed:
771 * Do not delete an existing valid credential cache, if the user
772 * e.g. enters a wrong password
774 if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
775 && user_ccache_file != NULL) {
776 return result;
779 /* we could have created a new credential cache with a valid tgt in it
780 * but we werent able to get or verify the service ticket for this
781 * local host and therefor didn't get the PAC, we need to remove that
782 * cache entirely now */
784 krb5_ret = ads_kdestroy(cc);
785 if (krb5_ret) {
786 DEBUG(3,("winbindd_raw_kerberos_login: "
787 "could not destroy krb5 credential cache: "
788 "%s\n", error_message(krb5_ret)));
791 if (!NT_STATUS_IS_OK(remove_ccache(user))) {
792 DEBUG(3,("winbindd_raw_kerberos_login: "
793 "could not remove ccache for user %s\n",
794 user));
797 return result;
798 #else
799 return NT_STATUS_NOT_SUPPORTED;
800 #endif /* HAVE_KRB5 */
803 /****************************************************************
804 ****************************************************************/
806 bool check_request_flags(uint32_t flags)
808 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
809 WBFLAG_PAM_INFO3_TEXT |
810 WBFLAG_PAM_INFO3_NDR;
812 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
813 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
814 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
815 !(flags & flags_edata) ) {
816 return true;
819 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
820 flags));
822 return false;
825 /****************************************************************
826 ****************************************************************/
828 NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
829 struct winbindd_response *resp,
830 uint32_t request_flags,
831 struct netr_SamInfo3 *info3,
832 const char *name_domain,
833 const char *name_user)
835 NTSTATUS result;
837 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
838 memcpy(resp->data.auth.user_session_key,
839 info3->base.key.key,
840 sizeof(resp->data.auth.user_session_key)
841 /* 16 */);
844 if (request_flags & WBFLAG_PAM_LMKEY) {
845 memcpy(resp->data.auth.first_8_lm_hash,
846 info3->base.LMSessKey.key,
847 sizeof(resp->data.auth.first_8_lm_hash)
848 /* 8 */);
851 if (request_flags & WBFLAG_PAM_UNIX_NAME) {
852 result = append_unix_username(mem_ctx, resp,
853 info3, name_domain, name_user);
854 if (!NT_STATUS_IS_OK(result)) {
855 DEBUG(10,("Failed to append Unix Username: %s\n",
856 nt_errstr(result)));
857 return result;
861 /* currently, anything from here on potentially overwrites extra_data. */
863 if (request_flags & WBFLAG_PAM_INFO3_NDR) {
864 result = append_info3_as_ndr(mem_ctx, resp, info3);
865 if (!NT_STATUS_IS_OK(result)) {
866 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
867 nt_errstr(result)));
868 return result;
872 if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
873 result = append_info3_as_txt(mem_ctx, resp, info3);
874 if (!NT_STATUS_IS_OK(result)) {
875 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
876 nt_errstr(result)));
877 return result;
881 if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
882 result = append_afs_token(mem_ctx, resp,
883 info3, name_domain, name_user);
884 if (!NT_STATUS_IS_OK(result)) {
885 DEBUG(10,("Failed to append AFS token: %s\n",
886 nt_errstr(result)));
887 return result;
891 return NT_STATUS_OK;
894 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
895 struct winbindd_cli_state *state,
896 struct netr_SamInfo3 **info3)
898 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
899 uint16_t max_allowed_bad_attempts;
900 fstring name_domain, name_user;
901 struct dom_sid sid;
902 enum lsa_SidType type;
903 uchar new_nt_pass[NT_HASH_LEN];
904 const uint8_t *cached_nt_pass;
905 const uint8_t *cached_salt;
906 struct netr_SamInfo3 *my_info3;
907 time_t kickoff_time, must_change_time;
908 bool password_good = false;
909 #ifdef HAVE_KRB5
910 struct winbindd_tdc_domain *tdc_domain = NULL;
911 #endif
913 *info3 = NULL;
915 ZERO_STRUCTP(info3);
917 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
919 /* Parse domain and username */
921 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
924 if (!lookup_cached_name(name_domain,
925 name_user,
926 &sid,
927 &type)) {
928 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
929 return NT_STATUS_NO_SUCH_USER;
932 if (type != SID_NAME_USER) {
933 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
934 return NT_STATUS_LOGON_FAILURE;
937 result = winbindd_get_creds(domain,
938 state->mem_ctx,
939 &sid,
940 &my_info3,
941 &cached_nt_pass,
942 &cached_salt);
943 if (!NT_STATUS_IS_OK(result)) {
944 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
945 return result;
948 *info3 = my_info3;
950 E_md4hash(state->request->data.auth.pass, new_nt_pass);
952 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
953 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
954 if (cached_salt) {
955 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
958 if (cached_salt) {
959 /* In this case we didn't store the nt_hash itself,
960 but the MD5 combination of salt + nt_hash. */
961 uchar salted_hash[NT_HASH_LEN];
962 E_md5hash(cached_salt, new_nt_pass, salted_hash);
964 password_good = (memcmp(cached_nt_pass, salted_hash,
965 NT_HASH_LEN) == 0);
966 } else {
967 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
968 password_good = (memcmp(cached_nt_pass, new_nt_pass,
969 NT_HASH_LEN) == 0);
972 if (password_good) {
974 /* User *DOES* know the password, update logon_time and reset
975 * bad_pw_count */
977 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
979 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
980 return NT_STATUS_ACCOUNT_LOCKED_OUT;
983 if (my_info3->base.acct_flags & ACB_DISABLED) {
984 return NT_STATUS_ACCOUNT_DISABLED;
987 if (my_info3->base.acct_flags & ACB_WSTRUST) {
988 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
991 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
992 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
995 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
996 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
999 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
1000 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
1001 my_info3->base.acct_flags));
1002 return NT_STATUS_LOGON_FAILURE;
1005 kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
1006 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
1007 return NT_STATUS_ACCOUNT_EXPIRED;
1010 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
1011 if (must_change_time != 0 && must_change_time < time(NULL)) {
1012 /* we allow grace logons when the password has expired */
1013 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
1014 /* return NT_STATUS_PASSWORD_EXPIRED; */
1015 goto success;
1018 #ifdef HAVE_KRB5
1019 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
1020 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
1021 ((tdc_domain->trust_type & LSA_TRUST_TYPE_UPLEVEL) ||
1022 /* used to cope with the case winbindd starting without network. */
1023 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
1025 uid_t uid = -1;
1026 const char *cc = NULL;
1027 char *realm = NULL;
1028 const char *principal_s = NULL;
1029 const char *service = NULL;
1030 const char *user_ccache_file;
1032 if (domain->alt_name == NULL) {
1033 return NT_STATUS_INVALID_PARAMETER;
1036 uid = get_uid_from_request(state->request);
1037 if (uid == -1) {
1038 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1039 return NT_STATUS_INVALID_PARAMETER;
1042 cc = generate_krb5_ccache(state->mem_ctx,
1043 state->request->data.auth.krb5_cc_type,
1044 state->request->data.auth.uid,
1045 &user_ccache_file);
1046 if (cc == NULL) {
1047 return NT_STATUS_NO_MEMORY;
1050 realm = talloc_strdup(state->mem_ctx, domain->alt_name);
1051 if (realm == NULL) {
1052 return NT_STATUS_NO_MEMORY;
1055 if (!strupper_m(realm)) {
1056 return NT_STATUS_INVALID_PARAMETER;
1059 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1060 if (principal_s == NULL) {
1061 return NT_STATUS_NO_MEMORY;
1064 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1065 if (service == NULL) {
1066 return NT_STATUS_NO_MEMORY;
1069 if (user_ccache_file != NULL) {
1071 fstrcpy(state->response->data.auth.krb5ccname,
1072 user_ccache_file);
1074 result = add_ccache_to_list(principal_s,
1076 service,
1077 state->request->data.auth.user,
1078 state->request->data.auth.pass,
1079 realm,
1080 uid,
1081 time(NULL),
1082 time(NULL) + lp_winbind_cache_time(),
1083 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1084 true);
1086 if (!NT_STATUS_IS_OK(result)) {
1087 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1088 "to add ccache to list: %s\n",
1089 nt_errstr(result)));
1093 #endif /* HAVE_KRB5 */
1094 success:
1095 /* FIXME: we possibly should handle logon hours as well (does xp when
1096 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1098 unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
1099 my_info3->base.bad_password_count = 0;
1101 result = winbindd_update_creds_by_info3(domain,
1102 state->request->data.auth.user,
1103 state->request->data.auth.pass,
1104 my_info3);
1105 if (!NT_STATUS_IS_OK(result)) {
1106 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1107 nt_errstr(result)));
1108 return result;
1111 return NT_STATUS_OK;
1115 /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
1116 if (domain->online == false) {
1117 goto failed;
1120 /* failure of this is not critical */
1121 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1122 if (!NT_STATUS_IS_OK(result)) {
1123 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1124 "Won't be able to honour account lockout policies\n"));
1127 /* increase counter */
1128 my_info3->base.bad_password_count++;
1130 if (max_allowed_bad_attempts == 0) {
1131 goto failed;
1134 /* lockout user */
1135 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1137 uint32_t password_properties;
1139 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1140 if (!NT_STATUS_IS_OK(result)) {
1141 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1144 if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
1145 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1146 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1150 failed:
1151 result = winbindd_update_creds_by_info3(domain,
1152 state->request->data.auth.user,
1153 NULL,
1154 my_info3);
1156 if (!NT_STATUS_IS_OK(result)) {
1157 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1158 nt_errstr(result)));
1161 return NT_STATUS_LOGON_FAILURE;
1164 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1165 struct winbindd_cli_state *state,
1166 struct netr_SamInfo3 **info3)
1168 struct winbindd_domain *contact_domain;
1169 fstring name_domain, name_user;
1170 NTSTATUS result;
1172 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1174 /* Parse domain and username */
1176 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1178 /* what domain should we contact? */
1180 if ( IS_DC ) {
1181 if (!(contact_domain = find_domain_from_name(name_domain))) {
1182 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1183 state->request->data.auth.user, name_domain, name_user, name_domain));
1184 result = NT_STATUS_NO_SUCH_USER;
1185 goto done;
1188 } else {
1189 if (is_myname(name_domain)) {
1190 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1191 result = NT_STATUS_NO_SUCH_USER;
1192 goto done;
1195 contact_domain = find_domain_from_name(name_domain);
1196 if (contact_domain == NULL) {
1197 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1198 state->request->data.auth.user, name_domain, name_user, name_domain));
1200 result = NT_STATUS_NO_SUCH_USER;
1201 goto done;
1205 if (contact_domain->initialized &&
1206 contact_domain->active_directory) {
1207 goto try_login;
1210 if (!contact_domain->initialized) {
1211 init_dc_connection(contact_domain, false);
1214 if (!contact_domain->active_directory) {
1215 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1216 return NT_STATUS_INVALID_LOGON_TYPE;
1218 try_login:
1219 result = winbindd_raw_kerberos_login(
1220 state->mem_ctx, contact_domain,
1221 state->request->data.auth.user,
1222 state->request->data.auth.pass,
1223 state->request->data.auth.krb5_cc_type,
1224 get_uid_from_request(state->request),
1225 info3, state->response->data.auth.krb5ccname);
1226 done:
1227 return result;
1230 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
1231 uint32_t logon_parameters,
1232 const char *domain, const char *user,
1233 const DATA_BLOB *challenge,
1234 const DATA_BLOB *lm_resp,
1235 const DATA_BLOB *nt_resp,
1236 bool interactive,
1237 struct netr_SamInfo3 **pinfo3)
1239 struct auth_context *auth_context;
1240 struct auth_serversupplied_info *server_info;
1241 struct auth_usersupplied_info *user_info = NULL;
1242 struct tsocket_address *local;
1243 struct netr_SamInfo3 *info3;
1244 NTSTATUS status;
1245 int rc;
1246 TALLOC_CTX *frame = talloc_stackframe();
1248 rc = tsocket_address_inet_from_strings(frame,
1249 "ip",
1250 "127.0.0.1",
1252 &local);
1253 if (rc < 0) {
1254 TALLOC_FREE(frame);
1255 return NT_STATUS_NO_MEMORY;
1257 status = make_user_info(frame, &user_info, user, user, domain, domain,
1258 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1259 NULL, AUTH_PASSWORD_RESPONSE);
1260 if (!NT_STATUS_IS_OK(status)) {
1261 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1262 TALLOC_FREE(frame);
1263 return status;
1266 user_info->logon_parameters = logon_parameters;
1268 /* We don't want any more mapping of the username */
1269 user_info->mapped_state = True;
1271 /* We don't want to come back to winbindd or to do PAM account checks */
1272 user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
1274 if (interactive) {
1275 user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
1278 status = make_auth_context_fixed(frame, &auth_context, challenge->data);
1280 if (!NT_STATUS_IS_OK(status)) {
1281 DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
1282 TALLOC_FREE(frame);
1283 return status;
1286 status = auth_check_ntlm_password(mem_ctx,
1287 auth_context,
1288 user_info,
1289 &server_info);
1291 if (!NT_STATUS_IS_OK(status)) {
1292 TALLOC_FREE(frame);
1293 return status;
1296 info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1297 if (info3 == NULL) {
1298 TALLOC_FREE(frame);
1299 return NT_STATUS_NO_MEMORY;
1302 status = serverinfo_to_SamInfo3(server_info, info3);
1303 if (!NT_STATUS_IS_OK(status)) {
1304 TALLOC_FREE(frame);
1305 TALLOC_FREE(info3);
1306 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1307 nt_errstr(status)));
1308 return status;
1311 *pinfo3 = info3;
1312 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1313 user, nt_errstr(status)));
1314 TALLOC_FREE(frame);
1315 return status;
1318 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1319 TALLOC_CTX *mem_ctx,
1320 uint32_t logon_parameters,
1321 const char *username,
1322 const char *password,
1323 const char *domainname,
1324 const char *workstation,
1325 const uint8_t chal[8],
1326 DATA_BLOB lm_response,
1327 DATA_BLOB nt_response,
1328 bool interactive,
1329 struct netr_SamInfo3 **info3)
1331 int attempts = 0;
1332 int netr_attempts = 0;
1333 bool retry = false;
1334 NTSTATUS result;
1336 do {
1337 struct rpc_pipe_client *netlogon_pipe;
1338 uint8_t authoritative = 0;
1339 uint32_t flags = 0;
1341 ZERO_STRUCTP(info3);
1342 retry = false;
1344 result = cm_connect_netlogon(domain, &netlogon_pipe);
1346 if (!NT_STATUS_IS_OK(result)) {
1347 DEBUG(3,("Could not open handle to NETLOGON pipe "
1348 "(error: %s, attempts: %d)\n",
1349 nt_errstr(result), netr_attempts));
1351 /* After the first retry always close the connection */
1352 if (netr_attempts > 0) {
1353 DEBUG(3, ("This is again a problem for this "
1354 "particular call, forcing the close "
1355 "of this connection\n"));
1356 invalidate_cm_connection(domain);
1359 /* After the second retry failover to the next DC */
1360 if (netr_attempts > 1) {
1362 * If the netlogon server is not reachable then
1363 * it is possible that the DC is rebuilding
1364 * sysvol and shutdown netlogon for that time.
1365 * We should failover to the next dc.
1367 DEBUG(3, ("This is the third problem for this "
1368 "particular call, adding DC to the "
1369 "negative cache list\n"));
1370 add_failed_connection_entry(domain->name,
1371 domain->dcname,
1372 result);
1373 saf_delete(domain->name);
1376 /* Only allow 3 retries */
1377 if (netr_attempts < 3) {
1378 DEBUG(3, ("The connection to netlogon "
1379 "failed, retrying\n"));
1380 netr_attempts++;
1381 retry = true;
1382 continue;
1384 return result;
1386 netr_attempts = 0;
1387 if (domain->conn.netlogon_creds == NULL) {
1388 DBG_NOTICE("No security credentials available for "
1389 "domain [%s]\n", domainname);
1390 result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1391 } else if (interactive && username != NULL && password != NULL) {
1392 result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1393 netlogon_pipe->binding_handle,
1394 mem_ctx,
1395 logon_parameters,
1396 domainname,
1397 username,
1398 password,
1399 workstation,
1400 NetlogonInteractiveInformation,
1401 info3);
1402 } else {
1403 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1404 netlogon_pipe->binding_handle,
1405 mem_ctx,
1406 logon_parameters,
1407 username,
1408 domainname,
1409 workstation,
1410 chal,
1411 lm_response,
1412 nt_response,
1413 &authoritative,
1414 &flags,
1415 info3);
1419 * we increment this after the "feature negotiation"
1420 * for can_do_samlogon_ex and can_do_validation6
1422 attempts += 1;
1424 /* We have to try a second time as cm_connect_netlogon
1425 might not yet have noticed that the DC has killed
1426 our connection. */
1428 if (!rpccli_is_connected(netlogon_pipe)) {
1429 retry = true;
1430 continue;
1433 /* if we get access denied, a possible cause was that we had
1434 and open connection to the DC, but someone changed our
1435 machine account password out from underneath us using 'net
1436 rpc changetrustpw' */
1438 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1439 DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1440 "ACCESS_DENIED. Maybe the DC has Restrict "
1441 "NTLM set or the trust account "
1442 "password was changed and we didn't know it. "
1443 "Killing connections to domain %s\n",
1444 domainname));
1445 invalidate_cm_connection(domain);
1446 retry = true;
1449 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1451 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1452 * (no Ex). This happens against old Samba
1453 * DCs, if LogonSamLogonEx() fails with an error
1454 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1456 * The server will log something like this:
1457 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1459 * This sets the whole connection into a fault_state mode
1460 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1462 * This also happens to our retry with LogonSamLogonWithFlags()
1463 * and LogonSamLogon().
1465 * In order to recover from this situation, we need to
1466 * drop the connection.
1468 invalidate_cm_connection(domain);
1469 result = NT_STATUS_LOGON_FAILURE;
1470 break;
1473 } while ( (attempts < 2) && retry );
1475 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1476 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1477 "returned NT_STATUS_IO_TIMEOUT after the retry."
1478 "Killing connections to domain %s\n",
1479 domainname));
1480 invalidate_cm_connection(domain);
1482 return result;
1485 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1486 struct winbindd_domain *domain,
1487 const char *user,
1488 const char *pass,
1489 uint32_t request_flags,
1490 struct netr_SamInfo3 **info3)
1493 uchar chal[8];
1494 DATA_BLOB lm_resp;
1495 DATA_BLOB nt_resp;
1496 unsigned char local_nt_response[24];
1497 fstring name_domain, name_user;
1498 NTSTATUS result;
1499 struct netr_SamInfo3 *my_info3 = NULL;
1501 *info3 = NULL;
1503 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1505 /* Parse domain and username */
1507 parse_domain_user(user, name_domain, name_user);
1509 /* do password magic */
1511 generate_random_buffer(chal, sizeof(chal));
1513 if (lp_client_ntlmv2_auth()) {
1514 DATA_BLOB server_chal;
1515 DATA_BLOB names_blob;
1516 server_chal = data_blob_const(chal, 8);
1518 /* note that the 'workgroup' here is for the local
1519 machine. The 'server name' must match the
1520 'workstation' passed to the actual SamLogon call.
1522 names_blob = NTLMv2_generate_names_blob(
1523 mem_ctx, lp_netbios_name(), lp_workgroup());
1525 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1526 pass,
1527 &server_chal,
1528 &names_blob,
1529 &lm_resp, &nt_resp, NULL, NULL)) {
1530 data_blob_free(&names_blob);
1531 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1532 result = NT_STATUS_NO_MEMORY;
1533 goto done;
1535 data_blob_free(&names_blob);
1536 } else {
1537 lm_resp = data_blob_null;
1538 SMBNTencrypt(pass, chal, local_nt_response);
1540 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1541 sizeof(local_nt_response));
1544 if (strequal(name_domain, get_global_sam_name())) {
1545 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1547 result = winbindd_dual_auth_passdb(
1548 mem_ctx, 0, name_domain, name_user,
1549 &chal_blob, &lm_resp, &nt_resp,
1550 true, /* interactive */
1551 info3);
1554 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1556 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1557 goto done;
1561 /* check authentication loop */
1563 result = winbind_samlogon_retry_loop(domain,
1564 mem_ctx,
1566 name_user,
1567 pass,
1568 name_domain,
1569 lp_netbios_name(),
1570 chal,
1571 lm_resp,
1572 nt_resp,
1573 true, /* interactive */
1574 &my_info3);
1575 if (!NT_STATUS_IS_OK(result)) {
1576 goto done;
1579 /* handle the case where a NT4 DC does not fill in the acct_flags in
1580 * the samlogon reply info3. When accurate info3 is required by the
1581 * caller, we look up the account flags ourselve - gd */
1583 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1584 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1586 struct rpc_pipe_client *samr_pipe;
1587 struct policy_handle samr_domain_handle, user_pol;
1588 union samr_UserInfo *info = NULL;
1589 NTSTATUS status_tmp, result_tmp;
1590 uint32_t acct_flags;
1591 struct dcerpc_binding_handle *b;
1593 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1594 &samr_pipe, &samr_domain_handle);
1596 if (!NT_STATUS_IS_OK(status_tmp)) {
1597 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1598 nt_errstr(status_tmp)));
1599 goto done;
1602 b = samr_pipe->binding_handle;
1604 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1605 &samr_domain_handle,
1606 MAXIMUM_ALLOWED_ACCESS,
1607 my_info3->base.rid,
1608 &user_pol,
1609 &result_tmp);
1611 if (!NT_STATUS_IS_OK(status_tmp)) {
1612 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1613 nt_errstr(status_tmp)));
1614 goto done;
1616 if (!NT_STATUS_IS_OK(result_tmp)) {
1617 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1618 nt_errstr(result_tmp)));
1619 goto done;
1622 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1623 &user_pol,
1625 &info,
1626 &result_tmp);
1628 if (!NT_STATUS_IS_OK(status_tmp)) {
1629 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1630 nt_errstr(status_tmp)));
1631 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1632 goto done;
1634 if (!NT_STATUS_IS_OK(result_tmp)) {
1635 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1636 nt_errstr(result_tmp)));
1637 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1638 goto done;
1641 acct_flags = info->info16.acct_flags;
1643 if (acct_flags == 0) {
1644 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1645 goto done;
1648 my_info3->base.acct_flags = acct_flags;
1650 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1652 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1655 *info3 = my_info3;
1656 done:
1657 return result;
1660 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1661 struct winbindd_cli_state *state)
1663 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1664 NTSTATUS krb5_result = NT_STATUS_OK;
1665 fstring name_domain, name_user;
1666 char *mapped_user;
1667 fstring domain_user;
1668 struct netr_SamInfo3 *info3 = NULL;
1669 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1671 /* Ensure null termination */
1672 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1674 /* Ensure null termination */
1675 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1677 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1678 state->request->data.auth.user));
1680 /* Parse domain and username */
1682 name_map_status = normalize_name_unmap(state->mem_ctx,
1683 state->request->data.auth.user,
1684 &mapped_user);
1686 /* If the name normalization didnt' actually do anything,
1687 just use the original name */
1689 if (!NT_STATUS_IS_OK(name_map_status) &&
1690 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1692 mapped_user = state->request->data.auth.user;
1695 parse_domain_user(mapped_user, name_domain, name_user);
1697 if ( mapped_user != state->request->data.auth.user ) {
1698 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1699 *lp_winbind_separator(),
1700 name_user );
1701 strlcpy( state->request->data.auth.user, domain_user,
1702 sizeof(state->request->data.auth.user));
1705 if (!domain->online) {
1706 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1707 if (domain->startup) {
1708 /* Logons are very important to users. If we're offline and
1709 we get a request within the first 30 seconds of startup,
1710 try very hard to find a DC and go online. */
1712 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1713 "request in startup mode.\n", domain->name ));
1715 winbindd_flush_negative_conn_cache(domain);
1716 result = init_dc_connection(domain, false);
1720 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1722 /* Check for Kerberos authentication */
1723 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1725 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1726 /* save for later */
1727 krb5_result = result;
1730 if (NT_STATUS_IS_OK(result)) {
1731 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1732 goto process_result;
1733 } else {
1734 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1737 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1738 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1739 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1740 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1741 set_domain_offline( domain );
1742 goto cached_logon;
1745 /* there are quite some NT_STATUS errors where there is no
1746 * point in retrying with a samlogon, we explictly have to take
1747 * care not to increase the bad logon counter on the DC */
1749 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1750 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1751 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1752 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1753 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1754 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1755 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1756 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1757 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1758 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1759 goto done;
1762 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1763 DEBUG(3,("falling back to samlogon\n"));
1764 goto sam_logon;
1765 } else {
1766 goto cached_logon;
1770 sam_logon:
1771 /* Check for Samlogon authentication */
1772 if (domain->online) {
1773 result = winbindd_dual_pam_auth_samlogon(
1774 state->mem_ctx, domain,
1775 state->request->data.auth.user,
1776 state->request->data.auth.pass,
1777 state->request->flags,
1778 &info3);
1780 if (NT_STATUS_IS_OK(result)) {
1781 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1782 /* add the Krb5 err if we have one */
1783 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1784 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1786 goto process_result;
1789 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1790 nt_errstr(result)));
1792 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1793 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1794 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1796 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1797 set_domain_offline( domain );
1798 goto cached_logon;
1801 if (domain->online) {
1802 /* We're still online - fail. */
1803 goto done;
1807 cached_logon:
1808 /* Check for Cached logons */
1809 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1810 lp_winbind_offline_logon()) {
1812 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1814 if (NT_STATUS_IS_OK(result)) {
1815 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1816 goto process_result;
1817 } else {
1818 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1819 goto done;
1823 process_result:
1825 if (NT_STATUS_IS_OK(result)) {
1827 struct dom_sid user_sid;
1829 /* In all codepaths where result == NT_STATUS_OK info3 must have
1830 been initialized. */
1831 if (!info3) {
1832 result = NT_STATUS_INTERNAL_ERROR;
1833 goto done;
1836 sid_compose(&user_sid, info3->base.domain_sid,
1837 info3->base.rid);
1839 if (info3->base.full_name.string == NULL) {
1840 struct netr_SamInfo3 *cached_info3;
1842 cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1843 &user_sid);
1844 if (cached_info3 != NULL &&
1845 cached_info3->base.full_name.string != NULL) {
1846 info3->base.full_name.string =
1847 talloc_strdup(info3,
1848 cached_info3->base.full_name.string);
1849 } else {
1851 /* this might fail so we don't check the return code */
1852 wcache_query_user_fullname(domain,
1853 info3,
1854 &user_sid,
1855 &info3->base.full_name.string);
1859 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1860 &user_sid);
1861 netsamlogon_cache_store(name_user, info3);
1863 /* save name_to_sid info as early as possible (only if
1864 this is our primary domain so we don't invalidate
1865 the cache entry by storing the seq_num for the wrong
1866 domain). */
1867 if ( domain->primary ) {
1868 cache_name2sid(domain, name_domain, name_user,
1869 SID_NAME_USER, &user_sid);
1872 /* Check if the user is in the right group */
1874 result = check_info3_in_group(
1875 info3,
1876 state->request->data.auth.require_membership_of_sid);
1877 if (!NT_STATUS_IS_OK(result)) {
1878 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1879 state->request->data.auth.user,
1880 state->request->data.auth.require_membership_of_sid));
1881 goto done;
1884 result = append_auth_data(state->mem_ctx, state->response,
1885 state->request->flags, info3,
1886 name_domain, name_user);
1887 if (!NT_STATUS_IS_OK(result)) {
1888 goto done;
1891 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1892 && lp_winbind_offline_logon()) {
1894 result = winbindd_store_creds(domain,
1895 state->request->data.auth.user,
1896 state->request->data.auth.pass,
1897 info3);
1900 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1901 struct winbindd_domain *our_domain = find_our_domain();
1903 /* This is not entirely correct I believe, but it is
1904 consistent. Only apply the password policy settings
1905 too warn users for our own domain. Cannot obtain these
1906 from trusted DCs all the time so don't do it at all.
1907 -- jerry */
1909 result = NT_STATUS_NOT_SUPPORTED;
1910 if (our_domain == domain ) {
1911 result = fillup_password_policy(
1912 our_domain, state->response);
1915 if (!NT_STATUS_IS_OK(result)
1916 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1918 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1919 domain->name, nt_errstr(result)));
1920 goto done;
1924 result = NT_STATUS_OK;
1927 done:
1928 /* give us a more useful (more correct?) error code */
1929 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1930 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1931 result = NT_STATUS_NO_LOGON_SERVERS;
1934 set_auth_errors(state->response, result);
1936 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1937 state->request->data.auth.user,
1938 state->response->data.auth.nt_status_string,
1939 state->response->data.auth.pam_error));
1941 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1944 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1945 TALLOC_CTX *mem_ctx,
1946 uint32_t logon_parameters,
1947 const char *name_user,
1948 const char *name_domain,
1949 const char *workstation,
1950 const uint8_t chal[8],
1951 DATA_BLOB lm_response,
1952 DATA_BLOB nt_response,
1953 struct netr_SamInfo3 **info3)
1955 NTSTATUS result;
1957 if (strequal(name_domain, get_global_sam_name())) {
1958 DATA_BLOB chal_blob = data_blob_const(
1959 chal, 8);
1961 result = winbindd_dual_auth_passdb(
1962 mem_ctx,
1963 logon_parameters,
1964 name_domain, name_user,
1965 &chal_blob, &lm_response, &nt_response,
1966 false, /* interactive */
1967 info3);
1970 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1972 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1973 goto process_result;
1977 result = winbind_samlogon_retry_loop(domain,
1978 mem_ctx,
1979 logon_parameters,
1980 name_user,
1981 NULL, /* password */
1982 name_domain,
1983 /* Bug #3248 - found by Stefan Burkei. */
1984 workstation, /* We carefully set this above so use it... */
1985 chal,
1986 lm_response,
1987 nt_response,
1988 false, /* interactive */
1989 info3);
1990 if (!NT_STATUS_IS_OK(result)) {
1991 goto done;
1994 process_result:
1996 if (NT_STATUS_IS_OK(result)) {
1997 struct dom_sid user_sid;
1999 sid_compose(&user_sid, (*info3)->base.domain_sid,
2000 (*info3)->base.rid);
2002 if ((*info3)->base.full_name.string == NULL) {
2003 struct netr_SamInfo3 *cached_info3;
2005 cached_info3 = netsamlogon_cache_get(mem_ctx,
2006 &user_sid);
2007 if (cached_info3 != NULL &&
2008 cached_info3->base.full_name.string != NULL) {
2009 (*info3)->base.full_name.string =
2010 talloc_strdup(*info3,
2011 cached_info3->base.full_name.string);
2012 } else {
2014 /* this might fail so we don't check the return code */
2015 wcache_query_user_fullname(domain,
2016 *info3,
2017 &user_sid,
2018 &(*info3)->base.full_name.string);
2022 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2023 &user_sid);
2024 netsamlogon_cache_store(name_user, *info3);
2027 done:
2029 /* give us a more useful (more correct?) error code */
2030 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2031 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2032 result = NT_STATUS_NO_LOGON_SERVERS;
2035 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2036 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2037 name_domain,
2038 name_user,
2039 nt_errstr(result)));
2041 return result;
2044 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2045 struct winbindd_cli_state *state)
2047 NTSTATUS result;
2048 struct netr_SamInfo3 *info3 = NULL;
2049 const char *name_user = NULL;
2050 const char *name_domain = NULL;
2051 const char *workstation;
2053 DATA_BLOB lm_resp, nt_resp;
2055 /* This is child-only, so no check for privileged access is needed
2056 anymore */
2058 /* Ensure null termination */
2059 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2060 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2062 name_user = state->request->data.auth_crap.user;
2063 name_domain = state->request->data.auth_crap.domain;
2064 workstation = state->request->data.auth_crap.workstation;
2066 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2067 name_domain, name_user));
2069 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2070 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2071 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2072 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2073 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2074 state->request->data.auth_crap.lm_resp_len,
2075 state->request->data.auth_crap.nt_resp_len));
2076 result = NT_STATUS_INVALID_PARAMETER;
2077 goto done;
2081 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2082 state->request->data.auth_crap.lm_resp_len);
2084 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2085 nt_resp = data_blob_talloc(state->mem_ctx,
2086 state->request->extra_data.data,
2087 state->request->data.auth_crap.nt_resp_len);
2088 } else {
2089 nt_resp = data_blob_talloc(state->mem_ctx,
2090 state->request->data.auth_crap.nt_resp,
2091 state->request->data.auth_crap.nt_resp_len);
2094 result = winbind_dual_SamLogon(domain,
2095 state->mem_ctx,
2096 state->request->data.auth_crap.logon_parameters,
2097 name_user,
2098 name_domain,
2099 /* Bug #3248 - found by Stefan Burkei. */
2100 workstation, /* We carefully set this above so use it... */
2101 state->request->data.auth_crap.chal,
2102 lm_resp,
2103 nt_resp,
2104 &info3);
2105 if (!NT_STATUS_IS_OK(result)) {
2106 goto done;
2109 if (NT_STATUS_IS_OK(result)) {
2110 /* Check if the user is in the right group */
2112 result = check_info3_in_group(
2113 info3,
2114 state->request->data.auth_crap.require_membership_of_sid);
2115 if (!NT_STATUS_IS_OK(result)) {
2116 DEBUG(3, ("User %s is not in the required group (%s), so "
2117 "crap authentication is rejected\n",
2118 state->request->data.auth_crap.user,
2119 state->request->data.auth_crap.require_membership_of_sid));
2120 goto done;
2123 result = append_auth_data(state->mem_ctx, state->response,
2124 state->request->flags, info3,
2125 name_domain, name_user);
2126 if (!NT_STATUS_IS_OK(result)) {
2127 goto done;
2131 done:
2133 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2134 result = nt_status_squash(result);
2137 set_auth_errors(state->response, result);
2139 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2142 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2143 struct winbindd_cli_state *state)
2145 char *oldpass;
2146 char *newpass = NULL;
2147 struct policy_handle dom_pol;
2148 struct rpc_pipe_client *cli = NULL;
2149 bool got_info = false;
2150 struct samr_DomInfo1 *info = NULL;
2151 struct userPwdChangeFailureInformation *reject = NULL;
2152 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2153 fstring domain, user;
2154 struct dcerpc_binding_handle *b = NULL;
2156 ZERO_STRUCT(dom_pol);
2158 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2159 state->request->data.auth.user));
2161 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2162 goto done;
2165 /* Change password */
2167 oldpass = state->request->data.chauthtok.oldpass;
2168 newpass = state->request->data.chauthtok.newpass;
2170 /* Initialize reject reason */
2171 state->response->data.auth.reject_reason = Undefined;
2173 /* Get sam handle */
2175 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2176 &dom_pol);
2177 if (!NT_STATUS_IS_OK(result)) {
2178 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2179 goto done;
2182 b = cli->binding_handle;
2184 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2185 user,
2186 newpass,
2187 oldpass,
2188 &info,
2189 &reject);
2191 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2193 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2195 fill_in_password_policy(state->response, info);
2197 state->response->data.auth.reject_reason =
2198 reject->extendedFailureReason;
2200 got_info = true;
2203 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2204 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2205 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2206 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2208 /* only fallback when the chgpasswd_user3 call is not supported */
2209 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2210 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2211 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2212 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2214 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2215 nt_errstr(result)));
2217 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2219 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2220 Map to the same status code as Windows 2003. */
2222 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2223 result = NT_STATUS_PASSWORD_RESTRICTION;
2227 done:
2229 if (NT_STATUS_IS_OK(result)
2230 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2231 && lp_winbind_offline_logon()) {
2232 result = winbindd_update_creds_by_name(contact_domain, user,
2233 newpass);
2234 /* Again, this happens when we login from gdm or xdm
2235 * and the password expires, *BUT* cached crendentials
2236 * doesn't exist. winbindd_update_creds_by_name()
2237 * returns NT_STATUS_NO_SUCH_USER.
2238 * This is not a failure.
2239 * --- BoYang
2240 * */
2241 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2242 result = NT_STATUS_OK;
2245 if (!NT_STATUS_IS_OK(result)) {
2246 DEBUG(10, ("Failed to store creds: %s\n",
2247 nt_errstr(result)));
2248 goto process_result;
2252 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2254 NTSTATUS policy_ret;
2256 policy_ret = fillup_password_policy(
2257 contact_domain, state->response);
2259 /* failure of this is non critical, it will just provide no
2260 * additional information to the client why the change has
2261 * failed - Guenther */
2263 if (!NT_STATUS_IS_OK(policy_ret)) {
2264 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2265 goto process_result;
2269 process_result:
2271 if (strequal(contact_domain->name, get_global_sam_name())) {
2272 /* FIXME: internal rpc pipe does not cache handles yet */
2273 if (b) {
2274 if (is_valid_policy_hnd(&dom_pol)) {
2275 NTSTATUS _result;
2276 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2278 TALLOC_FREE(cli);
2282 set_auth_errors(state->response, result);
2284 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2285 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2286 domain,
2287 user,
2288 state->response->data.auth.nt_status_string,
2289 state->response->data.auth.pam_error));
2291 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2294 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2295 struct winbindd_cli_state *state)
2297 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2299 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2300 state->request->data.logoff.user));
2302 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2303 result = NT_STATUS_OK;
2304 goto process_result;
2307 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2308 result = NT_STATUS_OK;
2309 goto process_result;
2312 #ifdef HAVE_KRB5
2314 if (state->request->data.logoff.uid == (uid_t)-1) {
2315 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2316 goto process_result;
2319 /* what we need here is to find the corresponding krb5 ccache name *we*
2320 * created for a given username and destroy it */
2322 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2323 result = NT_STATUS_OK;
2324 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2325 goto process_result;
2328 if (!ccache_entry_identical(state->request->data.logoff.user,
2329 state->request->data.logoff.uid,
2330 state->request->data.logoff.krb5ccname)) {
2331 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2332 goto process_result;
2335 result = remove_ccache(state->request->data.logoff.user);
2336 if (!NT_STATUS_IS_OK(result)) {
2337 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2338 nt_errstr(result)));
2339 goto process_result;
2343 * Remove any mlock'ed memory creds in the child
2344 * we might be using for krb5 ticket renewal.
2347 winbindd_delete_memory_creds(state->request->data.logoff.user);
2349 #else
2350 result = NT_STATUS_NOT_SUPPORTED;
2351 #endif
2353 process_result:
2356 set_auth_errors(state->response, result);
2358 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2361 /* Change user password with auth crap*/
2363 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2365 NTSTATUS result;
2366 DATA_BLOB new_nt_password;
2367 DATA_BLOB old_nt_hash_enc;
2368 DATA_BLOB new_lm_password;
2369 DATA_BLOB old_lm_hash_enc;
2370 fstring domain,user;
2371 struct policy_handle dom_pol;
2372 struct winbindd_domain *contact_domain = domainSt;
2373 struct rpc_pipe_client *cli = NULL;
2374 struct dcerpc_binding_handle *b = NULL;
2376 ZERO_STRUCT(dom_pol);
2378 /* Ensure null termination */
2379 state->request->data.chng_pswd_auth_crap.user[
2380 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2381 state->request->data.chng_pswd_auth_crap.domain[
2382 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2383 *domain = 0;
2384 *user = 0;
2386 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2387 (unsigned long)state->pid,
2388 state->request->data.chng_pswd_auth_crap.domain,
2389 state->request->data.chng_pswd_auth_crap.user));
2391 if (lp_winbind_offline_logon()) {
2392 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2393 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2394 result = NT_STATUS_ACCESS_DENIED;
2395 goto done;
2398 if (*state->request->data.chng_pswd_auth_crap.domain) {
2399 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2400 } else {
2401 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2402 domain, user);
2404 if(!*domain) {
2405 DEBUG(3,("no domain specified with username (%s) - "
2406 "failing auth\n",
2407 state->request->data.chng_pswd_auth_crap.user));
2408 result = NT_STATUS_NO_SUCH_USER;
2409 goto done;
2413 if (!*domain && lp_winbind_use_default_domain()) {
2414 fstrcpy(domain,lp_workgroup());
2417 if(!*user) {
2418 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2421 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2422 (unsigned long)state->pid, domain, user));
2424 /* Change password */
2425 new_nt_password = data_blob_const(
2426 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2427 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2429 old_nt_hash_enc = data_blob_const(
2430 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2431 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2433 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2434 new_lm_password = data_blob_const(
2435 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2436 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2438 old_lm_hash_enc = data_blob_const(
2439 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2440 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2441 } else {
2442 new_lm_password = data_blob_null;
2443 old_lm_hash_enc = data_blob_null;
2446 /* Get sam handle */
2448 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2449 if (!NT_STATUS_IS_OK(result)) {
2450 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2451 goto done;
2454 b = cli->binding_handle;
2456 result = rpccli_samr_chng_pswd_auth_crap(
2457 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2458 new_lm_password, old_lm_hash_enc);
2460 done:
2462 if (strequal(contact_domain->name, get_global_sam_name())) {
2463 /* FIXME: internal rpc pipe does not cache handles yet */
2464 if (b) {
2465 if (is_valid_policy_hnd(&dom_pol)) {
2466 NTSTATUS _result;
2467 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2469 TALLOC_FREE(cli);
2473 set_auth_errors(state->response, result);
2475 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2476 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2477 domain, user,
2478 state->response->data.auth.nt_status_string,
2479 state->response->data.auth.pam_error));
2481 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2484 #ifdef HAVE_KRB5
2485 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2486 struct PAC_LOGON_INFO **logon_info)
2488 krb5_context krbctx = NULL;
2489 krb5_error_code k5ret;
2490 krb5_keytab keytab;
2491 krb5_kt_cursor cursor;
2492 krb5_keytab_entry entry;
2493 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2495 ZERO_STRUCT(entry);
2496 ZERO_STRUCT(cursor);
2498 k5ret = krb5_init_context(&krbctx);
2499 if (k5ret) {
2500 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2501 error_message(k5ret)));
2502 status = krb5_to_nt_status(k5ret);
2503 goto out;
2506 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2507 if (k5ret) {
2508 DEBUG(1, ("Failed to get keytab: %s\n",
2509 error_message(k5ret)));
2510 status = krb5_to_nt_status(k5ret);
2511 goto out_free;
2514 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2515 if (k5ret) {
2516 DEBUG(1, ("Failed to start seq: %s\n",
2517 error_message(k5ret)));
2518 status = krb5_to_nt_status(k5ret);
2519 goto out_keytab;
2522 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2523 while (k5ret == 0) {
2524 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2525 krbctx, NULL,
2526 KRB5_KT_KEY(&entry), NULL, 0,
2527 logon_info);
2528 if (NT_STATUS_IS_OK(status)) {
2529 break;
2531 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2532 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2535 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2536 if (k5ret) {
2537 DEBUG(1, ("Failed to end seq: %s\n",
2538 error_message(k5ret)));
2540 out_keytab:
2541 k5ret = krb5_kt_close(krbctx, keytab);
2542 if (k5ret) {
2543 DEBUG(1, ("Failed to close keytab: %s\n",
2544 error_message(k5ret)));
2546 out_free:
2547 krb5_free_context(krbctx);
2548 out:
2549 return status;
2552 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2553 struct netr_SamInfo3 **info3)
2555 struct winbindd_request *req = state->request;
2556 DATA_BLOB pac_blob;
2557 struct PAC_LOGON_INFO *logon_info = NULL;
2558 struct netr_SamInfo3 *info3_copy = NULL;
2559 NTSTATUS result;
2561 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2562 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2563 if (!NT_STATUS_IS_OK(result) &&
2564 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2565 DEBUG(1, ("Error during PAC signature verification: %s\n",
2566 nt_errstr(result)));
2567 return result;
2570 if (logon_info) {
2571 /* Signature verification succeeded, trust the PAC */
2572 result = create_info3_from_pac_logon_info(state->mem_ctx,
2573 logon_info,
2574 &info3_copy);
2575 if (!NT_STATUS_IS_OK(result)) {
2576 return result;
2578 netsamlogon_cache_store(NULL, info3_copy);
2580 } else {
2581 /* Try without signature verification */
2582 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2583 NULL, NULL, NULL, 0,
2584 &logon_info);
2585 if (!NT_STATUS_IS_OK(result)) {
2586 DEBUG(10, ("Could not extract PAC: %s\n",
2587 nt_errstr(result)));
2588 return result;
2590 if (logon_info) {
2592 * Don't strictly need to copy here,
2593 * but it makes it explicit we're
2594 * returning a copy talloc'ed off
2595 * the state->mem_ctx.
2597 info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2598 &logon_info->info3);
2599 if (info3_copy == NULL) {
2600 return NT_STATUS_NO_MEMORY;
2605 *info3 = info3_copy;
2607 return NT_STATUS_OK;
2609 #else /* HAVE_KRB5 */
2610 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2611 struct netr_SamInfo3 **info3)
2613 return NT_STATUS_NO_SUCH_USER;
2615 #endif /* HAVE_KRB5 */