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 2 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, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #define DBGC_CLASS DBGC_WINBIND
31 static NTSTATUS
append_info3_as_txt(TALLOC_CTX
*mem_ctx
,
32 struct winbindd_cli_state
*state
,
33 NET_USER_INFO_3
*info3
)
37 state
->response
.data
.auth
.info3
.logon_time
=
38 nt_time_to_unix(&info3
->logon_time
);
39 state
->response
.data
.auth
.info3
.logoff_time
=
40 nt_time_to_unix(&info3
->logoff_time
);
41 state
->response
.data
.auth
.info3
.kickoff_time
=
42 nt_time_to_unix(&info3
->kickoff_time
);
43 state
->response
.data
.auth
.info3
.pass_last_set_time
=
44 nt_time_to_unix(&info3
->pass_last_set_time
);
45 state
->response
.data
.auth
.info3
.pass_can_change_time
=
46 nt_time_to_unix(&info3
->pass_can_change_time
);
47 state
->response
.data
.auth
.info3
.pass_must_change_time
=
48 nt_time_to_unix(&info3
->pass_must_change_time
);
50 state
->response
.data
.auth
.info3
.logon_count
= info3
->logon_count
;
51 state
->response
.data
.auth
.info3
.bad_pw_count
= info3
->bad_pw_count
;
53 state
->response
.data
.auth
.info3
.user_rid
= info3
->user_rid
;
54 state
->response
.data
.auth
.info3
.group_rid
= info3
->group_rid
;
55 sid_to_string(str_sid
, &(info3
->dom_sid
.sid
));
56 fstrcpy(state
->response
.data
.auth
.info3
.dom_sid
, str_sid
);
58 state
->response
.data
.auth
.info3
.num_groups
= info3
->num_groups
;
59 state
->response
.data
.auth
.info3
.user_flgs
= info3
->user_flgs
;
61 state
->response
.data
.auth
.info3
.acct_flags
= info3
->acct_flags
;
62 state
->response
.data
.auth
.info3
.num_other_sids
= info3
->num_other_sids
;
64 unistr2_to_ascii(state
->response
.data
.auth
.info3
.user_name
,
65 &info3
->uni_user_name
, -1);
66 unistr2_to_ascii(state
->response
.data
.auth
.info3
.full_name
,
67 &info3
->uni_full_name
, -1);
68 unistr2_to_ascii(state
->response
.data
.auth
.info3
.logon_script
,
69 &info3
->uni_logon_script
, -1);
70 unistr2_to_ascii(state
->response
.data
.auth
.info3
.profile_path
,
71 &info3
->uni_profile_path
, -1);
72 unistr2_to_ascii(state
->response
.data
.auth
.info3
.home_dir
,
73 &info3
->uni_home_dir
, -1);
74 unistr2_to_ascii(state
->response
.data
.auth
.info3
.dir_drive
,
75 &info3
->uni_dir_drive
, -1);
77 unistr2_to_ascii(state
->response
.data
.auth
.info3
.logon_srv
,
78 &info3
->uni_logon_srv
, -1);
79 unistr2_to_ascii(state
->response
.data
.auth
.info3
.logon_dom
,
80 &info3
->uni_logon_dom
, -1);
85 static NTSTATUS
append_info3_as_ndr(TALLOC_CTX
*mem_ctx
,
86 struct winbindd_cli_state
*state
,
87 NET_USER_INFO_3
*info3
)
91 if (!prs_init(&ps
, 256 /* Random, non-zero number */, mem_ctx
, MARSHALL
)) {
92 return NT_STATUS_NO_MEMORY
;
94 if (!net_io_user_info3("", info3
, &ps
, 1, 3, False
)) {
96 return NT_STATUS_UNSUCCESSFUL
;
99 size
= prs_data_size(&ps
);
100 SAFE_FREE(state
->response
.extra_data
.data
);
101 state
->response
.extra_data
.data
= SMB_MALLOC(size
);
102 if (!state
->response
.extra_data
.data
) {
104 return NT_STATUS_NO_MEMORY
;
106 memset( state
->response
.extra_data
.data
, '\0', size
);
107 prs_copy_all_data_out((char *)state
->response
.extra_data
.data
, &ps
);
108 state
->response
.length
+= size
;
113 static NTSTATUS
check_info3_in_group(TALLOC_CTX
*mem_ctx
,
114 NET_USER_INFO_3
*info3
,
115 const char *group_sid
)
117 DOM_SID require_membership_of_sid
;
119 size_t num_all_sids
= (2 + info3
->num_groups2
+ info3
->num_other_sids
);
122 /* Parse the 'required group' SID */
124 if (!group_sid
|| !group_sid
[0]) {
125 /* NO sid supplied, all users may access */
129 if (!string_to_sid(&require_membership_of_sid
, group_sid
)) {
130 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!",
133 return NT_STATUS_INVALID_PARAMETER
;
136 all_sids
= TALLOC_ARRAY(mem_ctx
, DOM_SID
, num_all_sids
);
138 return NT_STATUS_NO_MEMORY
;
140 /* and create (by appending rids) the 'domain' sids */
142 sid_copy(&all_sids
[0], &(info3
->dom_sid
.sid
));
144 if (!sid_append_rid(&all_sids
[0], info3
->user_rid
)) {
145 DEBUG(3,("could not append user's primary RID 0x%x\n",
148 return NT_STATUS_INVALID_PARAMETER
;
152 sid_copy(&all_sids
[1], &(info3
->dom_sid
.sid
));
154 if (!sid_append_rid(&all_sids
[1], info3
->group_rid
)) {
155 DEBUG(3,("could not append additional group rid 0x%x\n",
158 return NT_STATUS_INVALID_PARAMETER
;
162 for (i
= 0; i
< info3
->num_groups2
; i
++) {
164 sid_copy(&all_sids
[j
], &(info3
->dom_sid
.sid
));
166 if (!sid_append_rid(&all_sids
[j
], info3
->gids
[i
].g_rid
)) {
167 DEBUG(3,("could not append additional group rid 0x%x\n",
168 info3
->gids
[i
].g_rid
));
170 return NT_STATUS_INVALID_PARAMETER
;
175 /* Copy 'other' sids. We need to do sid filtering here to
176 prevent possible elevation of privileges. See:
178 http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
181 for (i
= 0; i
< info3
->num_other_sids
; i
++) {
182 sid_copy(&all_sids
[info3
->num_groups2
+ i
+ 2],
183 &info3
->other_sids
[i
].sid
);
187 for (i
= 0; i
< j
; i
++) {
189 DEBUG(10, ("User has SID: %s\n",
190 sid_to_string(sid1
, &all_sids
[i
])));
191 if (sid_equal(&require_membership_of_sid
, &all_sids
[i
])) {
192 DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n",
193 sid_to_string(sid1
, &require_membership_of_sid
), sid_to_string(sid2
, &all_sids
[i
])));
198 /* Do not distinguish this error from a wrong username/pw */
200 return NT_STATUS_LOGON_FAILURE
;
203 struct winbindd_domain
*find_auth_domain(struct winbindd_cli_state
*state
,
204 const char *domain_name
)
206 struct winbindd_domain
*domain
;
209 domain
= find_domain_from_name_noinit(domain_name
);
210 if (domain
== NULL
) {
211 DEBUG(3, ("Authentication for domain [%s] refused"
212 "as it is not a trusted domain\n",
218 if (is_myname(domain_name
)) {
219 DEBUG(3, ("Authentication for domain %s (local domain "
220 "to this server) not supported at this "
221 "stage\n", domain_name
));
225 /* we can auth against trusted domains */
226 if (state
->request
.flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
227 domain
= find_domain_from_name_noinit(domain_name
);
228 if (domain
== NULL
) {
229 DEBUG(3, ("Authentication for domain [%s] skipped "
230 "as it is not a trusted domain\n",
237 return find_our_domain();
240 static void set_auth_errors(struct winbindd_response
*resp
, NTSTATUS result
)
242 resp
->data
.auth
.nt_status
= NT_STATUS_V(result
);
243 fstrcpy(resp
->data
.auth
.nt_status_string
, nt_errstr(result
));
245 /* we might have given a more useful error above */
246 if (*resp
->data
.auth
.error_string
== '\0')
247 fstrcpy(resp
->data
.auth
.error_string
,
248 get_friendly_nt_error_msg(result
));
249 resp
->data
.auth
.pam_error
= nt_status_to_pam(result
);
252 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
253 struct winbindd_cli_state
*state
)
255 struct winbindd_methods
*methods
;
256 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
257 SAM_UNK_INFO_1 password_policy
;
259 methods
= domain
->methods
;
261 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
262 if (NT_STATUS_IS_ERR(status
)) {
266 state
->response
.data
.auth
.policy
.min_length_password
=
267 password_policy
.min_length_password
;
268 state
->response
.data
.auth
.policy
.password_history
=
269 password_policy
.password_history
;
270 state
->response
.data
.auth
.policy
.password_properties
=
271 password_policy
.password_properties
;
272 state
->response
.data
.auth
.policy
.expire
=
273 nt_time_to_unix_abs(&(password_policy
.expire
));
274 state
->response
.data
.auth
.policy
.min_passwordage
=
275 nt_time_to_unix_abs(&(password_policy
.min_passwordage
));
280 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
282 uint16
*max_allowed_bad_attempts
)
284 struct winbindd_methods
*methods
;
285 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
286 SAM_UNK_INFO_12 lockout_policy
;
288 *max_allowed_bad_attempts
= 0;
290 methods
= domain
->methods
;
292 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
293 if (NT_STATUS_IS_ERR(status
)) {
297 *max_allowed_bad_attempts
= lockout_policy
.bad_attempt_lockout
;
302 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
304 uint32
*password_properties
)
306 struct winbindd_methods
*methods
;
307 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
308 SAM_UNK_INFO_1 password_policy
;
310 *password_properties
= 0;
312 methods
= domain
->methods
;
314 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
315 if (NT_STATUS_IS_ERR(status
)) {
319 *password_properties
= password_policy
.password_properties
;
326 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
329 BOOL
*internal_ccache
)
331 /* accept FILE and WRFILE as krb5_cc_type from the client and then
332 * build the full ccname string based on the user's uid here -
335 const char *gen_cc
= NULL
;
337 *internal_ccache
= True
;
343 if (!type
|| type
[0] == '\0') {
347 if (strequal(type
, "FILE")) {
348 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
349 } else if (strequal(type
, "WRFILE")) {
350 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
352 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
356 *internal_ccache
= False
;
360 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbindd_pam_ccache");
363 if (gen_cc
== NULL
) {
364 DEBUG(0,("out of memory\n"));
368 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
373 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
375 const char *type
= state
->request
.data
.auth
.krb5_cc_type
;
377 state
->response
.data
.auth
.krb5ccname
[0] = '\0';
379 if (type
[0] == '\0') {
383 if (!strequal(type
, "FILE") &&
384 !strequal(type
, "WRFILE")) {
385 DEBUG(10,("won't return krbccname for a %s type ccache\n",
390 fstrcpy(state
->response
.data
.auth
.krb5ccname
, cc
);
395 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
399 uid
= state
->request
.data
.auth
.uid
;
402 DEBUG(1,("invalid uid: '%d'\n", uid
));
408 /**********************************************************************
409 Authenticate a user with a clear text password using Kerberos and fill up
411 **********************************************************************/
413 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
414 struct winbindd_cli_state
*state
,
415 NET_USER_INFO_3
**info3
)
418 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
419 krb5_error_code krb5_ret
;
420 DATA_BLOB tkt
, session_key_krb5
;
421 DATA_BLOB ap_rep
, session_key
;
422 PAC_DATA
*pac_data
= NULL
;
423 PAC_LOGON_INFO
*logon_info
= NULL
;
424 char *client_princ
= NULL
;
425 char *client_princ_out
= NULL
;
426 char *local_service
= NULL
;
427 const char *cc
= NULL
;
428 const char *principal_s
= NULL
;
429 const char *service
= NULL
;
431 fstring name_domain
, name_user
;
432 time_t ticket_lifetime
= 0;
433 time_t renewal_until
= 0;
436 time_t time_offset
= 0;
437 BOOL internal_ccache
= True
;
439 ZERO_STRUCT(session_key
);
440 ZERO_STRUCT(session_key_krb5
);
449 * prepare a krb5_cc_cache string for the user */
451 uid
= get_uid_from_state(state
);
453 DEBUG(0,("no valid uid\n"));
456 cc
= generate_krb5_ccache(state
->mem_ctx
,
457 state
->request
.data
.auth
.krb5_cc_type
,
458 state
->request
.data
.auth
.uid
,
461 return NT_STATUS_NO_MEMORY
;
466 * get kerberos properties */
468 if (domain
->private_data
) {
469 ads
= (ADS_STRUCT
*)domain
->private_data
;
470 time_offset
= ads
->auth
.time_offset
;
475 * do kerberos auth and setup ccache as the user */
477 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
479 realm
= domain
->alt_name
;
482 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
483 if (principal_s
== NULL
) {
484 return NT_STATUS_NO_MEMORY
;
487 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
488 if (service
== NULL
) {
489 return NT_STATUS_NO_MEMORY
;
492 /* if this is a user ccache, we need to act as the user to let the krb5
493 * library handle the chown, etc. */
495 /************************ NON-ROOT **********************/
497 if (!internal_ccache
) {
499 set_effective_uid(uid
);
500 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
503 krb5_ret
= kerberos_kinit_password_ext(principal_s
,
504 state
->request
.data
.auth
.pass
,
511 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
);
514 DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n",
515 principal_s
, error_message(krb5_ret
), krb5_ret
));
516 result
= krb5_to_nt_status(krb5_ret
);
520 /* does http_timestring use heimdals libroken strftime?? - Guenther */
521 DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n",
523 http_timestring(ticket_lifetime
), (int)ticket_lifetime
,
524 http_timestring(renewal_until
), (int)renewal_until
));
526 client_princ
= talloc_strdup(state
->mem_ctx
, global_myname());
527 if (client_princ
== NULL
) {
528 result
= NT_STATUS_NO_MEMORY
;
531 strlower_m(client_princ
);
533 local_service
= talloc_asprintf(state
->mem_ctx
, "%s$@%s", client_princ
, lp_realm());
534 if (local_service
== NULL
) {
535 DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
536 result
= NT_STATUS_NO_MEMORY
;
540 krb5_ret
= cli_krb5_get_ticket(local_service
,
547 DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for %s: %s\n",
548 local_service
, error_message(krb5_ret
)));
549 result
= krb5_to_nt_status(krb5_ret
);
553 if (!internal_ccache
) {
554 gain_root_privilege();
557 /************************ NON-ROOT **********************/
559 result
= ads_verify_ticket(state
->mem_ctx
,
567 if (!NT_STATUS_IS_OK(result
)) {
568 DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n",
574 DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
575 result
= NT_STATUS_INVALID_PARAMETER
;
579 logon_info
= get_logon_info_from_pac(pac_data
);
580 if (logon_info
== NULL
) {
581 DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
582 result
= NT_STATUS_INVALID_PARAMETER
;
586 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
591 * put results together */
593 *info3
= &logon_info
->info3
;
595 /* if we had a user's ccache then return that string for the pam
598 if (!internal_ccache
) {
600 setup_return_cc_name(state
, cc
);
602 result
= add_ccache_to_list(principal_s
,
605 state
->request
.data
.auth
.user
,
611 lp_winbind_refresh_tickets(),
614 if (!NT_STATUS_IS_OK(result
)) {
615 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
620 result
= NT_STATUS_OK
;
626 /* we could have created a new credential cache with a valid tgt in it
627 * but we werent able to get or verify the service ticket for this
628 * local host and therefor didn't get the PAC, we need to remove that
629 * cache entirely now */
631 krb5_ret
= ads_kdestroy(cc
);
633 DEBUG(3,("winbindd_raw_kerberos_login: "
634 "could not destroy krb5 credential cache: "
635 "%s\n", error_message(krb5_ret
)));
638 if (!NT_STATUS_IS_OK(remove_ccache(state
->request
.data
.auth
.user
))) {
639 DEBUG(3,("winbindd_raw_kerberos_login: "
640 "could not remove ccache for user %s\n",
641 state
->request
.data
.auth
.user
));
645 data_blob_free(&session_key
);
646 data_blob_free(&session_key_krb5
);
647 data_blob_free(&ap_rep
);
648 data_blob_free(&tkt
);
650 SAFE_FREE(client_princ_out
);
652 if (!internal_ccache
) {
653 gain_root_privilege();
658 return NT_STATUS_NOT_SUPPORTED
;
659 #endif /* HAVE_KRB5 */
662 void winbindd_pam_auth(struct winbindd_cli_state
*state
)
664 struct winbindd_domain
*domain
;
665 fstring name_domain
, name_user
;
667 /* Ensure null termination */
668 state
->request
.data
.auth
.user
669 [sizeof(state
->request
.data
.auth
.user
)-1]='\0';
671 /* Ensure null termination */
672 state
->request
.data
.auth
.pass
673 [sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
675 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
676 state
->request
.data
.auth
.user
));
678 /* Parse domain and username */
680 if (!canonicalize_username(state
->request
.data
.auth
.user
,
681 name_domain
, name_user
)) {
682 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
683 DEBUG(5, ("Plain text authentication for %s returned %s "
685 state
->request
.data
.auth
.user
,
686 state
->response
.data
.auth
.nt_status_string
,
687 state
->response
.data
.auth
.pam_error
));
688 request_error(state
);
692 domain
= find_auth_domain(state
, name_domain
);
694 if (domain
== NULL
) {
695 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
696 DEBUG(5, ("Plain text authentication for %s returned %s "
698 state
->request
.data
.auth
.user
,
699 state
->response
.data
.auth
.nt_status_string
,
700 state
->response
.data
.auth
.pam_error
));
701 request_error(state
);
705 sendto_domain(state
, domain
);
708 NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
709 struct winbindd_cli_state
*state
,
710 NET_USER_INFO_3
**info3
)
712 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
713 uint16 max_allowed_bad_attempts
;
714 fstring name_domain
, name_user
;
716 enum SID_NAME_USE type
;
717 uchar new_nt_pass
[NT_HASH_LEN
];
718 const uint8
*cached_nt_pass
;
719 const uint8
*cached_salt
;
720 NET_USER_INFO_3
*my_info3
;
721 time_t kickoff_time
, must_change_time
;
722 BOOL password_good
= False
;
728 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
730 /* Parse domain and username */
732 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
735 if (!lookup_cached_name(state
->mem_ctx
,
740 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
741 return NT_STATUS_NO_SUCH_USER
;
744 if (type
!= SID_NAME_USER
) {
745 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
746 return NT_STATUS_LOGON_FAILURE
;
749 result
= winbindd_get_creds(domain
,
755 if (!NT_STATUS_IS_OK(result
)) {
756 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
762 E_md4hash(state
->request
.data
.auth
.pass
, new_nt_pass
);
765 dump_data(100, (const char *)new_nt_pass
, NT_HASH_LEN
);
766 dump_data(100, (const char *)cached_nt_pass
, NT_HASH_LEN
);
768 dump_data(100, (const char *)cached_salt
, NT_HASH_LEN
);
773 /* In this case we didn't store the nt_hash itself,
774 but the MD5 combination of salt + nt_hash. */
775 uchar salted_hash
[NT_HASH_LEN
];
776 E_md5hash(cached_salt
, new_nt_pass
, salted_hash
);
778 password_good
= (memcmp(cached_nt_pass
, salted_hash
, NT_HASH_LEN
) == 0) ?
781 /* Old cached cred - direct store of nt_hash (bad bad bad !). */
782 password_good
= (memcmp(cached_nt_pass
, new_nt_pass
, NT_HASH_LEN
) == 0) ?
788 /* User *DOES* know the password, update logon_time and reset
791 my_info3
->user_flgs
|= LOGON_CACHED_ACCOUNT
;
793 if (my_info3
->acct_flags
& ACB_AUTOLOCK
) {
794 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
797 if (my_info3
->acct_flags
& ACB_DISABLED
) {
798 return NT_STATUS_ACCOUNT_DISABLED
;
801 if (my_info3
->acct_flags
& ACB_WSTRUST
) {
802 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
805 if (my_info3
->acct_flags
& ACB_SVRTRUST
) {
806 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
809 if (my_info3
->acct_flags
& ACB_DOMTRUST
) {
810 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
813 /* The info3 acct_flags in NT4's samlogon reply don't have
816 if (!(my_info3
->acct_flags
& ACB_NORMAL
)) {
817 DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
818 my_info3
->acct_flags
));
819 return NT_STATUS_LOGON_FAILURE
;
822 kickoff_time
= nt_time_to_unix(&my_info3
->kickoff_time
);
823 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
824 return NT_STATUS_ACCOUNT_EXPIRED
;
827 must_change_time
= nt_time_to_unix(&my_info3
->pass_must_change_time
);
828 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
829 return NT_STATUS_PASSWORD_EXPIRED
;
832 /* FIXME: we possibly should handle logon hours as well (does xp when
833 * offline?) see auth/auth_sam.c:sam_account_ok for details */
835 unix_to_nt_time(&my_info3
->logon_time
, time(NULL
));
836 my_info3
->bad_pw_count
= 0;
838 result
= winbindd_update_creds_by_info3(domain
,
840 state
->request
.data
.auth
.user
,
841 state
->request
.data
.auth
.pass
,
843 if (!NT_STATUS_IS_OK(result
)) {
844 DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
850 /* FIXME: what else points out that the remote domain is AD ? */
851 if (!strequal(domain
->name
, domain
->alt_name
) &&
852 (state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
855 const char *cc
= NULL
;
857 const char *principal_s
= NULL
;
858 const char *service
= NULL
;
859 BOOL internal_ccache
= False
;
861 uid
= get_uid_from_state(state
);
863 DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
864 return NT_STATUS_INVALID_PARAMETER
;
867 cc
= generate_krb5_ccache(state
->mem_ctx
,
868 state
->request
.data
.auth
.krb5_cc_type
,
869 state
->request
.data
.auth
.uid
,
872 return NT_STATUS_NO_MEMORY
;
875 realm
= domain
->alt_name
;
878 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
879 if (principal_s
== NULL
) {
880 return NT_STATUS_NO_MEMORY
;
883 service
= talloc_asprintf(state
->mem_ctx
, "%s/%s@%s", KRB5_TGS_NAME
, realm
, realm
);
884 if (service
== NULL
) {
885 return NT_STATUS_NO_MEMORY
;
888 if (!internal_ccache
) {
890 setup_return_cc_name(state
, cc
);
892 result
= add_ccache_to_list(principal_s
,
895 state
->request
.data
.auth
.user
,
899 time(NULL
) + lp_winbind_cache_time(),
900 time(NULL
) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
,
901 lp_winbind_refresh_tickets(),
904 if (!NT_STATUS_IS_OK(result
)) {
905 DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
906 "to add ccache to list: %s\n",
911 #endif /* HAVE_KRB5 */
916 /* User does *NOT* know the correct password, modify info3 accordingly */
918 /* failure of this is not critical */
919 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
920 if (!NT_STATUS_IS_OK(result
)) {
921 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
922 "Won't be able to honour account lockout policies\n"));
925 /* increase counter */
926 my_info3
->bad_pw_count
++;
928 if (max_allowed_bad_attempts
== 0) {
933 if (my_info3
->bad_pw_count
>= max_allowed_bad_attempts
) {
935 uint32 password_properties
;
937 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
938 if (!NT_STATUS_IS_OK(result
)) {
939 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
942 if ((my_info3
->user_rid
!= DOMAIN_USER_RID_ADMIN
) ||
943 (password_properties
& DOMAIN_LOCKOUT_ADMINS
)) {
944 my_info3
->acct_flags
|= ACB_AUTOLOCK
;
949 result
= winbindd_update_creds_by_info3(domain
,
951 state
->request
.data
.auth
.user
,
955 if (!NT_STATUS_IS_OK(result
)) {
956 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
960 return NT_STATUS_LOGON_FAILURE
;
963 NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
964 struct winbindd_cli_state
*state
,
965 NET_USER_INFO_3
**info3
)
967 struct winbindd_domain
*contact_domain
;
968 fstring name_domain
, name_user
;
971 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
973 /* Parse domain and username */
975 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
977 /* what domain should we contact? */
980 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
981 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
982 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
983 result
= NT_STATUS_NO_SUCH_USER
;
988 if (is_myname(name_domain
)) {
989 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
990 result
= NT_STATUS_NO_SUCH_USER
;
994 contact_domain
= find_domain_from_name(name_domain
);
995 if (contact_domain
== NULL
) {
996 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
997 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
999 contact_domain
= find_our_domain();
1003 if (contact_domain
->initialized
&&
1004 contact_domain
->active_directory
) {
1008 if (!contact_domain
->initialized
) {
1009 init_dc_connection(contact_domain
);
1012 if (!contact_domain
->active_directory
) {
1013 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
1014 return NT_STATUS_INVALID_LOGON_TYPE
;
1017 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
1022 NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
1023 struct winbindd_cli_state
*state
,
1024 NET_USER_INFO_3
**info3
)
1027 struct rpc_pipe_client
*netlogon_pipe
;
1032 unsigned char local_lm_response
[24];
1033 unsigned char local_nt_response
[24];
1034 struct winbindd_domain
*contact_domain
;
1035 fstring name_domain
, name_user
;
1038 NET_USER_INFO_3
*my_info3
;
1040 ZERO_STRUCTP(info3
);
1044 my_info3
= TALLOC_ZERO_P(state
->mem_ctx
, NET_USER_INFO_3
);
1045 if (my_info3
== NULL
) {
1046 return NT_STATUS_NO_MEMORY
;
1050 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
1052 /* Parse domain and username */
1054 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1056 /* do password magic */
1059 generate_random_buffer(chal
, 8);
1060 if (lp_client_ntlmv2_auth()) {
1061 DATA_BLOB server_chal
;
1062 DATA_BLOB names_blob
;
1063 DATA_BLOB nt_response
;
1064 DATA_BLOB lm_response
;
1065 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
1067 /* note that the 'workgroup' here is a best guess - we don't know
1068 the server's domain at this point. The 'server name' is also
1071 names_blob
= NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
1073 if (!SMBNTLMv2encrypt(name_user
, name_domain
,
1074 state
->request
.data
.auth
.pass
,
1077 &lm_response
, &nt_response
, NULL
)) {
1078 data_blob_free(&names_blob
);
1079 data_blob_free(&server_chal
);
1080 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
1081 result
= NT_STATUS_NO_MEMORY
;
1084 data_blob_free(&names_blob
);
1085 data_blob_free(&server_chal
);
1086 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
1087 lm_response
.length
);
1088 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
1089 nt_response
.length
);
1090 data_blob_free(&lm_response
);
1091 data_blob_free(&nt_response
);
1094 if (lp_client_lanman_auth()
1095 && SMBencrypt(state
->request
.data
.auth
.pass
,
1097 local_lm_response
)) {
1098 lm_resp
= data_blob_talloc(state
->mem_ctx
,
1100 sizeof(local_lm_response
));
1102 lm_resp
= data_blob(NULL
, 0);
1104 SMBNTencrypt(state
->request
.data
.auth
.pass
,
1108 nt_resp
= data_blob_talloc(state
->mem_ctx
,
1110 sizeof(local_nt_response
));
1113 /* what domain should we contact? */
1116 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1117 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1118 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
1119 result
= NT_STATUS_NO_SUCH_USER
;
1124 if (is_myname(name_domain
)) {
1125 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1126 result
= NT_STATUS_NO_SUCH_USER
;
1130 contact_domain
= find_our_domain();
1133 /* check authentication loop */
1137 ZERO_STRUCTP(my_info3
);
1140 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1142 if (!NT_STATUS_IS_OK(result
)) {
1143 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1147 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1150 contact_domain
->dcname
, /* server name */
1151 name_user
, /* user name */
1152 name_domain
, /* target domain */
1153 global_myname(), /* workstation */
1160 /* We have to try a second time as cm_connect_netlogon
1161 might not yet have noticed that the DC has killed
1164 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1169 /* if we get access denied, a possible cause was that we had
1170 and open connection to the DC, but someone changed our
1171 machine account password out from underneath us using 'net
1172 rpc changetrustpw' */
1174 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1175 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1176 "ACCESS_DENIED. Maybe the trust account "
1177 "password was changed and we didn't know it. "
1178 "Killing connections to domain %s\n",
1180 invalidate_cm_connection(&contact_domain
->conn
);
1184 } while ( (attempts
< 2) && retry
);
1191 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1192 struct winbindd_cli_state
*state
)
1194 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1195 fstring name_domain
, name_user
;
1196 NET_USER_INFO_3
*info3
= NULL
;
1198 /* Ensure null termination */
1199 state
->request
.data
.auth
.user
[sizeof(state
->request
.data
.auth
.user
)-1]='\0';
1201 /* Ensure null termination */
1202 state
->request
.data
.auth
.pass
[sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
1204 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1205 state
->request
.data
.auth
.user
));
1207 /* Parse domain and username */
1209 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1211 if (domain
->online
== False
&& domain
->startup
) {
1212 /* Logons are very important to users. If we're offline and
1213 we get a request within the first 30 seconds of startup,
1214 try very hard to find a DC and go online. */
1216 DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
1217 "request in startup mode.\n", domain
->name
));
1219 winbindd_flush_negative_conn_cache(domain
);
1220 init_dc_connection(domain
);
1223 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1225 /* Check for Kerberos authentication */
1226 if (domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1228 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1230 if (NT_STATUS_IS_OK(result
)) {
1231 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1232 goto process_result
;
1234 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1237 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
) ||
1238 NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
1239 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1240 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1241 domain
->online
= False
;
1244 /* there are quite some NT_STATUS errors where there is no
1245 * point in retrying with a samlogon, we explictly have to take
1246 * care not to increase the bad logon counter on the DC */
1248 if (NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_DISABLED
) ||
1249 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_EXPIRED
) ||
1250 NT_STATUS_EQUAL(result
, NT_STATUS_ACCOUNT_LOCKED_OUT
) ||
1251 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_LOGON_HOURS
) ||
1252 NT_STATUS_EQUAL(result
, NT_STATUS_INVALID_WORKSTATION
) ||
1253 NT_STATUS_EQUAL(result
, NT_STATUS_LOGON_FAILURE
) ||
1254 NT_STATUS_EQUAL(result
, NT_STATUS_NO_SUCH_USER
) ||
1255 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_EXPIRED
) ||
1256 NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_MUST_CHANGE
) ||
1257 NT_STATUS_EQUAL(result
, NT_STATUS_WRONG_PASSWORD
)) {
1258 goto process_result
;
1261 if (state
->request
.flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1262 DEBUG(3,("falling back to samlogon\n"));
1270 /* Check for Samlogon authentication */
1271 if (domain
->online
) {
1272 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1274 if (NT_STATUS_IS_OK(result
)) {
1275 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1276 goto process_result
;
1278 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result
)));
1279 if (domain
->online
) {
1280 /* We're still online - fail. */
1283 /* Else drop through and see if we can check offline.... */
1288 /* Check for Cached logons */
1289 if (!domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1290 lp_winbind_offline_logon()) {
1292 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1294 if (NT_STATUS_IS_OK(result
)) {
1295 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1296 goto process_result
;
1298 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1305 if (NT_STATUS_IS_OK(result
)) {
1309 /* In all codepaths where result == NT_STATUS_OK info3 must have
1310 been initialized. */
1312 result
= NT_STATUS_INTERNAL_ERROR
;
1316 netsamlogon_cache_store(name_user
, info3
);
1317 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1319 /* save name_to_sid info as early as possible */
1320 sid_compose(&user_sid
, &info3
->dom_sid
.sid
, info3
->user_rid
);
1321 cache_name2sid(domain
, name_domain
, name_user
, SID_NAME_USER
, &user_sid
);
1323 /* Check if the user is in the right group */
1325 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1326 state
->request
.data
.auth
.require_membership_of_sid
))) {
1327 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1328 state
->request
.data
.auth
.user
,
1329 state
->request
.data
.auth
.require_membership_of_sid
));
1333 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1334 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
1335 if (!NT_STATUS_IS_OK(result
)) {
1336 DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result
)));
1341 if (state
->request
.flags
& WBFLAG_PAM_INFO3_TEXT
) {
1342 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
1343 if (!NT_STATUS_IS_OK(result
)) {
1344 DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result
)));
1350 if ((state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
1352 /* Store in-memory creds for single-signon using ntlm_auth. */
1353 result
= winbindd_add_memory_creds(state
->request
.data
.auth
.user
,
1354 get_uid_from_state(state
),
1355 state
->request
.data
.auth
.pass
);
1357 if (!NT_STATUS_IS_OK(result
)) {
1358 DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result
)));
1362 if (lp_winbind_offline_logon()) {
1363 result
= winbindd_store_creds(domain
,
1365 state
->request
.data
.auth
.user
,
1366 state
->request
.data
.auth
.pass
,
1368 if (!NT_STATUS_IS_OK(result
)) {
1370 /* Release refcount. */
1371 winbindd_delete_memory_creds(state
->request
.data
.auth
.user
);
1373 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1379 result
= fillup_password_policy(domain
, state
);
1381 if (!NT_STATUS_IS_OK(result
)) {
1382 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result
)));
1386 if (state
->request
.flags
& WBFLAG_PAM_UNIX_NAME
) {
1387 /* We've been asked to return the unix username, per
1388 'winbind use default domain' settings and the like */
1390 fstring username_out
;
1391 const char *nt_username
, *nt_domain
;
1393 if (!(nt_username
= unistr2_tdup(state
->mem_ctx
, &info3
->uni_user_name
))) {
1394 /* If the server didn't give us one, just use the one we sent them */
1395 nt_username
= name_user
;
1398 if (!(nt_domain
= unistr2_tdup(state
->mem_ctx
, &info3
->uni_logon_dom
))) {
1399 /* If the server didn't give us one, just use the one we sent them */
1400 nt_domain
= name_domain
;
1403 fill_domain_username(username_out
, nt_domain
, nt_username
, True
);
1405 DEBUG(5, ("Setting unix username to [%s]\n", username_out
));
1407 SAFE_FREE(state
->response
.extra_data
.data
);
1408 state
->response
.extra_data
.data
= SMB_STRDUP(username_out
);
1409 if (!state
->response
.extra_data
.data
) {
1410 result
= NT_STATUS_NO_MEMORY
;
1413 state
->response
.length
+=
1414 strlen((const char *)state
->response
.extra_data
.data
)+1;
1420 /* give us a more useful (more correct?) error code */
1421 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1422 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1423 result
= NT_STATUS_NO_LOGON_SERVERS
;
1426 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1427 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1429 /* we might have given a more useful error above */
1430 if (!*state
->response
.data
.auth
.error_string
)
1431 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1432 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1434 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1435 state
->request
.data
.auth
.user
,
1436 state
->response
.data
.auth
.nt_status_string
,
1437 state
->response
.data
.auth
.pam_error
));
1439 if ( NT_STATUS_IS_OK(result
) && info3
&&
1440 (state
->request
.flags
& WBFLAG_PAM_AFS_TOKEN
) ) {
1442 char *afsname
= talloc_strdup(state
->mem_ctx
,
1443 lp_afs_username_map());
1446 if (afsname
== NULL
) {
1450 afsname
= talloc_string_sub(state
->mem_ctx
,
1451 lp_afs_username_map(),
1453 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1455 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1462 sid_copy(&user_sid
, &info3
->dom_sid
.sid
);
1463 sid_append_rid(&user_sid
, info3
->user_rid
);
1464 sid_to_string(sidstr
, &user_sid
);
1465 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1469 if (afsname
== NULL
) {
1473 strlower_m(afsname
);
1475 DEBUG(10, ("Generating token for user %s\n", afsname
));
1477 cell
= strchr(afsname
, '@');
1486 /* Append an AFS token string */
1487 SAFE_FREE(state
->response
.extra_data
.data
);
1488 state
->response
.extra_data
.data
=
1489 afs_createtoken_str(afsname
, cell
);
1491 if (state
->response
.extra_data
.data
!= NULL
) {
1492 state
->response
.length
+=
1493 strlen((const char *)state
->response
.extra_data
.data
)+1;
1497 TALLOC_FREE(afsname
);
1500 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1504 /**********************************************************************
1505 Challenge Response Authentication Protocol
1506 **********************************************************************/
1508 void winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
1510 struct winbindd_domain
*domain
= NULL
;
1511 const char *domain_name
= NULL
;
1514 if (!state
->privileged
) {
1515 char *error_string
= NULL
;
1516 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1518 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1519 "on %s are set correctly.\n",
1520 get_winbind_priv_pipe_dir()));
1521 /* send a better message than ACCESS_DENIED */
1522 error_string
= talloc_asprintf(state
->mem_ctx
,
1523 "winbind client not authorized "
1524 "to use winbindd_pam_auth_crap."
1525 " Ensure permissions on %s "
1526 "are set correctly.",
1527 get_winbind_priv_pipe_dir());
1528 fstrcpy(state
->response
.data
.auth
.error_string
, error_string
);
1529 result
= NT_STATUS_ACCESS_DENIED
;
1533 /* Ensure null termination */
1534 state
->request
.data
.auth_crap
.user
1535 [sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1536 state
->request
.data
.auth_crap
.domain
1537 [sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1539 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1540 (unsigned long)state
->pid
,
1541 state
->request
.data
.auth_crap
.domain
,
1542 state
->request
.data
.auth_crap
.user
));
1544 if (*state
->request
.data
.auth_crap
.domain
!= '\0') {
1545 domain_name
= state
->request
.data
.auth_crap
.domain
;
1546 } else if (lp_winbind_use_default_domain()) {
1547 domain_name
= lp_workgroup();
1550 if (domain_name
!= NULL
)
1551 domain
= find_auth_domain(state
, domain_name
);
1553 if (domain
!= NULL
) {
1554 sendto_domain(state
, domain
);
1558 result
= NT_STATUS_NO_SUCH_USER
;
1561 set_auth_errors(&state
->response
, result
);
1562 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1563 state
->request
.data
.auth_crap
.domain
,
1564 state
->request
.data
.auth_crap
.user
,
1565 state
->response
.data
.auth
.nt_status_string
,
1566 state
->response
.data
.auth
.pam_error
));
1567 request_error(state
);
1572 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1573 struct winbindd_cli_state
*state
)
1576 NET_USER_INFO_3 info3
;
1577 struct rpc_pipe_client
*netlogon_pipe
;
1578 const char *name_user
= NULL
;
1579 const char *name_domain
= NULL
;
1580 const char *workstation
;
1581 struct winbindd_domain
*contact_domain
;
1585 DATA_BLOB lm_resp
, nt_resp
;
1587 /* This is child-only, so no check for privileged access is needed
1590 /* Ensure null termination */
1591 state
->request
.data
.auth_crap
.user
[sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1592 state
->request
.data
.auth_crap
.domain
[sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1594 name_user
= state
->request
.data
.auth_crap
.user
;
1596 if (*state
->request
.data
.auth_crap
.domain
) {
1597 name_domain
= state
->request
.data
.auth_crap
.domain
;
1598 } else if (lp_winbind_use_default_domain()) {
1599 name_domain
= lp_workgroup();
1601 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1603 result
= NT_STATUS_NO_SUCH_USER
;
1607 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1608 name_domain
, name_user
));
1610 if (*state
->request
.data
.auth_crap
.workstation
) {
1611 workstation
= state
->request
.data
.auth_crap
.workstation
;
1613 workstation
= global_myname();
1616 if (state
->request
.data
.auth_crap
.lm_resp_len
> sizeof(state
->request
.data
.auth_crap
.lm_resp
)
1617 || state
->request
.data
.auth_crap
.nt_resp_len
> sizeof(state
->request
.data
.auth_crap
.nt_resp
)) {
1618 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1619 state
->request
.data
.auth_crap
.lm_resp_len
,
1620 state
->request
.data
.auth_crap
.nt_resp_len
));
1621 result
= NT_STATUS_INVALID_PARAMETER
;
1625 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.lm_resp
,
1626 state
->request
.data
.auth_crap
.lm_resp_len
);
1627 nt_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.nt_resp
,
1628 state
->request
.data
.auth_crap
.nt_resp_len
);
1630 /* what domain should we contact? */
1633 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1634 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1635 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1636 result
= NT_STATUS_NO_SUCH_USER
;
1640 if (is_myname(name_domain
)) {
1641 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1642 result
= NT_STATUS_NO_SUCH_USER
;
1645 contact_domain
= find_our_domain();
1652 netlogon_pipe
= NULL
;
1653 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1655 if (!NT_STATUS_IS_OK(result
)) {
1656 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1657 nt_errstr(result
)));
1661 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1663 state
->request
.data
.auth_crap
.logon_parameters
,
1664 contact_domain
->dcname
,
1667 /* Bug #3248 - found by Stefan Burkei. */
1668 workstation
, /* We carefully set this above so use it... */
1669 state
->request
.data
.auth_crap
.chal
,
1676 /* We have to try a second time as cm_connect_netlogon
1677 might not yet have noticed that the DC has killed
1680 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1685 /* if we get access denied, a possible cause was that we had and open
1686 connection to the DC, but someone changed our machine account password
1687 out from underneath us using 'net rpc changetrustpw' */
1689 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1690 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1691 "ACCESS_DENIED. Maybe the trust account "
1692 "password was changed and we didn't know it. "
1693 "Killing connections to domain %s\n",
1695 invalidate_cm_connection(&contact_domain
->conn
);
1699 } while ( (attempts
< 2) && retry
);
1701 if (NT_STATUS_IS_OK(result
)) {
1703 netsamlogon_cache_store(name_user
, &info3
);
1704 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), &info3
);
1706 /* Check if the user is in the right group */
1708 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, &info3
,
1709 state
->request
.data
.auth_crap
.require_membership_of_sid
))) {
1710 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1711 state
->request
.data
.auth_crap
.user
,
1712 state
->request
.data
.auth_crap
.require_membership_of_sid
));
1716 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1717 result
= append_info3_as_ndr(state
->mem_ctx
, state
, &info3
);
1718 } else if (state
->request
.flags
& WBFLAG_PAM_UNIX_NAME
) {
1719 /* ntlm_auth should return the unix username, per
1720 'winbind use default domain' settings and the like */
1722 fstring username_out
;
1723 const char *nt_username
, *nt_domain
;
1724 if (!(nt_username
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_user_name
)))) {
1725 /* If the server didn't give us one, just use the one we sent them */
1726 nt_username
= name_user
;
1729 if (!(nt_domain
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_logon_dom
)))) {
1730 /* If the server didn't give us one, just use the one we sent them */
1731 nt_domain
= name_domain
;
1734 fill_domain_username(username_out
, nt_domain
, nt_username
, True
);
1736 DEBUG(5, ("Setting unix username to [%s]\n", username_out
));
1738 SAFE_FREE(state
->response
.extra_data
.data
);
1739 state
->response
.extra_data
.data
= SMB_STRDUP(username_out
);
1740 if (!state
->response
.extra_data
.data
) {
1741 result
= NT_STATUS_NO_MEMORY
;
1744 state
->response
.length
+=
1745 strlen((const char *)state
->response
.extra_data
.data
)+1;
1748 if (state
->request
.flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
1749 memcpy(state
->response
.data
.auth
.user_session_key
, info3
.user_sess_key
,
1750 sizeof(state
->response
.data
.auth
.user_session_key
) /* 16 */);
1752 if (state
->request
.flags
& WBFLAG_PAM_LMKEY
) {
1753 memcpy(state
->response
.data
.auth
.first_8_lm_hash
, info3
.lm_sess_key
,
1754 sizeof(state
->response
.data
.auth
.first_8_lm_hash
) /* 8 */);
1760 /* give us a more useful (more correct?) error code */
1761 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1762 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1763 result
= NT_STATUS_NO_LOGON_SERVERS
;
1766 if (state
->request
.flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1767 result
= nt_status_squash(result
);
1770 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1771 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1773 /* we might have given a more useful error above */
1774 if (!*state
->response
.data
.auth
.error_string
) {
1775 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1777 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1779 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1780 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1783 state
->response
.data
.auth
.nt_status_string
,
1784 state
->response
.data
.auth
.pam_error
));
1786 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1789 /* Change a user password */
1791 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
1793 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1795 char *newpass
= NULL
;
1796 fstring domain
, user
;
1798 struct winbindd_domain
*contact_domain
;
1799 struct rpc_pipe_client
*cli
;
1800 BOOL got_info
= False
;
1801 SAM_UNK_INFO_1 info
;
1802 SAMR_CHANGE_REJECT reject
;
1804 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
1805 state
->request
.data
.chauthtok
.user
));
1809 if (!canonicalize_username(state
->request
.data
.chauthtok
.user
, domain
, user
)) {
1810 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
1811 DEBUG(5, ("winbindd_pam_chauthtok: canonicalize_username %s failed with %s"
1813 state
->request
.data
.auth
.user
,
1814 state
->response
.data
.auth
.nt_status_string
,
1815 state
->response
.data
.auth
.pam_error
));
1816 request_error(state
);
1820 contact_domain
= find_domain_from_name(domain
);
1821 if (!contact_domain
) {
1822 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
1823 state
->request
.data
.chauthtok
.user
, domain
, user
, domain
));
1824 result
= NT_STATUS_NO_SUCH_USER
;
1828 /* Change password */
1830 oldpass
= state
->request
.data
.chauthtok
.oldpass
;
1831 newpass
= state
->request
.data
.chauthtok
.newpass
;
1833 /* Initialize reject reason */
1834 state
->response
.data
.auth
.reject_reason
= Undefined
;
1836 /* Get sam handle */
1838 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1840 if (!NT_STATUS_IS_OK(result
)) {
1841 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
1845 result
= rpccli_samr_chgpasswd3(cli
, state
->mem_ctx
, user
, newpass
, oldpass
, &info
, &reject
);
1847 /* FIXME: need to check for other error codes ? */
1848 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
)) {
1850 state
->response
.data
.auth
.policy
.min_length_password
=
1851 info
.min_length_password
;
1852 state
->response
.data
.auth
.policy
.password_history
=
1853 info
.password_history
;
1854 state
->response
.data
.auth
.policy
.password_properties
=
1855 info
.password_properties
;
1856 state
->response
.data
.auth
.policy
.expire
=
1857 nt_time_to_unix_abs(&info
.expire
);
1858 state
->response
.data
.auth
.policy
.min_passwordage
=
1859 nt_time_to_unix_abs(&info
.min_passwordage
);
1861 state
->response
.data
.auth
.reject_reason
=
1862 reject
.reject_reason
;
1866 /* only fallback when the chgpasswd3 call is not supported */
1867 } else if ((NT_STATUS_EQUAL(result
, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR
))) ||
1868 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_SUPPORTED
)) ||
1869 (NT_STATUS_EQUAL(result
, NT_STATUS_NOT_IMPLEMENTED
))) {
1871 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
1872 nt_errstr(result
)));
1874 result
= rpccli_samr_chgpasswd_user(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
1879 if (NT_STATUS_IS_OK(result
) && (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
)) {
1881 /* Update the single sign-on memory creds. */
1882 result
= winbindd_replace_memory_creds(state
->request
.data
.chauthtok
.user
,
1885 if (!NT_STATUS_IS_OK(result
)) {
1886 DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result
)));
1887 goto process_result
;
1890 if (lp_winbind_offline_logon()) {
1891 result
= winbindd_update_creds_by_name(contact_domain
,
1892 state
->mem_ctx
, user
,
1894 if (!NT_STATUS_IS_OK(result
)) {
1895 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1896 goto process_result
;
1901 if (!NT_STATUS_IS_OK(result
) && !got_info
&& contact_domain
) {
1903 NTSTATUS policy_ret
;
1905 policy_ret
= fillup_password_policy(contact_domain
, state
);
1907 /* failure of this is non critical, it will just provide no
1908 * additional information to the client why the change has
1909 * failed - Guenther */
1911 if (!NT_STATUS_IS_OK(policy_ret
)) {
1912 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
1913 goto process_result
;
1919 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1920 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1921 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1922 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1924 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1925 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1928 state
->response
.data
.auth
.nt_status_string
,
1929 state
->response
.data
.auth
.pam_error
));
1931 if (NT_STATUS_IS_OK(result
)) {
1934 request_error(state
);
1938 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
1940 struct winbindd_domain
*domain
;
1941 fstring name_domain
, user
;
1943 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
1944 state
->request
.data
.logoff
.user
));
1946 /* Ensure null termination */
1947 state
->request
.data
.logoff
.user
1948 [sizeof(state
->request
.data
.logoff
.user
)-1]='\0';
1950 state
->request
.data
.logoff
.krb5ccname
1951 [sizeof(state
->request
.data
.logoff
.krb5ccname
)-1]='\0';
1953 if (!canonicalize_username(state
->request
.data
.logoff
.user
, name_domain
, user
)) {
1957 if ((domain
= find_auth_domain(state
, name_domain
)) == NULL
) {
1961 sendto_domain(state
, domain
);
1965 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
1966 DEBUG(5, ("Pam Logoff for %s returned %s "
1968 state
->request
.data
.logoff
.user
,
1969 state
->response
.data
.auth
.nt_status_string
,
1970 state
->response
.data
.auth
.pam_error
));
1971 request_error(state
);
1975 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
1976 struct winbindd_cli_state
*state
)
1978 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
1983 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
1984 state
->request
.data
.logoff
.user
));
1986 if (!(state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1987 result
= NT_STATUS_OK
;
1988 goto process_result
;
1991 if (state
->request
.data
.logoff
.krb5ccname
[0] == '\0') {
1992 result
= NT_STATUS_OK
;
1993 goto process_result
;
1998 if (state
->request
.data
.logoff
.uid
< 0) {
1999 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
2000 goto process_result
;
2003 /* what we need here is to find the corresponding krb5 ccache name *we*
2004 * created for a given username and destroy it (as the user who created it) */
2006 if (!ccache_entry_identical(state
->request
.data
.logoff
.user
,
2007 state
->request
.data
.logoff
.uid
,
2008 state
->request
.data
.logoff
.krb5ccname
)) {
2009 DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n"));
2010 goto process_result
;
2013 ret
= ads_kdestroy(state
->request
.data
.logoff
.krb5ccname
);
2016 DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n",
2017 state
->request
.data
.logoff
.krb5ccname
, error_message(ret
)));
2019 DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n",
2020 state
->request
.data
.logoff
.krb5ccname
, state
->request
.data
.logoff
.user
));
2023 remove_ccache(state
->request
.data
.logoff
.user
);
2025 result
= krb5_to_nt_status(ret
);
2027 result
= NT_STATUS_NOT_SUPPORTED
;
2032 winbindd_delete_memory_creds(state
->request
.data
.logoff
.user
);
2034 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
2035 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
2036 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
2037 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
2039 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
2042 /* Change user password with auth crap*/
2044 void winbindd_pam_chng_pswd_auth_crap(struct winbindd_cli_state
*state
)
2046 struct winbindd_domain
*domain
= NULL
;
2047 const char *domain_name
= NULL
;
2049 /* Ensure null termination */
2050 state
->request
.data
.chng_pswd_auth_crap
.user
[
2051 sizeof(state
->request
.data
.chng_pswd_auth_crap
.user
)-1]=0;
2052 state
->request
.data
.chng_pswd_auth_crap
.domain
[
2053 sizeof(state
->request
.data
.chng_pswd_auth_crap
.domain
)-1]=0;
2055 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2056 (unsigned long)state
->pid
,
2057 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2058 state
->request
.data
.chng_pswd_auth_crap
.user
));
2060 if (*state
->request
.data
.chng_pswd_auth_crap
.domain
!= '\0') {
2061 domain_name
= state
->request
.data
.chng_pswd_auth_crap
.domain
;
2062 } else if (lp_winbind_use_default_domain()) {
2063 domain_name
= lp_workgroup();
2066 if (domain_name
!= NULL
)
2067 domain
= find_domain_from_name(domain_name
);
2069 if (domain
!= NULL
) {
2070 DEBUG(7, ("[%5lu]: pam auth crap changing pswd in domain: "
2071 "%s\n", (unsigned long)state
->pid
,domain
->name
));
2072 sendto_domain(state
, domain
);
2076 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
2077 DEBUG(5, ("CRAP change password for %s\\%s returned %s (PAM: %d)\n",
2078 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2079 state
->request
.data
.chng_pswd_auth_crap
.user
,
2080 state
->response
.data
.auth
.nt_status_string
,
2081 state
->response
.data
.auth
.pam_error
));
2082 request_error(state
);
2086 enum winbindd_result
winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain
*domainSt
, struct winbindd_cli_state
*state
)
2089 DATA_BLOB new_nt_password
;
2090 DATA_BLOB old_nt_hash_enc
;
2091 DATA_BLOB new_lm_password
;
2092 DATA_BLOB old_lm_hash_enc
;
2093 fstring domain
,user
;
2095 struct winbindd_domain
*contact_domain
= domainSt
;
2096 struct rpc_pipe_client
*cli
;
2098 /* Ensure null termination */
2099 state
->request
.data
.chng_pswd_auth_crap
.user
[
2100 sizeof(state
->request
.data
.chng_pswd_auth_crap
.user
)-1]=0;
2101 state
->request
.data
.chng_pswd_auth_crap
.domain
[
2102 sizeof(state
->request
.data
.chng_pswd_auth_crap
.domain
)-1]=0;
2106 DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
2107 (unsigned long)state
->pid
,
2108 state
->request
.data
.chng_pswd_auth_crap
.domain
,
2109 state
->request
.data
.chng_pswd_auth_crap
.user
));
2111 if (lp_winbind_offline_logon()) {
2112 DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
2113 DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
2114 result
= NT_STATUS_ACCESS_DENIED
;
2118 if (*state
->request
.data
.chng_pswd_auth_crap
.domain
) {
2119 fstrcpy(domain
,state
->request
.data
.chng_pswd_auth_crap
.domain
);
2121 parse_domain_user(state
->request
.data
.chng_pswd_auth_crap
.user
,
2125 DEBUG(3,("no domain specified with username (%s) - "
2127 state
->request
.data
.chng_pswd_auth_crap
.user
));
2128 result
= NT_STATUS_NO_SUCH_USER
;
2133 if (!*domain
&& lp_winbind_use_default_domain()) {
2134 fstrcpy(domain
,(char *)lp_workgroup());
2138 fstrcpy(user
, state
->request
.data
.chng_pswd_auth_crap
.user
);
2141 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n",
2142 (unsigned long)state
->pid
, domain
, user
));
2144 /* Change password */
2145 new_nt_password
= data_blob_talloc(
2147 state
->request
.data
.chng_pswd_auth_crap
.new_nt_pswd
,
2148 state
->request
.data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
2150 old_nt_hash_enc
= data_blob_talloc(
2152 state
->request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
2153 state
->request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
2155 if(state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
> 0) {
2156 new_lm_password
= data_blob_talloc(
2158 state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd
,
2159 state
->request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
2161 old_lm_hash_enc
= data_blob_talloc(
2163 state
->request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
2164 state
->request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
2166 new_lm_password
.length
= 0;
2167 old_lm_hash_enc
.length
= 0;
2170 /* Get sam handle */
2172 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
, &dom_pol
);
2173 if (!NT_STATUS_IS_OK(result
)) {
2174 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
2178 result
= rpccli_samr_chng_pswd_auth_crap(
2179 cli
, state
->mem_ctx
, user
, new_nt_password
, old_nt_hash_enc
,
2180 new_lm_password
, old_lm_hash_enc
);
2183 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
2184 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
2185 fstrcpy(state
->response
.data
.auth
.error_string
,
2186 get_friendly_nt_error_msg(result
));
2187 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
2189 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
2190 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
2192 state
->response
.data
.auth
.nt_status_string
,
2193 state
->response
.data
.auth
.pam_error
));
2195 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;