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);
834 fstrcpy(mapped_user, state->request->data.auth.user);
837 if (!canonicalize_username(mapped_user, name_domain, name_user)) {
838 result = NT_STATUS_NO_SUCH_USER;
842 domain = find_auth_domain(state->request->flags, name_domain);
844 if (domain == NULL) {
845 result = NT_STATUS_NO_SUCH_USER;
849 sendto_domain(state, domain);
852 set_auth_errors(state->response, result);
853 DEBUG(5, ("Plain text authentication for %s returned %s "
855 state->request->data.auth.user,
856 state->response->data.auth.nt_status_string,
857 state->response->data.auth.pam_error));
858 request_error(state);
861 static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
862 struct winbindd_cli_state *state,
863 struct netr_SamInfo3 **info3)
865 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
866 uint16 max_allowed_bad_attempts;
867 fstring name_domain, name_user;
869 enum lsa_SidType type;
870 uchar new_nt_pass[NT_HASH_LEN];
871 const uint8 *cached_nt_pass;
872 const uint8 *cached_salt;
873 struct netr_SamInfo3 *my_info3;
874 time_t kickoff_time, must_change_time;
875 bool password_good = false;
877 struct winbindd_tdc_domain *tdc_domain = NULL;
884 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
886 /* Parse domain and username */
888 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
891 if (!lookup_cached_name(state->mem_ctx,
896 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
897 return NT_STATUS_NO_SUCH_USER;
900 if (type != SID_NAME_USER) {
901 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
902 return NT_STATUS_LOGON_FAILURE;
905 result = winbindd_get_creds(domain,
911 if (!NT_STATUS_IS_OK(result)) {
912 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
918 E_md4hash(state->request->data.auth.pass, new_nt_pass);
920 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
921 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
923 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
927 /* In this case we didn't store the nt_hash itself,
928 but the MD5 combination of salt + nt_hash. */
929 uchar salted_hash[NT_HASH_LEN];
930 E_md5hash(cached_salt, new_nt_pass, salted_hash);
932 password_good = (memcmp(cached_nt_pass, salted_hash,
935 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
936 password_good = (memcmp(cached_nt_pass, new_nt_pass,
942 /* User *DOES* know the password, update logon_time and reset
945 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
947 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
948 return NT_STATUS_ACCOUNT_LOCKED_OUT;
951 if (my_info3->base.acct_flags & ACB_DISABLED) {
952 return NT_STATUS_ACCOUNT_DISABLED;
955 if (my_info3->base.acct_flags & ACB_WSTRUST) {
956 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
959 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
960 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
963 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
964 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
967 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
968 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
969 my_info3->base.acct_flags));
970 return NT_STATUS_LOGON_FAILURE;
973 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
974 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
975 return NT_STATUS_ACCOUNT_EXPIRED;
978 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
979 if (must_change_time != 0 && must_change_time < time(NULL)) {
980 /* we allow grace logons when the password has expired */
981 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
982 /* return NT_STATUS_PASSWORD_EXPIRED; */
987 if ((state->request->flags & WBFLAG_PAM_KRB5) &&
988 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
989 ((tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL) ||
990 /* used to cope with the case winbindd starting without network. */
991 !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
994 const char *cc = NULL;
996 const char *principal_s = NULL;
997 const char *service = NULL;
998 bool internal_ccache = false;
1000 uid = get_uid_from_state(state);
1002 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1003 return NT_STATUS_INVALID_PARAMETER;
1006 cc = generate_krb5_ccache(state->mem_ctx,
1007 state->request->data.auth.krb5_cc_type,
1008 state->request->data.auth.uid,
1011 return NT_STATUS_NO_MEMORY;
1014 realm = domain->alt_name;
1017 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1018 if (principal_s == NULL) {
1019 return NT_STATUS_NO_MEMORY;
1022 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1023 if (service == NULL) {
1024 return NT_STATUS_NO_MEMORY;
1027 if (!internal_ccache) {
1029 setup_return_cc_name(state, cc);
1031 result = add_ccache_to_list(principal_s,
1034 state->request->data.auth.user,
1038 time(NULL) + lp_winbind_cache_time(),
1039 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1042 if (!NT_STATUS_IS_OK(result)) {
1043 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1044 "to add ccache to list: %s\n",
1045 nt_errstr(result)));
1049 #endif /* HAVE_KRB5 */
1051 /* FIXME: we possibly should handle logon hours as well (does xp when
1052 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1054 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1055 my_info3->base.bad_password_count = 0;
1057 result = winbindd_update_creds_by_info3(domain,
1059 state->request->data.auth.user,
1060 state->request->data.auth.pass,
1062 if (!NT_STATUS_IS_OK(result)) {
1063 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1064 nt_errstr(result)));
1068 return NT_STATUS_OK;
1072 /* User does *NOT* know the correct password, modify info3 accordingly */
1074 /* failure of this is not critical */
1075 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1076 if (!NT_STATUS_IS_OK(result)) {
1077 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1078 "Won't be able to honour account lockout policies\n"));
1081 /* increase counter */
1082 my_info3->base.bad_password_count++;
1084 if (max_allowed_bad_attempts == 0) {
1089 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1091 uint32 password_properties;
1093 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1094 if (!NT_STATUS_IS_OK(result)) {
1095 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1098 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1099 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1100 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1105 result = winbindd_update_creds_by_info3(domain,
1107 state->request->data.auth.user,
1111 if (!NT_STATUS_IS_OK(result)) {
1112 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1113 nt_errstr(result)));
1116 return NT_STATUS_LOGON_FAILURE;
1119 static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1120 struct winbindd_cli_state *state,
1121 struct netr_SamInfo3 **info3)
1123 struct winbindd_domain *contact_domain;
1124 fstring name_domain, name_user;
1127 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1129 /* Parse domain and username */
1131 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1133 /* what domain should we contact? */
1136 if (!(contact_domain = find_domain_from_name(name_domain))) {
1137 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1138 state->request->data.auth.user, name_domain, name_user, name_domain));
1139 result = NT_STATUS_NO_SUCH_USER;
1144 if (is_myname(name_domain)) {
1145 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1146 result = NT_STATUS_NO_SUCH_USER;
1150 contact_domain = find_domain_from_name(name_domain);
1151 if (contact_domain == NULL) {
1152 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1153 state->request->data.auth.user, name_domain, name_user, name_domain));
1155 contact_domain = find_our_domain();
1159 if (contact_domain->initialized &&
1160 contact_domain->active_directory) {
1164 if (!contact_domain->initialized) {
1165 init_dc_connection(contact_domain);
1168 if (!contact_domain->active_directory) {
1169 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1170 return NT_STATUS_INVALID_LOGON_TYPE;
1173 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1178 typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli,
1179 TALLOC_CTX *mem_ctx,
1180 uint32 logon_parameters,
1182 const char *username,
1184 const char *workstation,
1185 const uint8 chal[8],
1186 DATA_BLOB lm_response,
1187 DATA_BLOB nt_response,
1188 struct netr_SamInfo3 **info3);
1190 static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1191 struct winbindd_cli_state *state,
1192 struct netr_SamInfo3 **info3)
1195 struct rpc_pipe_client *netlogon_pipe;
1200 unsigned char local_lm_response[24];
1201 unsigned char local_nt_response[24];
1202 struct winbindd_domain *contact_domain;
1203 fstring name_domain, name_user;
1206 struct netr_SamInfo3 *my_info3 = NULL;
1210 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1212 /* Parse domain and username */
1214 parse_domain_user(state->request->data.auth.user, name_domain, name_user);
1216 /* do password magic */
1219 generate_random_buffer(chal, 8);
1220 if (lp_client_ntlmv2_auth()) {
1221 DATA_BLOB server_chal;
1222 DATA_BLOB names_blob;
1223 DATA_BLOB nt_response;
1224 DATA_BLOB lm_response;
1225 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1227 /* note that the 'workgroup' here is a best guess - we don't know
1228 the server's domain at this point. The 'server name' is also
1231 names_blob = NTLMv2_generate_names_blob(state->mem_ctx, global_myname(), lp_workgroup());
1233 if (!SMBNTLMv2encrypt(NULL, name_user, name_domain,
1234 state->request->data.auth.pass,
1237 &lm_response, &nt_response, NULL, NULL)) {
1238 data_blob_free(&names_blob);
1239 data_blob_free(&server_chal);
1240 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1241 result = NT_STATUS_NO_MEMORY;
1244 data_blob_free(&names_blob);
1245 data_blob_free(&server_chal);
1246 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1247 lm_response.length);
1248 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1249 nt_response.length);
1250 data_blob_free(&lm_response);
1251 data_blob_free(&nt_response);
1254 if (lp_client_lanman_auth()
1255 && SMBencrypt(state->request->data.auth.pass,
1257 local_lm_response)) {
1258 lm_resp = data_blob_talloc(state->mem_ctx,
1260 sizeof(local_lm_response));
1262 lm_resp = data_blob_null;
1264 SMBNTencrypt(state->request->data.auth.pass,
1268 nt_resp = data_blob_talloc(state->mem_ctx,
1270 sizeof(local_nt_response));
1273 /* what domain should we contact? */
1276 if (!(contact_domain = find_domain_from_name(name_domain))) {
1277 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1278 state->request->data.auth.user, name_domain, name_user, name_domain));
1279 result = NT_STATUS_NO_SUCH_USER;
1284 if (is_myname(name_domain)) {
1285 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1286 result = NT_STATUS_NO_SUCH_USER;
1290 contact_domain = find_our_domain();
1293 /* check authentication loop */
1296 netlogon_fn_t logon_fn;
1298 ZERO_STRUCTP(my_info3);
1301 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1303 if (!NT_STATUS_IS_OK(result)) {
1304 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1308 /* It is really important to try SamLogonEx here,
1309 * because in a clustered environment, we want to use
1310 * one machine account from multiple physical
1313 * With a normal SamLogon call, we must keep the
1314 * credentials chain updated and intact between all
1315 * users of the machine account (which would imply
1316 * cross-node communication for every NTLM logon).
1318 * (The credentials chain is not per NETLOGON pipe
1319 * connection, but globally on the server/client pair
1322 * When using SamLogonEx, the credentials are not
1323 * supplied, but the session key is implied by the
1324 * wrapping SamLogon context.
1326 * -- abartlet 21 April 2008
1329 logon_fn = contact_domain->can_do_samlogon_ex
1330 ? rpccli_netlogon_sam_network_logon_ex
1331 : rpccli_netlogon_sam_network_logon;
1333 result = logon_fn(netlogon_pipe,
1336 contact_domain->dcname, /* server name */
1337 name_user, /* user name */
1338 name_domain, /* target domain */
1339 global_myname(), /* workstation */
1346 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1347 && contact_domain->can_do_samlogon_ex) {
1348 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1349 "retrying with NetSamLogon\n"));
1350 contact_domain->can_do_samlogon_ex = false;
1355 /* We have to try a second time as cm_connect_netlogon
1356 might not yet have noticed that the DC has killed
1359 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1364 /* if we get access denied, a possible cause was that we had
1365 and open connection to the DC, but someone changed our
1366 machine account password out from underneath us using 'net
1367 rpc changetrustpw' */
1369 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1370 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1371 "ACCESS_DENIED. Maybe the trust account "
1372 "password was changed and we didn't know it. "
1373 "Killing connections to domain %s\n",
1375 invalidate_cm_connection(&contact_domain->conn);
1379 } while ( (attempts < 2) && retry );
1381 /* handle the case where a NT4 DC does not fill in the acct_flags in
1382 * the samlogon reply info3. When accurate info3 is required by the
1383 * caller, we look up the account flags ourselve - gd */
1385 if ((state->request->flags & WBFLAG_PAM_INFO3_TEXT) &&
1386 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1388 struct rpc_pipe_client *samr_pipe;
1389 struct policy_handle samr_domain_handle, user_pol;
1390 union samr_UserInfo *info = NULL;
1391 NTSTATUS status_tmp;
1394 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1395 &samr_pipe, &samr_domain_handle);
1397 if (!NT_STATUS_IS_OK(status_tmp)) {
1398 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1399 nt_errstr(status_tmp)));
1403 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1404 &samr_domain_handle,
1405 MAXIMUM_ALLOWED_ACCESS,
1409 if (!NT_STATUS_IS_OK(status_tmp)) {
1410 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1411 nt_errstr(status_tmp)));
1415 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1420 if (!NT_STATUS_IS_OK(status_tmp)) {
1421 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1422 nt_errstr(status_tmp)));
1423 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1427 acct_flags = info->info16.acct_flags;
1429 if (acct_flags == 0) {
1430 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1434 my_info3->base.acct_flags = acct_flags;
1436 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1438 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1446 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1447 struct winbindd_cli_state *state)
1449 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1450 NTSTATUS krb5_result = NT_STATUS_OK;
1451 fstring name_domain, name_user;
1453 fstring domain_user;
1454 struct netr_SamInfo3 *info3 = NULL;
1455 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
1457 /* Ensure null termination */
1458 state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
1460 /* Ensure null termination */
1461 state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
1463 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1464 state->request->data.auth.user));
1466 if (!check_request_flags(state->request->flags)) {
1467 result = NT_STATUS_INVALID_PARAMETER_MIX;
1471 /* Parse domain and username */
1473 name_map_status = normalize_name_unmap(state->mem_ctx,
1474 state->request->data.auth.user,
1477 /* If the name normalization didnt' actually do anything,
1478 just use the original name */
1480 if (!NT_STATUS_IS_OK(name_map_status) &&
1481 !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
1483 mapped_user = state->request->data.auth.user;
1486 parse_domain_user(mapped_user, name_domain, name_user);
1488 if ( mapped_user != state->request->data.auth.user ) {
1489 fstr_sprintf( domain_user, "%s\\%s", name_domain, name_user );
1490 safe_strcpy( state->request->data.auth.user, domain_user,
1491 sizeof(state->request->data.auth.user)-1 );
1494 if (domain->online == false) {
1495 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1496 if (domain->startup) {
1497 /* Logons are very important to users. If we're offline and
1498 we get a request within the first 30 seconds of startup,
1499 try very hard to find a DC and go online. */
1501 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1502 "request in startup mode.\n", domain->name ));
1504 winbindd_flush_negative_conn_cache(domain);
1505 result = init_dc_connection(domain);
1509 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1511 /* Check for Kerberos authentication */
1512 if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
1514 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1515 /* save for later */
1516 krb5_result = result;
1519 if (NT_STATUS_IS_OK(result)) {
1520 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1521 goto process_result;
1523 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1526 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1527 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1528 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1529 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1530 set_domain_offline( domain );
1534 /* there are quite some NT_STATUS errors where there is no
1535 * point in retrying with a samlogon, we explictly have to take
1536 * care not to increase the bad logon counter on the DC */
1538 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1540 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1541 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1542 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1543 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1544 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1545 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1546 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1547 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1548 goto process_result;
1551 if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1552 DEBUG(3,("falling back to samlogon\n"));
1560 /* Check for Samlogon authentication */
1561 if (domain->online) {
1562 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1564 if (NT_STATUS_IS_OK(result)) {
1565 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1566 /* add the Krb5 err if we have one */
1567 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1568 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1570 goto process_result;
1573 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1574 nt_errstr(result)));
1576 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1577 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1578 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1580 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1581 set_domain_offline( domain );
1585 if (domain->online) {
1586 /* We're still online - fail. */
1592 /* Check for Cached logons */
1593 if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
1594 lp_winbind_offline_logon()) {
1596 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1598 if (NT_STATUS_IS_OK(result)) {
1599 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1600 goto process_result;
1602 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1609 if (NT_STATUS_IS_OK(result)) {
1613 /* In all codepaths where result == NT_STATUS_OK info3 must have
1614 been initialized. */
1616 result = NT_STATUS_INTERNAL_ERROR;
1620 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1621 netsamlogon_cache_store(name_user, info3);
1623 /* save name_to_sid info as early as possible (only if
1624 this is our primary domain so we don't invalidate
1625 the cache entry by storing the seq_num for the wrong
1627 if ( domain->primary ) {
1628 sid_compose(&user_sid, info3->base.domain_sid,
1630 cache_name2sid(domain, name_domain, name_user,
1631 SID_NAME_USER, &user_sid);
1634 /* Check if the user is in the right group */
1636 result = check_info3_in_group(
1638 state->request->data.auth.require_membership_of_sid);
1639 if (!NT_STATUS_IS_OK(result)) {
1640 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1641 state->request->data.auth.user,
1642 state->request->data.auth.require_membership_of_sid));
1646 result = append_auth_data(state, info3, name_domain,
1648 if (!NT_STATUS_IS_OK(result)) {
1652 if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
1654 /* Store in-memory creds for single-signon using ntlm_auth. */
1655 result = winbindd_add_memory_creds(state->request->data.auth.user,
1656 get_uid_from_state(state),
1657 state->request->data.auth.pass);
1659 if (!NT_STATUS_IS_OK(result)) {
1660 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1664 if (lp_winbind_offline_logon()) {
1665 result = winbindd_store_creds(domain,
1667 state->request->data.auth.user,
1668 state->request->data.auth.pass,
1670 if (!NT_STATUS_IS_OK(result)) {
1672 /* Release refcount. */
1673 winbindd_delete_memory_creds(state->request->data.auth.user);
1675 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1682 if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
1683 struct winbindd_domain *our_domain = find_our_domain();
1685 /* This is not entirely correct I believe, but it is
1686 consistent. Only apply the password policy settings
1687 too warn users for our own domain. Cannot obtain these
1688 from trusted DCs all the time so don't do it at all.
1691 result = NT_STATUS_NOT_SUPPORTED;
1692 if (our_domain == domain ) {
1693 result = fillup_password_policy(our_domain, state);
1696 if (!NT_STATUS_IS_OK(result)
1697 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1699 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1700 domain->name, nt_errstr(result)));
1705 result = NT_STATUS_OK;
1709 /* give us a more useful (more correct?) error code */
1710 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1711 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1712 result = NT_STATUS_NO_LOGON_SERVERS;
1715 set_auth_errors(state->response, result);
1717 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1718 state->request->data.auth.user,
1719 state->response->data.auth.nt_status_string,
1720 state->response->data.auth.pam_error));
1722 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1726 /**********************************************************************
1727 Challenge Response Authentication Protocol
1728 **********************************************************************/
1730 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1732 struct winbindd_domain *domain = NULL;
1733 const char *domain_name = NULL;
1736 if (!check_request_flags(state->request->flags)) {
1737 result = NT_STATUS_INVALID_PARAMETER_MIX;
1741 if (!state->privileged) {
1742 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1744 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1745 "on %s are set correctly.\n",
1746 get_winbind_priv_pipe_dir()));
1747 /* send a better message than ACCESS_DENIED */
1748 fstr_sprintf(state->response->data.auth.error_string,
1749 "winbind client not authorized to use "
1750 "winbindd_pam_auth_crap. Ensure permissions on "
1751 "%s are set correctly.",
1752 get_winbind_priv_pipe_dir());
1753 result = NT_STATUS_ACCESS_DENIED;
1757 /* Ensure null termination */
1758 state->request->data.auth_crap.user
1759 [sizeof(state->request->data.auth_crap.user)-1]=0;
1760 state->request->data.auth_crap.domain
1761 [sizeof(state->request->data.auth_crap.domain)-1]=0;
1763 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1764 (unsigned long)state->pid,
1765 state->request->data.auth_crap.domain,
1766 state->request->data.auth_crap.user));
1768 if (*state->request->data.auth_crap.domain != '\0') {
1769 domain_name = state->request->data.auth_crap.domain;
1770 } else if (lp_winbind_use_default_domain()) {
1771 domain_name = lp_workgroup();
1774 if (domain_name != NULL)
1775 domain = find_auth_domain(state->request->flags, domain_name);
1777 if (domain != NULL) {
1778 sendto_domain(state, domain);
1782 result = NT_STATUS_NO_SUCH_USER;
1785 set_auth_errors(state->response, result);
1786 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1787 state->request->data.auth_crap.domain,
1788 state->request->data.auth_crap.user,
1789 state->response->data.auth.nt_status_string,
1790 state->response->data.auth.pam_error));
1791 request_error(state);
1796 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1797 struct winbindd_cli_state *state)
1800 struct netr_SamInfo3 *info3 = NULL;
1801 struct rpc_pipe_client *netlogon_pipe;
1802 const char *name_user = NULL;
1803 const char *name_domain = NULL;
1804 const char *workstation;
1805 struct winbindd_domain *contact_domain;
1809 DATA_BLOB lm_resp, nt_resp;
1811 /* This is child-only, so no check for privileged access is needed
1814 /* Ensure null termination */
1815 state->request->data.auth_crap.user[sizeof(state->request->data.auth_crap.user)-1]=0;
1816 state->request->data.auth_crap.domain[sizeof(state->request->data.auth_crap.domain)-1]=0;
1818 if (!check_request_flags(state->request->flags)) {
1819 result = NT_STATUS_INVALID_PARAMETER_MIX;
1823 name_user = state->request->data.auth_crap.user;
1825 if (*state->request->data.auth_crap.domain) {
1826 name_domain = state->request->data.auth_crap.domain;
1827 } else if (lp_winbind_use_default_domain()) {
1828 name_domain = lp_workgroup();
1830 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1832 result = NT_STATUS_NO_SUCH_USER;
1836 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1837 name_domain, name_user));
1839 if (*state->request->data.auth_crap.workstation) {
1840 workstation = state->request->data.auth_crap.workstation;
1842 workstation = global_myname();
1845 if (state->request->data.auth_crap.lm_resp_len > sizeof(state->request->data.auth_crap.lm_resp)
1846 || state->request->data.auth_crap.nt_resp_len > sizeof(state->request->data.auth_crap.nt_resp)) {
1847 if (!(state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) ||
1848 state->request->extra_len != state->request->data.auth_crap.nt_resp_len) {
1849 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1850 state->request->data.auth_crap.lm_resp_len,
1851 state->request->data.auth_crap.nt_resp_len));
1852 result = NT_STATUS_INVALID_PARAMETER;
1857 lm_resp = data_blob_talloc(state->mem_ctx, state->request->data.auth_crap.lm_resp,
1858 state->request->data.auth_crap.lm_resp_len);
1860 if (state->request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
1861 nt_resp = data_blob_talloc(state->mem_ctx,
1862 state->request->extra_data.data,
1863 state->request->data.auth_crap.nt_resp_len);
1865 nt_resp = data_blob_talloc(state->mem_ctx,
1866 state->request->data.auth_crap.nt_resp,
1867 state->request->data.auth_crap.nt_resp_len);
1870 /* what domain should we contact? */
1873 if (!(contact_domain = find_domain_from_name(name_domain))) {
1874 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1875 state->request->data.auth_crap.user, name_domain, name_user, name_domain));
1876 result = NT_STATUS_NO_SUCH_USER;
1880 if (is_myname(name_domain)) {
1881 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1882 result = NT_STATUS_NO_SUCH_USER;
1885 contact_domain = find_our_domain();
1889 netlogon_fn_t logon_fn;
1893 netlogon_pipe = NULL;
1894 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1896 if (!NT_STATUS_IS_OK(result)) {
1897 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1898 nt_errstr(result)));
1902 logon_fn = contact_domain->can_do_samlogon_ex
1903 ? rpccli_netlogon_sam_network_logon_ex
1904 : rpccli_netlogon_sam_network_logon;
1906 result = logon_fn(netlogon_pipe,
1908 state->request->data.auth_crap.logon_parameters,
1909 contact_domain->dcname,
1912 /* Bug #3248 - found by Stefan Burkei. */
1913 workstation, /* We carefully set this above so use it... */
1914 state->request->data.auth_crap.chal,
1919 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1920 && contact_domain->can_do_samlogon_ex) {
1921 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1922 "retrying with NetSamLogon\n"));
1923 contact_domain->can_do_samlogon_ex = false;
1930 /* We have to try a second time as cm_connect_netlogon
1931 might not yet have noticed that the DC has killed
1934 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1939 /* if we get access denied, a possible cause was that we had and open
1940 connection to the DC, but someone changed our machine account password
1941 out from underneath us using 'net rpc changetrustpw' */
1943 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1944 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1945 "ACCESS_DENIED. Maybe the trust account "
1946 "password was changed and we didn't know it. "
1947 "Killing connections to domain %s\n",
1949 invalidate_cm_connection(&contact_domain->conn);
1953 } while ( (attempts < 2) && retry );
1955 if (NT_STATUS_IS_OK(result)) {
1957 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1958 netsamlogon_cache_store(name_user, info3);
1960 /* Check if the user is in the right group */
1962 result = check_info3_in_group(
1964 state->request->data.auth_crap.require_membership_of_sid);
1965 if (!NT_STATUS_IS_OK(result)) {
1966 DEBUG(3, ("User %s is not in the required group (%s), so "
1967 "crap authentication is rejected\n",
1968 state->request->data.auth_crap.user,
1969 state->request->data.auth_crap.require_membership_of_sid));
1973 result = append_auth_data(state, info3, name_domain,
1975 if (!NT_STATUS_IS_OK(result)) {
1982 /* give us a more useful (more correct?) error code */
1983 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1984 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1985 result = NT_STATUS_NO_LOGON_SERVERS;
1988 if (state->request->flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1989 result = nt_status_squash(result);
1992 set_auth_errors(state->response, result);
1994 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1995 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1998 state->response->data.auth.nt_status_string,
1999 state->response->data.auth.pam_error));
2001 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2004 /* Change a user password */
2006 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
2008 fstring domain, user;
2010 struct winbindd_domain *contact_domain;
2011 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
2013 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
2014 state->request->data.chauthtok.user));
2018 nt_status = normalize_name_unmap(state->mem_ctx,
2019 state->request->data.chauthtok.user,
2022 /* Update the chauthtok name if we did any mapping */
2024 if (NT_STATUS_IS_OK(nt_status) ||
2025 NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
2027 fstrcpy(state->request->data.chauthtok.user, mapped_user);
2030 /* Must pass in state->...chauthtok.user because
2031 canonicalize_username() assumes an fstring(). Since
2032 we have already copied it (if necessary), this is ok. */
2034 if (!canonicalize_username(state->request->data.chauthtok.user, domain, user)) {
2035 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2036 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
2038 state->request->data.auth.user,
2039 state->response->data.auth.nt_status_string,
2040 state->response->data.auth.pam_error));
2041 request_error(state);
2045 contact_domain = find_domain_from_name(domain);
2046 if (!contact_domain) {
2047 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2048 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2049 state->request->data.chauthtok.user, domain, user, domain));
2050 request_error(state);
2054 sendto_domain(state, contact_domain);
2057 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2058 struct winbindd_cli_state *state)
2061 char *newpass = NULL;
2062 struct policy_handle dom_pol;
2063 struct rpc_pipe_client *cli;
2064 bool got_info = false;
2065 struct samr_DomInfo1 *info = NULL;
2066 struct userPwdChangeFailureInformation *reject = NULL;
2067 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2068 fstring domain, user;
2070 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2071 state->request->data.auth.user));
2073 if (!parse_domain_user(state->request->data.chauthtok.user, domain, user)) {
2077 /* Change password */
2079 oldpass = state->request->data.chauthtok.oldpass;
2080 newpass = state->request->data.chauthtok.newpass;
2082 /* Initialize reject reason */
2083 state->response->data.auth.reject_reason = Undefined;
2085 /* Get sam handle */
2087 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2089 if (!NT_STATUS_IS_OK(result)) {
2090 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2094 result = rpccli_samr_chgpasswd_user3(cli, state->mem_ctx,
2101 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2103 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2105 fill_in_password_policy(state->response, info);
2107 state->response->data.auth.reject_reason =
2108 reject->extendedFailureReason;
2113 /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
2114 * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
2115 * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
2116 * short to comply with the samr_ChangePasswordUser3 idl - gd */
2118 /* only fallback when the chgpasswd_user3 call is not supported */
2119 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2120 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2121 (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) ||
2122 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2124 DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
2125 nt_errstr(result)));
2127 result = rpccli_samr_chgpasswd_user2(cli, state->mem_ctx, user, newpass, oldpass);
2129 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2130 Map to the same status code as Windows 2003. */
2132 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2133 result = NT_STATUS_PASSWORD_RESTRICTION;
2139 if (NT_STATUS_IS_OK(result) && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN)) {
2141 /* Update the single sign-on memory creds. */
2142 result = winbindd_replace_memory_creds(state->request->data.chauthtok.user,
2145 /* When we login from gdm or xdm and password expires,
2146 * we change password, but there are no memory crendentials
2147 * So, winbindd_replace_memory_creds() returns
2148 * NT_STATUS_OBJECT_NAME_NOT_FOUND. This is not a failure.
2151 if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
2152 result = NT_STATUS_OK;
2155 if (!NT_STATUS_IS_OK(result)) {
2156 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2157 goto process_result;
2160 if (lp_winbind_offline_logon()) {
2161 result = winbindd_update_creds_by_name(contact_domain,
2162 state->mem_ctx, user,
2164 /* Again, this happens when we login from gdm or xdm
2165 * and the password expires, *BUT* cached crendentials
2166 * doesn't exist. winbindd_update_creds_by_name()
2167 * returns NT_STATUS_NO_SUCH_USER.
2168 * This is not a failure.
2171 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
2172 result = NT_STATUS_OK;
2175 if (!NT_STATUS_IS_OK(result)) {
2176 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2177 goto process_result;
2182 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2184 NTSTATUS policy_ret;
2186 policy_ret = fillup_password_policy(contact_domain, state);
2188 /* failure of this is non critical, it will just provide no
2189 * additional information to the client why the change has
2190 * failed - Guenther */
2192 if (!NT_STATUS_IS_OK(policy_ret)) {
2193 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2194 goto process_result;
2200 set_auth_errors(state->response, result);
2202 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2203 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2206 state->response->data.auth.nt_status_string,
2207 state->response->data.auth.pam_error));
2209 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2212 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2214 struct winbindd_domain *domain;
2215 fstring name_domain, user;
2216 uid_t caller_uid = (uid_t)-1;
2217 uid_t request_uid = state->request->data.logoff.uid;
2219 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2220 state->request->data.logoff.user));
2222 /* Ensure null termination */
2223 state->request->data.logoff.user
2224 [sizeof(state->request->data.logoff.user)-1]='\0';
2226 state->request->data.logoff.krb5ccname
2227 [sizeof(state->request->data.logoff.krb5ccname)-1]='\0';
2229 if (request_uid == (gid_t)-1) {
2233 if (!canonicalize_username(state->request->data.logoff.user, name_domain, user)) {
2237 if ((domain = find_auth_domain(state->request->flags,
2238 name_domain)) == NULL) {
2242 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2243 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2248 switch (caller_uid) {
2252 /* root must be able to logoff any user - gd */
2253 state->request->data.logoff.uid = request_uid;
2256 if (caller_uid != request_uid) {
2257 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2260 state->request->data.logoff.uid = caller_uid;
2264 sendto_domain(state, domain);
2268 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2269 DEBUG(5, ("Pam Logoff for %s returned %s "
2271 state->request->data.logoff.user,
2272 state->response->data.auth.nt_status_string,
2273 state->response->data.auth.pam_error));
2274 request_error(state);
2278 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2279 struct winbindd_cli_state *state)
2281 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2283 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2284 state->request->data.logoff.user));
2286 if (!(state->request->flags & WBFLAG_PAM_KRB5)) {
2287 result = NT_STATUS_OK;
2288 goto process_result;
2291 if (state->request->data.logoff.krb5ccname[0] == '\0') {
2292 result = NT_STATUS_OK;
2293 goto process_result;
2298 if (state->request->data.logoff.uid < 0) {
2299 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2300 goto process_result;
2303 /* what we need here is to find the corresponding krb5 ccache name *we*
2304 * created for a given username and destroy it */
2306 if (!ccache_entry_exists(state->request->data.logoff.user)) {
2307 result = NT_STATUS_OK;
2308 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2309 goto process_result;
2312 if (!ccache_entry_identical(state->request->data.logoff.user,
2313 state->request->data.logoff.uid,
2314 state->request->data.logoff.krb5ccname)) {
2315 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2316 goto process_result;
2319 result = remove_ccache(state->request->data.logoff.user);
2320 if (!NT_STATUS_IS_OK(result)) {
2321 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2322 nt_errstr(result)));
2323 goto process_result;
2327 result = NT_STATUS_NOT_SUPPORTED;
2332 winbindd_delete_memory_creds(state->request->data.logoff.user);
2334 set_auth_errors(state->response, result);
2336 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2339 /* Change user password with auth crap*/
2341 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2343 struct winbindd_domain *domain = NULL;
2344 const char *domain_name = NULL;
2346 /* Ensure null termination */
2347 state->request->data.chng_pswd_auth_crap.user[
2348 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2349 state->request->data.chng_pswd_auth_crap.domain[
2350 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2352 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2353 (unsigned long)state->pid,
2354 state->request->data.chng_pswd_auth_crap.domain,
2355 state->request->data.chng_pswd_auth_crap.user));
2357 if (*state->request->data.chng_pswd_auth_crap.domain != '\0') {
2358 domain_name = state->request->data.chng_pswd_auth_crap.domain;
2359 } else if (lp_winbind_use_default_domain()) {
2360 domain_name = lp_workgroup();
2363 if (domain_name != NULL)
2364 domain = find_domain_from_name(domain_name);
2366 if (domain != NULL) {
2367 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2368 "%s\n", (unsigned long)state->pid,domain->name));
2369 sendto_domain(state, domain);
2373 set_auth_errors(state->response, NT_STATUS_NO_SUCH_USER);
2374 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2375 state->request->data.chng_pswd_auth_crap.domain,
2376 state->request->data.chng_pswd_auth_crap.user,
2377 state->response->data.auth.nt_status_string,
2378 state->response->data.auth.pam_error));
2379 request_error(state);
2383 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2386 DATA_BLOB new_nt_password;
2387 DATA_BLOB old_nt_hash_enc;
2388 DATA_BLOB new_lm_password;
2389 DATA_BLOB old_lm_hash_enc;
2390 fstring domain,user;
2391 struct policy_handle dom_pol;
2392 struct winbindd_domain *contact_domain = domainSt;
2393 struct rpc_pipe_client *cli;
2395 /* Ensure null termination */
2396 state->request->data.chng_pswd_auth_crap.user[
2397 sizeof(state->request->data.chng_pswd_auth_crap.user)-1]=0;
2398 state->request->data.chng_pswd_auth_crap.domain[
2399 sizeof(state->request->data.chng_pswd_auth_crap.domain)-1]=0;
2403 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2404 (unsigned long)state->pid,
2405 state->request->data.chng_pswd_auth_crap.domain,
2406 state->request->data.chng_pswd_auth_crap.user));
2408 if (lp_winbind_offline_logon()) {
2409 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2410 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2411 result = NT_STATUS_ACCESS_DENIED;
2415 if (*state->request->data.chng_pswd_auth_crap.domain) {
2416 fstrcpy(domain,state->request->data.chng_pswd_auth_crap.domain);
2418 parse_domain_user(state->request->data.chng_pswd_auth_crap.user,
2422 DEBUG(3,("no domain specified with username (%s) - "
2424 state->request->data.chng_pswd_auth_crap.user));
2425 result = NT_STATUS_NO_SUCH_USER;
2430 if (!*domain && lp_winbind_use_default_domain()) {
2431 fstrcpy(domain,(char *)lp_workgroup());
2435 fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
2438 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2439 (unsigned long)state->pid, domain, user));
2441 /* Change password */
2442 new_nt_password = data_blob_talloc(
2444 state->request->data.chng_pswd_auth_crap.new_nt_pswd,
2445 state->request->data.chng_pswd_auth_crap.new_nt_pswd_len);
2447 old_nt_hash_enc = data_blob_talloc(
2449 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc,
2450 state->request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2452 if(state->request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2453 new_lm_password = data_blob_talloc(
2455 state->request->data.chng_pswd_auth_crap.new_lm_pswd,
2456 state->request->data.chng_pswd_auth_crap.new_lm_pswd_len);
2458 old_lm_hash_enc = data_blob_talloc(
2460 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc,
2461 state->request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2463 new_lm_password.length = 0;
2464 old_lm_hash_enc.length = 0;
2467 /* Get sam handle */
2469 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2470 if (!NT_STATUS_IS_OK(result)) {
2471 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2475 result = rpccli_samr_chng_pswd_auth_crap(
2476 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2477 new_lm_password, old_lm_hash_enc);
2481 set_auth_errors(state->response, result);
2483 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2484 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2486 state->response->data.auth.nt_status_string,
2487 state->response->data.auth.pam_error));
2489 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;