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/>.
28 #define DBGC_CLASS DBGC_WINBIND
30 #define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
32 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
33 struct winbindd_cli_state *state,
34 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(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 size = talloc_get_size(ex);
110 SAFE_FREE(state->response.extra_data.data);
111 state->response.extra_data.data = SMB_MALLOC(size);
112 if (!state->response.extra_data.data) {
113 return NT_STATUS_NO_MEMORY;
115 memcpy(state->response.extra_data.data, ex, size);
118 state->response.length += size;
123 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
124 struct winbindd_cli_state *state,
125 struct netr_SamInfo3 *info3)
128 enum ndr_err_code ndr_err;
130 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
131 (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
132 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133 DEBUG(0,("append_info3_as_ndr: failed to append\n"));
134 return ndr_map_error2ntstatus(ndr_err);
137 SAFE_FREE(state->response.extra_data.data);
138 state->response.extra_data.data = SMB_MALLOC(blob.length);
139 if (!state->response.extra_data.data) {
140 data_blob_free(&blob);
141 return NT_STATUS_NO_MEMORY;
144 memset(state->response.extra_data.data, '\0', blob.length);
145 memcpy(state->response.extra_data.data, blob.data, blob.length);
146 state->response.length += blob.length;
148 data_blob_free(&blob);
153 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
154 struct winbindd_cli_state *state,
155 const struct netr_SamInfo3 *info3,
156 const char *name_domain,
157 const char *name_user)
159 /* We've been asked to return the unix username, per
160 'winbind use default domain' settings and the like */
162 const char *nt_username, *nt_domain;
164 nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string);
166 /* If the server didn't give us one, just use the one
168 nt_domain = name_domain;
171 nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string);
173 /* If the server didn't give us one, just use the one
175 nt_username = name_user;
178 fill_domain_username(state->response.data.auth.unix_username,
179 nt_domain, nt_username, True);
181 DEBUG(5,("Setting unix username to [%s]\n",
182 state->response.data.auth.unix_username));
187 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
188 struct winbindd_cli_state *state,
189 const struct netr_SamInfo3 *info3,
190 const char *name_domain,
191 const char *name_user)
193 char *afsname = NULL;
196 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
197 if (afsname == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 afsname = talloc_string_sub(mem_ctx,
202 lp_afs_username_map(),
204 afsname = talloc_string_sub(mem_ctx, afsname,
206 afsname = talloc_string_sub(mem_ctx, afsname,
213 sid_copy(&user_sid, info3->base.domain_sid);
214 sid_append_rid(&user_sid, info3->base.rid);
215 sid_to_fstring(sidstr, &user_sid);
216 afsname = talloc_string_sub(mem_ctx, afsname,
220 if (afsname == NULL) {
221 return NT_STATUS_NO_MEMORY;
226 DEBUG(10, ("Generating token for user %s\n", afsname));
228 cell = strchr(afsname, '@');
231 return NT_STATUS_NO_MEMORY;
237 /* Append an AFS token string */
238 SAFE_FREE(state->response.extra_data.data);
239 state->response.extra_data.data =
240 afs_createtoken_str(afsname, cell);
242 if (state->response.extra_data.data != NULL) {
243 state->response.length +=
244 strlen((const char *)state->response.extra_data.data)+1;
250 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
251 struct netr_SamInfo3 *info3,
252 const char *group_sid)
254 * Check whether a user belongs to a group or list of groups.
256 * @param mem_ctx talloc memory context.
257 * @param info3 user information, including group membership info.
258 * @param group_sid One or more groups , separated by commas.
260 * @return NT_STATUS_OK on success,
261 * NT_STATUS_LOGON_FAILURE if the user does not belong,
262 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
265 DOM_SID *require_membership_of_sid;
266 size_t num_require_membership_of_sid;
271 struct nt_user_token *token;
272 TALLOC_CTX *frame = NULL;
275 /* Parse the 'required group' SID */
277 if (!group_sid || !group_sid[0]) {
278 /* NO sid supplied, all users may access */
282 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
283 DEBUG(0, ("talloc failed\n"));
284 return NT_STATUS_NO_MEMORY;
287 num_require_membership_of_sid = 0;
288 require_membership_of_sid = NULL;
292 frame = talloc_stackframe();
293 while (next_token_talloc(frame, &p, &req_sid, ",")) {
294 if (!string_to_sid(&sid, req_sid)) {
295 DEBUG(0, ("check_info3_in_group: could not parse %s "
296 "as a SID!", req_sid));
298 return NT_STATUS_INVALID_PARAMETER;
301 status = add_sid_to_array(mem_ctx, &sid,
302 &require_membership_of_sid,
303 &num_require_membership_of_sid);
304 if (!NT_STATUS_IS_OK(status)) {
305 DEBUG(0, ("add_sid_to_array failed\n"));
313 status = sid_array_from_info3(mem_ctx, info3,
317 if (!NT_STATUS_IS_OK(status)) {
321 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
323 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
325 DEBUG(3, ("could not add aliases: %s\n",
330 debug_nt_user_token(DBGC_CLASS, 10, token);
332 for (i=0; i<num_require_membership_of_sid; i++) {
333 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
334 &require_membership_of_sid[i])));
335 if (nt_token_check_sid(&require_membership_of_sid[i],
337 DEBUG(10, ("Access ok\n"));
342 /* Do not distinguish this error from a wrong username/pw */
344 return NT_STATUS_LOGON_FAILURE;
347 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
348 const char *domain_name)
350 struct winbindd_domain *domain;
353 domain = find_domain_from_name_noinit(domain_name);
354 if (domain == NULL) {
355 DEBUG(3, ("Authentication for domain [%s] refused "
356 "as it is not a trusted domain\n",
362 if (is_myname(domain_name)) {
363 DEBUG(3, ("Authentication for domain %s (local domain "
364 "to this server) not supported at this "
365 "stage\n", domain_name));
369 /* we can auth against trusted domains */
370 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
371 domain = find_domain_from_name_noinit(domain_name);
372 if (domain == NULL) {
373 DEBUG(3, ("Authentication for domain [%s] skipped "
374 "as it is not a trusted domain\n",
381 return find_our_domain();
384 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
386 resp->data.auth.nt_status = NT_STATUS_V(result);
387 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
389 /* we might have given a more useful error above */
390 if (*resp->data.auth.error_string == '\0')
391 fstrcpy(resp->data.auth.error_string,
392 get_friendly_nt_error_msg(result));
393 resp->data.auth.pam_error = nt_status_to_pam(result);
396 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
397 struct winbindd_cli_state *state)
399 struct winbindd_methods *methods;
400 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
401 struct samr_DomInfo1 password_policy;
403 if ( !winbindd_can_contact_domain( domain ) ) {
404 DEBUG(5,("fillup_password_policy: No inbound trust to "
405 "contact domain %s\n", domain->name));
406 return NT_STATUS_NOT_SUPPORTED;
409 methods = domain->methods;
411 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
412 if (NT_STATUS_IS_ERR(status)) {
416 state->response.data.auth.policy.min_length_password =
417 password_policy.min_password_length;
418 state->response.data.auth.policy.password_history =
419 password_policy.password_history_length;
420 state->response.data.auth.policy.password_properties =
421 password_policy.password_properties;
422 state->response.data.auth.policy.expire =
423 nt_time_to_unix_abs((NTTIME *)&(password_policy.max_password_age));
424 state->response.data.auth.policy.min_passwordage =
425 nt_time_to_unix_abs((NTTIME *)&(password_policy.min_password_age));
430 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
432 uint16 *lockout_threshold)
434 struct winbindd_methods *methods;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 struct samr_DomInfo12 lockout_policy;
438 *lockout_threshold = 0;
440 methods = domain->methods;
442 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
443 if (NT_STATUS_IS_ERR(status)) {
447 *lockout_threshold = lockout_policy.lockout_threshold;
452 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
454 uint32 *password_properties)
456 struct winbindd_methods *methods;
457 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458 struct samr_DomInfo1 password_policy;
460 *password_properties = 0;
462 methods = domain->methods;
464 status = methods->password_policy(domain, mem_ctx, &password_policy);
465 if (NT_STATUS_IS_ERR(status)) {
469 *password_properties = password_policy.password_properties;
476 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
479 bool *internal_ccache)
481 /* accept FILE and WRFILE as krb5_cc_type from the client and then
482 * build the full ccname string based on the user's uid here -
485 const char *gen_cc = NULL;
487 *internal_ccache = True;
493 if (!type || type[0] == '\0') {
497 if (strequal(type, "FILE")) {
498 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
499 } else if (strequal(type, "WRFILE")) {
500 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
502 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
506 *internal_ccache = False;
510 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
513 if (gen_cc == NULL) {
514 DEBUG(0,("out of memory\n"));
518 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
523 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
525 const char *type = state->request.data.auth.krb5_cc_type;
527 state->response.data.auth.krb5ccname[0] = '\0';
529 if (type[0] == '\0') {
533 if (!strequal(type, "FILE") &&
534 !strequal(type, "WRFILE")) {
535 DEBUG(10,("won't return krbccname for a %s type ccache\n",
540 fstrcpy(state->response.data.auth.krb5ccname, cc);
545 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
549 uid = state->request.data.auth.uid;
552 DEBUG(1,("invalid uid: '%d'\n", uid));
558 /**********************************************************************
559 Authenticate a user with a clear text password using Kerberos and fill up
561 **********************************************************************/
563 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
564 struct winbindd_cli_state *state,
565 struct netr_SamInfo3 **info3)
568 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
569 krb5_error_code krb5_ret;
570 const char *cc = NULL;
571 const char *principal_s = NULL;
572 const char *service = NULL;
574 fstring name_domain, name_user;
575 time_t ticket_lifetime = 0;
576 time_t renewal_until = 0;
579 time_t time_offset = 0;
580 bool internal_ccache = True;
587 * prepare a krb5_cc_cache string for the user */
589 uid = get_uid_from_state(state);
591 DEBUG(0,("no valid uid\n"));
594 cc = generate_krb5_ccache(state->mem_ctx,
595 state->request.data.auth.krb5_cc_type,
596 state->request.data.auth.uid,
599 return NT_STATUS_NO_MEMORY;
604 * get kerberos properties */
606 if (domain->private_data) {
607 ads = (ADS_STRUCT *)domain->private_data;
608 time_offset = ads->auth.time_offset;
613 * do kerberos auth and setup ccache as the user */
615 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
617 realm = domain->alt_name;
620 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
621 if (principal_s == NULL) {
622 return NT_STATUS_NO_MEMORY;
625 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
626 if (service == NULL) {
627 return NT_STATUS_NO_MEMORY;
630 /* if this is a user ccache, we need to act as the user to let the krb5
631 * library handle the chown, etc. */
633 /************************ ENTERING NON-ROOT **********************/
635 if (!internal_ccache) {
636 set_effective_uid(uid);
637 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
640 result = kerberos_return_info3_from_pac(state->mem_ctx,
642 state->request.data.auth.pass,
649 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
651 if (!internal_ccache) {
652 gain_root_privilege();
655 /************************ RETURNED TO ROOT **********************/
657 if (!NT_STATUS_IS_OK(result)) {
661 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
664 /* if we had a user's ccache then return that string for the pam
667 if (!internal_ccache) {
669 setup_return_cc_name(state, cc);
671 result = add_ccache_to_list(principal_s,
674 state->request.data.auth.user,
682 if (!NT_STATUS_IS_OK(result)) {
683 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
688 /* need to delete the memory cred cache, it is not used anymore */
690 krb5_ret = ads_kdestroy(cc);
692 DEBUG(3,("winbindd_raw_kerberos_login: "
693 "could not destroy krb5 credential cache: "
694 "%s\n", error_message(krb5_ret)));
703 /* we could have created a new credential cache with a valid tgt in it
704 * but we werent able to get or verify the service ticket for this
705 * local host and therefor didn't get the PAC, we need to remove that
706 * cache entirely now */
708 krb5_ret = ads_kdestroy(cc);
710 DEBUG(3,("winbindd_raw_kerberos_login: "
711 "could not destroy krb5 credential cache: "
712 "%s\n", error_message(krb5_ret)));
715 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
716 DEBUG(3,("winbindd_raw_kerberos_login: "
717 "could not remove ccache for user %s\n",
718 state->request.data.auth.user));
723 return NT_STATUS_NOT_SUPPORTED;
724 #endif /* HAVE_KRB5 */
727 /****************************************************************
728 ****************************************************************/
730 static bool check_request_flags(uint32_t flags)
732 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
733 WBFLAG_PAM_INFO3_TEXT |
734 WBFLAG_PAM_INFO3_NDR;
736 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
737 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
738 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
739 !(flags & flags_edata) ) {
743 DEBUG(1,("check_request_flags: invalid request flags[0x%08X]\n",flags));
748 /****************************************************************
749 ****************************************************************/
751 static NTSTATUS append_data(struct winbindd_cli_state *state,
752 struct netr_SamInfo3 *info3,
753 const char *name_domain,
754 const char *name_user)
757 uint32_t flags = state->request.flags;
759 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
760 memcpy(state->response.data.auth.user_session_key,
762 sizeof(state->response.data.auth.user_session_key)
766 if (flags & WBFLAG_PAM_LMKEY) {
767 memcpy(state->response.data.auth.first_8_lm_hash,
768 info3->base.LMSessKey.key,
769 sizeof(state->response.data.auth.first_8_lm_hash)
773 if (flags & WBFLAG_PAM_INFO3_TEXT) {
774 result = append_info3_as_txt(state->mem_ctx, state, info3);
775 if (!NT_STATUS_IS_OK(result)) {
776 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
782 /* currently, anything from here on potentially overwrites extra_data. */
784 if (flags & WBFLAG_PAM_INFO3_NDR) {
785 result = append_info3_as_ndr(state->mem_ctx, state, info3);
786 if (!NT_STATUS_IS_OK(result)) {
787 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
793 if (flags & WBFLAG_PAM_UNIX_NAME) {
794 result = append_unix_username(state->mem_ctx, state, info3,
795 name_domain, name_user);
796 if (!NT_STATUS_IS_OK(result)) {
797 DEBUG(10,("Failed to append Unix Username: %s\n",
803 if (flags & WBFLAG_PAM_AFS_TOKEN) {
804 result = append_afs_token(state->mem_ctx, state, info3,
805 name_domain, name_user);
806 if (!NT_STATUS_IS_OK(result)) {
807 DEBUG(10,("Failed to append AFS token: %s\n",
816 void winbindd_pam_auth(struct winbindd_cli_state *state)
818 struct winbindd_domain *domain;
819 fstring name_domain, name_user;
822 /* Ensure null termination */
823 state->request.data.auth.user
824 [sizeof(state->request.data.auth.user)-1]='\0';
826 /* Ensure null termination */
827 state->request.data.auth.pass
828 [sizeof(state->request.data.auth.pass)-1]='\0';
830 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
831 state->request.data.auth.user));
833 if (!check_request_flags(state->request.flags)) {
834 result = NT_STATUS_INVALID_PARAMETER_MIX;
838 /* Parse domain and username */
840 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
842 if (!canonicalize_username(state->request.data.auth.user,
843 name_domain, name_user)) {
844 result = NT_STATUS_NO_SUCH_USER;
848 domain = find_auth_domain(state, name_domain);
850 if (domain == NULL) {
851 result = NT_STATUS_NO_SUCH_USER;
855 sendto_domain(state, domain);
858 set_auth_errors(&state->response, result);
859 DEBUG(5, ("Plain text authentication for %s returned %s "
861 state->request.data.auth.user,
862 state->response.data.auth.nt_status_string,
863 state->response.data.auth.pam_error));
864 request_error(state);
867 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
868 struct winbindd_cli_state *state,
869 struct netr_SamInfo3 **info3)
871 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
872 uint16 max_allowed_bad_attempts;
873 fstring name_domain, name_user;
875 enum lsa_SidType type;
876 uchar new_nt_pass[NT_HASH_LEN];
877 const uint8 *cached_nt_pass;
878 const uint8 *cached_salt;
879 struct netr_SamInfo3 *my_info3;
880 time_t kickoff_time, must_change_time;
881 bool password_good = False;
883 struct winbindd_tdc_domain *tdc_domain = NULL;
890 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
892 /* Parse domain and username */
894 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
897 if (!lookup_cached_name(state->mem_ctx,
902 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
903 return NT_STATUS_NO_SUCH_USER;
906 if (type != SID_NAME_USER) {
907 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
908 return NT_STATUS_LOGON_FAILURE;
911 result = winbindd_get_creds(domain,
917 if (!NT_STATUS_IS_OK(result)) {
918 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
924 E_md4hash(state->request.data.auth.pass, new_nt_pass);
926 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
927 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
929 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
933 /* In this case we didn't store the nt_hash itself,
934 but the MD5 combination of salt + nt_hash. */
935 uchar salted_hash[NT_HASH_LEN];
936 E_md5hash(cached_salt, new_nt_pass, salted_hash);
938 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
941 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
942 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
948 /* User *DOES* know the password, update logon_time and reset
951 my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
953 if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
954 return NT_STATUS_ACCOUNT_LOCKED_OUT;
957 if (my_info3->base.acct_flags & ACB_DISABLED) {
958 return NT_STATUS_ACCOUNT_DISABLED;
961 if (my_info3->base.acct_flags & ACB_WSTRUST) {
962 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
965 if (my_info3->base.acct_flags & ACB_SVRTRUST) {
966 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
969 if (my_info3->base.acct_flags & ACB_DOMTRUST) {
970 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
973 if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
974 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
975 my_info3->base.acct_flags));
976 return NT_STATUS_LOGON_FAILURE;
979 kickoff_time = nt_time_to_unix(my_info3->base.acct_expiry);
980 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
981 return NT_STATUS_ACCOUNT_EXPIRED;
984 must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
985 if (must_change_time != 0 && must_change_time < time(NULL)) {
986 /* we allow grace logons when the password has expired */
987 my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
988 /* return NT_STATUS_PASSWORD_EXPIRED; */
993 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
994 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
995 (tdc_domain->trust_type & NETR_TRUST_TYPE_UPLEVEL)) {
998 const char *cc = NULL;
1000 const char *principal_s = NULL;
1001 const char *service = NULL;
1002 bool internal_ccache = False;
1004 uid = get_uid_from_state(state);
1006 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
1007 return NT_STATUS_INVALID_PARAMETER;
1010 cc = generate_krb5_ccache(state->mem_ctx,
1011 state->request.data.auth.krb5_cc_type,
1012 state->request.data.auth.uid,
1015 return NT_STATUS_NO_MEMORY;
1018 realm = domain->alt_name;
1021 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
1022 if (principal_s == NULL) {
1023 return NT_STATUS_NO_MEMORY;
1026 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1027 if (service == NULL) {
1028 return NT_STATUS_NO_MEMORY;
1031 if (!internal_ccache) {
1033 setup_return_cc_name(state, cc);
1035 result = add_ccache_to_list(principal_s,
1038 state->request.data.auth.user,
1042 time(NULL) + lp_winbind_cache_time(),
1043 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1046 if (!NT_STATUS_IS_OK(result)) {
1047 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1048 "to add ccache to list: %s\n",
1049 nt_errstr(result)));
1053 #endif /* HAVE_KRB5 */
1055 /* FIXME: we possibly should handle logon hours as well (does xp when
1056 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1058 unix_to_nt_time(&my_info3->base.last_logon, time(NULL));
1059 my_info3->base.bad_password_count = 0;
1061 result = winbindd_update_creds_by_info3(domain,
1063 state->request.data.auth.user,
1064 state->request.data.auth.pass,
1066 if (!NT_STATUS_IS_OK(result)) {
1067 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1068 nt_errstr(result)));
1072 return NT_STATUS_OK;
1076 /* User does *NOT* know the correct password, modify info3 accordingly */
1078 /* failure of this is not critical */
1079 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1080 if (!NT_STATUS_IS_OK(result)) {
1081 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1082 "Won't be able to honour account lockout policies\n"));
1085 /* increase counter */
1086 my_info3->base.bad_password_count++;
1088 if (max_allowed_bad_attempts == 0) {
1093 if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
1095 uint32 password_properties;
1097 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1098 if (!NT_STATUS_IS_OK(result)) {
1099 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1102 if ((my_info3->base.rid != DOMAIN_USER_RID_ADMIN) ||
1103 (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
1104 my_info3->base.acct_flags |= ACB_AUTOLOCK;
1109 result = winbindd_update_creds_by_info3(domain,
1111 state->request.data.auth.user,
1115 if (!NT_STATUS_IS_OK(result)) {
1116 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1117 nt_errstr(result)));
1120 return NT_STATUS_LOGON_FAILURE;
1123 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1124 struct winbindd_cli_state *state,
1125 struct netr_SamInfo3 **info3)
1127 struct winbindd_domain *contact_domain;
1128 fstring name_domain, name_user;
1131 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1133 /* Parse domain and username */
1135 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1137 /* what domain should we contact? */
1140 if (!(contact_domain = find_domain_from_name(name_domain))) {
1141 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1142 state->request.data.auth.user, name_domain, name_user, name_domain));
1143 result = NT_STATUS_NO_SUCH_USER;
1148 if (is_myname(name_domain)) {
1149 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1150 result = NT_STATUS_NO_SUCH_USER;
1154 contact_domain = find_domain_from_name(name_domain);
1155 if (contact_domain == NULL) {
1156 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1157 state->request.data.auth.user, name_domain, name_user, name_domain));
1159 contact_domain = find_our_domain();
1163 if (contact_domain->initialized &&
1164 contact_domain->active_directory) {
1168 if (!contact_domain->initialized) {
1169 init_dc_connection(contact_domain);
1172 if (!contact_domain->active_directory) {
1173 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1174 return NT_STATUS_INVALID_LOGON_TYPE;
1177 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1182 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1183 struct winbindd_cli_state *state,
1184 struct netr_SamInfo3 **info3)
1187 struct rpc_pipe_client *netlogon_pipe;
1192 unsigned char local_lm_response[24];
1193 unsigned char local_nt_response[24];
1194 struct winbindd_domain *contact_domain;
1195 fstring name_domain, name_user;
1198 struct netr_SamInfo3 *my_info3 = NULL;
1202 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1204 /* Parse domain and username */
1206 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1208 /* do password magic */
1211 generate_random_buffer(chal, 8);
1212 if (lp_client_ntlmv2_auth()) {
1213 DATA_BLOB server_chal;
1214 DATA_BLOB names_blob;
1215 DATA_BLOB nt_response;
1216 DATA_BLOB lm_response;
1217 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1219 /* note that the 'workgroup' here is a best guess - we don't know
1220 the server's domain at this point. The 'server name' is also
1223 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1225 if (!SMBNTLMv2encrypt(name_user, name_domain,
1226 state->request.data.auth.pass,
1229 &lm_response, &nt_response, NULL)) {
1230 data_blob_free(&names_blob);
1231 data_blob_free(&server_chal);
1232 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1233 result = NT_STATUS_NO_MEMORY;
1236 data_blob_free(&names_blob);
1237 data_blob_free(&server_chal);
1238 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1239 lm_response.length);
1240 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1241 nt_response.length);
1242 data_blob_free(&lm_response);
1243 data_blob_free(&nt_response);
1246 if (lp_client_lanman_auth()
1247 && SMBencrypt(state->request.data.auth.pass,
1249 local_lm_response)) {
1250 lm_resp = data_blob_talloc(state->mem_ctx,
1252 sizeof(local_lm_response));
1254 lm_resp = data_blob_null;
1256 SMBNTencrypt(state->request.data.auth.pass,
1260 nt_resp = data_blob_talloc(state->mem_ctx,
1262 sizeof(local_nt_response));
1265 /* what domain should we contact? */
1268 if (!(contact_domain = find_domain_from_name(name_domain))) {
1269 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1270 state->request.data.auth.user, name_domain, name_user, name_domain));
1271 result = NT_STATUS_NO_SUCH_USER;
1276 if (is_myname(name_domain)) {
1277 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1278 result = NT_STATUS_NO_SUCH_USER;
1282 contact_domain = find_our_domain();
1285 /* check authentication loop */
1288 NTSTATUS (*logon_fn)(struct rpc_pipe_client *cli,
1289 TALLOC_CTX *mem_ctx,
1290 uint32 logon_parameters,
1292 const char *username,
1294 const char *workstation,
1295 const uint8 chal[8],
1296 DATA_BLOB lm_response,
1297 DATA_BLOB nt_response,
1298 struct netr_SamInfo3 **info3);
1300 ZERO_STRUCTP(my_info3);
1303 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1305 if (!NT_STATUS_IS_OK(result)) {
1306 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1310 logon_fn = contact_domain->can_do_samlogon_ex
1311 ? rpccli_netlogon_sam_network_logon_ex
1312 : rpccli_netlogon_sam_network_logon;
1314 result = logon_fn(netlogon_pipe,
1317 contact_domain->dcname, /* server name */
1318 name_user, /* user name */
1319 name_domain, /* target domain */
1320 global_myname(), /* workstation */
1327 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1328 && contact_domain->can_do_samlogon_ex) {
1329 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1330 "retrying with NetSamLogon\n"));
1331 contact_domain->can_do_samlogon_ex = False;
1336 /* We have to try a second time as cm_connect_netlogon
1337 might not yet have noticed that the DC has killed
1340 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1345 /* if we get access denied, a possible cause was that we had
1346 and open connection to the DC, but someone changed our
1347 machine account password out from underneath us using 'net
1348 rpc changetrustpw' */
1350 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1351 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1352 "ACCESS_DENIED. Maybe the trust account "
1353 "password was changed and we didn't know it. "
1354 "Killing connections to domain %s\n",
1356 invalidate_cm_connection(&contact_domain->conn);
1360 } while ( (attempts < 2) && retry );
1362 /* handle the case where a NT4 DC does not fill in the acct_flags in
1363 * the samlogon reply info3. When accurate info3 is required by the
1364 * caller, we look up the account flags ourselve - gd */
1366 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1367 NT_STATUS_IS_OK(result) && (my_info3->base.acct_flags == 0)) {
1369 struct rpc_pipe_client *samr_pipe;
1370 POLICY_HND samr_domain_handle, user_pol;
1371 union samr_UserInfo *info = NULL;
1372 NTSTATUS status_tmp;
1375 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1376 &samr_pipe, &samr_domain_handle);
1378 if (!NT_STATUS_IS_OK(status_tmp)) {
1379 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1380 nt_errstr(status_tmp)));
1384 status_tmp = rpccli_samr_OpenUser(samr_pipe, state->mem_ctx,
1385 &samr_domain_handle,
1386 MAXIMUM_ALLOWED_ACCESS,
1390 if (!NT_STATUS_IS_OK(status_tmp)) {
1391 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1392 nt_errstr(status_tmp)));
1396 status_tmp = rpccli_samr_QueryUserInfo(samr_pipe, state->mem_ctx,
1401 if (!NT_STATUS_IS_OK(status_tmp)) {
1402 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1403 nt_errstr(status_tmp)));
1404 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1408 acct_flags = info->info16.acct_flags;
1410 if (acct_flags == 0) {
1411 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1415 my_info3->base.acct_flags = acct_flags;
1417 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1419 rpccli_samr_Close(samr_pipe, state->mem_ctx, &user_pol);
1427 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1428 struct winbindd_cli_state *state)
1430 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1431 NTSTATUS krb5_result = NT_STATUS_OK;
1432 fstring name_domain, name_user;
1433 struct netr_SamInfo3 *info3 = NULL;
1435 /* Ensure null termination */
1436 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1438 /* Ensure null termination */
1439 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1441 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1442 state->request.data.auth.user));
1444 if (!check_request_flags(state->request.flags)) {
1445 result = NT_STATUS_INVALID_PARAMETER_MIX;
1449 /* Parse domain and username */
1451 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1453 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1455 if (domain->online == False) {
1456 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1457 if (domain->startup) {
1458 /* Logons are very important to users. If we're offline and
1459 we get a request within the first 30 seconds of startup,
1460 try very hard to find a DC and go online. */
1462 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1463 "request in startup mode.\n", domain->name ));
1465 winbindd_flush_negative_conn_cache(domain);
1466 result = init_dc_connection(domain);
1470 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1472 /* Check for Kerberos authentication */
1473 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1475 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1476 /* save for later */
1477 krb5_result = result;
1480 if (NT_STATUS_IS_OK(result)) {
1481 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1482 goto process_result;
1484 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1487 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1488 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1489 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1490 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1491 set_domain_offline( domain );
1495 /* there are quite some NT_STATUS errors where there is no
1496 * point in retrying with a samlogon, we explictly have to take
1497 * care not to increase the bad logon counter on the DC */
1499 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1500 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1501 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1502 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1503 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1504 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1505 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1506 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1507 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1508 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1509 goto process_result;
1512 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1513 DEBUG(3,("falling back to samlogon\n"));
1521 /* Check for Samlogon authentication */
1522 if (domain->online) {
1523 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1525 if (NT_STATUS_IS_OK(result)) {
1526 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1527 /* add the Krb5 err if we have one */
1528 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1529 info3->base.user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1531 goto process_result;
1534 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1535 nt_errstr(result)));
1537 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1538 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1539 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1541 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1542 set_domain_offline( domain );
1546 if (domain->online) {
1547 /* We're still online - fail. */
1553 /* Check for Cached logons */
1554 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1555 lp_winbind_offline_logon()) {
1557 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1559 if (NT_STATUS_IS_OK(result)) {
1560 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1561 goto process_result;
1563 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1570 if (NT_STATUS_IS_OK(result)) {
1574 /* In all codepaths where result == NT_STATUS_OK info3 must have
1575 been initialized. */
1577 result = NT_STATUS_INTERNAL_ERROR;
1581 netsamlogon_cache_store(name_user, info3);
1582 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1584 /* save name_to_sid info as early as possible (only if
1585 this is our primary domain so we don't invalidate
1586 the cache entry by storing the seq_num for the wrong
1588 if ( domain->primary ) {
1589 sid_compose(&user_sid, info3->base.domain_sid,
1591 cache_name2sid(domain, name_domain, name_user,
1592 SID_NAME_USER, &user_sid);
1595 /* Check if the user is in the right group */
1597 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1598 state->request.data.auth.require_membership_of_sid))) {
1599 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1600 state->request.data.auth.user,
1601 state->request.data.auth.require_membership_of_sid));
1605 result = append_data(state, info3, name_domain, name_user);
1606 if (!NT_STATUS_IS_OK(result)) {
1610 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1612 /* Store in-memory creds for single-signon using ntlm_auth. */
1613 result = winbindd_add_memory_creds(state->request.data.auth.user,
1614 get_uid_from_state(state),
1615 state->request.data.auth.pass);
1617 if (!NT_STATUS_IS_OK(result)) {
1618 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1622 if (lp_winbind_offline_logon()) {
1623 result = winbindd_store_creds(domain,
1625 state->request.data.auth.user,
1626 state->request.data.auth.pass,
1628 if (!NT_STATUS_IS_OK(result)) {
1630 /* Release refcount. */
1631 winbindd_delete_memory_creds(state->request.data.auth.user);
1633 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1640 if (state->request.flags & WBFLAG_PAM_GET_PWD_POLICY) {
1641 struct winbindd_domain *our_domain = find_our_domain();
1643 /* This is not entirely correct I believe, but it is
1644 consistent. Only apply the password policy settings
1645 too warn users for our own domain. Cannot obtain these
1646 from trusted DCs all the time so don't do it at all.
1649 result = NT_STATUS_NOT_SUPPORTED;
1650 if (our_domain == domain ) {
1651 result = fillup_password_policy(our_domain, state);
1654 if (!NT_STATUS_IS_OK(result)
1655 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1657 DEBUG(10,("Failed to get password policies for domain %s: %s\n",
1658 domain->name, nt_errstr(result)));
1663 result = NT_STATUS_OK;
1667 /* give us a more useful (more correct?) error code */
1668 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1669 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1670 result = NT_STATUS_NO_LOGON_SERVERS;
1673 state->response.data.auth.nt_status = NT_STATUS_V(result);
1674 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1676 /* we might have given a more useful error above */
1677 if (!*state->response.data.auth.error_string)
1678 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1679 state->response.data.auth.pam_error = nt_status_to_pam(result);
1681 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1682 state->request.data.auth.user,
1683 state->response.data.auth.nt_status_string,
1684 state->response.data.auth.pam_error));
1686 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1690 /**********************************************************************
1691 Challenge Response Authentication Protocol
1692 **********************************************************************/
1694 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1696 struct winbindd_domain *domain = NULL;
1697 const char *domain_name = NULL;
1700 if (!check_request_flags(state->request.flags)) {
1701 result = NT_STATUS_INVALID_PARAMETER_MIX;
1705 if (!state->privileged) {
1706 char *error_string = NULL;
1707 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1709 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1710 "on %s are set correctly.\n",
1711 get_winbind_priv_pipe_dir()));
1712 /* send a better message than ACCESS_DENIED */
1713 error_string = talloc_asprintf(state->mem_ctx,
1714 "winbind client not authorized "
1715 "to use winbindd_pam_auth_crap."
1716 " Ensure permissions on %s "
1717 "are set correctly.",
1718 get_winbind_priv_pipe_dir());
1719 fstrcpy(state->response.data.auth.error_string, error_string);
1720 result = NT_STATUS_ACCESS_DENIED;
1724 /* Ensure null termination */
1725 state->request.data.auth_crap.user
1726 [sizeof(state->request.data.auth_crap.user)-1]=0;
1727 state->request.data.auth_crap.domain
1728 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1730 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1731 (unsigned long)state->pid,
1732 state->request.data.auth_crap.domain,
1733 state->request.data.auth_crap.user));
1735 if (*state->request.data.auth_crap.domain != '\0') {
1736 domain_name = state->request.data.auth_crap.domain;
1737 } else if (lp_winbind_use_default_domain()) {
1738 domain_name = lp_workgroup();
1741 if (domain_name != NULL)
1742 domain = find_auth_domain(state, domain_name);
1744 if (domain != NULL) {
1745 sendto_domain(state, domain);
1749 result = NT_STATUS_NO_SUCH_USER;
1752 set_auth_errors(&state->response, result);
1753 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1754 state->request.data.auth_crap.domain,
1755 state->request.data.auth_crap.user,
1756 state->response.data.auth.nt_status_string,
1757 state->response.data.auth.pam_error));
1758 request_error(state);
1763 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1764 struct winbindd_cli_state *state)
1767 struct netr_SamInfo3 *info3 = NULL;
1768 struct rpc_pipe_client *netlogon_pipe;
1769 const char *name_user = NULL;
1770 const char *name_domain = NULL;
1771 const char *workstation;
1772 struct winbindd_domain *contact_domain;
1776 DATA_BLOB lm_resp, nt_resp;
1778 /* This is child-only, so no check for privileged access is needed
1781 /* Ensure null termination */
1782 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1783 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1785 if (!check_request_flags(state->request.flags)) {
1786 result = NT_STATUS_INVALID_PARAMETER_MIX;
1790 name_user = state->request.data.auth_crap.user;
1792 if (*state->request.data.auth_crap.domain) {
1793 name_domain = state->request.data.auth_crap.domain;
1794 } else if (lp_winbind_use_default_domain()) {
1795 name_domain = lp_workgroup();
1797 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1799 result = NT_STATUS_NO_SUCH_USER;
1803 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1804 name_domain, name_user));
1806 if (*state->request.data.auth_crap.workstation) {
1807 workstation = state->request.data.auth_crap.workstation;
1809 workstation = global_myname();
1812 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1813 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1814 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1815 state->request.data.auth_crap.lm_resp_len,
1816 state->request.data.auth_crap.nt_resp_len));
1817 result = NT_STATUS_INVALID_PARAMETER;
1821 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1822 state->request.data.auth_crap.lm_resp_len);
1823 nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1824 state->request.data.auth_crap.nt_resp_len);
1826 /* what domain should we contact? */
1829 if (!(contact_domain = find_domain_from_name(name_domain))) {
1830 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1831 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1832 result = NT_STATUS_NO_SUCH_USER;
1836 if (is_myname(name_domain)) {
1837 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1838 result = NT_STATUS_NO_SUCH_USER;
1841 contact_domain = find_our_domain();
1845 NTSTATUS (*logon_fn)(struct rpc_pipe_client *cli,
1846 TALLOC_CTX *mem_ctx,
1847 uint32 logon_parameters,
1849 const char *username,
1851 const char *workstation,
1852 const uint8 chal[8],
1853 DATA_BLOB lm_response,
1854 DATA_BLOB nt_response,
1855 struct netr_SamInfo3 **info3);
1859 netlogon_pipe = NULL;
1860 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1862 if (!NT_STATUS_IS_OK(result)) {
1863 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1864 nt_errstr(result)));
1868 logon_fn = contact_domain->can_do_samlogon_ex
1869 ? rpccli_netlogon_sam_network_logon_ex
1870 : rpccli_netlogon_sam_network_logon;
1872 result = logon_fn(netlogon_pipe,
1874 state->request.data.auth_crap.logon_parameters,
1875 contact_domain->dcname,
1878 /* Bug #3248 - found by Stefan Burkei. */
1879 workstation, /* We carefully set this above so use it... */
1880 state->request.data.auth_crap.chal,
1885 if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR)
1886 && contact_domain->can_do_samlogon_ex) {
1887 DEBUG(3, ("Got a DC that can not do NetSamLogonEx, "
1888 "retrying with NetSamLogon\n"));
1889 contact_domain->can_do_samlogon_ex = False;
1896 /* We have to try a second time as cm_connect_netlogon
1897 might not yet have noticed that the DC has killed
1900 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1905 /* if we get access denied, a possible cause was that we had and open
1906 connection to the DC, but someone changed our machine account password
1907 out from underneath us using 'net rpc changetrustpw' */
1909 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1910 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1911 "ACCESS_DENIED. Maybe the trust account "
1912 "password was changed and we didn't know it. "
1913 "Killing connections to domain %s\n",
1915 invalidate_cm_connection(&contact_domain->conn);
1919 } while ( (attempts < 2) && retry );
1921 if (NT_STATUS_IS_OK(result)) {
1923 netsamlogon_cache_store(name_user, info3);
1924 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1926 /* Check if the user is in the right group */
1928 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1929 state->request.data.auth_crap.require_membership_of_sid))) {
1930 DEBUG(3, ("User %s is not in the required group (%s), so "
1931 "crap authentication is rejected\n",
1932 state->request.data.auth_crap.user,
1933 state->request.data.auth_crap.require_membership_of_sid));
1937 result = append_data(state, info3, name_domain, name_user);
1938 if (!NT_STATUS_IS_OK(result)) {
1945 /* give us a more useful (more correct?) error code */
1946 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1947 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1948 result = NT_STATUS_NO_LOGON_SERVERS;
1951 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1952 result = nt_status_squash(result);
1955 state->response.data.auth.nt_status = NT_STATUS_V(result);
1956 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1958 /* we might have given a more useful error above */
1959 if (!*state->response.data.auth.error_string) {
1960 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1962 state->response.data.auth.pam_error = nt_status_to_pam(result);
1964 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1965 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1968 state->response.data.auth.nt_status_string,
1969 state->response.data.auth.pam_error));
1971 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1974 /* Change a user password */
1976 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1978 fstring domain, user;
1979 struct winbindd_domain *contact_domain;
1981 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1982 state->request.data.chauthtok.user));
1986 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1988 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
1989 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1990 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
1992 state->request.data.auth.user,
1993 state->response.data.auth.nt_status_string,
1994 state->response.data.auth.pam_error));
1995 request_error(state);
1999 contact_domain = find_domain_from_name(domain);
2000 if (!contact_domain) {
2001 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2002 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
2003 state->request.data.chauthtok.user, domain, user, domain));
2004 request_error(state);
2008 sendto_domain(state, contact_domain);
2011 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
2012 struct winbindd_cli_state *state)
2015 char *newpass = NULL;
2017 struct rpc_pipe_client *cli;
2018 bool got_info = False;
2019 struct samr_DomInfo1 *info = NULL;
2020 struct samr_ChangeReject *reject = NULL;
2021 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2022 fstring domain, user;
2024 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
2025 state->request.data.auth.user));
2027 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
2031 /* Change password */
2033 oldpass = state->request.data.chauthtok.oldpass;
2034 newpass = state->request.data.chauthtok.newpass;
2036 /* Initialize reject reason */
2037 state->response.data.auth.reject_reason = Undefined;
2039 /* Get sam handle */
2041 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
2043 if (!NT_STATUS_IS_OK(result)) {
2044 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2048 result = rpccli_samr_chgpasswd3(cli, state->mem_ctx,
2055 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
2057 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
2058 state->response.data.auth.policy.min_length_password =
2059 info->min_password_length;
2060 state->response.data.auth.policy.password_history =
2061 info->password_history_length;
2062 state->response.data.auth.policy.password_properties =
2063 info->password_properties;
2064 state->response.data.auth.policy.expire =
2065 nt_time_to_unix_abs((NTTIME *)&info->max_password_age);
2066 state->response.data.auth.policy.min_passwordage =
2067 nt_time_to_unix_abs((NTTIME *)&info->min_password_age);
2069 state->response.data.auth.reject_reason =
2075 /* only fallback when the chgpasswd3 call is not supported */
2076 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
2077 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
2078 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
2080 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
2081 nt_errstr(result)));
2083 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
2085 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
2086 Map to the same status code as Windows 2003. */
2088 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2089 result = NT_STATUS_PASSWORD_RESTRICTION;
2095 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2097 /* Update the single sign-on memory creds. */
2098 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2101 if (!NT_STATUS_IS_OK(result)) {
2102 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2103 goto process_result;
2106 if (lp_winbind_offline_logon()) {
2107 result = winbindd_update_creds_by_name(contact_domain,
2108 state->mem_ctx, user,
2110 if (!NT_STATUS_IS_OK(result)) {
2111 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2112 goto process_result;
2117 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2119 NTSTATUS policy_ret;
2121 policy_ret = fillup_password_policy(contact_domain, state);
2123 /* failure of this is non critical, it will just provide no
2124 * additional information to the client why the change has
2125 * failed - Guenther */
2127 if (!NT_STATUS_IS_OK(policy_ret)) {
2128 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2129 goto process_result;
2135 state->response.data.auth.nt_status = NT_STATUS_V(result);
2136 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2137 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2138 state->response.data.auth.pam_error = nt_status_to_pam(result);
2140 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2141 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2144 state->response.data.auth.nt_status_string,
2145 state->response.data.auth.pam_error));
2147 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2150 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2152 struct winbindd_domain *domain;
2153 fstring name_domain, user;
2154 uid_t caller_uid = (uid_t)-1;
2155 uid_t request_uid = state->request.data.logoff.uid;
2157 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2158 state->request.data.logoff.user));
2160 /* Ensure null termination */
2161 state->request.data.logoff.user
2162 [sizeof(state->request.data.logoff.user)-1]='\0';
2164 state->request.data.logoff.krb5ccname
2165 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2167 if (request_uid == (gid_t)-1) {
2171 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2175 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2179 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2180 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2185 switch (caller_uid) {
2189 /* root must be able to logoff any user - gd */
2190 state->request.data.logoff.uid = request_uid;
2193 if (caller_uid != request_uid) {
2194 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2197 state->request.data.logoff.uid = caller_uid;
2201 sendto_domain(state, domain);
2205 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2206 DEBUG(5, ("Pam Logoff for %s returned %s "
2208 state->request.data.logoff.user,
2209 state->response.data.auth.nt_status_string,
2210 state->response.data.auth.pam_error));
2211 request_error(state);
2215 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2216 struct winbindd_cli_state *state)
2218 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2220 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2221 state->request.data.logoff.user));
2223 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2224 result = NT_STATUS_OK;
2225 goto process_result;
2228 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2229 result = NT_STATUS_OK;
2230 goto process_result;
2235 if (state->request.data.logoff.uid < 0) {
2236 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2237 goto process_result;
2240 /* what we need here is to find the corresponding krb5 ccache name *we*
2241 * created for a given username and destroy it */
2243 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2244 result = NT_STATUS_OK;
2245 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2246 goto process_result;
2249 if (!ccache_entry_identical(state->request.data.logoff.user,
2250 state->request.data.logoff.uid,
2251 state->request.data.logoff.krb5ccname)) {
2252 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2253 goto process_result;
2256 result = remove_ccache(state->request.data.logoff.user);
2257 if (!NT_STATUS_IS_OK(result)) {
2258 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2259 nt_errstr(result)));
2260 goto process_result;
2264 result = NT_STATUS_NOT_SUPPORTED;
2269 winbindd_delete_memory_creds(state->request.data.logoff.user);
2271 state->response.data.auth.nt_status = NT_STATUS_V(result);
2272 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2273 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2274 state->response.data.auth.pam_error = nt_status_to_pam(result);
2276 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2279 /* Change user password with auth crap*/
2281 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2283 struct winbindd_domain *domain = NULL;
2284 const char *domain_name = NULL;
2286 /* Ensure null termination */
2287 state->request.data.chng_pswd_auth_crap.user[
2288 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2289 state->request.data.chng_pswd_auth_crap.domain[
2290 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2292 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2293 (unsigned long)state->pid,
2294 state->request.data.chng_pswd_auth_crap.domain,
2295 state->request.data.chng_pswd_auth_crap.user));
2297 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2298 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2299 } else if (lp_winbind_use_default_domain()) {
2300 domain_name = lp_workgroup();
2303 if (domain_name != NULL)
2304 domain = find_domain_from_name(domain_name);
2306 if (domain != NULL) {
2307 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2308 "%s\n", (unsigned long)state->pid,domain->name));
2309 sendto_domain(state, domain);
2313 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2314 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2315 state->request.data.chng_pswd_auth_crap.domain,
2316 state->request.data.chng_pswd_auth_crap.user,
2317 state->response.data.auth.nt_status_string,
2318 state->response.data.auth.pam_error));
2319 request_error(state);
2323 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2326 DATA_BLOB new_nt_password;
2327 DATA_BLOB old_nt_hash_enc;
2328 DATA_BLOB new_lm_password;
2329 DATA_BLOB old_lm_hash_enc;
2330 fstring domain,user;
2332 struct winbindd_domain *contact_domain = domainSt;
2333 struct rpc_pipe_client *cli;
2335 /* Ensure null termination */
2336 state->request.data.chng_pswd_auth_crap.user[
2337 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2338 state->request.data.chng_pswd_auth_crap.domain[
2339 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2343 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2344 (unsigned long)state->pid,
2345 state->request.data.chng_pswd_auth_crap.domain,
2346 state->request.data.chng_pswd_auth_crap.user));
2348 if (lp_winbind_offline_logon()) {
2349 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2350 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2351 result = NT_STATUS_ACCESS_DENIED;
2355 if (*state->request.data.chng_pswd_auth_crap.domain) {
2356 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2358 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2362 DEBUG(3,("no domain specified with username (%s) - "
2364 state->request.data.chng_pswd_auth_crap.user));
2365 result = NT_STATUS_NO_SUCH_USER;
2370 if (!*domain && lp_winbind_use_default_domain()) {
2371 fstrcpy(domain,(char *)lp_workgroup());
2375 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2378 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2379 (unsigned long)state->pid, domain, user));
2381 /* Change password */
2382 new_nt_password = data_blob_talloc(
2384 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2385 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2387 old_nt_hash_enc = data_blob_talloc(
2389 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2390 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2392 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2393 new_lm_password = data_blob_talloc(
2395 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2396 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2398 old_lm_hash_enc = data_blob_talloc(
2400 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2401 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2403 new_lm_password.length = 0;
2404 old_lm_hash_enc.length = 0;
2407 /* Get sam handle */
2409 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2410 if (!NT_STATUS_IS_OK(result)) {
2411 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2415 result = rpccli_samr_chng_pswd_auth_crap(
2416 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2417 new_lm_password, old_lm_hash_enc);
2420 state->response.data.auth.nt_status = NT_STATUS_V(result);
2421 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2422 fstrcpy(state->response.data.auth.error_string,
2423 get_friendly_nt_error_msg(result));
2424 state->response.data.auth.pam_error = nt_status_to_pam(result);
2426 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2427 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2429 state->response.data.auth.nt_status_string,
2430 state->response.data.auth.pam_error));
2432 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;