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 state
->response
.extra_data
= SMB_MALLOC(size
);
101 if (!state
->response
.extra_data
) {
103 return NT_STATUS_NO_MEMORY
;
105 memset( state
->response
.extra_data
, '\0', size
);
106 prs_copy_all_data_out(state
->response
.extra_data
, &ps
);
107 state
->response
.length
+= size
;
112 static NTSTATUS
check_info3_in_group(TALLOC_CTX
*mem_ctx
,
113 NET_USER_INFO_3
*info3
,
114 const char *group_sid
)
116 DOM_SID require_membership_of_sid
;
118 size_t num_all_sids
= (2 + info3
->num_groups2
+ info3
->num_other_sids
);
121 /* Parse the 'required group' SID */
123 if (!group_sid
|| !group_sid
[0]) {
124 /* NO sid supplied, all users may access */
128 if (!string_to_sid(&require_membership_of_sid
, group_sid
)) {
129 DEBUG(0, ("check_info3_in_group: could not parse %s as a SID!",
132 return NT_STATUS_INVALID_PARAMETER
;
135 all_sids
= TALLOC_ARRAY(mem_ctx
, DOM_SID
, num_all_sids
);
137 return NT_STATUS_NO_MEMORY
;
139 /* and create (by appending rids) the 'domain' sids */
141 sid_copy(&all_sids
[0], &(info3
->dom_sid
.sid
));
143 if (!sid_append_rid(&all_sids
[0], info3
->user_rid
)) {
144 DEBUG(3,("could not append user's primary RID 0x%x\n",
147 return NT_STATUS_INVALID_PARAMETER
;
151 sid_copy(&all_sids
[1], &(info3
->dom_sid
.sid
));
153 if (!sid_append_rid(&all_sids
[1], info3
->group_rid
)) {
154 DEBUG(3,("could not append additional group rid 0x%x\n",
157 return NT_STATUS_INVALID_PARAMETER
;
161 for (i
= 0; i
< info3
->num_groups2
; i
++) {
163 sid_copy(&all_sids
[j
], &(info3
->dom_sid
.sid
));
165 if (!sid_append_rid(&all_sids
[j
], info3
->gids
[i
].g_rid
)) {
166 DEBUG(3,("could not append additional group rid 0x%x\n",
167 info3
->gids
[i
].g_rid
));
169 return NT_STATUS_INVALID_PARAMETER
;
174 /* Copy 'other' sids. We need to do sid filtering here to
175 prevent possible elevation of privileges. See:
177 http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
180 for (i
= 0; i
< info3
->num_other_sids
; i
++) {
181 sid_copy(&all_sids
[info3
->num_groups2
+ i
+ 2],
182 &info3
->other_sids
[i
].sid
);
186 for (i
= 0; i
< j
; i
++) {
188 DEBUG(10, ("User has SID: %s\n",
189 sid_to_string(sid1
, &all_sids
[i
])));
190 if (sid_equal(&require_membership_of_sid
, &all_sids
[i
])) {
191 DEBUG(10, ("SID %s matches %s - user permitted to authenticate!\n",
192 sid_to_string(sid1
, &require_membership_of_sid
), sid_to_string(sid2
, &all_sids
[i
])));
197 /* Do not distinguish this error from a wrong username/pw */
199 return NT_STATUS_LOGON_FAILURE
;
202 static struct winbindd_domain
*find_auth_domain(struct winbindd_cli_state
*state
,
203 const char *domain_name
)
205 struct winbindd_domain
*domain
;
208 domain
= find_domain_from_name_noinit(domain_name
);
209 if (domain
== NULL
) {
210 DEBUG(3, ("Authentication for domain [%s] refused"
211 "as it is not a trusted domain\n",
217 if (is_myname(domain_name
)) {
218 DEBUG(3, ("Authentication for domain %s (local domain "
219 "to this server) not supported at this "
220 "stage\n", domain_name
));
224 /* we can auth against trusted domains */
225 if (state
->request
.flags
& WBFLAG_PAM_CONTACT_TRUSTDOM
) {
226 domain
= find_domain_from_name_noinit(domain_name
);
227 if (domain
== NULL
) {
228 DEBUG(3, ("Authentication for domain [%s] skipped "
229 "as it is not a trusted domain\n",
236 return find_our_domain();
239 static void set_auth_errors(struct winbindd_response
*resp
, NTSTATUS result
)
241 resp
->data
.auth
.nt_status
= NT_STATUS_V(result
);
242 fstrcpy(resp
->data
.auth
.nt_status_string
, nt_errstr(result
));
244 /* we might have given a more useful error above */
245 if (*resp
->data
.auth
.error_string
== '\0')
246 fstrcpy(resp
->data
.auth
.error_string
,
247 get_friendly_nt_error_msg(result
));
248 resp
->data
.auth
.pam_error
= nt_status_to_pam(result
);
251 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
252 struct winbindd_cli_state
*state
)
254 struct winbindd_methods
*methods
;
255 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
256 SAM_UNK_INFO_1 password_policy
;
258 methods
= domain
->methods
;
260 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
261 if (NT_STATUS_IS_ERR(status
)) {
265 state
->response
.data
.auth
.policy
.min_length_password
=
266 password_policy
.min_length_password
;
267 state
->response
.data
.auth
.policy
.password_history
=
268 password_policy
.password_history
;
269 state
->response
.data
.auth
.policy
.password_properties
=
270 password_policy
.password_properties
;
271 state
->response
.data
.auth
.policy
.expire
=
272 nt_time_to_unix_abs(&(password_policy
.expire
));
273 state
->response
.data
.auth
.policy
.min_passwordage
=
274 nt_time_to_unix_abs(&(password_policy
.min_passwordage
));
279 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
281 uint16
*max_allowed_bad_attempts
)
283 struct winbindd_methods
*methods
;
284 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
285 SAM_UNK_INFO_12 lockout_policy
;
287 *max_allowed_bad_attempts
= 0;
289 methods
= domain
->methods
;
291 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
292 if (NT_STATUS_IS_ERR(status
)) {
296 *max_allowed_bad_attempts
= lockout_policy
.bad_attempt_lockout
;
302 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
305 BOOL
*internal_ccache
)
307 /* accept KCM, FILE and WRFILE as krb5_cc_type from the client and then
308 * build the full ccname string based on the user's uid here -
311 const char *gen_cc
= NULL
;
313 *internal_ccache
= True
;
319 if (!type
|| type
[0] == '\0') {
323 if (strequal(type
, "FILE")) {
324 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
325 } else if (strequal(type
, "WRFILE")) {
326 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
328 } else if (strequal(type
, "KCM")) {
329 gen_cc
= talloc_asprintf(mem_ctx
, "KCM:%d", uid
);
332 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
336 *internal_ccache
= False
;
340 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbind_cache");
343 if (gen_cc
== NULL
) {
344 DEBUG(0,("out of memory\n"));
348 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
353 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
357 uid
= state
->request
.data
.auth
.uid
;
360 DEBUG(1,("invalid uid: '%d'\n", uid
));
366 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
368 const char *type
= state
->request
.data
.auth
.krb5_cc_type
;
370 state
->response
.data
.auth
.krb5ccname
[0] = '\0';
372 if (type
[0] == '\0') {
376 if (!strequal(type
, "FILE") &&
378 !strequal(type
, "KCM") &&
380 !strequal(type
, "WRFILE")) {
381 DEBUG(10,("won't return krbccname for a %s type ccache\n",
386 fstrcpy(state
->response
.data
.auth
.krb5ccname
, cc
);
389 /**********************************************************************
390 Authenticate a user with a clear text password using Kerberos and fill up
392 **********************************************************************/
393 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
394 struct winbindd_cli_state
*state
,
395 NET_USER_INFO_3
**info3
)
398 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
399 krb5_error_code krb5_ret
;
400 DATA_BLOB tkt
, session_key_krb5
;
401 DATA_BLOB ap_rep
, session_key
;
402 PAC_DATA
*pac_data
= NULL
;
403 PAC_LOGON_INFO
*logon_info
= NULL
;
404 char *client_princ
= NULL
;
405 char *client_princ_out
= NULL
;
406 char *local_service
= NULL
;
407 const char *cc
= NULL
;
408 const char *principal_s
= NULL
;
409 const char *service
= NULL
;
411 fstring name_domain
, name_user
;
412 time_t ticket_lifetime
= 0;
413 time_t renewal_until
= 0;
416 time_t time_offset
= 0;
417 BOOL internal_ccache
= True
;
419 ZERO_STRUCT(session_key
);
420 ZERO_STRUCT(session_key_krb5
);
429 * prepare a krb5_cc_cache string for the user */
431 uid
= get_uid_from_state(state
);
433 DEBUG(0,("no valid uid\n"));
436 cc
= generate_krb5_ccache(state
->mem_ctx
,
437 state
->request
.data
.auth
.krb5_cc_type
,
438 state
->request
.data
.auth
.uid
,
441 return NT_STATUS_NO_MEMORY
;
446 * get kerberos properties */
448 if (domain
->private_data
) {
449 ads
= (ADS_STRUCT
*)domain
->private_data
;
450 time_offset
= ads
->auth
.time_offset
;
455 * do kerberos auth and setup ccache as the user */
457 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
459 realm
= domain
->alt_name
;
462 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
463 if (principal_s
== NULL
) {
464 return NT_STATUS_NO_MEMORY
;
467 service
= talloc_asprintf(state
->mem_ctx
, "krbtgt/%s@%s", realm
, realm
);
468 if (service
== NULL
) {
469 return NT_STATUS_NO_MEMORY
;
472 /* if this is a user ccache, we need to act as the user to let the krb5
473 * library handle the chown, etc. */
475 /************************ NON-ROOT **********************/
477 if (!internal_ccache
) {
480 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
483 krb5_ret
= kerberos_kinit_password(principal_s
,
484 state
->request
.data
.auth
.pass
,
490 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
);
493 DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n",
494 principal_s
, error_message(krb5_ret
), krb5_ret
));
495 result
= krb5_to_nt_status(krb5_ret
);
499 /* does http_timestring use heimdals libroken strftime?? - Guenther */
500 DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n",
502 http_timestring(ticket_lifetime
), (int)ticket_lifetime
,
503 http_timestring(renewal_until
), (int)renewal_until
));
505 client_princ
= talloc_strdup(state
->mem_ctx
, global_myname());
506 if (client_princ
== NULL
) {
507 result
= NT_STATUS_NO_MEMORY
;
510 strlower_m(client_princ
);
512 local_service
= talloc_asprintf(state
->mem_ctx
, "HOST/%s@%s", client_princ
, lp_realm());
513 if (local_service
== NULL
) {
514 DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
515 result
= NT_STATUS_NO_MEMORY
;
519 krb5_ret
= cli_krb5_get_ticket(local_service
,
526 DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for: %s\n",
528 result
= krb5_to_nt_status(krb5_ret
);
532 if (!internal_ccache
) {
536 /************************ NON-ROOT **********************/
538 result
= ads_verify_ticket(state
->mem_ctx
,
545 if (!NT_STATUS_IS_OK(result
)) {
546 DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n",
551 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
555 DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
556 result
= NT_STATUS_INVALID_PARAMETER
;
560 logon_info
= get_logon_info_from_pac(pac_data
);
561 if (logon_info
== NULL
) {
562 DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
563 result
= NT_STATUS_INVALID_PARAMETER
;
569 * put results together */
571 *info3
= &logon_info
->info3
;
573 /* if we had a user's ccache then return that string for the pam
576 if (!internal_ccache
) {
578 setup_return_cc_name(state
, cc
);
580 result
= add_ccache_to_list(principal_s
,
583 state
->request
.data
.auth
.user
,
585 state
->request
.data
.auth
.pass
,
590 lp_winbind_refresh_tickets());
592 if (!NT_STATUS_IS_OK(result
)) {
593 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
598 result
= NT_STATUS_OK
;
601 data_blob_free(&session_key
);
602 data_blob_free(&session_key_krb5
);
603 data_blob_free(&ap_rep
);
604 data_blob_free(&tkt
);
606 SAFE_FREE(client_princ_out
);
608 if (!internal_ccache
) {
614 return NT_STATUS_NOT_SUPPORTED
;
615 #endif /* HAVE_KRB5 */
618 void winbindd_pam_auth(struct winbindd_cli_state
*state
)
620 struct winbindd_domain
*domain
;
621 fstring name_domain
, name_user
;
623 /* Ensure null termination */
624 state
->request
.data
.auth
.user
625 [sizeof(state
->request
.data
.auth
.user
)-1]='\0';
627 /* Ensure null termination */
628 state
->request
.data
.auth
.pass
629 [sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
631 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
632 state
->request
.data
.auth
.user
));
634 /* Parse domain and username */
636 if (!parse_domain_user(state
->request
.data
.auth
.user
,
637 name_domain
, name_user
)) {
638 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
639 DEBUG(5, ("Plain text authentication for %s returned %s "
641 state
->request
.data
.auth
.user
,
642 state
->response
.data
.auth
.nt_status_string
,
643 state
->response
.data
.auth
.pam_error
));
644 request_error(state
);
648 domain
= find_auth_domain(state
, name_domain
);
650 if (domain
== NULL
) {
651 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
652 DEBUG(5, ("Plain text authentication for %s returned %s "
654 state
->request
.data
.auth
.user
,
655 state
->response
.data
.auth
.nt_status_string
,
656 state
->response
.data
.auth
.pam_error
));
657 request_error(state
);
661 sendto_domain(state
, domain
);
664 NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
665 struct winbindd_cli_state
*state
,
666 NET_USER_INFO_3
**info3
)
668 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
669 uint16 max_allowed_bad_attempts
;
670 fstring name_domain
, name_user
;
672 enum SID_NAME_USE type
;
673 uchar new_nt_pass
[NT_HASH_LEN
];
674 const uint8
*cached_nt_pass
;
675 NET_USER_INFO_3
*my_info3
;
676 time_t kickoff_time
, must_change_time
;
682 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
684 /* Parse domain and username */
686 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
689 if (!lookup_cached_name(state
->mem_ctx
,
694 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
695 return NT_STATUS_NO_SUCH_USER
;
698 if (type
!= SID_NAME_USER
) {
699 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
700 return NT_STATUS_LOGON_FAILURE
;
703 result
= winbindd_get_creds(domain
,
708 if (!NT_STATUS_IS_OK(result
)) {
709 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
715 E_md4hash(state
->request
.data
.auth
.pass
, new_nt_pass
);
717 dump_data(100, (const char *)new_nt_pass
, NT_HASH_LEN
);
718 dump_data(100, (const char *)cached_nt_pass
, NT_HASH_LEN
);
720 if (!memcmp(cached_nt_pass
, new_nt_pass
, NT_HASH_LEN
)) {
722 /* User *DOES* know the password, update logon_time and reset
725 my_info3
->user_flgs
|= LOGON_CACHED_ACCOUNT
;
727 if (my_info3
->acct_flags
& ACB_AUTOLOCK
) {
728 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
731 if (my_info3
->acct_flags
& ACB_DISABLED
) {
732 return NT_STATUS_ACCOUNT_DISABLED
;
735 if (my_info3
->acct_flags
& ACB_WSTRUST
) {
736 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
739 if (my_info3
->acct_flags
& ACB_SVRTRUST
) {
740 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
743 if (my_info3
->acct_flags
& ACB_DOMTRUST
) {
744 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
747 /* The info3 acct_flags in NT4's samlogon reply don't have
748 * ACB_NORMAL set. Disable this paranoia check until we
749 * can research this more - Guenther */
751 if (!(my_info3
->acct_flags
& ACB_NORMAL
)) {
752 DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
753 my_info3
->acct_flags
));
754 return NT_STATUS_LOGON_FAILURE
;
757 kickoff_time
= nt_time_to_unix(&my_info3
->kickoff_time
);
758 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
759 return NT_STATUS_ACCOUNT_EXPIRED
;
762 must_change_time
= nt_time_to_unix(&my_info3
->pass_must_change_time
);
763 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
764 return NT_STATUS_PASSWORD_EXPIRED
;
767 /* FIXME: we possibly should handle logon hours as well (does xp when
768 * offline?) see auth/auth_sam.c:sam_account_ok for details */
770 unix_to_nt_time(&my_info3
->logon_time
, time(NULL
));
771 my_info3
->bad_pw_count
= 0;
773 result
= winbindd_update_creds_by_info3(domain
,
775 state
->request
.data
.auth
.user
,
776 state
->request
.data
.auth
.pass
,
778 if (!NT_STATUS_IS_OK(result
)) {
779 DEBUG(1,("failed to update creds: %s\n", nt_errstr(result
)));
787 /* User does *NOT* know the correct password, modify info3 accordingly */
789 /* failure of this is not critical */
790 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
791 if (!NT_STATUS_IS_OK(result
)) {
792 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
793 "Won't be able to honour account lockout policies\n"));
796 if (max_allowed_bad_attempts
== 0) {
797 return NT_STATUS_WRONG_PASSWORD
;
800 /* increase counter */
801 if (my_info3
->bad_pw_count
< max_allowed_bad_attempts
) {
803 my_info3
->bad_pw_count
++;
807 if (my_info3
->bad_pw_count
>= max_allowed_bad_attempts
) {
809 my_info3
->acct_flags
|= ACB_AUTOLOCK
;
812 result
= winbindd_update_creds_by_info3(domain
,
814 state
->request
.data
.auth
.user
,
818 if (!NT_STATUS_IS_OK(result
)) {
819 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
823 return NT_STATUS_LOGON_FAILURE
;
826 NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
827 struct winbindd_cli_state
*state
,
828 NET_USER_INFO_3
**info3
)
830 struct winbindd_domain
*contact_domain
;
831 fstring name_domain
, name_user
;
834 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
836 /* Parse domain and username */
838 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
840 /* what domain should we contact? */
843 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
844 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
845 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
846 result
= NT_STATUS_NO_SUCH_USER
;
851 if (is_myname(name_domain
)) {
852 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
853 result
= NT_STATUS_NO_SUCH_USER
;
857 contact_domain
= find_domain_from_name(name_domain
);
858 if (contact_domain
== NULL
) {
859 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
860 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
862 contact_domain
= find_our_domain();
866 set_dc_type_and_flags(contact_domain
);
868 if (!contact_domain
->active_directory
) {
869 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
870 return NT_STATUS_INVALID_LOGON_TYPE
;
873 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
878 NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
879 struct winbindd_cli_state
*state
,
880 NET_USER_INFO_3
**info3
)
883 struct rpc_pipe_client
*netlogon_pipe
;
888 unsigned char local_lm_response
[24];
889 unsigned char local_nt_response
[24];
890 struct winbindd_domain
*contact_domain
;
891 fstring name_domain
, name_user
;
894 NET_USER_INFO_3
*my_info3
;
900 my_info3
= TALLOC_ZERO_P(state
->mem_ctx
, NET_USER_INFO_3
);
901 if (my_info3
== NULL
) {
902 return NT_STATUS_NO_MEMORY
;
906 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
908 /* Parse domain and username */
910 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
912 /* do password magic */
915 generate_random_buffer(chal
, 8);
916 if (lp_client_ntlmv2_auth()) {
917 DATA_BLOB server_chal
;
918 DATA_BLOB names_blob
;
919 DATA_BLOB nt_response
;
920 DATA_BLOB lm_response
;
921 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
923 /* note that the 'workgroup' here is a best guess - we don't know
924 the server's domain at this point. The 'server name' is also
927 names_blob
= NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
929 if (!SMBNTLMv2encrypt(name_user
, name_domain
,
930 state
->request
.data
.auth
.pass
,
933 &lm_response
, &nt_response
, NULL
)) {
934 data_blob_free(&names_blob
);
935 data_blob_free(&server_chal
);
936 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
937 result
= NT_STATUS_NO_MEMORY
;
940 data_blob_free(&names_blob
);
941 data_blob_free(&server_chal
);
942 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
944 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
946 data_blob_free(&lm_response
);
947 data_blob_free(&nt_response
);
950 if (lp_client_lanman_auth()
951 && SMBencrypt(state
->request
.data
.auth
.pass
,
953 local_lm_response
)) {
954 lm_resp
= data_blob_talloc(state
->mem_ctx
,
956 sizeof(local_lm_response
));
958 lm_resp
= data_blob(NULL
, 0);
960 SMBNTencrypt(state
->request
.data
.auth
.pass
,
964 nt_resp
= data_blob_talloc(state
->mem_ctx
,
966 sizeof(local_nt_response
));
969 /* what domain should we contact? */
972 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
973 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
974 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
975 result
= NT_STATUS_NO_SUCH_USER
;
980 if (is_myname(name_domain
)) {
981 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
982 result
= NT_STATUS_NO_SUCH_USER
;
986 contact_domain
= find_our_domain();
989 /* check authentication loop */
993 ZERO_STRUCTP(my_info3
);
996 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
998 if (!NT_STATUS_IS_OK(result
)) {
999 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1003 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1006 contact_domain
->dcname
, /* server name */
1007 name_user
, /* user name */
1008 name_domain
, /* target domain */
1009 global_myname(), /* workstation */
1016 /* We have to try a second time as cm_connect_netlogon
1017 might not yet have noticed that the DC has killed
1020 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1025 /* if we get access denied, a possible cause was that we had
1026 and open connection to the DC, but someone changed our
1027 machine account password out from underneath us using 'net
1028 rpc changetrustpw' */
1030 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1031 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1032 "ACCESS_DENIED. Maybe the trust account "
1033 "password was changed and we didn't know it. "
1034 "Killing connections to domain %s\n",
1036 invalidate_cm_connection(&contact_domain
->conn
);
1040 } while ( (attempts
< 2) && retry
);
1047 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1048 struct winbindd_cli_state
*state
)
1050 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1051 fstring name_domain
, name_user
;
1052 NET_USER_INFO_3
*info3
;
1054 /* Ensure null termination */
1055 state
->request
.data
.auth
.user
[sizeof(state
->request
.data
.auth
.user
)-1]='\0';
1057 /* Ensure null termination */
1058 state
->request
.data
.auth
.pass
[sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
1060 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1061 state
->request
.data
.auth
.user
));
1063 /* Parse domain and username */
1065 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1067 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1069 /* Check for Kerberos authentication */
1070 if (domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1072 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1074 if (NT_STATUS_IS_OK(result
)) {
1075 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1076 goto process_result
;
1078 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1081 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
)) {
1082 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1083 domain
->online
= False
;
1086 if (state
->request
.flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1087 DEBUG(3,("falling back to samlogon\n"));
1095 /* Check for Samlogon authentication */
1096 if (domain
->online
) {
1097 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1099 if (NT_STATUS_IS_OK(result
)) {
1100 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1101 goto process_result
;
1103 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result
)));
1104 if (domain
->online
) {
1105 /* We're still online - fail. */
1108 /* Else drop through and see if we can check offline.... */
1113 /* Check for Cached logons */
1114 if (!domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1115 lp_winbind_offline_logon()) {
1117 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1119 if (NT_STATUS_IS_OK(result
)) {
1120 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1121 goto process_result
;
1123 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1130 if (NT_STATUS_IS_OK(result
)) {
1134 netsamlogon_cache_store(name_user
, info3
);
1135 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1137 /* save name_to_sid info as early as possible */
1138 sid_compose(&user_sid
, &info3
->dom_sid
.sid
, info3
->user_rid
);
1139 cache_name2sid(domain
, name_domain
, name_user
, SID_NAME_USER
, &user_sid
);
1141 /* Check if the user is in the right group */
1143 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1144 state
->request
.data
.auth
.require_membership_of_sid
))) {
1145 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1146 state
->request
.data
.auth
.user
,
1147 state
->request
.data
.auth
.require_membership_of_sid
));
1151 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1152 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
1153 if (!NT_STATUS_IS_OK(result
)) {
1154 DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result
)));
1159 if (state
->request
.flags
& WBFLAG_PAM_INFO3_TEXT
) {
1160 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
1161 if (!NT_STATUS_IS_OK(result
)) {
1162 DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result
)));
1168 if ((state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1169 lp_winbind_offline_logon()) {
1171 result
= winbindd_store_creds(domain
,
1173 state
->request
.data
.auth
.user
,
1174 state
->request
.data
.auth
.pass
,
1176 if (!NT_STATUS_IS_OK(result
)) {
1177 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1183 result
= fillup_password_policy(domain
, state
);
1185 if (!NT_STATUS_IS_OK(result
)) {
1186 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result
)));
1193 /* give us a more useful (more correct?) error code */
1194 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1195 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1196 result
= NT_STATUS_NO_LOGON_SERVERS
;
1199 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1200 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1202 /* we might have given a more useful error above */
1203 if (!*state
->response
.data
.auth
.error_string
)
1204 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1205 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1207 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1208 state
->request
.data
.auth
.user
,
1209 state
->response
.data
.auth
.nt_status_string
,
1210 state
->response
.data
.auth
.pam_error
));
1212 if ( NT_STATUS_IS_OK(result
) &&
1213 (state
->request
.flags
& WBFLAG_PAM_AFS_TOKEN
) ) {
1215 char *afsname
= talloc_strdup(state
->mem_ctx
,
1216 lp_afs_username_map());
1219 if (afsname
== NULL
) {
1223 afsname
= talloc_string_sub(state
->mem_ctx
,
1224 lp_afs_username_map(),
1226 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1228 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1235 sid_copy(&user_sid
, &info3
->dom_sid
.sid
);
1236 sid_append_rid(&user_sid
, info3
->user_rid
);
1237 sid_to_string(sidstr
, &user_sid
);
1238 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1242 if (afsname
== NULL
) {
1246 strlower_m(afsname
);
1248 DEBUG(10, ("Generating token for user %s\n", afsname
));
1250 cell
= strchr(afsname
, '@');
1259 /* Append an AFS token string */
1260 state
->response
.extra_data
=
1261 afs_createtoken_str(afsname
, cell
);
1263 if (state
->response
.extra_data
!= NULL
)
1264 state
->response
.length
+=
1265 strlen(state
->response
.extra_data
)+1;
1268 talloc_free(afsname
);
1271 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1275 /**********************************************************************
1276 Challenge Response Authentication Protocol
1277 **********************************************************************/
1279 void winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
1281 struct winbindd_domain
*domain
= NULL
;
1282 const char *domain_name
= NULL
;
1285 if (!state
->privileged
) {
1286 char *error_string
= NULL
;
1287 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1289 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1290 "on %s are set correctly.\n",
1291 get_winbind_priv_pipe_dir()));
1292 /* send a better message than ACCESS_DENIED */
1293 error_string
= talloc_asprintf(state
->mem_ctx
,
1294 "winbind client not authorized "
1295 "to use winbindd_pam_auth_crap."
1296 " Ensure permissions on %s "
1297 "are set correctly.",
1298 get_winbind_priv_pipe_dir());
1299 fstrcpy(state
->response
.data
.auth
.error_string
, error_string
);
1300 result
= NT_STATUS_ACCESS_DENIED
;
1304 /* Ensure null termination */
1305 state
->request
.data
.auth_crap
.user
1306 [sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1307 state
->request
.data
.auth_crap
.domain
1308 [sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1310 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1311 (unsigned long)state
->pid
,
1312 state
->request
.data
.auth_crap
.domain
,
1313 state
->request
.data
.auth_crap
.user
));
1315 if (*state
->request
.data
.auth_crap
.domain
!= '\0') {
1316 domain_name
= state
->request
.data
.auth_crap
.domain
;
1317 } else if (lp_winbind_use_default_domain()) {
1318 domain_name
= lp_workgroup();
1321 if (domain_name
!= NULL
)
1322 domain
= find_auth_domain(state
, domain_name
);
1324 if (domain
!= NULL
) {
1325 sendto_domain(state
, domain
);
1329 result
= NT_STATUS_NO_SUCH_USER
;
1332 set_auth_errors(&state
->response
, result
);
1333 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1334 state
->request
.data
.auth_crap
.domain
,
1335 state
->request
.data
.auth_crap
.user
,
1336 state
->response
.data
.auth
.nt_status_string
,
1337 state
->response
.data
.auth
.pam_error
));
1338 request_error(state
);
1343 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1344 struct winbindd_cli_state
*state
)
1347 NET_USER_INFO_3 info3
;
1348 struct rpc_pipe_client
*netlogon_pipe
;
1349 const char *name_user
= NULL
;
1350 const char *name_domain
= NULL
;
1351 const char *workstation
;
1352 struct winbindd_domain
*contact_domain
;
1356 DATA_BLOB lm_resp
, nt_resp
;
1358 /* This is child-only, so no check for privileged access is needed
1361 /* Ensure null termination */
1362 state
->request
.data
.auth_crap
.user
[sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1363 state
->request
.data
.auth_crap
.domain
[sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1365 name_user
= state
->request
.data
.auth_crap
.user
;
1367 if (*state
->request
.data
.auth_crap
.domain
) {
1368 name_domain
= state
->request
.data
.auth_crap
.domain
;
1369 } else if (lp_winbind_use_default_domain()) {
1370 name_domain
= lp_workgroup();
1372 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1374 result
= NT_STATUS_NO_SUCH_USER
;
1378 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1379 name_domain
, name_user
));
1381 if (*state
->request
.data
.auth_crap
.workstation
) {
1382 workstation
= state
->request
.data
.auth_crap
.workstation
;
1384 workstation
= global_myname();
1387 if (state
->request
.data
.auth_crap
.lm_resp_len
> sizeof(state
->request
.data
.auth_crap
.lm_resp
)
1388 || state
->request
.data
.auth_crap
.nt_resp_len
> sizeof(state
->request
.data
.auth_crap
.nt_resp
)) {
1389 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1390 state
->request
.data
.auth_crap
.lm_resp_len
,
1391 state
->request
.data
.auth_crap
.nt_resp_len
));
1392 result
= NT_STATUS_INVALID_PARAMETER
;
1396 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.lm_resp
,
1397 state
->request
.data
.auth_crap
.lm_resp_len
);
1398 nt_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.nt_resp
,
1399 state
->request
.data
.auth_crap
.nt_resp_len
);
1401 /* what domain should we contact? */
1404 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1405 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1406 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1407 result
= NT_STATUS_NO_SUCH_USER
;
1411 if (is_myname(name_domain
)) {
1412 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1413 result
= NT_STATUS_NO_SUCH_USER
;
1416 contact_domain
= find_our_domain();
1423 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1425 if (!NT_STATUS_IS_OK(result
)) {
1426 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1427 nt_errstr(result
)));
1431 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1433 state
->request
.data
.auth_crap
.logon_parameters
,
1434 contact_domain
->dcname
,
1437 /* Bug #3248 - found by Stefan Burkei. */
1438 workstation
, /* We carefully set this above so use it... */
1439 state
->request
.data
.auth_crap
.chal
,
1446 /* We have to try a second time as cm_connect_netlogon
1447 might not yet have noticed that the DC has killed
1450 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1455 /* if we get access denied, a possible cause was that we had and open
1456 connection to the DC, but someone changed our machine account password
1457 out from underneath us using 'net rpc changetrustpw' */
1459 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1460 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1461 "ACCESS_DENIED. Maybe the trust account "
1462 "password was changed and we didn't know it. "
1463 "Killing connections to domain %s\n",
1465 invalidate_cm_connection(&contact_domain
->conn
);
1469 } while ( (attempts
< 2) && retry
);
1471 if (NT_STATUS_IS_OK(result
)) {
1473 netsamlogon_cache_store(name_user
, &info3
);
1474 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), &info3
);
1476 /* Check if the user is in the right group */
1478 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, &info3
,
1479 state
->request
.data
.auth_crap
.require_membership_of_sid
))) {
1480 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1481 state
->request
.data
.auth_crap
.user
,
1482 state
->request
.data
.auth_crap
.require_membership_of_sid
));
1486 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1487 result
= append_info3_as_ndr(state
->mem_ctx
, state
, &info3
);
1488 } else if (state
->request
.flags
& WBFLAG_PAM_UNIX_NAME
) {
1489 /* ntlm_auth should return the unix username, per
1490 'winbind use default domain' settings and the like */
1492 fstring username_out
;
1493 const char *nt_username
, *nt_domain
;
1494 if (!(nt_username
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_user_name
)))) {
1495 /* If the server didn't give us one, just use the one we sent them */
1496 nt_username
= name_user
;
1499 if (!(nt_domain
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_logon_dom
)))) {
1500 /* If the server didn't give us one, just use the one we sent them */
1501 nt_domain
= name_domain
;
1504 fill_domain_username(username_out
, nt_domain
, nt_username
);
1506 DEBUG(5, ("Setting unix username to [%s]\n", username_out
));
1508 state
->response
.extra_data
= SMB_STRDUP(username_out
);
1509 if (!state
->response
.extra_data
) {
1510 result
= NT_STATUS_NO_MEMORY
;
1513 state
->response
.length
+= strlen(state
->response
.extra_data
)+1;
1516 if (state
->request
.flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
1517 memcpy(state
->response
.data
.auth
.user_session_key
, info3
.user_sess_key
,
1518 sizeof(state
->response
.data
.auth
.user_session_key
) /* 16 */);
1520 if (state
->request
.flags
& WBFLAG_PAM_LMKEY
) {
1521 memcpy(state
->response
.data
.auth
.first_8_lm_hash
, info3
.lm_sess_key
,
1522 sizeof(state
->response
.data
.auth
.first_8_lm_hash
) /* 8 */);
1528 /* give us a more useful (more correct?) error code */
1529 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1530 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1531 result
= NT_STATUS_NO_LOGON_SERVERS
;
1534 if (state
->request
.flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1535 result
= nt_status_squash(result
);
1538 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1539 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1541 /* we might have given a more useful error above */
1542 if (!*state
->response
.data
.auth
.error_string
) {
1543 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1545 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1547 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1548 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1551 state
->response
.data
.auth
.nt_status_string
,
1552 state
->response
.data
.auth
.pam_error
));
1554 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1557 /* Change a user password */
1559 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
1561 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1563 char *newpass
= NULL
;
1564 fstring domain
, user
;
1566 struct winbindd_domain
*contact_domain
;
1567 struct rpc_pipe_client
*cli
;
1568 BOOL got_info
= False
;
1569 SAM_UNK_INFO_1 info
;
1570 SAMR_CHANGE_REJECT reject
;
1572 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
1573 state
->request
.data
.chauthtok
.user
));
1577 parse_domain_user(state
->request
.data
.chauthtok
.user
, domain
, user
);
1579 if (!(contact_domain
= find_domain_from_name(domain
))) {
1580 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
1581 state
->request
.data
.chauthtok
.user
, domain
, user
, domain
));
1582 result
= NT_STATUS_NO_SUCH_USER
;
1586 /* Change password */
1588 oldpass
= state
->request
.data
.chauthtok
.oldpass
;
1589 newpass
= state
->request
.data
.chauthtok
.newpass
;
1591 /* Get sam handle */
1593 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1595 if (!NT_STATUS_IS_OK(result
)) {
1596 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
1600 result
= rpccli_samr_chgpasswd3(cli
, state
->mem_ctx
, user
, newpass
, oldpass
, &info
, &reject
);
1602 /* FIXME: need to check for other error codes ? */
1603 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
)) {
1605 state
->response
.data
.auth
.policy
.min_length_password
=
1606 info
.min_length_password
;
1607 state
->response
.data
.auth
.policy
.password_history
=
1608 info
.password_history
;
1609 state
->response
.data
.auth
.policy
.password_properties
=
1610 info
.password_properties
;
1611 state
->response
.data
.auth
.policy
.expire
=
1612 nt_time_to_unix_abs(&info
.expire
);
1613 state
->response
.data
.auth
.policy
.min_passwordage
=
1614 nt_time_to_unix_abs(&info
.min_passwordage
);
1616 state
->response
.data
.auth
.reject_reason
=
1617 reject
.reject_reason
;
1621 } else if (!NT_STATUS_IS_OK(result
)) {
1623 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
1624 nt_errstr(result
)));
1626 state
->response
.data
.auth
.reject_reason
= 0;
1628 result
= rpccli_samr_chgpasswd_user(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
1632 if (NT_STATUS_IS_OK(result
) && (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1633 lp_winbind_offline_logon()) {
1637 cred_ret
= winbindd_update_creds_by_name(contact_domain
,
1638 state
->mem_ctx
, user
,
1640 if (!NT_STATUS_IS_OK(cred_ret
)) {
1641 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(cred_ret
)));
1642 goto process_result
; /* FIXME: hm, risking inconsistant cache ? */
1646 if (!NT_STATUS_IS_OK(result
) && !got_info
) {
1648 NTSTATUS policy_ret
;
1650 policy_ret
= fillup_password_policy(contact_domain
, state
);
1652 /* failure of this is non critical, it will just provide no
1653 * additional information to the client why the change has
1654 * failed - Guenther */
1656 if (!NT_STATUS_IS_OK(policy_ret
)) {
1657 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
1658 goto process_result
;
1664 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1665 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1666 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1667 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1669 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1670 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1673 state
->response
.data
.auth
.nt_status_string
,
1674 state
->response
.data
.auth
.pam_error
));
1676 if (NT_STATUS_IS_OK(result
))
1679 request_error(state
);
1682 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
1684 struct winbindd_domain
*domain
;
1685 fstring name_domain
, user
;
1687 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
1688 state
->request
.data
.logoff
.user
));
1690 /* Ensure null termination */
1691 state
->request
.data
.logoff
.user
1692 [sizeof(state
->request
.data
.logoff
.user
)-1]='\0';
1694 state
->request
.data
.logoff
.krb5ccname
1695 [sizeof(state
->request
.data
.logoff
.krb5ccname
)-1]='\0';
1697 parse_domain_user(state
->request
.data
.logoff
.user
, name_domain
, user
);
1699 domain
= find_auth_domain(state
, name_domain
);
1701 if (domain
== NULL
) {
1702 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
1703 DEBUG(5, ("Pam Logoff for %s returned %s "
1705 state
->request
.data
.auth
.user
,
1706 state
->response
.data
.auth
.nt_status_string
,
1707 state
->response
.data
.auth
.pam_error
));
1708 request_error(state
);
1712 sendto_domain(state
, domain
);
1715 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
1716 struct winbindd_cli_state
*state
)
1718 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
1719 struct WINBINDD_CCACHE_ENTRY
*entry
;
1722 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
1723 state
->request
.data
.logoff
.user
));
1725 if (!(state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1726 result
= NT_STATUS_OK
;
1727 goto process_result
;
1732 /* what we need here is to find the corresponding krb5 ccache name *we*
1733 * created for a given username and destroy it (as the user who created it) */
1735 entry
= get_ccache_by_username(state
->request
.data
.logoff
.user
);
1736 if (entry
== NULL
) {
1737 DEBUG(10,("winbindd_pam_logoff: could not get ccname for user %s\n",
1738 state
->request
.data
.logoff
.user
));
1739 goto process_result
;
1742 DEBUG(10,("winbindd_pam_logoff: found ccache [%s]\n", entry
->ccname
));
1744 if (entry
->uid
< 0 || state
->request
.data
.logoff
.uid
< 0) {
1745 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1746 goto process_result
;
1749 if (entry
->uid
!= state
->request
.data
.logoff
.uid
) {
1750 DEBUG(0,("winbindd_pam_logoff: uid's differ: %d != %d\n",
1751 entry
->uid
, state
->request
.data
.logoff
.uid
));
1752 goto process_result
;
1755 if (!strcsequal(entry
->ccname
, state
->request
.data
.logoff
.krb5ccname
)) {
1756 DEBUG(0,("winbindd_pam_logoff: krb5ccnames differ: (daemon) %s != (client) %s\n",
1757 entry
->ccname
, state
->request
.data
.logoff
.krb5ccname
));
1758 goto process_result
;
1761 seteuid(entry
->uid
);
1763 ret
= ads_kdestroy(entry
->ccname
);
1768 DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n",
1769 entry
->ccname
, error_message(ret
)));
1771 DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n",
1772 entry
->ccname
, state
->request
.data
.logoff
.user
));
1773 remove_ccache_by_ccname(entry
->ccname
);
1776 result
= krb5_to_nt_status(ret
);
1778 result
= NT_STATUS_NOT_SUPPORTED
;
1782 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1783 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1784 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1785 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1787 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;