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"
29 #define DBGC_CLASS DBGC_WINBIND
31 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
33 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
34 struct winbindd_cli_state *state,
35 struct netr_SamInfo3 *info3)
40 state->response->data.auth.info3.logon_time =
41 nt_time_to_unix(info3->base.last_logon);
42 state->response->data.auth.info3.logoff_time =
43 nt_time_to_unix(info3->base.last_logoff);
44 state->response->data.auth.info3.kickoff_time =
45 nt_time_to_unix(info3->base.acct_expiry);
46 state->response->data.auth.info3.pass_last_set_time =
47 nt_time_to_unix(info3->base.last_password_change);
48 state->response->data.auth.info3.pass_can_change_time =
49 nt_time_to_unix(info3->base.allow_password_change);
50 state->response->data.auth.info3.pass_must_change_time =
51 nt_time_to_unix(info3->base.force_password_change);
53 state->response->data.auth.info3.logon_count = info3->base.logon_count;
54 state->response->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
56 state->response->data.auth.info3.user_rid = info3->base.rid;
57 state->response->data.auth.info3.group_rid = info3->base.primary_gid;
58 sid_to_fstring(state->response->data.auth.info3.dom_sid, info3->base.domain_sid);
60 state->response->data.auth.info3.num_groups = info3->base.groups.count;
61 state->response->data.auth.info3.user_flgs = info3->base.user_flags;
63 state->response->data.auth.info3.acct_flags = info3->base.acct_flags;
64 state->response->data.auth.info3.num_other_sids = info3->sidcount;
66 fstrcpy(state->response->data.auth.info3.user_name,
67 info3->base.account_name.string);
68 fstrcpy(state->response->data.auth.info3.full_name,
69 info3->base.full_name.string);
70 fstrcpy(state->response->data.auth.info3.logon_script,
71 info3->base.logon_script.string);
72 fstrcpy(state->response->data.auth.info3.profile_path,
73 info3->base.profile_path.string);
74 fstrcpy(state->response->data.auth.info3.home_dir,
75 info3->base.home_directory.string);
76 fstrcpy(state->response->data.auth.info3.dir_drive,
77 info3->base.home_drive.string);
79 fstrcpy(state->response->data.auth.info3.logon_srv,
80 info3->base.logon_server.string);
81 fstrcpy(state->response->data.auth.info3.logon_dom,
82 info3->base.domain.string);
84 ex = talloc_strdup(state->mem_ctx, "");
85 NT_STATUS_HAVE_NO_MEMORY(ex);
87 for (i=0; i < info3->base.groups.count; i++) {
88 ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
89 info3->base.groups.rids[i].rid,
90 info3->base.groups.rids[i].attributes);
91 NT_STATUS_HAVE_NO_MEMORY(ex);
94 for (i=0; i < info3->sidcount; i++) {
97 sid = dom_sid_string(mem_ctx, info3->sids[i].sid);
98 NT_STATUS_HAVE_NO_MEMORY(sid);
100 ex = talloc_asprintf_append_buffer(ex, "%s:0x%08X\n",
102 info3->sids[i].attributes);
103 NT_STATUS_HAVE_NO_MEMORY(ex);
108 state->response->extra_data.data = ex;
109 state->response->length += talloc_get_size(ex);
114 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
115 struct winbindd_cli_state *state,
116 struct netr_SamInfo3 *info3)
119 enum ndr_err_code ndr_err;
121 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, info3,
122 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
125 return ndr_map_error2ntstatus(ndr_err);
128 state->response->extra_data.data = blob.data;
129 state->response->length += blob.length;
134 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
135 struct winbindd_cli_state *state,
136 const struct netr_SamInfo3 *info3,
137 const char *name_domain,
138 const char *name_user)
140 /* We've been asked to return the unix username, per
141 'winbind use default domain' settings and the like */
143 const char *nt_username, *nt_domain;
145 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
147 /* If the server didn't give us one, just use the one
149 nt_domain = name_domain;
152 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
154 /* If the server didn't give us one, just use the one
156 nt_username = name_user;
159 fill_domain_username(state->response->data.auth.unix_username,
160 nt_domain, nt_username, true);
162 DEBUG(5,("Setting unix username to [%s]\n",
163 state->response->data.auth.unix_username));
168 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
169 struct winbindd_cli_state *state,
170 const struct netr_SamInfo3 *info3,
171 const char *name_domain,
172 const char *name_user)
174 char *afsname = NULL;
178 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
179 if (afsname == NULL) {
180 return NT_STATUS_NO_MEMORY;
183 afsname = talloc_string_sub(mem_ctx,
184 lp_afs_username_map(),
186 afsname = talloc_string_sub(mem_ctx, afsname,
188 afsname = talloc_string_sub(mem_ctx, afsname,
195 sid_copy(&user_sid, info3->base.domain_sid);
196 sid_append_rid(&user_sid, info3->base.rid);
197 sid_to_fstring(sidstr, &user_sid);
198 afsname = talloc_string_sub(mem_ctx, afsname,
202 if (afsname == NULL) {
203 return NT_STATUS_NO_MEMORY;
208 DEBUG(10, ("Generating token for user %s\n", afsname));
210 cell = strchr(afsname, '@');
213 return NT_STATUS_NO_MEMORY;
219 token = afs_createtoken_str(afsname, cell);
223 state->response->extra_data.data = talloc_strdup(state->mem_ctx,
225 if (state->response->extra_data.data == NULL) {
226 return NT_STATUS_NO_MEMORY;
228 state->response->length +=
229 strlen((const char *)state->response->extra_data.data)+1;
234 NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
235 const char *group_sid)
237 * Check whether a user belongs to a group or list of groups.
239 * @param mem_ctx talloc memory context.
240 * @param info3 user information, including group membership info.
241 * @param group_sid One or more groups , separated by commas.
243 * @return NT_STATUS_OK on success,
244 * NT_STATUS_LOGON_FAILURE if the user does not belong,
245 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
248 DOM_SID *require_membership_of_sid;
249 size_t num_require_membership_of_sid;
254 struct nt_user_token *token;
255 TALLOC_CTX *frame = talloc_stackframe();
258 /* Parse the 'required group' SID */
260 if (!group_sid || !group_sid[0]) {
261 /* NO sid supplied, all users may access */
265 token = talloc_zero(talloc_tos(), struct nt_user_token);
267 DEBUG(0, ("talloc failed\n"));
269 return NT_STATUS_NO_MEMORY;
272 num_require_membership_of_sid = 0;
273 require_membership_of_sid = NULL;
277 while (next_token_talloc(talloc_tos(), &p, &req_sid, ",")) {
278 if (!string_to_sid(&sid, req_sid)) {
279 DEBUG(0, ("check_info3_in_group: could not parse %s "
280 "as a SID!", req_sid));
282 return NT_STATUS_INVALID_PARAMETER;
285 status = add_sid_to_array(talloc_tos(), &sid,
286 &require_membership_of_sid,
287 &num_require_membership_of_sid);
288 if (!NT_STATUS_IS_OK(status)) {
289 DEBUG(0, ("add_sid_to_array failed\n"));
295 status = sid_array_from_info3(talloc_tos(), info3,
299 if (!NT_STATUS_IS_OK(status)) {
304 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
306 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
308 DEBUG(3, ("could not add aliases: %s\n",
314 debug_nt_user_token(DBGC_CLASS, 10, token);
316 for (i=0; i<num_require_membership_of_sid; i++) {
317 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
318 &require_membership_of_sid[i])));
319 if (nt_token_check_sid(&require_membership_of_sid[i],
321 DEBUG(10, ("Access ok\n"));
327 /* Do not distinguish this error from a wrong username/pw */
330 return NT_STATUS_LOGON_FAILURE;
333 struct winbindd_domain *find_auth_domain(uint8_t flags,
334 const char *domain_name)
336 struct winbindd_domain *domain;
339 domain = find_domain_from_name_noinit(domain_name);
340 if (domain == NULL) {
341 DEBUG(3, ("Authentication for domain [%s] refused "
342 "as it is not a trusted domain\n",
348 if (is_myname(domain_name)) {
349 DEBUG(3, ("Authentication for domain %s (local domain "
350 "to this server) not supported at this "
351 "stage\n", domain_name));
355 /* we can auth against trusted domains */
356 if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
357 domain = find_domain_from_name_noinit(domain_name);
358 if (domain == NULL) {
359 DEBUG(3, ("Authentication for domain [%s] skipped "
360 "as it is not a trusted domain\n",
367 return find_our_domain();
370 static void fill_in_password_policy(struct winbindd_response *r,
371 const struct samr_DomInfo1 *p)
373 r->data.auth.policy.min_length_password =
374 p->min_password_length;
375 r->data.auth.policy.password_history =
376 p->password_history_length;
377 r->data.auth.policy.password_properties =
378 p->password_properties;
379 r->data.auth.policy.expire =
380 nt_time_to_unix_abs((NTTIME *)&(p->max_password_age));
381 r->data.auth.policy.min_passwordage =
382 nt_time_to_unix_abs((NTTIME *)&(p->min_password_age));
385 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
386 struct winbindd_cli_state *state)
388 struct winbindd_methods *methods;
389 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
390 struct samr_DomInfo1 password_policy;
392 if ( !winbindd_can_contact_domain( domain ) ) {
393 DEBUG(5,("fillup_password_policy: No inbound trust to "
394 "contact domain %s\n", domain->name));
395 return NT_STATUS_NOT_SUPPORTED;
398 methods = domain->methods;
400 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
401 if (NT_STATUS_IS_ERR(status)) {
405 fill_in_password_policy(state->response, &password_policy);
410 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
412 uint16 *lockout_threshold)
414 struct winbindd_methods *methods;
415 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
416 struct samr_DomInfo12 lockout_policy;
418 *lockout_threshold = 0;
420 methods = domain->methods;
422 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
423 if (NT_STATUS_IS_ERR(status)) {
427 *lockout_threshold = lockout_policy.lockout_threshold;
432 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
434 uint32 *password_properties)
436 struct winbindd_methods *methods;
437 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
438 struct samr_DomInfo1 password_policy;
440 *password_properties = 0;
442 methods = domain->methods;
444 status = methods->password_policy(domain, mem_ctx, &password_policy);
445 if (NT_STATUS_IS_ERR(status)) {
449 *password_properties = password_policy.password_properties;
456 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
459 bool *internal_ccache)
461 /* accept FILE and WRFILE as krb5_cc_type from the client and then
462 * build the full ccname string based on the user's uid here -
465 const char *gen_cc = NULL;
467 *internal_ccache = true;
473 if (!type || type[0] == '\0') {
477 if (strequal(type, "FILE")) {
478 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
479 } else if (strequal(type, "WRFILE")) {
480 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
482 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
486 *internal_ccache = false;
490 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
493 if (gen_cc == NULL) {
494 DEBUG(0,("out of memory\n"));
498 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
503 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
505 const char *type = state->request->data.auth.krb5_cc_type;
507 state->response->data.auth.krb5ccname[0] = '\0';
509 if (type[0] == '\0') {
513 if (!strequal(type, "FILE") &&
514 !strequal(type, "WRFILE")) {
515 DEBUG(10,("won't return krbccname for a %s type ccache\n",
520 fstrcpy(state->response->data.auth.krb5ccname, cc);
525 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
529 uid = state->request->data.auth.uid;
532 DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
538 /**********************************************************************
539 Authenticate a user with a clear text password using Kerberos and fill up
541 **********************************************************************/
543 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
544 struct winbindd_cli_state *state,
545 struct netr_SamInfo3 **info3)
548 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
549 krb5_error_code krb5_ret;
550 const char *cc = NULL;
551 const char *principal_s = NULL;
552 const char *service = NULL;
554 fstring name_domain, name_user;
555 time_t ticket_lifetime = 0;
556 time_t renewal_until = 0;
559 time_t time_offset = 0;
560 bool internal_ccache = true;
567 * prepare a krb5_cc_cache string for the user */
569 uid = get_uid_from_state(state);
571 DEBUG(0,("no valid uid\n"));
574 cc = generate_krb5_ccache(state->mem_ctx,
575 state->request->data.auth.krb5_cc_type,
576 state->request->data.auth.uid,
579 return NT_STATUS_NO_MEMORY;
584 * get kerberos properties */
586 if (domain->private_data) {
587 ads = (ADS_STRUCT *)domain->private_data;
588 time_offset = ads->auth.time_offset;
593 * do kerberos auth and setup ccache as the user */
595 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
597 realm = domain->alt_name;
600 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
601 if (principal_s == NULL) {
602 return NT_STATUS_NO_MEMORY;
605 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
606 if (service == NULL) {
607 return NT_STATUS_NO_MEMORY;
610 /* if this is a user ccache, we need to act as the user to let the krb5
611 * library handle the chown, etc. */
613 /************************ ENTERING NON-ROOT **********************/
615 if (!internal_ccache) {
616 set_effective_uid(uid);
617 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
620 result = kerberos_return_info3_from_pac(state->mem_ctx,
622 state->request->data.auth.pass,
629 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
631 if (!internal_ccache) {
632 gain_root_privilege();
635 /************************ RETURNED TO ROOT **********************/
637 if (!NT_STATUS_IS_OK(result)) {
641 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
644 /* if we had a user's ccache then return that string for the pam
647 if (!internal_ccache) {
649 setup_return_cc_name(state, cc);
651 result = add_ccache_to_list(principal_s,
654 state->request->data.auth.user,
662 if (!NT_STATUS_IS_OK(result)) {
663 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
668 /* need to delete the memory cred cache, it is not used anymore */
670 krb5_ret = ads_kdestroy(cc);
672 DEBUG(3,("winbindd_raw_kerberos_login: "
673 "could not destroy krb5 credential cache: "
674 "%s\n", error_message(krb5_ret)));
683 /* we could have created a new credential cache with a valid tgt in it
684 * but we werent able to get or verify the service ticket for this
685 * local host and therefor didn't get the PAC, we need to remove that
686 * cache entirely now */
688 krb5_ret = ads_kdestroy(cc);
690 DEBUG(3,("winbindd_raw_kerberos_login: "
691 "could not destroy krb5 credential cache: "
692 "%s\n", error_message(krb5_ret)));
695 if (!NT_STATUS_IS_OK(remove_ccache(state->request->data.auth.user))) {
696 DEBUG(3,("winbindd_raw_kerberos_login: "
697 "could not remove ccache for user %s\n",
698 state->request->data.auth.user));
703 return NT_STATUS_NOT_SUPPORTED;
704 #endif /* HAVE_KRB5 */
707 /****************************************************************
708 ****************************************************************/
710 bool check_request_flags(uint32_t flags)
712 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
713 WBFLAG_PAM_INFO3_TEXT |
714 WBFLAG_PAM_INFO3_NDR;
716 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
717 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
718 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
719 !(flags & flags_edata) ) {
723 DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
729 /****************************************************************
730 ****************************************************************/
732 NTSTATUS append_auth_data(struct winbindd_cli_state *state,
733 struct netr_SamInfo3 *info3,
734 const char *name_domain,
735 const char *name_user)
738 uint32_t flags = state->request->flags;
740 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
741 memcpy(state->response->data.auth.user_session_key,
743 sizeof(state->response->data.auth.user_session_key)
747 if (flags & WBFLAG_PAM_LMKEY) {
748 memcpy(state->response->data.auth.first_8_lm_hash,
749 info3->base.LMSessKey.key,
750 sizeof(state->response->data.auth.first_8_lm_hash)
754 if (flags & WBFLAG_PAM_INFO3_TEXT) {
755 result = append_info3_as_txt(state->mem_ctx, state, info3);
756 if (!NT_STATUS_IS_OK(result)) {
757 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
763 /* currently, anything from here on potentially overwrites extra_data. */
765 if (flags & WBFLAG_PAM_INFO3_NDR) {
766 result = append_info3_as_ndr(state->mem_ctx, state, info3);
767 if (!NT_STATUS_IS_OK(result)) {
768 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
774 if (flags & WBFLAG_PAM_UNIX_NAME) {
775 result = append_unix_username(state->mem_ctx, state, info3,
776 name_domain, name_user);
777 if (!NT_STATUS_IS_OK(result)) {
778 DEBUG(10,("Failed to append Unix Username: %s\n",
784 if (flags & WBFLAG_PAM_AFS_TOKEN) {
785 result = append_afs_token(state->mem_ctx, state, info3,
786 name_domain, name_user);
787 if (!NT_STATUS_IS_OK(result)) {
788 DEBUG(10,("Failed to append AFS token: %s\n",
797 void winbindd_pam_auth(struct winbindd_cli_state *state)
799 struct winbindd_domain *domain;
800 fstring name_domain, name_user, mapped_user;
803 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
805 /* Ensure null termination */
806 state->request->data.auth.user
807 [sizeof(state->request->data.auth.user)-1]='\0';
809 /* Ensure null termination */
810 state->request->data.auth.pass
811 [sizeof(state->request->data.auth.pass)-1]='\0';
813 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
814 state->request->data.auth.user));
816 if (!check_request_flags(state->request->flags)) {
817 result = NT_STATUS_INVALID_PARAMETER_MIX;
821 /* Parse domain and username */
823 name_map_status = normalize_name_unmap(state->mem_ctx,
824 state->request->data.auth.user,
827 /* If the name normalization didnt' actually do anything,
828 just use the original name */
830 if (NT_STATUS_IS_OK(name_map_status)
831 ||NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
832 fstrcpy(mapped_user, mapped);
835 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
836 result = NT_STATUS_NO_SUCH_USER;
840 domain = find_auth_domain(state->request->flags, name_domain);
842 if (domain == NULL) {
843 result = NT_STATUS_NO_SUCH_USER;
847 sendto_domain(state, domain);
850 set_auth_errors(state->response, result);
851 DEBUG(5, ("Plain text authentication for %s returned %s "
853 state->request->data.auth.user,
854 state->response->data.auth.nt_status_string,
855 state->response->data.auth.pam_error));
856 request_error(state);
859 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
860 struct winbindd_cli_state *state,
861 struct netr_SamInfo3 **info3)
863 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
864 uint16 max_allowed_bad_attempts;
865 fstring name_domain, name_user;
867 enum lsa_SidType type;
868 uchar new_nt_pass[NT_HASH_LEN];
869 const uint8 *cached_nt_pass;
870 const uint8 *cached_salt;
871 struct netr_SamInfo3 *my_info3;
872 time_t kickoff_time, must_change_time;
873 bool password_good = false;
875 struct winbindd_tdc_domain *tdc_domain = NULL;
882 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
884 /* Parse domain and username */
886 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
889 if (!lookup_cached_name(state->mem_ctx,
894 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
895 return NT_STATUS_NO_SUCH_USER;
898 if (type != SID_NAME_USER) {
899 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
900 return NT_STATUS_LOGON_FAILURE;
903 result = winbindd_get_creds(domain,
909 if (!NT_STATUS_IS_OK(result)) {
910 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
916 E_md4hash(state->request->data.auth.pass, new_nt_pass);
918 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
919 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
921 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
925 /* In this case we didn't store the nt_hash itself,
926 but the MD5 combination of salt + nt_hash. */
927 uchar salted_hash[NT_HASH_LEN];
928 E_md5hash(cached_salt, new_nt_pass, salted_hash);
930 password_good = (memcmp(cached_nt_pass, salted_hash,
933 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
934 password_good = (memcmp(cached_nt_pass, new_nt_pass,
940 /* User *DOES* know the password, update logon_time and reset
943 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
945 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
946 return NT_STATUS_ACCOUNT_LOCKED_OUT;
949 if (my_info3->base.acct_flags & ACB_DISABLED) {
950 return NT_STATUS_ACCOUNT_DISABLED;
953 if (my_info3->base.acct_flags & ACB_WSTRUST) {
954 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
957 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
958 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
961 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
962 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
965 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
966 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
967 my_info3->base.acct_flags));
968 return NT_STATUS_LOGON_FAILURE;
971 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
972 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
973 return NT_STATUS_ACCOUNT_EXPIRED;
976 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
977 if (must_change_time != 0 && must_change_time < time(NULL)) {
978 /* we allow grace logons when the password has expired */
979 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
980 /* return NT_STATUS_PASSWORD_EXPIRED; */
985 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
986 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
987 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
990 const char *cc = NULL;
992 const char *principal_s = NULL;
993 const char *service = NULL;
994 bool internal_ccache = false;
996 uid = get_uid_from_state(state);
998 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
999 return NT_STATUS_INVALID_PARAMETER;
1002 cc = generate_krb5_ccache(state->mem_ctx,
1003 state->request->data.auth.krb5_cc_type,
1004 state->request->data.auth.uid,
1007 return NT_STATUS_NO_MEMORY;
1010 realm = domain->alt_name;
1013 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1014 if (principal_s == NULL) {
1015 return NT_STATUS_NO_MEMORY;
1018 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1019 if (service == NULL) {
1020 return NT_STATUS_NO_MEMORY;
1023 if (!internal_ccache) {
1025 setup_return_cc_name(state, cc);
1027 result = add_ccache_to_list(principal_s,
1030 state->request->data.auth.user,
1034 time(NULL) + lp_winbind_cache_time(),
1035 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1038 if (!NT_STATUS_IS_OK(result)) {
1039 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1040 "to add ccache to list: %s\n",
1041 nt_errstr(result)));
1045 #endif /* HAVE_KRB5 */
1047 /* FIXME: we possibly should handle logon hours as well (does xp when
1048 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1050 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1051 my_info3->base.bad_password_count = 0;
1053 result = winbindd_update_creds_by_info3(domain,
1055 state->request->data.auth.user,
1056 state->request->data.auth.pass,
1058 if (!NT_STATUS_IS_OK(result)) {
1059 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1060 nt_errstr(result)));
1064 return NT_STATUS_OK;
1068 /* User does *NOT* know the correct password, modify info3 accordingly */
1070 /* failure of this is not critical */
1071 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1072 if (!NT_STATUS_IS_OK(result)) {
1073 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1074 "Won't be able to honour account lockout policies\n"));
1077 /* increase counter */
1078 my_info3->base.bad_password_count++;
1080 if (max_allowed_bad_attempts == 0) {
1085 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1087 uint32 password_properties;
1089 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1090 if (!NT_STATUS_IS_OK(result)) {
1091 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1094 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1095 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1096 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1101 result = winbindd_update_creds_by_info3(domain,
1103 state->request->data.auth.user,
1107 if (!NT_STATUS_IS_OK(result)) {
1108 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1109 nt_errstr(result)));
1112 return NT_STATUS_LOGON_FAILURE;
1115 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1116 struct winbindd_cli_state *state,
1117 struct netr_SamInfo3 **info3)
1119 struct winbindd_domain *contact_domain;
1120 fstring name_domain, name_user;
1123 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1125 /* Parse domain and username */
1127 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1129 /* what domain should we contact? */
1132 if (!(contact_domain = find_domain_from_name(name_domain))) {
1133 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1134 state->request->data.auth.user, name_domain, name_user, name_domain));
1135 result = NT_STATUS_NO_SUCH_USER;
1140 if (is_myname(name_domain)) {
1141 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1142 result = NT_STATUS_NO_SUCH_USER;
1146 contact_domain = find_domain_from_name(name_domain);
1147 if (contact_domain == NULL) {
1148 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1149 state->request->data.auth.user, name_domain, name_user, name_domain));
1151 contact_domain = find_our_domain();
1155 if (contact_domain->initialized &&
1156 contact_domain->active_directory) {
1160 if (!contact_domain->initialized) {
1161 init_dc_connection(contact_domain);
1164 if (!contact_domain->active_directory) {
1165 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1166 return NT_STATUS_INVALID_LOGON_TYPE;
1169 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1174 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1175 TALLOC_CTX *mem_ctx,
1176 uint32 logon_parameters,
1178 const char *username,
1180 const char *workstation,
1181 const uint8 chal[8],
1182 DATA_BLOB lm_response,
1183 DATA_BLOB nt_response,
1184 struct netr_SamInfo3 **info3);
1186 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1187 struct winbindd_cli_state *state,
1188 struct netr_SamInfo3 **info3)
1191 struct rpc_pipe_client *netlogon_pipe;
1196 unsigned char local_lm_response[24];
1197 unsigned char local_nt_response[24];
1198 struct winbindd_domain *contact_domain;
1199 fstring name_domain, name_user;
1202 struct netr_SamInfo3 *my_info3 = NULL;
1206 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1208 /* Parse domain and username */
1210 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1212 /* do password magic */
1215 generate_random_buffer(chal, 8);
1216 if (lp_client_ntlmv2_auth()) {
1217 DATA_BLOB server_chal;
1218 DATA_BLOB names_blob;
1219 DATA_BLOB nt_response;
1220 DATA_BLOB lm_response;
1221 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1223 /* note that the 'workgroup' here is a best guess - we don't know
1224 the server's domain at this point. The 'server name' is also
1227 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1229 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1230 state->request->data.auth.pass,
1233 &lm_response, &nt_response, NULL, NULL)) {
1234 data_blob_free(&names_blob);
1235 data_blob_free(&server_chal);
1236 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1237 result = NT_STATUS_NO_MEMORY;
1240 data_blob_free(&names_blob);
1241 data_blob_free(&server_chal);
1242 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1243 lm_response.length);
1244 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1245 nt_response.length);
1246 data_blob_free(&lm_response);
1247 data_blob_free(&nt_response);
1250 if (lp_client_lanman_auth()
1251 && SMBencrypt(state->request->data.auth.pass,
1253 local_lm_response)) {
1254 lm_resp = data_blob_talloc(state->mem_ctx,
1256 sizeof(local_lm_response));
1258 lm_resp = data_blob_null;
1260 SMBNTencrypt(state->request->data.auth.pass,
1264 nt_resp = data_blob_talloc(state->mem_ctx,
1266 sizeof(local_nt_response));
1269 /* what domain should we contact? */
1272 if (!(contact_domain = find_domain_from_name(name_domain))) {
1273 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1274 state->request->data.auth.user, name_domain, name_user, name_domain));
1275 result = NT_STATUS_NO_SUCH_USER;
1280 if (is_myname(name_domain)) {
1281 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1282 result = NT_STATUS_NO_SUCH_USER;
1286 contact_domain = find_our_domain();
1289 /* check authentication loop */
1292 netlogon_fn_t logon_fn;
1294 ZERO_STRUCTP(my_info3);
1297 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1299 if (!NT_STATUS_IS_OK(result)) {
1300 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1304 /* It is really important to try SamLogonEx here,
1305 * because in a clustered environment, we want to use
1306 * one machine account from multiple physical
1309 * With a normal SamLogon call, we must keep the
1310 * credentials chain updated and intact between all
1311 * users of the machine account (which would imply
1312 * cross-node communication for every NTLM logon).
1314 * (The credentials chain is not per NETLOGON pipe
1315 * connection, but globally on the server/client pair
1318 * When using SamLogonEx, the credentials are not
1319 * supplied, but the session key is implied by the
1320 * wrapping SamLogon context.
1322 * -- abartlet 21 April 2008
1325 logon_fn = contact_domain->can_do_samlogon_ex
1326 ? rpccli_netlogon_sam_network_logon_ex
1327 : rpccli_netlogon_sam_network_logon;
1329 result = logon_fn(netlogon_pipe,
1332 contact_domain->dcname, /* server name */
1333 name_user, /* user name */
1334 name_domain, /* target domain */
1335 global_myname(), /* workstation */
1342 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1343 && contact_domain->can_do_samlogon_ex) {
1344 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1345 "retrying with NetSamLogon\n"));
1346 contact_domain->can_do_samlogon_ex = false;
1351 /* We have to try a second time as cm_connect_netlogon
1352 might not yet have noticed that the DC has killed
1355 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1360 /* if we get access denied, a possible cause was that we had
1361 and open connection to the DC, but someone changed our
1362 machine account password out from underneath us using 'net
1363 rpc changetrustpw' */
1365 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1366 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1367 "ACCESS_DENIED. Maybe the trust account "
1368 "password was changed and we didn't know it. "
1369 "Killing connections to domain %s\n",
1371 invalidate_cm_connection(&contact_domain->conn);
1375 } while ( (attempts < 2) && retry );
1377 /* handle the case where a NT4 DC does not fill in the acct_flags in
1378 * the samlogon reply info3. When accurate info3 is required by the
1379 * caller, we look up the account flags ourselve - gd */
1381 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1382 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1384 struct rpc_pipe_client *samr_pipe;
1385 struct policy_handle samr_domain_handle, user_pol;
1386 union samr_UserInfo *info = NULL;
1387 NTSTATUS status_tmp;
1390 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1391 &samr_pipe, &samr_domain_handle);
1393 if (!NT_STATUS_IS_OK(status_tmp)) {
1394 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1395 nt_errstr(status_tmp)));
1399 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1400 &samr_domain_handle,
1401 MAXIMUM_ALLOWED_ACCESS,
1405 if (!NT_STATUS_IS_OK(status_tmp)) {
1406 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1407 nt_errstr(status_tmp)));
1411 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1416 if (!NT_STATUS_IS_OK(status_tmp)) {
1417 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1418 nt_errstr(status_tmp)));
1419 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1423 acct_flags = info->info16.acct_flags;
1425 if (acct_flags == 0) {
1426 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1430 my_info3->base.acct_flags = acct_flags;
1432 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1434 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1442 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1443 struct winbindd_cli_state *state)
1445 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1446 NTSTATUS krb5_result = NT_STATUS_OK;
1447 fstring name_domain, name_user;
1449 fstring domain_user;
1450 struct netr_SamInfo3 *info3 = NULL;
1451 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1453 /* Ensure null termination */
1454 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1456 /* Ensure null termination */
1457 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1459 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1460 state->request->data.auth.user));
1462 if (!check_request_flags(state->request->flags)) {
1463 result = NT_STATUS_INVALID_PARAMETER_MIX;
1467 /* Parse domain and username */
1469 name_map_status = normalize_name_unmap(state->mem_ctx,
1470 state->request->data.auth.user,
1473 /* If the name normalization didnt' actually do anything,
1474 just use the original name */
1476 if (!NT_STATUS_IS_OK(name_map_status) &&
1477 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1479 mapped_user = state->request->data.auth.user;
1482 parse_domain_user(mapped_user, name_domain, name_user);
1484 if ( mapped_user != state->request->data.auth.user ) {
1485 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1486 safe_strcpy( state->request->data.auth.user, domain_user,
1487 sizeof(state->request->data.auth.user)-1 );
1490 if (domain->online == false) {
1491 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1492 if (domain->startup) {
1493 /* Logons are very important to users. If we're offline and
1494 we get a request within the first 30 seconds of startup,
1495 try very hard to find a DC and go online. */
1497 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1498 "request in startup mode.\n", domain->name ));
1500 winbindd_flush_negative_conn_cache(domain);
1501 result = init_dc_connection(domain);
1505 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1507 /* Check for Kerberos authentication */
1508 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1510 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1511 /* save for later */
1512 krb5_result = result;
1515 if (NT_STATUS_IS_OK(result)) {
1516 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1517 goto process_result;
1519 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1522 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1523 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1524 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1525 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1526 set_domain_offline( domain );
1530 /* there are quite some NT_STATUS errors where there is no
1531 * point in retrying with a samlogon, we explictly have to take
1532 * care not to increase the bad logon counter on the DC */
1534 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1535 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1536 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1537 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1542 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1543 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1544 goto process_result;
1547 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1548 DEBUG(3,("falling back to samlogon\n"));
1556 /* Check for Samlogon authentication */
1557 if (domain->online) {
1558 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1560 if (NT_STATUS_IS_OK(result)) {
1561 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1562 /* add the Krb5 err if we have one */
1563 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1564 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1566 goto process_result;
1569 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1570 nt_errstr(result)));
1572 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1573 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1574 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1576 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1577 set_domain_offline( domain );
1581 if (domain->online) {
1582 /* We're still online - fail. */
1588 /* Check for Cached logons */
1589 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1590 lp_winbind_offline_logon()) {
1592 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1594 if (NT_STATUS_IS_OK(result)) {
1595 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1596 goto process_result;
1598 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1605 if (NT_STATUS_IS_OK(result)) {
1609 /* In all codepaths where result == NT_STATUS_OK info3 must have
1610 been initialized. */
1612 result = NT_STATUS_INTERNAL_ERROR;
1616 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1617 netsamlogon_cache_store(name_user, info3);
1619 /* save name_to_sid info as early as possible (only if
1620 this is our primary domain so we don't invalidate
1621 the cache entry by storing the seq_num for the wrong
1623 if ( domain->primary ) {
1624 sid_compose(&user_sid, info3->base.domain_sid,
1626 cache_name2sid(domain, name_domain, name_user,
1627 SID_NAME_USER, &user_sid);
1630 /* Check if the user is in the right group */
1632 result = check_info3_in_group(
1634 state->request->data.auth.require_membership_of_sid);
1635 if (!NT_STATUS_IS_OK(result)) {
1636 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1637 state->request->data.auth.user,
1638 state->request->data.auth.require_membership_of_sid));
1642 result = append_auth_data(state, info3, name_domain,
1644 if (!NT_STATUS_IS_OK(result)) {
1648 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1650 /* Store in-memory creds for single-signon using ntlm_auth. */
1651 result = winbindd_add_memory_creds(state->request->data.auth.user,
1652 get_uid_from_state(state),
1653 state->request->data.auth.pass);
1655 if (!NT_STATUS_IS_OK(result)) {
1656 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1660 if (lp_winbind_offline_logon()) {
1661 result = winbindd_store_creds(domain,
1663 state->request->data.auth.user,
1664 state->request->data.auth.pass,
1666 if (!NT_STATUS_IS_OK(result)) {
1668 /* Release refcount. */
1669 winbindd_delete_memory_creds(state->request->data.auth.user);
1671 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1678 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1679 struct winbindd_domain *our_domain = find_our_domain();
1681 /* This is not entirely correct I believe, but it is
1682 consistent. Only apply the password policy settings
1683 too warn users for our own domain. Cannot obtain these
1684 from trusted DCs all the time so don't do it at all.
1687 result = NT_STATUS_NOT_SUPPORTED;
1688 if (our_domain == domain ) {
1689 result = fillup_password_policy(our_domain, state);
1692 if (!NT_STATUS_IS_OK(result)
1693 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1695 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1696 domain->name, nt_errstr(result)));
1701 result = NT_STATUS_OK;
1705 /* give us a more useful (more correct?) error code */
1706 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1707 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1708 result = NT_STATUS_NO_LOGON_SERVERS;
1711 set_auth_errors(state->response, result);
1713 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1714 state->request->data.auth.user,
1715 state->response->data.auth.nt_status_string,
1716 state->response->data.auth.pam_error));
1718 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1722 /**********************************************************************
1723 Challenge Response Authentication Protocol
1724 **********************************************************************/
1726 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1728 struct winbindd_domain *domain = NULL;
1729 const char *domain_name = NULL;
1732 if (!check_request_flags(state->request->flags)) {
1733 result = NT_STATUS_INVALID_PARAMETER_MIX;
1737 if (!state->privileged) {
1738 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1740 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1741 "on %s are set correctly.\n",
1742 get_winbind_priv_pipe_dir()));
1743 /* send a better message than ACCESS_DENIED */
1744 fstr_sprintf(state->response->data.auth.error_string,
1745 "winbind client not authorized to use "
1746 "winbindd_pam_auth_crap. Ensure permissions on "
1747 "%s are set correctly.",
1748 get_winbind_priv_pipe_dir());
1749 result = NT_STATUS_ACCESS_DENIED;
1753 /* Ensure null termination */
1754 state->request->data.auth_crap.user
1755 [sizeof(state->request->data.auth_crap.user)-1]=0;
1756 state->request->data.auth_crap.domain
1757 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1759 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1760 (unsigned long)state->pid,
1761 state->request->data.auth_crap.domain,
1762 state->request->data.auth_crap.user));
1764 if (*state->request->data.auth_crap.domain != '\0') {
1765 domain_name = state->request->data.auth_crap.domain;
1766 } else if (lp_winbind_use_default_domain()) {
1767 domain_name = lp_workgroup();
1770 if (domain_name != NULL)
1771 domain = find_auth_domain(state->request->flags, domain_name);
1773 if (domain != NULL) {
1774 sendto_domain(state, domain);
1778 result = NT_STATUS_NO_SUCH_USER;
1781 set_auth_errors(state->response, result);
1782 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1783 state->request->data.auth_crap.domain,
1784 state->request->data.auth_crap.user,
1785 state->response->data.auth.nt_status_string,
1786 state->response->data.auth.pam_error));
1787 request_error(state);
1792 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1793 struct winbindd_cli_state *state)
1796 struct netr_SamInfo3 *info3 = NULL;
1797 struct rpc_pipe_client *netlogon_pipe;
1798 const char *name_user = NULL;
1799 const char *name_domain = NULL;
1800 const char *workstation;
1801 struct winbindd_domain *contact_domain;
1805 DATA_BLOB lm_resp, nt_resp;
1807 /* This is child-only, so no check for privileged access is needed
1810 /* Ensure null termination */
1811 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1812 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1814 if (!check_request_flags(state->request->flags)) {
1815 result = NT_STATUS_INVALID_PARAMETER_MIX;
1819 name_user = state->request->data.auth_crap.user;
1821 if (*state->request->data.auth_crap.domain) {
1822 name_domain = state->request->data.auth_crap.domain;
1823 } else if (lp_winbind_use_default_domain()) {
1824 name_domain = lp_workgroup();
1826 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1828 result = NT_STATUS_NO_SUCH_USER;
1832 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1833 name_domain, name_user));
1835 if (*state->request->data.auth_crap.workstation) {
1836 workstation = state->request->data.auth_crap.workstation;
1838 workstation = global_myname();
1841 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1842 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1843 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1844 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1845 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1846 state->request->data.auth_crap.lm_resp_len,
1847 state->request->data.auth_crap.nt_resp_len));
1848 result = NT_STATUS_INVALID_PARAMETER;
1853 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1854 state->request->data.auth_crap.lm_resp_len);
1856 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1857 nt_resp = data_blob_talloc(state->mem_ctx,
1858 state->request->extra_data.data,
1859 state->request->data.auth_crap.nt_resp_len);
1861 nt_resp = data_blob_talloc(state->mem_ctx,
1862 state->request->data.auth_crap.nt_resp,
1863 state->request->data.auth_crap.nt_resp_len);
1866 /* what domain should we contact? */
1869 if (!(contact_domain = find_domain_from_name(name_domain))) {
1870 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1871 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1872 result = NT_STATUS_NO_SUCH_USER;
1876 if (is_myname(name_domain)) {
1877 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1878 result = NT_STATUS_NO_SUCH_USER;
1881 contact_domain = find_our_domain();
1885 netlogon_fn_t logon_fn;
1889 netlogon_pipe = NULL;
1890 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1892 if (!NT_STATUS_IS_OK(result)) {
1893 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1894 nt_errstr(result)));
1898 logon_fn = contact_domain->can_do_samlogon_ex
1899 ? rpccli_netlogon_sam_network_logon_ex
1900 : rpccli_netlogon_sam_network_logon;
1902 result = logon_fn(netlogon_pipe,
1904 state->request->data.auth_crap.logon_parameters,
1905 contact_domain->dcname,
1908 /* Bug #3248 - found by Stefan Burkei. */
1909 workstation, /* We carefully set this above so use it... */
1910 state->request->data.auth_crap.chal,
1915 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1916 && contact_domain->can_do_samlogon_ex) {
1917 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1918 "retrying with NetSamLogon\n"));
1919 contact_domain->can_do_samlogon_ex = false;
1926 /* We have to try a second time as cm_connect_netlogon
1927 might not yet have noticed that the DC has killed
1930 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1935 /* if we get access denied, a possible cause was that we had and open
1936 connection to the DC, but someone changed our machine account password
1937 out from underneath us using 'net rpc changetrustpw' */
1939 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1940 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1941 "ACCESS_DENIED. Maybe the trust account "
1942 "password was changed and we didn't know it. "
1943 "Killing connections to domain %s\n",
1945 invalidate_cm_connection(&contact_domain->conn);
1949 } while ( (attempts < 2) && retry );
1951 if (NT_STATUS_IS_OK(result)) {
1953 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1954 netsamlogon_cache_store(name_user, info3);
1956 /* Check if the user is in the right group */
1958 result = check_info3_in_group(
1960 state->request->data.auth_crap.require_membership_of_sid);
1961 if (!NT_STATUS_IS_OK(result)) {
1962 DEBUG(3, ("User %s is not in the required group (%s), so "
1963 "crap authentication is rejected\n",
1964 state->request->data.auth_crap.user,
1965 state->request->data.auth_crap.require_membership_of_sid));
1969 result = append_auth_data(state, info3, name_domain,
1971 if (!NT_STATUS_IS_OK(result)) {
1978 /* give us a more useful (more correct?) error code */
1979 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1980 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1981 result = NT_STATUS_NO_LOGON_SERVERS;
1984 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1985 result = nt_status_squash(result);
1988 set_auth_errors(state->response, result);
1990 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1991 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1994 state->response->data.auth.nt_status_string,
1995 state->response->data.auth.pam_error));
1997 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2000 /* Change a user password */
2002 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2004 fstring domain, user;
2006 struct winbindd_domain *contact_domain;
2007 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2009 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2010 state->request->data.chauthtok.user));
2014 nt_status = normalize_name_unmap(state->mem_ctx,
2015 state->request->data.chauthtok.user,
2018 /* Update the chauthtok name if we did any mapping */
2020 if (NT_STATUS_IS_OK(nt_status) ||
2021 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2023 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2026 /* Must pass in state->...chauthtok.user because
2027 canonicalize_username() assumes an fstring(). Since
2028 we have already copied it (if necessary), this is ok. */
2030 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2031 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2032 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2034 state->request->data.auth.user,
2035 state->response->data.auth.nt_status_string,
2036 state->response->data.auth.pam_error));
2037 request_error(state);
2041 contact_domain = find_domain_from_name(domain);
2042 if (!contact_domain) {
2043 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2044 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2045 state->request->data.chauthtok.user, domain, user, domain));
2046 request_error(state);
2050 sendto_domain(state, contact_domain);
2053 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2054 struct winbindd_cli_state *state)
2057 char *newpass = NULL;
2058 struct policy_handle dom_pol;
2059 struct rpc_pipe_client *cli;
2060 bool got_info = false;
2061 struct samr_DomInfo1 *info = NULL;
2062 struct userPwdChangeFailureInformation *reject = NULL;
2063 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2064 fstring domain, user;
2066 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2067 state->request->data.auth.user));
2069 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2073 /* Change password */
2075 oldpass = state->request->data.chauthtok.oldpass;
2076 newpass = state->request->data.chauthtok.newpass;
2078 /* Initialize reject reason */
2079 state->response->data.auth.reject_reason = Undefined;
2081 /* Get sam handle */
2083 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2085 if (!NT_STATUS_IS_OK(result)) {
2086 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2090 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2097 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2099 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2101 fill_in_password_policy(state->response, info);
2103 state->response->data.auth.reject_reason =
2104 reject->extendedFailureReason;
2109 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2110 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2111 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2112 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2114 /* only fallback when the chgpasswd_user3 call is not supported */
2115 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2116 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2117 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2118 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2120 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2121 nt_errstr(result)));
2123 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2125 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2126 Map to the same status code as Windows 2003. */
2128 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2129 result = NT_STATUS_PASSWORD_RESTRICTION;
2135 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2137 /* Update the single sign-on memory creds. */
2138 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2141 /* When we login from gdm or xdm and password expires,
2142 * we change password, but there are no memory crendentials
2143 * So, winbindd_replace_memory_creds() returns
2144 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2147 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2148 result = NT_STATUS_OK;
2151 if (!NT_STATUS_IS_OK(result)) {
2152 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2153 goto process_result;
2156 if (lp_winbind_offline_logon()) {
2157 result = winbindd_update_creds_by_name(contact_domain,
2158 state->mem_ctx, user,
2160 /* Again, this happens when we login from gdm or xdm
2161 * and the password expires, *BUT* cached crendentials
2162 * doesn't exist. winbindd_update_creds_by_name()
2163 * returns NT_STATUS_NO_SUCH_USER.
2164 * This is not a failure.
2167 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2168 result = NT_STATUS_OK;
2171 if (!NT_STATUS_IS_OK(result)) {
2172 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2173 goto process_result;
2178 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2180 NTSTATUS policy_ret;
2182 policy_ret = fillup_password_policy(contact_domain, state);
2184 /* failure of this is non critical, it will just provide no
2185 * additional information to the client why the change has
2186 * failed - Guenther */
2188 if (!NT_STATUS_IS_OK(policy_ret)) {
2189 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2190 goto process_result;
2196 set_auth_errors(state->response, result);
2198 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2199 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2202 state->response->data.auth.nt_status_string,
2203 state->response->data.auth.pam_error));
2205 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2208 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2210 struct winbindd_domain *domain;
2211 fstring name_domain, user;
2212 uid_t caller_uid = (uid_t)-1;
2213 uid_t request_uid = state->request->data.logoff.uid;
2215 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2216 state->request->data.logoff.user));
2218 /* Ensure null termination */
2219 state->request->data.logoff.user
2220 [sizeof(state->request->data.logoff.user)-1]='\0';
2222 state->request->data.logoff.krb5ccname
2223 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2225 if (request_uid == (gid_t)-1) {
2229 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2233 if ((domain = find_auth_domain(state->request->flags,
2234 name_domain)) == NULL) {
2238 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2239 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2244 switch (caller_uid) {
2248 /* root must be able to logoff any user - gd */
2249 state->request->data.logoff.uid = request_uid;
2252 if (caller_uid != request_uid) {
2253 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2256 state->request->data.logoff.uid = caller_uid;
2260 sendto_domain(state, domain);
2264 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2265 DEBUG(5, ("Pam Logoff for %s returned %s "
2267 state->request->data.logoff.user,
2268 state->response->data.auth.nt_status_string,
2269 state->response->data.auth.pam_error));
2270 request_error(state);
2274 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2275 struct winbindd_cli_state *state)
2277 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2279 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2280 state->request->data.logoff.user));
2282 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2283 result = NT_STATUS_OK;
2284 goto process_result;
2287 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2288 result = NT_STATUS_OK;
2289 goto process_result;
2294 if (state->request->data.logoff.uid < 0) {
2295 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2296 goto process_result;
2299 /* what we need here is to find the corresponding krb5 ccache name *we*
2300 * created for a given username and destroy it */
2302 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2303 result = NT_STATUS_OK;
2304 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2305 goto process_result;
2308 if (!ccache_entry_identical(state->request->data.logoff.user,
2309 state->request->data.logoff.uid,
2310 state->request->data.logoff.krb5ccname)) {
2311 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2312 goto process_result;
2315 result = remove_ccache(state->request->data.logoff.user);
2316 if (!NT_STATUS_IS_OK(result)) {
2317 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2318 nt_errstr(result)));
2319 goto process_result;
2323 result = NT_STATUS_NOT_SUPPORTED;
2328 winbindd_delete_memory_creds(state->request->data.logoff.user);
2330 set_auth_errors(state->response, result);
2332 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2335 /* Change user password with auth crap*/
2337 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2339 struct winbindd_domain *domain = NULL;
2340 const char *domain_name = NULL;
2342 /* Ensure null termination */
2343 state->request->data.chng_pswd_auth_crap.user[
2344 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2345 state->request->data.chng_pswd_auth_crap.domain[
2346 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2348 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2349 (unsigned long)state->pid,
2350 state->request->data.chng_pswd_auth_crap.domain,
2351 state->request->data.chng_pswd_auth_crap.user));
2353 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2354 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2355 } else if (lp_winbind_use_default_domain()) {
2356 domain_name = lp_workgroup();
2359 if (domain_name != NULL)
2360 domain = find_domain_from_name(domain_name);
2362 if (domain != NULL) {
2363 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2364 "%s\n", (unsigned long)state->pid,domain->name));
2365 sendto_domain(state, domain);
2369 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2370 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2371 state->request->data.chng_pswd_auth_crap.domain,
2372 state->request->data.chng_pswd_auth_crap.user,
2373 state->response->data.auth.nt_status_string,
2374 state->response->data.auth.pam_error));
2375 request_error(state);
2379 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2382 DATA_BLOB new_nt_password;
2383 DATA_BLOB old_nt_hash_enc;
2384 DATA_BLOB new_lm_password;
2385 DATA_BLOB old_lm_hash_enc;
2386 fstring domain,user;
2387 struct policy_handle dom_pol;
2388 struct winbindd_domain *contact_domain = domainSt;
2389 struct rpc_pipe_client *cli;
2391 /* Ensure null termination */
2392 state->request->data.chng_pswd_auth_crap.user[
2393 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2394 state->request->data.chng_pswd_auth_crap.domain[
2395 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2399 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2400 (unsigned long)state->pid,
2401 state->request->data.chng_pswd_auth_crap.domain,
2402 state->request->data.chng_pswd_auth_crap.user));
2404 if (lp_winbind_offline_logon()) {
2405 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2406 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2407 result = NT_STATUS_ACCESS_DENIED;
2411 if (*state->request->data.chng_pswd_auth_crap.domain) {
2412 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2414 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2418 DEBUG(3,("no domain specified with username (%s) - "
2420 state->request->data.chng_pswd_auth_crap.user));
2421 result = NT_STATUS_NO_SUCH_USER;
2426 if (!*domain && lp_winbind_use_default_domain()) {
2427 fstrcpy(domain,(char *)lp_workgroup());
2431 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2434 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2435 (unsigned long)state->pid, domain, user));
2437 /* Change password */
2438 new_nt_password = data_blob_talloc(
2440 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2441 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2443 old_nt_hash_enc = data_blob_talloc(
2445 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2446 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2448 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2449 new_lm_password = data_blob_talloc(
2451 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2452 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2454 old_lm_hash_enc = data_blob_talloc(
2456 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2457 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2459 new_lm_password.length = 0;
2460 old_lm_hash_enc.length = 0;
2463 /* Get sam handle */
2465 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2466 if (!NT_STATUS_IS_OK(result)) {
2467 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2471 result = rpccli_samr_chng_pswd_auth_crap(
2472 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2473 new_lm_password, old_lm_hash_enc);
2477 set_auth_errors(state->response, result);
2479 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2480 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2482 state->response->data.auth.nt_status_string,
2483 state->response->data.auth.pam_error));
2485 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;