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 static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
31 struct winbindd_cli_state *state,
32 NET_USER_INFO_3 *info3)
36 state->response.data.auth.info3.logon_time =
37 nt_time_to_unix(info3->logon_time);
38 state->response.data.auth.info3.logoff_time =
39 nt_time_to_unix(info3->logoff_time);
40 state->response.data.auth.info3.kickoff_time =
41 nt_time_to_unix(info3->kickoff_time);
42 state->response.data.auth.info3.pass_last_set_time =
43 nt_time_to_unix(info3->pass_last_set_time);
44 state->response.data.auth.info3.pass_can_change_time =
45 nt_time_to_unix(info3->pass_can_change_time);
46 state->response.data.auth.info3.pass_must_change_time =
47 nt_time_to_unix(info3->pass_must_change_time);
49 state->response.data.auth.info3.logon_count = info3->logon_count;
50 state->response.data.auth.info3.bad_pw_count = info3->bad_pw_count;
52 state->response.data.auth.info3.user_rid = info3->user_rid;
53 state->response.data.auth.info3.group_rid = info3->group_rid;
54 sid_to_string(str_sid, &(info3->dom_sid.sid));
55 fstrcpy(state->response.data.auth.info3.dom_sid, str_sid);
57 state->response.data.auth.info3.num_groups = info3->num_groups;
58 state->response.data.auth.info3.user_flgs = info3->user_flgs;
60 state->response.data.auth.info3.acct_flags = info3->acct_flags;
61 state->response.data.auth.info3.num_other_sids = info3->num_other_sids;
63 unistr2_to_ascii(state->response.data.auth.info3.user_name,
64 &info3->uni_user_name,
65 sizeof(state->response.data.auth.info3.user_name));
66 unistr2_to_ascii(state->response.data.auth.info3.full_name,
67 &info3->uni_full_name,
68 sizeof(state->response.data.auth.info3.full_name));
69 unistr2_to_ascii(state->response.data.auth.info3.logon_script,
70 &info3->uni_logon_script,
71 sizeof(state->response.data.auth.info3.logon_script));
72 unistr2_to_ascii(state->response.data.auth.info3.profile_path,
73 &info3->uni_profile_path,
74 sizeof(state->response.data.auth.info3.profile_path));
75 unistr2_to_ascii(state->response.data.auth.info3.home_dir,
77 sizeof(state->response.data.auth.info3.home_dir));
78 unistr2_to_ascii(state->response.data.auth.info3.dir_drive,
79 &info3->uni_dir_drive,
80 sizeof(state->response.data.auth.info3.dir_drive));
82 unistr2_to_ascii(state->response.data.auth.info3.logon_srv,
83 &info3->uni_logon_srv,
84 sizeof(state->response.data.auth.info3.logon_srv));
85 unistr2_to_ascii(state->response.data.auth.info3.logon_dom,
86 &info3->uni_logon_dom,
87 sizeof(state->response.data.auth.info3.logon_dom));
92 static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
93 struct winbindd_cli_state *state,
94 NET_USER_INFO_3 *info3)
98 if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
99 return NT_STATUS_NO_MEMORY;
101 if (!net_io_user_info3("", info3, &ps, 1, 3, False)) {
103 return NT_STATUS_UNSUCCESSFUL;
106 size = prs_data_size(&ps);
107 SAFE_FREE(state->response.extra_data.data);
108 state->response.extra_data.data = SMB_MALLOC(size);
109 if (!state->response.extra_data.data) {
111 return NT_STATUS_NO_MEMORY;
113 memset( state->response.extra_data.data, '\0', size );
114 prs_copy_all_data_out((char *)state->response.extra_data.data, &ps);
115 state->response.length += size;
120 static NTSTATUS append_unix_username(TALLOC_CTX *mem_ctx,
121 struct winbindd_cli_state *state,
122 const NET_USER_INFO_3 *info3,
123 const char *name_domain,
124 const char *name_user)
126 /* We've been asked to return the unix username, per
127 'winbind use default domain' settings and the like */
129 fstring username_out;
130 const char *nt_username, *nt_domain;
132 if (!(nt_domain = unistr2_to_ascii_talloc(mem_ctx,
133 &info3->uni_logon_dom))) {
134 /* If the server didn't give us one, just use the one
136 nt_domain = name_domain;
139 if (!(nt_username = unistr2_to_ascii_talloc(mem_ctx,
140 &info3->uni_user_name))) {
141 /* If the server didn't give us one, just use the one
143 nt_username = name_user;
146 fill_domain_username(username_out, nt_domain, nt_username,
149 DEBUG(5,("Setting unix username to [%s]\n", username_out));
151 SAFE_FREE(state->response.extra_data.data);
152 state->response.extra_data.data = SMB_STRDUP(username_out);
153 if (!state->response.extra_data.data) {
154 return NT_STATUS_NO_MEMORY;
156 state->response.length +=
157 strlen((const char *)state->response.extra_data.data)+1;
162 static NTSTATUS append_afs_token(TALLOC_CTX *mem_ctx,
163 struct winbindd_cli_state *state,
164 const NET_USER_INFO_3 *info3,
165 const char *name_domain,
166 const char *name_user)
168 char *afsname = NULL;
171 afsname = talloc_strdup(mem_ctx, lp_afs_username_map());
172 if (afsname == NULL) {
173 return NT_STATUS_NO_MEMORY;
176 afsname = talloc_string_sub(mem_ctx,
177 lp_afs_username_map(),
179 afsname = talloc_string_sub(mem_ctx, afsname,
181 afsname = talloc_string_sub(mem_ctx, afsname,
188 sid_copy(&user_sid, &info3->dom_sid.sid);
189 sid_append_rid(&user_sid, info3->user_rid);
190 sid_to_string(sidstr, &user_sid);
191 afsname = talloc_string_sub(mem_ctx, afsname,
195 if (afsname == NULL) {
196 return NT_STATUS_NO_MEMORY;
201 DEBUG(10, ("Generating token for user %s\n", afsname));
203 cell = strchr(afsname, '@');
206 return NT_STATUS_NO_MEMORY;
212 /* Append an AFS token string */
213 SAFE_FREE(state->response.extra_data.data);
214 state->response.extra_data.data =
215 afs_createtoken_str(afsname, cell);
217 if (state->response.extra_data.data != NULL) {
218 state->response.length +=
219 strlen((const char *)state->response.extra_data.data)+1;
225 static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
226 NET_USER_INFO_3 *info3,
227 const char *group_sid)
229 * Check whether a user belongs to a group or list of groups.
231 * @param mem_ctx talloc memory context.
232 * @param info3 user information, including group membership info.
233 * @param group_sid One or more groups , separated by commas.
235 * @return NT_STATUS_OK on success,
236 * NT_STATUS_LOGON_FAILURE if the user does not belong,
237 * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
240 DOM_SID *require_membership_of_sid;
241 size_t num_require_membership_of_sid;
246 struct nt_user_token *token;
247 TALLOC_CTX *frame = NULL;
250 /* Parse the 'required group' SID */
252 if (!group_sid || !group_sid[0]) {
253 /* NO sid supplied, all users may access */
257 if (!(token = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) {
258 DEBUG(0, ("talloc failed\n"));
259 return NT_STATUS_NO_MEMORY;
262 num_require_membership_of_sid = 0;
263 require_membership_of_sid = NULL;
267 frame = talloc_stackframe();
268 while (next_token_talloc(frame, &p, &req_sid, ",")) {
269 if (!string_to_sid(&sid, req_sid)) {
270 DEBUG(0, ("check_info3_in_group: could not parse %s "
271 "as a SID!", req_sid));
273 return NT_STATUS_INVALID_PARAMETER;
276 if (!add_sid_to_array(mem_ctx, &sid,
277 &require_membership_of_sid,
278 &num_require_membership_of_sid)) {
279 DEBUG(0, ("add_sid_to_array failed\n"));
281 return NT_STATUS_NO_MEMORY;
287 status = sid_array_from_info3(mem_ctx, info3,
291 if (!NT_STATUS_IS_OK(status)) {
295 if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
297 || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
299 DEBUG(3, ("could not add aliases: %s\n",
304 debug_nt_user_token(DBGC_CLASS, 10, token);
306 for (i=0; i<num_require_membership_of_sid; i++) {
307 DEBUG(10, ("Checking SID %s\n", sid_string_dbg(
308 &require_membership_of_sid[i])));
309 if (nt_token_check_sid(&require_membership_of_sid[i],
311 DEBUG(10, ("Access ok\n"));
316 /* Do not distinguish this error from a wrong username/pw */
318 return NT_STATUS_LOGON_FAILURE;
321 struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
322 const char *domain_name)
324 struct winbindd_domain *domain;
327 domain = find_domain_from_name_noinit(domain_name);
328 if (domain == NULL) {
329 DEBUG(3, ("Authentication for domain [%s] refused"
330 "as it is not a trusted domain\n",
336 if (is_myname(domain_name)) {
337 DEBUG(3, ("Authentication for domain %s (local domain "
338 "to this server) not supported at this "
339 "stage\n", domain_name));
343 /* we can auth against trusted domains */
344 if (state->request.flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
345 domain = find_domain_from_name_noinit(domain_name);
346 if (domain == NULL) {
347 DEBUG(3, ("Authentication for domain [%s] skipped "
348 "as it is not a trusted domain\n",
355 return find_our_domain();
358 static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
360 resp->data.auth.nt_status = NT_STATUS_V(result);
361 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
363 /* we might have given a more useful error above */
364 if (*resp->data.auth.error_string == '\0')
365 fstrcpy(resp->data.auth.error_string,
366 get_friendly_nt_error_msg(result));
367 resp->data.auth.pam_error = nt_status_to_pam(result);
370 static NTSTATUS fillup_password_policy(struct winbindd_domain *domain,
371 struct winbindd_cli_state *state)
373 struct winbindd_methods *methods;
374 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
375 SAM_UNK_INFO_1 password_policy;
377 if ( !winbindd_can_contact_domain( domain ) ) {
378 DEBUG(5,("fillup_password_policy: No inbound trust to "
379 "contact domain %s\n", domain->name));
380 return NT_STATUS_NOT_SUPPORTED;
383 methods = domain->methods;
385 status = methods->password_policy(domain, state->mem_ctx, &password_policy);
386 if (NT_STATUS_IS_ERR(status)) {
390 state->response.data.auth.policy.min_length_password =
391 password_policy.min_length_password;
392 state->response.data.auth.policy.password_history =
393 password_policy.password_history;
394 state->response.data.auth.policy.password_properties =
395 password_policy.password_properties;
396 state->response.data.auth.policy.expire =
397 nt_time_to_unix_abs(&(password_policy.expire));
398 state->response.data.auth.policy.min_passwordage =
399 nt_time_to_unix_abs(&(password_policy.min_passwordage));
404 static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
406 uint16 *max_allowed_bad_attempts)
408 struct winbindd_methods *methods;
409 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
410 SAM_UNK_INFO_12 lockout_policy;
412 *max_allowed_bad_attempts = 0;
414 methods = domain->methods;
416 status = methods->lockout_policy(domain, mem_ctx, &lockout_policy);
417 if (NT_STATUS_IS_ERR(status)) {
421 *max_allowed_bad_attempts = lockout_policy.bad_attempt_lockout;
426 static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
428 uint32 *password_properties)
430 struct winbindd_methods *methods;
431 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
432 SAM_UNK_INFO_1 password_policy;
434 *password_properties = 0;
436 methods = domain->methods;
438 status = methods->password_policy(domain, mem_ctx, &password_policy);
439 if (NT_STATUS_IS_ERR(status)) {
443 *password_properties = password_policy.password_properties;
450 static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
453 bool *internal_ccache)
455 /* accept FILE and WRFILE as krb5_cc_type from the client and then
456 * build the full ccname string based on the user's uid here -
459 const char *gen_cc = NULL;
461 *internal_ccache = True;
467 if (!type || type[0] == '\0') {
471 if (strequal(type, "FILE")) {
472 gen_cc = talloc_asprintf(mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
473 } else if (strequal(type, "WRFILE")) {
474 gen_cc = talloc_asprintf(mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
476 DEBUG(10,("we don't allow to set a %s type ccache\n", type));
480 *internal_ccache = False;
484 gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
487 if (gen_cc == NULL) {
488 DEBUG(0,("out of memory\n"));
492 DEBUG(10,("using ccache: %s %s\n", gen_cc, *internal_ccache ? "(internal)":""));
497 static void setup_return_cc_name(struct winbindd_cli_state *state, const char *cc)
499 const char *type = state->request.data.auth.krb5_cc_type;
501 state->response.data.auth.krb5ccname[0] = '\0';
503 if (type[0] == '\0') {
507 if (!strequal(type, "FILE") &&
508 !strequal(type, "WRFILE")) {
509 DEBUG(10,("won't return krbccname for a %s type ccache\n",
514 fstrcpy(state->response.data.auth.krb5ccname, cc);
519 static uid_t get_uid_from_state(struct winbindd_cli_state *state)
523 uid = state->request.data.auth.uid;
526 DEBUG(1,("invalid uid: '%d'\n", uid));
532 /**********************************************************************
533 Authenticate a user with a clear text password using Kerberos and fill up
535 **********************************************************************/
537 static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
538 struct winbindd_cli_state *state,
539 NET_USER_INFO_3 **info3)
542 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
543 krb5_error_code krb5_ret;
544 const char *cc = NULL;
545 const char *principal_s = NULL;
546 const char *service = NULL;
548 fstring name_domain, name_user;
549 time_t ticket_lifetime = 0;
550 time_t renewal_until = 0;
553 time_t time_offset = 0;
554 bool internal_ccache = True;
561 * prepare a krb5_cc_cache string for the user */
563 uid = get_uid_from_state(state);
565 DEBUG(0,("no valid uid\n"));
568 cc = generate_krb5_ccache(state->mem_ctx,
569 state->request.data.auth.krb5_cc_type,
570 state->request.data.auth.uid,
573 return NT_STATUS_NO_MEMORY;
578 * get kerberos properties */
580 if (domain->private_data) {
581 ads = (ADS_STRUCT *)domain->private_data;
582 time_offset = ads->auth.time_offset;
587 * do kerberos auth and setup ccache as the user */
589 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
591 realm = domain->alt_name;
594 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
595 if (principal_s == NULL) {
596 return NT_STATUS_NO_MEMORY;
599 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
600 if (service == NULL) {
601 return NT_STATUS_NO_MEMORY;
604 /* if this is a user ccache, we need to act as the user to let the krb5
605 * library handle the chown, etc. */
607 /************************ ENTERING NON-ROOT **********************/
609 if (!internal_ccache) {
610 set_effective_uid(uid);
611 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
614 result = kerberos_return_info3_from_pac(state->mem_ctx,
616 state->request.data.auth.pass,
623 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
625 if (!internal_ccache) {
626 gain_root_privilege();
629 /************************ RETURNED TO ROOT **********************/
631 if (!NT_STATUS_IS_OK(result)) {
635 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
638 /* if we had a user's ccache then return that string for the pam
641 if (!internal_ccache) {
643 setup_return_cc_name(state, cc);
645 result = add_ccache_to_list(principal_s,
648 state->request.data.auth.user,
656 if (!NT_STATUS_IS_OK(result)) {
657 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
662 /* need to delete the memory cred cache, it is not used anymore */
664 krb5_ret = ads_kdestroy(cc);
666 DEBUG(3,("winbindd_raw_kerberos_login: "
667 "could not destroy krb5 credential cache: "
668 "%s\n", error_message(krb5_ret)));
677 /* we could have created a new credential cache with a valid tgt in it
678 * but we werent able to get or verify the service ticket for this
679 * local host and therefor didn't get the PAC, we need to remove that
680 * cache entirely now */
682 krb5_ret = ads_kdestroy(cc);
684 DEBUG(3,("winbindd_raw_kerberos_login: "
685 "could not destroy krb5 credential cache: "
686 "%s\n", error_message(krb5_ret)));
689 if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) {
690 DEBUG(3,("winbindd_raw_kerberos_login: "
691 "could not remove ccache for user %s\n",
692 state->request.data.auth.user));
697 return NT_STATUS_NOT_SUPPORTED;
698 #endif /* HAVE_KRB5 */
701 /****************************************************************
702 ****************************************************************/
704 static bool check_request_flags(uint32_t flags)
706 uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
707 WBFLAG_PAM_UNIX_NAME |
708 WBFLAG_PAM_INFO3_NDR;
710 if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
711 ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
712 ( (flags & flags_edata) == WBFLAG_PAM_UNIX_NAME) ||
713 !(flags & flags_edata) ) {
717 DEBUG(1,("check_request_flags: invalid request flags\n"));
722 /****************************************************************
723 ****************************************************************/
725 static NTSTATUS append_data(struct winbindd_cli_state *state,
726 NET_USER_INFO_3 *info3,
727 const char *name_domain,
728 const char *name_user)
731 uint32_t flags = state->request.flags;
733 if (flags & WBFLAG_PAM_USER_SESSION_KEY) {
734 memcpy(state->response.data.auth.user_session_key,
735 info3->user_sess_key,
736 sizeof(state->response.data.auth.user_session_key)
740 if (flags & WBFLAG_PAM_LMKEY) {
741 memcpy(state->response.data.auth.first_8_lm_hash,
743 sizeof(state->response.data.auth.first_8_lm_hash)
747 if (flags & WBFLAG_PAM_INFO3_TEXT) {
748 result = append_info3_as_txt(state->mem_ctx, state, info3);
749 if (!NT_STATUS_IS_OK(result)) {
750 DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
756 /* currently, anything from here on potentially overwrites extra_data. */
758 if (flags & WBFLAG_PAM_INFO3_NDR) {
759 result = append_info3_as_ndr(state->mem_ctx, state, info3);
760 if (!NT_STATUS_IS_OK(result)) {
761 DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
767 if (flags & WBFLAG_PAM_UNIX_NAME) {
768 result = append_unix_username(state->mem_ctx, state, info3,
769 name_domain, name_user);
770 if (!NT_STATUS_IS_OK(result)) {
771 DEBUG(10,("Failed to append Unix Username: %s\n",
777 if (flags & WBFLAG_PAM_AFS_TOKEN) {
778 result = append_afs_token(state->mem_ctx, state, info3,
779 name_domain, name_user);
780 if (!NT_STATUS_IS_OK(result)) {
781 DEBUG(10,("Failed to append AFS token: %s\n",
790 void winbindd_pam_auth(struct winbindd_cli_state *state)
792 struct winbindd_domain *domain;
793 fstring name_domain, name_user;
796 /* Ensure null termination */
797 state->request.data.auth.user
798 [sizeof(state->request.data.auth.user)-1]='\0';
800 /* Ensure null termination */
801 state->request.data.auth.pass
802 [sizeof(state->request.data.auth.pass)-1]='\0';
804 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
805 state->request.data.auth.user));
807 if (!check_request_flags(state->request.flags)) {
808 result = NT_STATUS_INVALID_PARAMETER_MIX;
812 /* Parse domain and username */
814 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
816 if (!canonicalize_username(state->request.data.auth.user,
817 name_domain, name_user)) {
818 result = NT_STATUS_NO_SUCH_USER;
822 domain = find_auth_domain(state, name_domain);
824 if (domain == NULL) {
825 result = NT_STATUS_NO_SUCH_USER;
829 sendto_domain(state, domain);
832 set_auth_errors(&state->response, result);
833 DEBUG(5, ("Plain text authentication for %s returned %s "
835 state->request.data.auth.user,
836 state->response.data.auth.nt_status_string,
837 state->response.data.auth.pam_error));
838 request_error(state);
841 NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
842 struct winbindd_cli_state *state,
843 NET_USER_INFO_3 **info3)
845 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
846 uint16 max_allowed_bad_attempts;
847 fstring name_domain, name_user;
849 enum lsa_SidType type;
850 uchar new_nt_pass[NT_HASH_LEN];
851 const uint8 *cached_nt_pass;
852 const uint8 *cached_salt;
853 NET_USER_INFO_3 *my_info3;
854 time_t kickoff_time, must_change_time;
855 bool password_good = False;
857 struct winbindd_tdc_domain *tdc_domain = NULL;
864 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
866 /* Parse domain and username */
868 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
871 if (!lookup_cached_name(state->mem_ctx,
876 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
877 return NT_STATUS_NO_SUCH_USER;
880 if (type != SID_NAME_USER) {
881 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
882 return NT_STATUS_LOGON_FAILURE;
885 result = winbindd_get_creds(domain,
891 if (!NT_STATUS_IS_OK(result)) {
892 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
898 E_md4hash(state->request.data.auth.pass, new_nt_pass);
900 dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
901 dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
903 dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
907 /* In this case we didn't store the nt_hash itself,
908 but the MD5 combination of salt + nt_hash. */
909 uchar salted_hash[NT_HASH_LEN];
910 E_md5hash(cached_salt, new_nt_pass, salted_hash);
912 password_good = (memcmp(cached_nt_pass, salted_hash, NT_HASH_LEN) == 0) ?
915 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
916 password_good = (memcmp(cached_nt_pass, new_nt_pass, NT_HASH_LEN) == 0) ?
922 /* User *DOES* know the password, update logon_time and reset
925 my_info3->user_flgs |= LOGON_CACHED_ACCOUNT;
927 if (my_info3->acct_flags & ACB_AUTOLOCK) {
928 return NT_STATUS_ACCOUNT_LOCKED_OUT;
931 if (my_info3->acct_flags & ACB_DISABLED) {
932 return NT_STATUS_ACCOUNT_DISABLED;
935 if (my_info3->acct_flags & ACB_WSTRUST) {
936 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
939 if (my_info3->acct_flags & ACB_SVRTRUST) {
940 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
943 if (my_info3->acct_flags & ACB_DOMTRUST) {
944 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
947 if (!(my_info3->acct_flags & ACB_NORMAL)) {
948 DEBUG(0,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
949 my_info3->acct_flags));
950 return NT_STATUS_LOGON_FAILURE;
953 kickoff_time = nt_time_to_unix(my_info3->kickoff_time);
954 if (kickoff_time != 0 && time(NULL) > kickoff_time) {
955 return NT_STATUS_ACCOUNT_EXPIRED;
958 must_change_time = nt_time_to_unix(my_info3->pass_must_change_time);
959 if (must_change_time != 0 && must_change_time < time(NULL)) {
960 /* we allow grace logons when the password has expired */
961 my_info3->user_flgs |= LOGON_GRACE_LOGON;
962 /* return NT_STATUS_PASSWORD_EXPIRED; */
967 if ((state->request.flags & WBFLAG_PAM_KRB5) &&
968 ((tdc_domain = wcache_tdc_fetch_domain(state->mem_ctx, name_domain)) != NULL) &&
969 (tdc_domain->trust_type & DS_DOMAIN_TRUST_TYPE_UPLEVEL)) {
972 const char *cc = NULL;
974 const char *principal_s = NULL;
975 const char *service = NULL;
976 bool internal_ccache = False;
978 uid = get_uid_from_state(state);
980 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
981 return NT_STATUS_INVALID_PARAMETER;
984 cc = generate_krb5_ccache(state->mem_ctx,
985 state->request.data.auth.krb5_cc_type,
986 state->request.data.auth.uid,
989 return NT_STATUS_NO_MEMORY;
992 realm = domain->alt_name;
995 principal_s = talloc_asprintf(state->mem_ctx, "%s@%s", name_user, realm);
996 if (principal_s == NULL) {
997 return NT_STATUS_NO_MEMORY;
1000 service = talloc_asprintf(state->mem_ctx, "%s/%s@%s", KRB5_TGS_NAME, realm, realm);
1001 if (service == NULL) {
1002 return NT_STATUS_NO_MEMORY;
1005 if (!internal_ccache) {
1007 setup_return_cc_name(state, cc);
1009 result = add_ccache_to_list(principal_s,
1012 state->request.data.auth.user,
1016 time(NULL) + lp_winbind_cache_time(),
1017 time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
1020 if (!NT_STATUS_IS_OK(result)) {
1021 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
1022 "to add ccache to list: %s\n",
1023 nt_errstr(result)));
1027 #endif /* HAVE_KRB5 */
1029 /* FIXME: we possibly should handle logon hours as well (does xp when
1030 * offline?) see auth/auth_sam.c:sam_account_ok for details */
1032 unix_to_nt_time(&my_info3->logon_time, time(NULL));
1033 my_info3->bad_pw_count = 0;
1035 result = winbindd_update_creds_by_info3(domain,
1037 state->request.data.auth.user,
1038 state->request.data.auth.pass,
1040 if (!NT_STATUS_IS_OK(result)) {
1041 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
1042 nt_errstr(result)));
1046 return NT_STATUS_OK;
1050 /* User does *NOT* know the correct password, modify info3 accordingly */
1052 /* failure of this is not critical */
1053 result = get_max_bad_attempts_from_lockout_policy(domain, state->mem_ctx, &max_allowed_bad_attempts);
1054 if (!NT_STATUS_IS_OK(result)) {
1055 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
1056 "Won't be able to honour account lockout policies\n"));
1059 /* increase counter */
1060 my_info3->bad_pw_count++;
1062 if (max_allowed_bad_attempts == 0) {
1067 if (my_info3->bad_pw_count >= max_allowed_bad_attempts) {
1069 uint32 password_properties;
1071 result = get_pwd_properties(domain, state->mem_ctx, &password_properties);
1072 if (!NT_STATUS_IS_OK(result)) {
1073 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
1076 if ((my_info3->user_rid != DOMAIN_USER_RID_ADMIN) ||
1077 (password_properties & DOMAIN_LOCKOUT_ADMINS)) {
1078 my_info3->acct_flags |= ACB_AUTOLOCK;
1083 result = winbindd_update_creds_by_info3(domain,
1085 state->request.data.auth.user,
1089 if (!NT_STATUS_IS_OK(result)) {
1090 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
1091 nt_errstr(result)));
1094 return NT_STATUS_LOGON_FAILURE;
1097 NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
1098 struct winbindd_cli_state *state,
1099 NET_USER_INFO_3 **info3)
1101 struct winbindd_domain *contact_domain;
1102 fstring name_domain, name_user;
1105 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
1107 /* Parse domain and username */
1109 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1111 /* what domain should we contact? */
1114 if (!(contact_domain = find_domain_from_name(name_domain))) {
1115 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1116 state->request.data.auth.user, name_domain, name_user, name_domain));
1117 result = NT_STATUS_NO_SUCH_USER;
1122 if (is_myname(name_domain)) {
1123 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1124 result = NT_STATUS_NO_SUCH_USER;
1128 contact_domain = find_domain_from_name(name_domain);
1129 if (contact_domain == NULL) {
1130 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1131 state->request.data.auth.user, name_domain, name_user, name_domain));
1133 contact_domain = find_our_domain();
1137 if (contact_domain->initialized &&
1138 contact_domain->active_directory) {
1142 if (!contact_domain->initialized) {
1143 init_dc_connection(contact_domain);
1146 if (!contact_domain->active_directory) {
1147 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1148 return NT_STATUS_INVALID_LOGON_TYPE;
1151 result = winbindd_raw_kerberos_login(contact_domain, state, info3);
1156 NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain,
1157 struct winbindd_cli_state *state,
1158 NET_USER_INFO_3 **info3)
1161 struct rpc_pipe_client *netlogon_pipe;
1166 unsigned char local_lm_response[24];
1167 unsigned char local_nt_response[24];
1168 struct winbindd_domain *contact_domain;
1169 fstring name_domain, name_user;
1172 NET_USER_INFO_3 *my_info3;
1174 ZERO_STRUCTP(info3);
1178 my_info3 = TALLOC_ZERO_P(state->mem_ctx, NET_USER_INFO_3);
1179 if (my_info3 == NULL) {
1180 return NT_STATUS_NO_MEMORY;
1184 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1186 /* Parse domain and username */
1188 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1190 /* do password magic */
1193 generate_random_buffer(chal, 8);
1194 if (lp_client_ntlmv2_auth()) {
1195 DATA_BLOB server_chal;
1196 DATA_BLOB names_blob;
1197 DATA_BLOB nt_response;
1198 DATA_BLOB lm_response;
1199 server_chal = data_blob_talloc(state->mem_ctx, chal, 8);
1201 /* note that the 'workgroup' here is a best guess - we don't know
1202 the server's domain at this point. The 'server name' is also
1205 names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1207 if (!SMBNTLMv2encrypt(name_user, name_domain,
1208 state->request.data.auth.pass,
1211 &lm_response, &nt_response, NULL)) {
1212 data_blob_free(&names_blob);
1213 data_blob_free(&server_chal);
1214 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1215 result = NT_STATUS_NO_MEMORY;
1218 data_blob_free(&names_blob);
1219 data_blob_free(&server_chal);
1220 lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data,
1221 lm_response.length);
1222 nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data,
1223 nt_response.length);
1224 data_blob_free(&lm_response);
1225 data_blob_free(&nt_response);
1228 if (lp_client_lanman_auth()
1229 && SMBencrypt(state->request.data.auth.pass,
1231 local_lm_response)) {
1232 lm_resp = data_blob_talloc(state->mem_ctx,
1234 sizeof(local_lm_response));
1236 lm_resp = data_blob_null;
1238 SMBNTencrypt(state->request.data.auth.pass,
1242 nt_resp = data_blob_talloc(state->mem_ctx,
1244 sizeof(local_nt_response));
1247 /* what domain should we contact? */
1250 if (!(contact_domain = find_domain_from_name(name_domain))) {
1251 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1252 state->request.data.auth.user, name_domain, name_user, name_domain));
1253 result = NT_STATUS_NO_SUCH_USER;
1258 if (is_myname(name_domain)) {
1259 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1260 result = NT_STATUS_NO_SUCH_USER;
1264 contact_domain = find_our_domain();
1267 /* check authentication loop */
1271 ZERO_STRUCTP(my_info3);
1274 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1276 if (!NT_STATUS_IS_OK(result)) {
1277 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1281 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
1284 contact_domain->dcname, /* server name */
1285 name_user, /* user name */
1286 name_domain, /* target domain */
1287 global_myname(), /* workstation */
1294 /* We have to try a second time as cm_connect_netlogon
1295 might not yet have noticed that the DC has killed
1298 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1303 /* if we get access denied, a possible cause was that we had
1304 and open connection to the DC, but someone changed our
1305 machine account password out from underneath us using 'net
1306 rpc changetrustpw' */
1308 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1309 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1310 "ACCESS_DENIED. Maybe the trust account "
1311 "password was changed and we didn't know it. "
1312 "Killing connections to domain %s\n",
1314 invalidate_cm_connection(&contact_domain->conn);
1318 } while ( (attempts < 2) && retry );
1320 /* handle the case where a NT4 DC does not fill in the acct_flags in
1321 * the samlogon reply info3. When accurate info3 is required by the
1322 * caller, we look up the account flags ourselve - gd */
1324 if ((state->request.flags & WBFLAG_PAM_INFO3_TEXT) &&
1325 (my_info3->acct_flags == 0) && NT_STATUS_IS_OK(result)) {
1327 struct rpc_pipe_client *samr_pipe;
1328 POLICY_HND samr_domain_handle, user_pol;
1329 SAM_USERINFO_CTR *user_ctr;
1330 NTSTATUS status_tmp;
1333 ZERO_STRUCT(user_ctr);
1335 status_tmp = cm_connect_sam(contact_domain, state->mem_ctx,
1336 &samr_pipe, &samr_domain_handle);
1338 if (!NT_STATUS_IS_OK(status_tmp)) {
1339 DEBUG(3, ("could not open handle to SAMR pipe: %s\n",
1340 nt_errstr(status_tmp)));
1344 status_tmp = rpccli_samr_open_user(samr_pipe, state->mem_ctx,
1345 &samr_domain_handle,
1346 MAXIMUM_ALLOWED_ACCESS,
1347 my_info3->user_rid, &user_pol);
1349 if (!NT_STATUS_IS_OK(status_tmp)) {
1350 DEBUG(3, ("could not open user handle on SAMR pipe: %s\n",
1351 nt_errstr(status_tmp)));
1355 status_tmp = rpccli_samr_query_userinfo(samr_pipe, state->mem_ctx,
1356 &user_pol, 16, &user_ctr);
1358 if (!NT_STATUS_IS_OK(status_tmp)) {
1359 DEBUG(3, ("could not query user info on SAMR pipe: %s\n",
1360 nt_errstr(status_tmp)));
1361 rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
1365 acct_flags = user_ctr->info.id16->acb_info;
1367 if (acct_flags == 0) {
1368 rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
1372 my_info3->acct_flags = acct_flags;
1374 DEBUG(10,("successfully retrieved acct_flags 0x%x\n", acct_flags));
1376 rpccli_samr_close(samr_pipe, state->mem_ctx, &user_pol);
1384 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
1385 struct winbindd_cli_state *state)
1387 NTSTATUS result = NT_STATUS_LOGON_FAILURE;
1388 NTSTATUS krb5_result = NT_STATUS_OK;
1389 fstring name_domain, name_user;
1390 NET_USER_INFO_3 *info3 = NULL;
1392 /* Ensure null termination */
1393 state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
1395 /* Ensure null termination */
1396 state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
1398 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
1399 state->request.data.auth.user));
1401 if (!check_request_flags(state->request.flags)) {
1402 result = NT_STATUS_INVALID_PARAMETER_MIX;
1406 /* Parse domain and username */
1408 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1410 parse_domain_user(state->request.data.auth.user, name_domain, name_user);
1412 if (domain->online == False) {
1413 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1414 if (domain->startup) {
1415 /* Logons are very important to users. If we're offline and
1416 we get a request within the first 30 seconds of startup,
1417 try very hard to find a DC and go online. */
1419 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1420 "request in startup mode.\n", domain->name ));
1422 winbindd_flush_negative_conn_cache(domain);
1423 result = init_dc_connection(domain);
1427 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
1429 /* Check for Kerberos authentication */
1430 if (domain->online && (state->request.flags & WBFLAG_PAM_KRB5)) {
1432 result = winbindd_dual_pam_auth_kerberos(domain, state, &info3);
1433 /* save for later */
1434 krb5_result = result;
1437 if (NT_STATUS_IS_OK(result)) {
1438 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1439 goto process_result;
1441 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result)));
1444 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1445 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1446 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1447 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1448 set_domain_offline( domain );
1452 /* there are quite some NT_STATUS errors where there is no
1453 * point in retrying with a samlogon, we explictly have to take
1454 * care not to increase the bad logon counter on the DC */
1456 if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
1457 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
1458 NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
1459 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
1460 NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
1461 NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
1462 NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
1463 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
1464 NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
1465 NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
1466 goto process_result;
1469 if (state->request.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
1470 DEBUG(3,("falling back to samlogon\n"));
1478 /* Check for Samlogon authentication */
1479 if (domain->online) {
1480 result = winbindd_dual_pam_auth_samlogon(domain, state, &info3);
1482 if (NT_STATUS_IS_OK(result)) {
1483 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1484 /* add the Krb5 err if we have one */
1485 if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
1486 info3->user_flgs |= LOGON_KRB5_FAIL_CLOCK_SKEW;
1488 goto process_result;
1491 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
1492 nt_errstr(result)));
1494 if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
1495 NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
1496 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
1498 DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
1499 set_domain_offline( domain );
1503 if (domain->online) {
1504 /* We're still online - fail. */
1510 /* Check for Cached logons */
1511 if (!domain->online && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) &&
1512 lp_winbind_offline_logon()) {
1514 result = winbindd_dual_pam_auth_cached(domain, state, &info3);
1516 if (NT_STATUS_IS_OK(result)) {
1517 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1518 goto process_result;
1520 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
1527 if (NT_STATUS_IS_OK(result)) {
1531 /* In all codepaths where result == NT_STATUS_OK info3 must have
1532 been initialized. */
1534 result = NT_STATUS_INTERNAL_ERROR;
1538 netsamlogon_cache_store(name_user, info3);
1539 wcache_invalidate_samlogon(find_domain_from_name(name_domain), info3);
1541 /* save name_to_sid info as early as possible (only if
1542 this is our primary domain so we don't invalidate
1543 the cache entry by storing the seq_num for the wrong
1545 if ( domain->primary ) {
1546 sid_compose(&user_sid, &info3->dom_sid.sid,
1548 cache_name2sid(domain, name_domain, name_user,
1549 SID_NAME_USER, &user_sid);
1552 /* Check if the user is in the right group */
1554 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, info3,
1555 state->request.data.auth.require_membership_of_sid))) {
1556 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1557 state->request.data.auth.user,
1558 state->request.data.auth.require_membership_of_sid));
1562 result = append_data(state, info3, name_domain, name_user);
1563 if (!NT_STATUS_IS_OK(result)) {
1567 if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
1569 /* Store in-memory creds for single-signon using ntlm_auth. */
1570 result = winbindd_add_memory_creds(state->request.data.auth.user,
1571 get_uid_from_state(state),
1572 state->request.data.auth.pass);
1574 if (!NT_STATUS_IS_OK(result)) {
1575 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result)));
1579 if (lp_winbind_offline_logon()) {
1580 result = winbindd_store_creds(domain,
1582 state->request.data.auth.user,
1583 state->request.data.auth.pass,
1585 if (!NT_STATUS_IS_OK(result)) {
1587 /* Release refcount. */
1588 winbindd_delete_memory_creds(state->request.data.auth.user);
1590 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
1596 result = fillup_password_policy(domain, state);
1598 if (!NT_STATUS_IS_OK(result)
1599 && !NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) )
1601 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result)));
1605 result = NT_STATUS_OK;
1609 /* give us a more useful (more correct?) error code */
1610 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1611 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1612 result = NT_STATUS_NO_LOGON_SERVERS;
1615 state->response.data.auth.nt_status = NT_STATUS_V(result);
1616 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1618 /* we might have given a more useful error above */
1619 if (!*state->response.data.auth.error_string)
1620 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1621 state->response.data.auth.pam_error = nt_status_to_pam(result);
1623 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1624 state->request.data.auth.user,
1625 state->response.data.auth.nt_status_string,
1626 state->response.data.auth.pam_error));
1628 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1632 /**********************************************************************
1633 Challenge Response Authentication Protocol
1634 **********************************************************************/
1636 void winbindd_pam_auth_crap(struct winbindd_cli_state *state)
1638 struct winbindd_domain *domain = NULL;
1639 const char *domain_name = NULL;
1642 if (!check_request_flags(state->request.flags)) {
1643 result = NT_STATUS_INVALID_PARAMETER_MIX;
1647 if (!state->privileged) {
1648 char *error_string = NULL;
1649 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1651 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1652 "on %s are set correctly.\n",
1653 get_winbind_priv_pipe_dir()));
1654 /* send a better message than ACCESS_DENIED */
1655 error_string = talloc_asprintf(state->mem_ctx,
1656 "winbind client not authorized "
1657 "to use winbindd_pam_auth_crap."
1658 " Ensure permissions on %s "
1659 "are set correctly.",
1660 get_winbind_priv_pipe_dir());
1661 fstrcpy(state->response.data.auth.error_string, error_string);
1662 result = NT_STATUS_ACCESS_DENIED;
1666 /* Ensure null termination */
1667 state->request.data.auth_crap.user
1668 [sizeof(state->request.data.auth_crap.user)-1]=0;
1669 state->request.data.auth_crap.domain
1670 [sizeof(state->request.data.auth_crap.domain)-1]=0;
1672 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1673 (unsigned long)state->pid,
1674 state->request.data.auth_crap.domain,
1675 state->request.data.auth_crap.user));
1677 if (*state->request.data.auth_crap.domain != '\0') {
1678 domain_name = state->request.data.auth_crap.domain;
1679 } else if (lp_winbind_use_default_domain()) {
1680 domain_name = lp_workgroup();
1683 if (domain_name != NULL)
1684 domain = find_auth_domain(state, domain_name);
1686 if (domain != NULL) {
1687 sendto_domain(state, domain);
1691 result = NT_STATUS_NO_SUCH_USER;
1694 set_auth_errors(&state->response, result);
1695 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1696 state->request.data.auth_crap.domain,
1697 state->request.data.auth_crap.user,
1698 state->response.data.auth.nt_status_string,
1699 state->response.data.auth.pam_error));
1700 request_error(state);
1705 enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
1706 struct winbindd_cli_state *state)
1709 NET_USER_INFO_3 info3;
1710 struct rpc_pipe_client *netlogon_pipe;
1711 const char *name_user = NULL;
1712 const char *name_domain = NULL;
1713 const char *workstation;
1714 struct winbindd_domain *contact_domain;
1718 DATA_BLOB lm_resp, nt_resp;
1720 /* This is child-only, so no check for privileged access is needed
1723 /* Ensure null termination */
1724 state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
1725 state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
1727 if (!check_request_flags(state->request.flags)) {
1728 result = NT_STATUS_INVALID_PARAMETER_MIX;
1732 name_user = state->request.data.auth_crap.user;
1734 if (*state->request.data.auth_crap.domain) {
1735 name_domain = state->request.data.auth_crap.domain;
1736 } else if (lp_winbind_use_default_domain()) {
1737 name_domain = lp_workgroup();
1739 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1741 result = NT_STATUS_NO_SUCH_USER;
1745 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
1746 name_domain, name_user));
1748 if (*state->request.data.auth_crap.workstation) {
1749 workstation = state->request.data.auth_crap.workstation;
1751 workstation = global_myname();
1754 if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
1755 || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
1756 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1757 state->request.data.auth_crap.lm_resp_len,
1758 state->request.data.auth_crap.nt_resp_len));
1759 result = NT_STATUS_INVALID_PARAMETER;
1763 lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp,
1764 state->request.data.auth_crap.lm_resp_len);
1765 nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp,
1766 state->request.data.auth_crap.nt_resp_len);
1768 /* what domain should we contact? */
1771 if (!(contact_domain = find_domain_from_name(name_domain))) {
1772 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1773 state->request.data.auth_crap.user, name_domain, name_user, name_domain));
1774 result = NT_STATUS_NO_SUCH_USER;
1778 if (is_myname(name_domain)) {
1779 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
1780 result = NT_STATUS_NO_SUCH_USER;
1783 contact_domain = find_our_domain();
1790 netlogon_pipe = NULL;
1791 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
1793 if (!NT_STATUS_IS_OK(result)) {
1794 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1795 nt_errstr(result)));
1799 result = rpccli_netlogon_sam_network_logon(netlogon_pipe,
1801 state->request.data.auth_crap.logon_parameters,
1802 contact_domain->dcname,
1805 /* Bug #3248 - found by Stefan Burkei. */
1806 workstation, /* We carefully set this above so use it... */
1807 state->request.data.auth_crap.chal,
1814 /* We have to try a second time as cm_connect_netlogon
1815 might not yet have noticed that the DC has killed
1818 if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
1823 /* if we get access denied, a possible cause was that we had and open
1824 connection to the DC, but someone changed our machine account password
1825 out from underneath us using 'net rpc changetrustpw' */
1827 if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
1828 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1829 "ACCESS_DENIED. Maybe the trust account "
1830 "password was changed and we didn't know it. "
1831 "Killing connections to domain %s\n",
1833 invalidate_cm_connection(&contact_domain->conn);
1837 } while ( (attempts < 2) && retry );
1839 if (NT_STATUS_IS_OK(result)) {
1841 netsamlogon_cache_store(name_user, &info3);
1842 wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
1844 /* Check if the user is in the right group */
1846 if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3,
1847 state->request.data.auth_crap.require_membership_of_sid))) {
1848 DEBUG(3, ("User %s is not in the required group (%s), so "
1849 "crap authentication is rejected\n",
1850 state->request.data.auth_crap.user,
1851 state->request.data.auth_crap.require_membership_of_sid));
1855 result = append_data(state, &info3, name_domain, name_user);
1856 if (!NT_STATUS_IS_OK(result)) {
1863 /* give us a more useful (more correct?) error code */
1864 if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
1865 (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
1866 result = NT_STATUS_NO_LOGON_SERVERS;
1869 if (state->request.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
1870 result = nt_status_squash(result);
1873 state->response.data.auth.nt_status = NT_STATUS_V(result);
1874 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
1876 /* we might have given a more useful error above */
1877 if (!*state->response.data.auth.error_string) {
1878 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
1880 state->response.data.auth.pam_error = nt_status_to_pam(result);
1882 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
1883 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1886 state->response.data.auth.nt_status_string,
1887 state->response.data.auth.pam_error));
1889 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
1892 /* Change a user password */
1894 void winbindd_pam_chauthtok(struct winbindd_cli_state *state)
1896 fstring domain, user;
1897 struct winbindd_domain *contact_domain;
1899 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
1900 state->request.data.chauthtok.user));
1904 ws_name_return( state->request.data.auth.user, WB_REPLACE_CHAR );
1906 if (!canonicalize_username(state->request.data.chauthtok.user, domain, user)) {
1907 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1908 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
1910 state->request.data.auth.user,
1911 state->response.data.auth.nt_status_string,
1912 state->response.data.auth.pam_error));
1913 request_error(state);
1917 contact_domain = find_domain_from_name(domain);
1918 if (!contact_domain) {
1919 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
1920 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
1921 state->request.data.chauthtok.user, domain, user, domain));
1922 request_error(state);
1926 sendto_domain(state, contact_domain);
1929 enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact_domain,
1930 struct winbindd_cli_state *state)
1933 char *newpass = NULL;
1935 struct rpc_pipe_client *cli;
1936 bool got_info = False;
1937 SAM_UNK_INFO_1 info;
1938 SAMR_CHANGE_REJECT reject;
1939 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1940 fstring domain, user;
1942 DEBUG(3, ("[%5lu]: dual pam chauthtok %s\n", (unsigned long)state->pid,
1943 state->request.data.auth.user));
1945 if (!parse_domain_user(state->request.data.chauthtok.user, domain, user)) {
1949 /* Change password */
1951 oldpass = state->request.data.chauthtok.oldpass;
1952 newpass = state->request.data.chauthtok.newpass;
1954 /* Initialize reject reason */
1955 state->response.data.auth.reject_reason = Undefined;
1957 /* Get sam handle */
1959 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli,
1961 if (!NT_STATUS_IS_OK(result)) {
1962 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
1966 result = rpccli_samr_chgpasswd3(cli, state->mem_ctx, user, newpass, oldpass, &info, &reject);
1968 /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
1970 if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
1971 state->response.data.auth.policy.min_length_password =
1972 info.min_length_password;
1973 state->response.data.auth.policy.password_history =
1974 info.password_history;
1975 state->response.data.auth.policy.password_properties =
1976 info.password_properties;
1977 state->response.data.auth.policy.expire =
1978 nt_time_to_unix_abs(&info.expire);
1979 state->response.data.auth.policy.min_passwordage =
1980 nt_time_to_unix_abs(&info.min_passwordage);
1982 state->response.data.auth.reject_reason =
1983 reject.reject_reason;
1988 /* only fallback when the chgpasswd3 call is not supported */
1989 if ((NT_STATUS_EQUAL(result, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR))) ||
1990 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) ||
1991 (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED))) {
1993 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
1994 nt_errstr(result)));
1996 result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, oldpass);
1998 /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
1999 Map to the same status code as Windows 2003. */
2001 if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
2002 result = NT_STATUS_PASSWORD_RESTRICTION;
2008 if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) {
2010 /* Update the single sign-on memory creds. */
2011 result = winbindd_replace_memory_creds(state->request.data.chauthtok.user,
2014 if (!NT_STATUS_IS_OK(result)) {
2015 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result)));
2016 goto process_result;
2019 if (lp_winbind_offline_logon()) {
2020 result = winbindd_update_creds_by_name(contact_domain,
2021 state->mem_ctx, user,
2023 if (!NT_STATUS_IS_OK(result)) {
2024 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result)));
2025 goto process_result;
2030 if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
2032 NTSTATUS policy_ret;
2034 policy_ret = fillup_password_policy(contact_domain, state);
2036 /* failure of this is non critical, it will just provide no
2037 * additional information to the client why the change has
2038 * failed - Guenther */
2040 if (!NT_STATUS_IS_OK(policy_ret)) {
2041 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
2042 goto process_result;
2048 state->response.data.auth.nt_status = NT_STATUS_V(result);
2049 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2050 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2051 state->response.data.auth.pam_error = nt_status_to_pam(result);
2053 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2054 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2057 state->response.data.auth.nt_status_string,
2058 state->response.data.auth.pam_error));
2060 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2063 void winbindd_pam_logoff(struct winbindd_cli_state *state)
2065 struct winbindd_domain *domain;
2066 fstring name_domain, user;
2067 uid_t caller_uid = (uid_t)-1;
2068 uid_t request_uid = state->request.data.logoff.uid;
2070 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state->pid,
2071 state->request.data.logoff.user));
2073 /* Ensure null termination */
2074 state->request.data.logoff.user
2075 [sizeof(state->request.data.logoff.user)-1]='\0';
2077 state->request.data.logoff.krb5ccname
2078 [sizeof(state->request.data.logoff.krb5ccname)-1]='\0';
2080 if (request_uid == (gid_t)-1) {
2084 if (!canonicalize_username(state->request.data.logoff.user, name_domain, user)) {
2088 if ((domain = find_auth_domain(state, name_domain)) == NULL) {
2092 if ((sys_getpeereid(state->sock, &caller_uid)) != 0) {
2093 DEBUG(1,("winbindd_pam_logoff: failed to check peerid: %s\n",
2098 switch (caller_uid) {
2102 /* root must be able to logoff any user - gd */
2103 state->request.data.logoff.uid = request_uid;
2106 if (caller_uid != request_uid) {
2107 DEBUG(1,("winbindd_pam_logoff: caller requested invalid uid\n"));
2110 state->request.data.logoff.uid = caller_uid;
2114 sendto_domain(state, domain);
2118 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2119 DEBUG(5, ("Pam Logoff for %s returned %s "
2121 state->request.data.logoff.user,
2122 state->response.data.auth.nt_status_string,
2123 state->response.data.auth.pam_error));
2124 request_error(state);
2128 enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
2129 struct winbindd_cli_state *state)
2131 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
2133 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid,
2134 state->request.data.logoff.user));
2136 if (!(state->request.flags & WBFLAG_PAM_KRB5)) {
2137 result = NT_STATUS_OK;
2138 goto process_result;
2141 if (state->request.data.logoff.krb5ccname[0] == '\0') {
2142 result = NT_STATUS_OK;
2143 goto process_result;
2148 if (state->request.data.logoff.uid < 0) {
2149 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2150 goto process_result;
2153 /* what we need here is to find the corresponding krb5 ccache name *we*
2154 * created for a given username and destroy it */
2156 if (!ccache_entry_exists(state->request.data.logoff.user)) {
2157 result = NT_STATUS_OK;
2158 DEBUG(10,("winbindd_pam_logoff: no entry found.\n"));
2159 goto process_result;
2162 if (!ccache_entry_identical(state->request.data.logoff.user,
2163 state->request.data.logoff.uid,
2164 state->request.data.logoff.krb5ccname)) {
2165 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2166 goto process_result;
2169 result = remove_ccache(state->request.data.logoff.user);
2170 if (!NT_STATUS_IS_OK(result)) {
2171 DEBUG(0,("winbindd_pam_logoff: failed to remove ccache: %s\n",
2172 nt_errstr(result)));
2173 goto process_result;
2177 result = NT_STATUS_NOT_SUPPORTED;
2182 winbindd_delete_memory_creds(state->request.data.logoff.user);
2184 state->response.data.auth.nt_status = NT_STATUS_V(result);
2185 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2186 fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
2187 state->response.data.auth.pam_error = nt_status_to_pam(result);
2189 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
2192 /* Change user password with auth crap*/
2194 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state *state)
2196 struct winbindd_domain *domain = NULL;
2197 const char *domain_name = NULL;
2199 /* Ensure null termination */
2200 state->request.data.chng_pswd_auth_crap.user[
2201 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2202 state->request.data.chng_pswd_auth_crap.domain[
2203 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2205 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2206 (unsigned long)state->pid,
2207 state->request.data.chng_pswd_auth_crap.domain,
2208 state->request.data.chng_pswd_auth_crap.user));
2210 if (*state->request.data.chng_pswd_auth_crap.domain != '\0') {
2211 domain_name = state->request.data.chng_pswd_auth_crap.domain;
2212 } else if (lp_winbind_use_default_domain()) {
2213 domain_name = lp_workgroup();
2216 if (domain_name != NULL)
2217 domain = find_domain_from_name(domain_name);
2219 if (domain != NULL) {
2220 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2221 "%s\n", (unsigned long)state->pid,domain->name));
2222 sendto_domain(state, domain);
2226 set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER);
2227 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2228 state->request.data.chng_pswd_auth_crap.domain,
2229 state->request.data.chng_pswd_auth_crap.user,
2230 state->response.data.auth.nt_status_string,
2231 state->response.data.auth.pam_error));
2232 request_error(state);
2236 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state)
2239 DATA_BLOB new_nt_password;
2240 DATA_BLOB old_nt_hash_enc;
2241 DATA_BLOB new_lm_password;
2242 DATA_BLOB old_lm_hash_enc;
2243 fstring domain,user;
2245 struct winbindd_domain *contact_domain = domainSt;
2246 struct rpc_pipe_client *cli;
2248 /* Ensure null termination */
2249 state->request.data.chng_pswd_auth_crap.user[
2250 sizeof(state->request.data.chng_pswd_auth_crap.user)-1]=0;
2251 state->request.data.chng_pswd_auth_crap.domain[
2252 sizeof(state->request.data.chng_pswd_auth_crap.domain)-1]=0;
2256 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2257 (unsigned long)state->pid,
2258 state->request.data.chng_pswd_auth_crap.domain,
2259 state->request.data.chng_pswd_auth_crap.user));
2261 if (lp_winbind_offline_logon()) {
2262 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2263 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2264 result = NT_STATUS_ACCESS_DENIED;
2268 if (*state->request.data.chng_pswd_auth_crap.domain) {
2269 fstrcpy(domain,state->request.data.chng_pswd_auth_crap.domain);
2271 parse_domain_user(state->request.data.chng_pswd_auth_crap.user,
2275 DEBUG(3,("no domain specified with username (%s) - "
2277 state->request.data.chng_pswd_auth_crap.user));
2278 result = NT_STATUS_NO_SUCH_USER;
2283 if (!*domain && lp_winbind_use_default_domain()) {
2284 fstrcpy(domain,(char *)lp_workgroup());
2288 fstrcpy(user, state->request.data.chng_pswd_auth_crap.user);
2291 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2292 (unsigned long)state->pid, domain, user));
2294 /* Change password */
2295 new_nt_password = data_blob_talloc(
2297 state->request.data.chng_pswd_auth_crap.new_nt_pswd,
2298 state->request.data.chng_pswd_auth_crap.new_nt_pswd_len);
2300 old_nt_hash_enc = data_blob_talloc(
2302 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc,
2303 state->request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
2305 if(state->request.data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
2306 new_lm_password = data_blob_talloc(
2308 state->request.data.chng_pswd_auth_crap.new_lm_pswd,
2309 state->request.data.chng_pswd_auth_crap.new_lm_pswd_len);
2311 old_lm_hash_enc = data_blob_talloc(
2313 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc,
2314 state->request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
2316 new_lm_password.length = 0;
2317 old_lm_hash_enc.length = 0;
2320 /* Get sam handle */
2322 result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, &dom_pol);
2323 if (!NT_STATUS_IS_OK(result)) {
2324 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
2328 result = rpccli_samr_chng_pswd_auth_crap(
2329 cli, state->mem_ctx, user, new_nt_password, old_nt_hash_enc,
2330 new_lm_password, old_lm_hash_enc);
2333 state->response.data.auth.nt_status = NT_STATUS_V(result);
2334 fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
2335 fstrcpy(state->response.data.auth.error_string,
2336 get_friendly_nt_error_msg(result));
2337 state->response.data.auth.pam_error = nt_status_to_pam(result);
2339 DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
2340 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2342 state->response.data.auth.nt_status_string,
2343 state->response.data.auth.pam_error));
2345 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;