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/>.
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"
34 #include "../lib/crypto/arcfour.h"
35 #include "../libcli/security/security.h"
37 #include "../librpc/gen_ndr/krb5pac.h"
38 #include "passdb/machine_sid.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"
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)
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++) {
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",
120 info3->sids[i].attributes);
121 NT_STATUS_HAVE_NO_MEMORY(ex);
126 resp->extra_data.data = ex;
127 resp->length += talloc_get_size(ex);
132 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
133 struct winbindd_response *resp,
134 struct netr_SamInfo3 *info3)
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;
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);
165 /* If the server didn't give us one, just use the one
167 nt_domain = name_domain;
170 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
172 /* If the server didn't give us one, just use the one
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));
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;
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(),
204 afsname = talloc_string_sub(mem_ctx, afsname,
206 afsname = talloc_string_sub(mem_ctx, afsname,
210 struct dom_sid user_sid;
213 sid_compose(&user_sid, info3->base.domain_sid,
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
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, '@');
233 return NT_STATUS_NO_MEMORY;
239 token = afs_createtoken_str(afsname, cell);
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;
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;
272 struct security_token *token;
273 TALLOC_CTX *frame = talloc_stackframe();
276 /* Parse the 'required group' SID */
278 if (!group_sid || !group_sid[0]) {
279 /* NO sid supplied, all users may access */
284 token = talloc_zero(talloc_tos(), struct security_token);
286 DEBUG(0, ("talloc failed\n"));
288 return NT_STATUS_NO_MEMORY;
291 num_require_membership_of_sid = 0;
292 require_membership_of_sid = NULL;
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));
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"));
314 status = sid_array_from_info3(talloc_tos(), info3,
318 if (!NT_STATUS_IS_OK(status)) {
323 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
325 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
327 DEBUG(3, ("could not add aliases: %s\n",
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],
340 DEBUG(10, ("Access ok\n"));
346 /* Do not distinguish this error from a wrong username/pw */
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;
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",
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",
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;
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;
416 methods = domain->methods;
418 status = methods->password_policy(domain, talloc_tos(), &password_policy);
419 if (NT_STATUS_IS_ERR(status)) {
423 fill_in_password_policy(response, &password_policy);
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
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)) {
447 *lockout_threshold = lockout_policy.lockout_threshold;
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
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)) {
469 *password_properties = password_policy.password_properties;
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
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 -
485 const char *gen_cc = NULL;
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 */
509 p = strchr(type, '%');
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,
523 /* remove_unsafe_characters */
527 /* allow_trailing_dollar */
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"));
544 DEBUG(10, ("using ccache: %s%s\n", gen_cc,
545 (*user_ccache_file == NULL) ? " (internal)":""));
552 uid_t get_uid_from_request(struct winbindd_request *request)
556 uid = request->data.auth.uid;
559 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
565 /**********************************************************************
566 Authenticate a user with a clear text password using Kerberos and fill up
568 **********************************************************************/
570 static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
571 struct winbindd_domain *domain,
574 const char *krb5_cc_type,
576 struct netr_SamInfo3 **info3,
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;
586 fstring name_domain, name_user;
587 time_t ticket_lifetime = 0;
588 time_t renewal_until = 0;
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;
597 struct netr_SamInfo3 *info3_copy = NULL;
601 if (domain->alt_name == NULL) {
602 return NT_STATUS_INVALID_PARAMETER;
606 * prepare a krb5_cc_cache string for the user */
609 DEBUG(0,("no valid uid\n"));
612 cc = generate_krb5_ccache(mem_ctx,
617 return NT_STATUS_NO_MEMORY;
622 * get kerberos properties */
624 if (domain->private_data) {
625 ads = (ADS_STRUCT *)domain->private_data;
626 time_offset = ads->auth.time_offset;
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);
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,
680 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
684 if (user_ccache_file != NULL) {
685 gain_root_privilege();
688 /************************ RETURNED TO ROOT **********************/
690 if (!NT_STATUS_IS_OK(result)) {
694 if (pac_data_ctr == NULL) {
698 pac_data = pac_data_ctr->pac_data;
699 if (pac_data == NULL) {
703 for (i=0; i < pac_data->num_buffers; i++) {
705 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
709 logon_info = pac_data->buffers[i].info->logon_info.info;
711 return NT_STATUS_INVALID_PARAMETER;
717 if (logon_info == NULL) {
718 DEBUG(10,("Missing logon_info in ticket of %s\n",
720 return NT_STATUS_INVALID_PARAMETER;
723 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
726 result = create_info3_from_pac_logon_info(mem_ctx, logon_info, &info3_copy);
727 if (!NT_STATUS_IS_OK(result)) {
731 /* if we had a user's ccache then return that string for the pam
734 if (user_ccache_file != NULL) {
736 fstrcpy(krb5ccname, user_ccache_file);
738 result = add_ccache_to_list(principal_s,
750 if (!NT_STATUS_IS_OK(result)) {
751 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
756 /* need to delete the memory cred cache, it is not used anymore */
758 krb5_ret = ads_kdestroy(cc);
760 DEBUG(3,("winbindd_raw_kerberos_login: "
761 "could not destroy krb5 credential cache: "
762 "%s\n", error_message(krb5_ret)));
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) {
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);
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",
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) ) {
819 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
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)
837 if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
838 memcpy(resp->data.auth.user_session_key,
840 sizeof(resp->data.auth.user_session_key)
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)
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",
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",
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",
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",
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;
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;
910 struct winbindd_tdc_domain *tdc_domain = NULL;
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,
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,
943 if (!NT_STATUS_IS_OK(result)) {
944 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
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);
955 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
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,
967 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
968 password_good = (memcmp(cached_nt_pass, new_nt_pass,
974 /* User *DOES* know the password, update logon_time and reset
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; */
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))) {
1026 const char *cc = 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);
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,
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,
1074 result = add_ccache_to_list(principal_s,
1077 state->request->data.auth.user,
1078 state->request->data.auth.pass,
1082 time(NULL) + lp_winbind_cache_time(),
1083 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
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 */
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,
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)));
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) {
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) {
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;
1151 result = winbindd_update_creds_by_info3(domain,
1152 state->request->data.auth.user,
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;
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? */
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;
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;
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;
1205 if (contact_domain->initialized &&
1206 contact_domain->active_directory) {
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;
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);
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 struct netr_SamInfo3 **pinfo3)
1238 struct auth_context *auth_context;
1239 struct auth_serversupplied_info *server_info;
1240 struct auth_usersupplied_info *user_info = NULL;
1241 struct tsocket_address *local;
1242 struct netr_SamInfo3 *info3;
1245 TALLOC_CTX *frame = talloc_stackframe();
1247 rc = tsocket_address_inet_from_strings(frame,
1254 return NT_STATUS_NO_MEMORY;
1256 status = make_user_info(frame, &user_info, user, user, domain, domain,
1257 lp_netbios_name(), local, lm_resp, nt_resp, NULL, NULL,
1258 NULL, AUTH_PASSWORD_RESPONSE);
1259 if (!NT_STATUS_IS_OK(status)) {
1260 DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
1265 user_info->logon_parameters = logon_parameters;
1267 /* We don't want any more mapping of the username */
1268 user_info->mapped_state = True;
1270 /* We don't want to come back to winbindd or to do PAM account checks */
1271 user_info->flags |= USER_INFO_LOCAL_SAM_ONLY | USER_INFO_INFO3_AND_NO_AUTHZ;
1273 status = make_auth_context_fixed(frame, &auth_context, challenge->data);
1275 if (!NT_STATUS_IS_OK(status)) {
1276 DEBUG(0, ("Failed to test authentication with check_sam_security_info3: %s\n", nt_errstr(status)));
1281 status = auth_check_ntlm_password(mem_ctx,
1286 if (!NT_STATUS_IS_OK(status)) {
1291 info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
1292 if (info3 == NULL) {
1294 return NT_STATUS_NO_MEMORY;
1297 status = serverinfo_to_SamInfo3(server_info, info3);
1298 if (!NT_STATUS_IS_OK(status)) {
1301 DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
1302 nt_errstr(status)));
1307 DEBUG(10, ("Authenticaticating user %s\\%s returned %s\n", domain,
1308 user, nt_errstr(status)));
1313 static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
1314 TALLOC_CTX *mem_ctx,
1315 uint32_t logon_parameters,
1316 const char *username,
1317 const char *password,
1318 const char *domainname,
1319 const char *workstation,
1320 const uint8_t chal[8],
1321 DATA_BLOB lm_response,
1322 DATA_BLOB nt_response,
1324 struct netr_SamInfo3 **info3)
1327 int netr_attempts = 0;
1332 struct rpc_pipe_client *netlogon_pipe;
1333 uint8_t authoritative = 0;
1336 ZERO_STRUCTP(info3);
1339 result = cm_connect_netlogon(domain, &netlogon_pipe);
1341 if (!NT_STATUS_IS_OK(result)) {
1342 DEBUG(3,("Could not open handle to NETLOGON pipe "
1343 "(error: %s, attempts: %d)\n",
1344 nt_errstr(result), netr_attempts));
1346 /* After the first retry always close the connection */
1347 if (netr_attempts > 0) {
1348 DEBUG(3, ("This is again a problem for this "
1349 "particular call, forcing the close "
1350 "of this connection\n"));
1351 invalidate_cm_connection(domain);
1354 /* After the second retry failover to the next DC */
1355 if (netr_attempts > 1) {
1357 * If the netlogon server is not reachable then
1358 * it is possible that the DC is rebuilding
1359 * sysvol and shutdown netlogon for that time.
1360 * We should failover to the next dc.
1362 DEBUG(3, ("This is the third problem for this "
1363 "particular call, adding DC to the "
1364 "negative cache list\n"));
1365 add_failed_connection_entry(domain->name,
1368 saf_delete(domain->name);
1371 /* Only allow 3 retries */
1372 if (netr_attempts < 3) {
1373 DEBUG(3, ("The connection to netlogon "
1374 "failed, retrying\n"));
1382 if (domain->conn.netlogon_creds == NULL) {
1383 DBG_NOTICE("No security credentials available for "
1384 "domain [%s]\n", domainname);
1385 result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1386 } else if (interactive && username != NULL && password != NULL) {
1387 result = rpccli_netlogon_password_logon(domain->conn.netlogon_creds,
1388 netlogon_pipe->binding_handle,
1395 NetlogonInteractiveInformation,
1398 result = rpccli_netlogon_network_logon(domain->conn.netlogon_creds,
1399 netlogon_pipe->binding_handle,
1414 * we increment this after the "feature negotiation"
1415 * for can_do_samlogon_ex and can_do_validation6
1419 /* We have to try a second time as cm_connect_netlogon
1420 might not yet have noticed that the DC has killed
1423 if (!rpccli_is_connected(netlogon_pipe)) {
1428 /* if we get access denied, a possible cause was that we had
1429 and open connection to the DC, but someone changed our
1430 machine account password out from underneath us using 'net
1431 rpc changetrustpw' */
1433 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1434 DEBUG(1,("winbind_samlogon_retry_loop: sam_logon returned "
1435 "ACCESS_DENIED. Maybe the DC has Restrict "
1436 "NTLM set or the trust account "
1437 "password was changed and we didn't know it. "
1438 "Killing connections to domain %s\n",
1440 invalidate_cm_connection(domain);
1444 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
1446 * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
1447 * (no Ex). This happens against old Samba
1448 * DCs, if LogonSamLogonEx() fails with an error
1449 * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
1451 * The server will log something like this:
1452 * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
1454 * This sets the whole connection into a fault_state mode
1455 * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
1457 * This also happens to our retry with LogonSamLogonWithFlags()
1458 * and LogonSamLogon().
1460 * In order to recover from this situation, we need to
1461 * drop the connection.
1463 invalidate_cm_connection(domain);
1464 result = NT_STATUS_LOGON_FAILURE;
1468 } while ( (attempts < 2) && retry );
1470 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT)) {
1471 DEBUG(3,("winbind_samlogon_retry_loop: sam_network_logon(ex) "
1472 "returned NT_STATUS_IO_TIMEOUT after the retry."
1473 "Killing connections to domain %s\n",
1475 invalidate_cm_connection(domain);
1480 static NTSTATUS winbindd_dual_pam_auth_samlogon(TALLOC_CTX *mem_ctx,
1481 struct winbindd_domain *domain,
1484 uint32_t request_flags,
1485 struct netr_SamInfo3 **info3)
1491 unsigned char local_nt_response[24];
1492 fstring name_domain, name_user;
1494 struct netr_SamInfo3 *my_info3 = NULL;
1498 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1500 /* Parse domain and username */
1502 parse_domain_user(user, name_domain, name_user);
1504 /* do password magic */
1506 generate_random_buffer(chal, sizeof(chal));
1508 if (lp_client_ntlmv2_auth()) {
1509 DATA_BLOB server_chal;
1510 DATA_BLOB names_blob;
1511 server_chal = data_blob_const(chal, 8);
1513 /* note that the 'workgroup' here is for the local
1514 machine. The 'server name' must match the
1515 'workstation' passed to the actual SamLogon call.
1517 names_blob = NTLMv2_generate_names_blob(
1518 mem_ctx, lp_netbios_name(), lp_workgroup());
1520 if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
1524 &lm_resp, &nt_resp, NULL, NULL)) {
1525 data_blob_free(&names_blob);
1526 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1527 result = NT_STATUS_NO_MEMORY;
1530 data_blob_free(&names_blob);
1532 lm_resp = data_blob_null;
1533 SMBNTencrypt(pass, chal, local_nt_response);
1535 nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
1536 sizeof(local_nt_response));
1539 if (strequal(name_domain, get_global_sam_name())) {
1540 DATA_BLOB chal_blob = data_blob_const(chal, sizeof(chal));
1542 result = winbindd_dual_auth_passdb(
1543 mem_ctx, 0, name_domain, name_user,
1544 &chal_blob, &lm_resp, &nt_resp, info3);
1547 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1549 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1554 /* check authentication loop */
1556 result = winbind_samlogon_retry_loop(domain,
1566 true, /* interactive */
1568 if (!NT_STATUS_IS_OK(result)) {
1572 /* handle the case where a NT4 DC does not fill in the acct_flags in
1573 * the samlogon reply info3. When accurate info3 is required by the
1574 * caller, we look up the account flags ourselve - gd */
1576 if ((request_flags & WBFLAG_PAM_INFO3_TEXT) &&
1577 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1579 struct rpc_pipe_client *samr_pipe;
1580 struct policy_handle samr_domain_handle, user_pol;
1581 union samr_UserInfo *info = NULL;
1582 NTSTATUS status_tmp, result_tmp;
1583 uint32_t acct_flags;
1584 struct dcerpc_binding_handle *b;
1586 status_tmp = cm_connect_sam(domain, mem_ctx, false,
1587 &samr_pipe, &samr_domain_handle);
1589 if (!NT_STATUS_IS_OK(status_tmp)) {
1590 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1591 nt_errstr(status_tmp)));
1595 b = samr_pipe->binding_handle;
1597 status_tmp = dcerpc_samr_OpenUser(b, mem_ctx,
1598 &samr_domain_handle,
1599 MAXIMUM_ALLOWED_ACCESS,
1604 if (!NT_STATUS_IS_OK(status_tmp)) {
1605 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1606 nt_errstr(status_tmp)));
1609 if (!NT_STATUS_IS_OK(result_tmp)) {
1610 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1611 nt_errstr(result_tmp)));
1615 status_tmp = dcerpc_samr_QueryUserInfo(b, mem_ctx,
1621 if (!NT_STATUS_IS_OK(status_tmp)) {
1622 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1623 nt_errstr(status_tmp)));
1624 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1627 if (!NT_STATUS_IS_OK(result_tmp)) {
1628 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1629 nt_errstr(result_tmp)));
1630 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1634 acct_flags = info->info16.acct_flags;
1636 if (acct_flags == 0) {
1637 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1641 my_info3->base.acct_flags = acct_flags;
1643 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1645 dcerpc_samr_Close(b, mem_ctx, &user_pol, &result_tmp);
1653 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1654 struct winbindd_cli_state *state)
1656 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1657 NTSTATUS krb5_result = NT_STATUS_OK;
1658 fstring name_domain, name_user;
1660 fstring domain_user;
1661 struct netr_SamInfo3 *info3 = NULL;
1662 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1664 /* Ensure null termination */
1665 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1667 /* Ensure null termination */
1668 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1670 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1671 state->request->data.auth.user));
1673 /* Parse domain and username */
1675 name_map_status = normalize_name_unmap(state->mem_ctx,
1676 state->request->data.auth.user,
1679 /* If the name normalization didnt' actually do anything,
1680 just use the original name */
1682 if (!NT_STATUS_IS_OK(name_map_status) &&
1683 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1685 mapped_user = state->request->data.auth.user;
1688 parse_domain_user(mapped_user, name_domain, name_user);
1690 if ( mapped_user != state->request->data.auth.user ) {
1691 fstr_sprintf( domain_user, "%s%c%s", name_domain,
1692 *lp_winbind_separator(),
1694 strlcpy( state->request->data.auth.user, domain_user,
1695 sizeof(state->request->data.auth.user));
1698 if (!domain->online) {
1699 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1700 if (domain->startup) {
1701 /* Logons are very important to users. If we're offline and
1702 we get a request within the first 30 seconds of startup,
1703 try very hard to find a DC and go online. */
1705 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1706 "request in startup mode.\n", domain->name ));
1708 winbindd_flush_negative_conn_cache(domain);
1709 result = init_dc_connection(domain, false);
1713 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1715 /* Check for Kerberos authentication */
1716 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1718 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1719 /* save for later */
1720 krb5_result = result;
1723 if (NT_STATUS_IS_OK(result)) {
1724 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1725 goto process_result;
1727 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1730 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1731 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1732 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1733 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1734 set_domain_offline( domain );
1738 /* there are quite some NT_STATUS errors where there is no
1739 * point in retrying with a samlogon, we explictly have to take
1740 * care not to increase the bad logon counter on the DC */
1742 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1743 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1744 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1745 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1746 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1747 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1748 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1749 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1750 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1751 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1755 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1756 DEBUG(3,("falling back to samlogon\n"));
1764 /* Check for Samlogon authentication */
1765 if (domain->online) {
1766 result = winbindd_dual_pam_auth_samlogon(
1767 state->mem_ctx, domain,
1768 state->request->data.auth.user,
1769 state->request->data.auth.pass,
1770 state->request->flags,
1773 if (NT_STATUS_IS_OK(result)) {
1774 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1775 /* add the Krb5 err if we have one */
1776 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1777 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1779 goto process_result;
1782 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1783 nt_errstr(result)));
1785 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1786 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1787 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1789 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1790 set_domain_offline( domain );
1794 if (domain->online) {
1795 /* We're still online - fail. */
1801 /* Check for Cached logons */
1802 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1803 lp_winbind_offline_logon()) {
1805 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1807 if (NT_STATUS_IS_OK(result)) {
1808 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1809 goto process_result;
1811 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1818 if (NT_STATUS_IS_OK(result)) {
1820 struct dom_sid user_sid;
1822 /* In all codepaths where result == NT_STATUS_OK info3 must have
1823 been initialized. */
1825 result = NT_STATUS_INTERNAL_ERROR;
1829 sid_compose(&user_sid, info3->base.domain_sid,
1832 if (info3->base.full_name.string == NULL) {
1833 struct netr_SamInfo3 *cached_info3;
1835 cached_info3 = netsamlogon_cache_get(state->mem_ctx,
1837 if (cached_info3 != NULL &&
1838 cached_info3->base.full_name.string != NULL) {
1839 info3->base.full_name.string =
1840 talloc_strdup(info3,
1841 cached_info3->base.full_name.string);
1844 /* this might fail so we dont check the return code */
1845 wcache_query_user_fullname(domain,
1848 &info3->base.full_name.string);
1852 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
1854 netsamlogon_cache_store(name_user, info3);
1856 /* save name_to_sid info as early as possible (only if
1857 this is our primary domain so we don't invalidate
1858 the cache entry by storing the seq_num for the wrong
1860 if ( domain->primary ) {
1861 cache_name2sid(domain, name_domain, name_user,
1862 SID_NAME_USER, &user_sid);
1865 /* Check if the user is in the right group */
1867 result = check_info3_in_group(
1869 state->request->data.auth.require_membership_of_sid);
1870 if (!NT_STATUS_IS_OK(result)) {
1871 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1872 state->request->data.auth.user,
1873 state->request->data.auth.require_membership_of_sid));
1877 result = append_auth_data(state->mem_ctx, state->response,
1878 state->request->flags, info3,
1879 name_domain, name_user);
1880 if (!NT_STATUS_IS_OK(result)) {
1884 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
1885 && lp_winbind_offline_logon()) {
1887 result = winbindd_store_creds(domain,
1888 state->request->data.auth.user,
1889 state->request->data.auth.pass,
1893 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1894 struct winbindd_domain *our_domain = find_our_domain();
1896 /* This is not entirely correct I believe, but it is
1897 consistent. Only apply the password policy settings
1898 too warn users for our own domain. Cannot obtain these
1899 from trusted DCs all the time so don't do it at all.
1902 result = NT_STATUS_NOT_SUPPORTED;
1903 if (our_domain == domain ) {
1904 result = fillup_password_policy(
1905 our_domain, state->response);
1908 if (!NT_STATUS_IS_OK(result)
1909 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1911 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1912 domain->name, nt_errstr(result)));
1917 result = NT_STATUS_OK;
1921 /* give us a more useful (more correct?) error code */
1922 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1923 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1924 result = NT_STATUS_NO_LOGON_SERVERS;
1927 set_auth_errors(state->response, result);
1929 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1930 state->request->data.auth.user,
1931 state->response->data.auth.nt_status_string,
1932 state->response->data.auth.pam_error));
1934 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1937 NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
1938 TALLOC_CTX *mem_ctx,
1939 uint32_t logon_parameters,
1940 const char *name_user,
1941 const char *name_domain,
1942 const char *workstation,
1943 const uint8_t chal[8],
1944 DATA_BLOB lm_response,
1945 DATA_BLOB nt_response,
1946 struct netr_SamInfo3 **info3)
1950 if (strequal(name_domain, get_global_sam_name())) {
1951 DATA_BLOB chal_blob = data_blob_const(
1954 result = winbindd_dual_auth_passdb(
1957 name_domain, name_user,
1958 &chal_blob, &lm_response, &nt_response, info3);
1961 * We need to try the remote NETLOGON server if this is NOT_IMPLEMENTED
1963 if (!NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
1964 goto process_result;
1968 result = winbind_samlogon_retry_loop(domain,
1972 NULL, /* password */
1974 /* Bug #3248 - found by Stefan Burkei. */
1975 workstation, /* We carefully set this above so use it... */
1979 false, /* interactive */
1981 if (!NT_STATUS_IS_OK(result)) {
1987 if (NT_STATUS_IS_OK(result)) {
1988 struct dom_sid user_sid;
1990 sid_compose(&user_sid, (*info3)->base.domain_sid,
1991 (*info3)->base.rid);
1993 if ((*info3)->base.full_name.string == NULL) {
1994 struct netr_SamInfo3 *cached_info3;
1996 cached_info3 = netsamlogon_cache_get(mem_ctx,
1998 if (cached_info3 != NULL &&
1999 cached_info3->base.full_name.string != NULL) {
2000 (*info3)->base.full_name.string =
2001 talloc_strdup(*info3,
2002 cached_info3->base.full_name.string);
2005 /* this might fail so we dont check the return code */
2006 wcache_query_user_fullname(domain,
2009 &(*info3)->base.full_name.string);
2013 wcache_invalidate_samlogon(find_domain_from_name(name_domain),
2015 netsamlogon_cache_store(name_user, *info3);
2020 /* give us a more useful (more correct?) error code */
2021 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
2022 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
2023 result = NT_STATUS_NO_LOGON_SERVERS;
2026 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2027 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
2030 nt_errstr(result)));
2035 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
2036 struct winbindd_cli_state *state)
2039 struct netr_SamInfo3 *info3 = NULL;
2040 const char *name_user = NULL;
2041 const char *name_domain = NULL;
2042 const char *workstation;
2044 DATA_BLOB lm_resp, nt_resp;
2046 /* This is child-only, so no check for privileged access is needed
2049 /* Ensure null termination */
2050 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
2051 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
2053 name_user = state->request->data.auth_crap.user;
2054 name_domain = state->request->data.auth_crap.domain;
2055 workstation = state->request->data.auth_crap.workstation;
2057 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
2058 name_domain, name_user));
2060 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
2061 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
2062 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
2063 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
2064 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
2065 state->request->data.auth_crap.lm_resp_len,
2066 state->request->data.auth_crap.nt_resp_len));
2067 result = NT_STATUS_INVALID_PARAMETER;
2072 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
2073 state->request->data.auth_crap.lm_resp_len);
2075 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
2076 nt_resp = data_blob_talloc(state->mem_ctx,
2077 state->request->extra_data.data,
2078 state->request->data.auth_crap.nt_resp_len);
2080 nt_resp = data_blob_talloc(state->mem_ctx,
2081 state->request->data.auth_crap.nt_resp,
2082 state->request->data.auth_crap.nt_resp_len);
2085 result = winbind_dual_SamLogon(domain,
2087 state->request->data.auth_crap.logon_parameters,
2090 /* Bug #3248 - found by Stefan Burkei. */
2091 workstation, /* We carefully set this above so use it... */
2092 state->request->data.auth_crap.chal,
2096 if (!NT_STATUS_IS_OK(result)) {
2100 if (NT_STATUS_IS_OK(result)) {
2101 /* Check if the user is in the right group */
2103 result = check_info3_in_group(
2105 state->request->data.auth_crap.require_membership_of_sid);
2106 if (!NT_STATUS_IS_OK(result)) {
2107 DEBUG(3, ("User %s is not in the required group (%s), so "
2108 "crap authentication is rejected\n",
2109 state->request->data.auth_crap.user,
2110 state->request->data.auth_crap.require_membership_of_sid));
2114 result = append_auth_data(state->mem_ctx, state->response,
2115 state->request->flags, info3,
2116 name_domain, name_user);
2117 if (!NT_STATUS_IS_OK(result)) {
2124 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
2125 result = nt_status_squash(result);
2128 set_auth_errors(state->response, result);
2130 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2133 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2134 struct winbindd_cli_state *state)
2137 char *newpass = NULL;
2138 struct policy_handle dom_pol;
2139 struct rpc_pipe_client *cli = NULL;
2140 bool got_info = false;
2141 struct samr_DomInfo1 *info = NULL;
2142 struct userPwdChangeFailureInformation *reject = NULL;
2143 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2144 fstring domain, user;
2145 struct dcerpc_binding_handle *b = NULL;
2147 ZERO_STRUCT(dom_pol);
2149 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2150 state->request->data.auth.user));
2152 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2156 /* Change password */
2158 oldpass = state->request->data.chauthtok.oldpass;
2159 newpass = state->request->data.chauthtok.newpass;
2161 /* Initialize reject reason */
2162 state->response->data.auth.reject_reason = Undefined;
2164 /* Get sam handle */
2166 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli,
2168 if (!NT_STATUS_IS_OK(result)) {
2169 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2173 b = cli->binding_handle;
2175 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2182 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2184 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2186 fill_in_password_policy(state->response, info);
2188 state->response->data.auth.reject_reason =
2189 reject->extendedFailureReason;
2194 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2195 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2196 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2197 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2199 /* only fallback when the chgpasswd_user3 call is not supported */
2200 if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
2201 NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
2202 NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
2203 NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
2205 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2206 nt_errstr(result)));
2208 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2210 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2211 Map to the same status code as Windows 2003. */
2213 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2214 result = NT_STATUS_PASSWORD_RESTRICTION;
2220 if (NT_STATUS_IS_OK(result)
2221 && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
2222 && lp_winbind_offline_logon()) {
2223 result = winbindd_update_creds_by_name(contact_domain, user,
2225 /* Again, this happens when we login from gdm or xdm
2226 * and the password expires, *BUT* cached crendentials
2227 * doesn't exist. winbindd_update_creds_by_name()
2228 * returns NT_STATUS_NO_SUCH_USER.
2229 * This is not a failure.
2232 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2233 result = NT_STATUS_OK;
2236 if (!NT_STATUS_IS_OK(result)) {
2237 DEBUG(10, ("Failed to store creds: %s\n",
2238 nt_errstr(result)));
2239 goto process_result;
2243 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2245 NTSTATUS policy_ret;
2247 policy_ret = fillup_password_policy(
2248 contact_domain, state->response);
2250 /* failure of this is non critical, it will just provide no
2251 * additional information to the client why the change has
2252 * failed - Guenther */
2254 if (!NT_STATUS_IS_OK(policy_ret)) {
2255 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2256 goto process_result;
2262 if (strequal(contact_domain->name, get_global_sam_name())) {
2263 /* FIXME: internal rpc pipe does not cache handles yet */
2265 if (is_valid_policy_hnd(&dom_pol)) {
2267 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2273 set_auth_errors(state->response, result);
2275 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2276 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2279 state->response->data.auth.nt_status_string,
2280 state->response->data.auth.pam_error));
2282 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2285 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2286 struct winbindd_cli_state *state)
2288 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2290 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2291 state->request->data.logoff.user));
2293 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2294 result = NT_STATUS_OK;
2295 goto process_result;
2298 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2299 result = NT_STATUS_OK;
2300 goto process_result;
2305 if (state->request->data.logoff.uid < 0) {
2306 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2307 goto process_result;
2310 /* what we need here is to find the corresponding krb5 ccache name *we*
2311 * created for a given username and destroy it */
2313 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2314 result = NT_STATUS_OK;
2315 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2316 goto process_result;
2319 if (!ccache_entry_identical(state->request->data.logoff.user,
2320 state->request->data.logoff.uid,
2321 state->request->data.logoff.krb5ccname)) {
2322 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2323 goto process_result;
2326 result = remove_ccache(state->request->data.logoff.user);
2327 if (!NT_STATUS_IS_OK(result)) {
2328 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2329 nt_errstr(result)));
2330 goto process_result;
2334 * Remove any mlock'ed memory creds in the child
2335 * we might be using for krb5 ticket renewal.
2338 winbindd_delete_memory_creds(state->request->data.logoff.user);
2341 result = NT_STATUS_NOT_SUPPORTED;
2347 set_auth_errors(state->response, result);
2349 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2352 /* Change user password with auth crap*/
2354 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2357 DATA_BLOB new_nt_password;
2358 DATA_BLOB old_nt_hash_enc;
2359 DATA_BLOB new_lm_password;
2360 DATA_BLOB old_lm_hash_enc;
2361 fstring domain,user;
2362 struct policy_handle dom_pol;
2363 struct winbindd_domain *contact_domain = domainSt;
2364 struct rpc_pipe_client *cli = NULL;
2365 struct dcerpc_binding_handle *b = NULL;
2367 ZERO_STRUCT(dom_pol);
2369 /* Ensure null termination */
2370 state->request->data.chng_pswd_auth_crap.user[
2371 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2372 state->request->data.chng_pswd_auth_crap.domain[
2373 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2377 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2378 (unsigned long)state->pid,
2379 state->request->data.chng_pswd_auth_crap.domain,
2380 state->request->data.chng_pswd_auth_crap.user));
2382 if (lp_winbind_offline_logon()) {
2383 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2384 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2385 result = NT_STATUS_ACCESS_DENIED;
2389 if (*state->request->data.chng_pswd_auth_crap.domain) {
2390 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2392 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2396 DEBUG(3,("no domain specified with username (%s) - "
2398 state->request->data.chng_pswd_auth_crap.user));
2399 result = NT_STATUS_NO_SUCH_USER;
2404 if (!*domain && lp_winbind_use_default_domain()) {
2405 fstrcpy(domain,lp_workgroup());
2409 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2412 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2413 (unsigned long)state->pid, domain, user));
2415 /* Change password */
2416 new_nt_password = data_blob_const(
2417 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2418 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2420 old_nt_hash_enc = data_blob_const(
2421 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2422 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2424 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2425 new_lm_password = data_blob_const(
2426 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2427 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2429 old_lm_hash_enc = data_blob_const(
2430 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2431 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2433 new_lm_password = data_blob_null;
2434 old_lm_hash_enc = data_blob_null;
2437 /* Get sam handle */
2439 result = cm_connect_sam(contact_domain, state->mem_ctx, true, &cli, &dom_pol);
2440 if (!NT_STATUS_IS_OK(result)) {
2441 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2445 b = cli->binding_handle;
2447 result = rpccli_samr_chng_pswd_auth_crap(
2448 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2449 new_lm_password, old_lm_hash_enc);
2453 if (strequal(contact_domain->name, get_global_sam_name())) {
2454 /* FIXME: internal rpc pipe does not cache handles yet */
2456 if (is_valid_policy_hnd(&dom_pol)) {
2458 dcerpc_samr_Close(b, state->mem_ctx, &dom_pol, &_result);
2464 set_auth_errors(state->response, result);
2466 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2467 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2469 state->response->data.auth.nt_status_string,
2470 state->response->data.auth.pam_error));
2472 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2476 static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
2477 struct PAC_LOGON_INFO **logon_info)
2479 krb5_context krbctx = NULL;
2480 krb5_error_code k5ret;
2482 krb5_kt_cursor cursor;
2483 krb5_keytab_entry entry;
2484 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
2487 ZERO_STRUCT(cursor);
2489 k5ret = krb5_init_context(&krbctx);
2491 DEBUG(1, ("Failed to initialize kerberos context: %s\n",
2492 error_message(k5ret)));
2493 status = krb5_to_nt_status(k5ret);
2497 k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
2499 DEBUG(1, ("Failed to get keytab: %s\n",
2500 error_message(k5ret)));
2501 status = krb5_to_nt_status(k5ret);
2505 k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
2507 DEBUG(1, ("Failed to start seq: %s\n",
2508 error_message(k5ret)));
2509 status = krb5_to_nt_status(k5ret);
2513 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2514 while (k5ret == 0) {
2515 status = kerberos_pac_logon_info(mem_ctx, pac_blob,
2517 KRB5_KT_KEY(&entry), NULL, 0,
2519 if (NT_STATUS_IS_OK(status)) {
2522 k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
2523 k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
2526 k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
2528 DEBUG(1, ("Failed to end seq: %s\n",
2529 error_message(k5ret)));
2532 k5ret = krb5_kt_close(krbctx, keytab);
2534 DEBUG(1, ("Failed to close keytab: %s\n",
2535 error_message(k5ret)));
2538 krb5_free_context(krbctx);
2543 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2544 struct netr_SamInfo3 **info3)
2546 struct winbindd_request *req = state->request;
2548 struct PAC_LOGON_INFO *logon_info = NULL;
2549 struct netr_SamInfo3 *info3_copy = NULL;
2552 pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
2553 result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info);
2554 if (!NT_STATUS_IS_OK(result) &&
2555 !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
2556 DEBUG(1, ("Error during PAC signature verification: %s\n",
2557 nt_errstr(result)));
2562 /* Signature verification succeeded, trust the PAC */
2563 result = create_info3_from_pac_logon_info(state->mem_ctx,
2566 if (!NT_STATUS_IS_OK(result)) {
2569 netsamlogon_cache_store(NULL, info3_copy);
2572 /* Try without signature verification */
2573 result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL,
2574 NULL, NULL, NULL, 0,
2576 if (!NT_STATUS_IS_OK(result)) {
2577 DEBUG(10, ("Could not extract PAC: %s\n",
2578 nt_errstr(result)));
2583 * Don't strictly need to copy here,
2584 * but it makes it explicit we're
2585 * returning a copy talloc'ed off
2586 * the state->mem_ctx.
2588 info3_copy = copy_netr_SamInfo3(state->mem_ctx,
2589 &logon_info->info3);
2590 if (info3_copy == NULL) {
2591 return NT_STATUS_NO_MEMORY;
2596 *info3 = info3_copy;
2598 return NT_STATUS_OK;
2600 #else /* HAVE_KRB5 */
2601 NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state,
2602 struct netr_SamInfo3 **info3)
2604 return NT_STATUS_NO_SUCH_USER;
2606 #endif /* HAVE_KRB5 */