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 return find_our_domain();
227 static void set_auth_errors(struct winbindd_response
*resp
, NTSTATUS result
)
229 resp
->data
.auth
.nt_status
= NT_STATUS_V(result
);
230 fstrcpy(resp
->data
.auth
.nt_status_string
, nt_errstr(result
));
232 /* we might have given a more useful error above */
233 if (*resp
->data
.auth
.error_string
== '\0')
234 fstrcpy(resp
->data
.auth
.error_string
,
235 get_friendly_nt_error_msg(result
));
236 resp
->data
.auth
.pam_error
= nt_status_to_pam(result
);
239 static NTSTATUS
fillup_password_policy(struct winbindd_domain
*domain
,
240 struct winbindd_cli_state
*state
)
242 struct winbindd_methods
*methods
;
243 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
244 SAM_UNK_INFO_1 password_policy
;
246 methods
= domain
->methods
;
248 status
= methods
->password_policy(domain
, state
->mem_ctx
, &password_policy
);
249 if (NT_STATUS_IS_ERR(status
)) {
253 state
->response
.data
.auth
.policy
.min_length_password
=
254 password_policy
.min_length_password
;
255 state
->response
.data
.auth
.policy
.password_history
=
256 password_policy
.password_history
;
257 state
->response
.data
.auth
.policy
.password_properties
=
258 password_policy
.password_properties
;
259 state
->response
.data
.auth
.policy
.expire
=
260 nt_time_to_unix_abs(&(password_policy
.expire
));
261 state
->response
.data
.auth
.policy
.min_passwordage
=
262 nt_time_to_unix_abs(&(password_policy
.min_passwordage
));
267 static NTSTATUS
get_max_bad_attempts_from_lockout_policy(struct winbindd_domain
*domain
,
269 uint16
*max_allowed_bad_attempts
)
271 struct winbindd_methods
*methods
;
272 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
273 SAM_UNK_INFO_12 lockout_policy
;
275 *max_allowed_bad_attempts
= 0;
277 methods
= domain
->methods
;
279 status
= methods
->lockout_policy(domain
, mem_ctx
, &lockout_policy
);
280 if (NT_STATUS_IS_ERR(status
)) {
284 *max_allowed_bad_attempts
= lockout_policy
.bad_attempt_lockout
;
289 static NTSTATUS
get_pwd_properties(struct winbindd_domain
*domain
,
291 uint32
*password_properties
)
293 struct winbindd_methods
*methods
;
294 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
295 SAM_UNK_INFO_1 password_policy
;
297 *password_properties
= 0;
299 methods
= domain
->methods
;
301 status
= methods
->password_policy(domain
, mem_ctx
, &password_policy
);
302 if (NT_STATUS_IS_ERR(status
)) {
306 *password_properties
= password_policy
.password_properties
;
311 static const char *generate_krb5_ccache(TALLOC_CTX
*mem_ctx
,
314 BOOL
*internal_ccache
)
316 /* accept FILE and WRFILE as krb5_cc_type from the client and then
317 * build the full ccname string based on the user's uid here -
320 const char *gen_cc
= NULL
;
322 *internal_ccache
= True
;
328 if (!type
|| type
[0] == '\0') {
332 if (strequal(type
, "FILE")) {
333 gen_cc
= talloc_asprintf(mem_ctx
, "FILE:/tmp/krb5cc_%d", uid
);
334 } else if (strequal(type
, "WRFILE")) {
335 gen_cc
= talloc_asprintf(mem_ctx
, "WRFILE:/tmp/krb5cc_%d", uid
);
337 DEBUG(10,("we don't allow to set a %s type ccache\n", type
));
341 *internal_ccache
= False
;
345 gen_cc
= talloc_strdup(mem_ctx
, "MEMORY:winbind_cache");
348 if (gen_cc
== NULL
) {
349 DEBUG(0,("out of memory\n"));
353 DEBUG(10,("using ccache: %s %s\n", gen_cc
, *internal_ccache
? "(internal)":""));
358 static uid_t
get_uid_from_state(struct winbindd_cli_state
*state
)
362 uid
= state
->request
.data
.auth
.uid
;
365 DEBUG(1,("invalid uid: '%d'\n", uid
));
371 static void setup_return_cc_name(struct winbindd_cli_state
*state
, const char *cc
)
373 const char *type
= state
->request
.data
.auth
.krb5_cc_type
;
375 state
->response
.data
.auth
.krb5ccname
[0] = '\0';
377 if (type
[0] == '\0') {
381 if (!strequal(type
, "FILE") &&
382 !strequal(type
, "WRFILE")) {
383 DEBUG(10,("won't return krbccname for a %s type ccache\n",
388 fstrcpy(state
->response
.data
.auth
.krb5ccname
, cc
);
391 /**********************************************************************
392 Authenticate a user with a clear text password using Kerberos and fill up
394 **********************************************************************/
395 static NTSTATUS
winbindd_raw_kerberos_login(struct winbindd_domain
*domain
,
396 struct winbindd_cli_state
*state
,
397 NET_USER_INFO_3
**info3
)
400 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
401 krb5_error_code krb5_ret
;
402 DATA_BLOB tkt
, session_key_krb5
;
403 DATA_BLOB ap_rep
, session_key
;
404 PAC_DATA
*pac_data
= NULL
;
405 PAC_LOGON_INFO
*logon_info
= NULL
;
406 char *client_princ
= NULL
;
407 char *client_princ_out
= NULL
;
408 char *local_service
= NULL
;
409 const char *cc
= NULL
;
410 const char *principal_s
= NULL
;
411 const char *service
= NULL
;
413 fstring name_domain
, name_user
;
414 time_t ticket_lifetime
= 0;
415 time_t renewal_until
= 0;
418 time_t time_offset
= 0;
419 BOOL internal_ccache
= True
;
421 ZERO_STRUCT(session_key
);
422 ZERO_STRUCT(session_key_krb5
);
431 * prepare a krb5_cc_cache string for the user */
433 uid
= get_uid_from_state(state
);
435 DEBUG(0,("no valid uid\n"));
438 cc
= generate_krb5_ccache(state
->mem_ctx
,
439 state
->request
.data
.auth
.krb5_cc_type
,
440 state
->request
.data
.auth
.uid
,
443 return NT_STATUS_NO_MEMORY
;
448 * get kerberos properties */
450 if (domain
->private_data
) {
451 ads
= (ADS_STRUCT
*)domain
->private_data
;
452 time_offset
= ads
->auth
.time_offset
;
457 * do kerberos auth and setup ccache as the user */
459 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
461 realm
= domain
->alt_name
;
464 principal_s
= talloc_asprintf(state
->mem_ctx
, "%s@%s", name_user
, realm
);
465 if (principal_s
== NULL
) {
466 return NT_STATUS_NO_MEMORY
;
469 service
= talloc_asprintf(state
->mem_ctx
, "krbtgt/%s@%s", realm
, realm
);
470 if (service
== NULL
) {
471 return NT_STATUS_NO_MEMORY
;
474 /* if this is a user ccache, we need to act as the user to let the krb5
475 * library handle the chown, etc. */
477 /************************ NON-ROOT **********************/
479 if (!internal_ccache
) {
482 DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid
));
485 krb5_ret
= kerberos_kinit_password(principal_s
,
486 state
->request
.data
.auth
.pass
,
492 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME
);
495 DEBUG(1,("winbindd_raw_kerberos_login: kinit failed for '%s' with: %s (%d)\n",
496 principal_s
, error_message(krb5_ret
), krb5_ret
));
497 result
= krb5_to_nt_status(krb5_ret
);
501 /* does http_timestring use heimdals libroken strftime?? - Guenther */
502 DEBUG(10,("got TGT for %s in %s (valid until: %s (%d), renewable till: %s (%d))\n",
504 http_timestring(ticket_lifetime
), (int)ticket_lifetime
,
505 http_timestring(renewal_until
), (int)renewal_until
));
507 client_princ
= talloc_strdup(state
->mem_ctx
, global_myname());
508 if (client_princ
== NULL
) {
509 result
= NT_STATUS_NO_MEMORY
;
512 strlower_m(client_princ
);
514 local_service
= talloc_asprintf(state
->mem_ctx
, "HOST/%s@%s", client_princ
, lp_realm());
515 if (local_service
== NULL
) {
516 DEBUG(0,("winbindd_raw_kerberos_login: out of memory\n"));
517 result
= NT_STATUS_NO_MEMORY
;
521 krb5_ret
= cli_krb5_get_ticket(local_service
,
528 DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for: %s\n",
530 result
= krb5_to_nt_status(krb5_ret
);
534 if (!internal_ccache
) {
538 /************************ NON-ROOT **********************/
540 result
= ads_verify_ticket(state
->mem_ctx
,
547 if (!NT_STATUS_IS_OK(result
)) {
548 DEBUG(0,("winbindd_raw_kerberos_login: ads_verify_ticket failed: %s\n",
553 DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
557 DEBUG(3,("winbindd_raw_kerberos_login: no pac data\n"));
558 result
= NT_STATUS_INVALID_PARAMETER
;
562 logon_info
= get_logon_info_from_pac(pac_data
);
563 if (logon_info
== NULL
) {
564 DEBUG(1,("winbindd_raw_kerberos_login: no logon info\n"));
565 result
= NT_STATUS_INVALID_PARAMETER
;
571 * put results together */
573 *info3
= &logon_info
->info3
;
575 /* if we had a user's ccache then return that string for the pam
578 if (!internal_ccache
) {
580 setup_return_cc_name(state
, cc
);
582 result
= add_ccache_to_list(principal_s
,
585 state
->request
.data
.auth
.user
,
587 state
->request
.data
.auth
.pass
,
592 lp_winbind_refresh_tickets());
594 if (!NT_STATUS_IS_OK(result
)) {
595 DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
600 result
= NT_STATUS_OK
;
603 data_blob_free(&session_key
);
604 data_blob_free(&session_key_krb5
);
605 data_blob_free(&ap_rep
);
606 data_blob_free(&tkt
);
608 SAFE_FREE(client_princ_out
);
610 if (!internal_ccache
) {
616 return NT_STATUS_NOT_SUPPORTED
;
617 #endif /* HAVE_KRB5 */
620 void winbindd_pam_auth(struct winbindd_cli_state
*state
)
622 struct winbindd_domain
*domain
;
623 fstring name_domain
, name_user
;
625 /* Ensure null termination */
626 state
->request
.data
.auth
.user
627 [sizeof(state
->request
.data
.auth
.user
)-1]='\0';
629 /* Ensure null termination */
630 state
->request
.data
.auth
.pass
631 [sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
633 DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state
->pid
,
634 state
->request
.data
.auth
.user
));
636 /* Parse domain and username */
638 if (!parse_domain_user(state
->request
.data
.auth
.user
,
639 name_domain
, name_user
)) {
640 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
641 DEBUG(5, ("Plain text authentication for %s returned %s "
643 state
->request
.data
.auth
.user
,
644 state
->response
.data
.auth
.nt_status_string
,
645 state
->response
.data
.auth
.pam_error
));
646 request_error(state
);
650 domain
= find_auth_domain(state
, name_domain
);
652 if (domain
== NULL
) {
653 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
654 DEBUG(5, ("Plain text authentication for %s returned %s "
656 state
->request
.data
.auth
.user
,
657 state
->response
.data
.auth
.nt_status_string
,
658 state
->response
.data
.auth
.pam_error
));
659 request_error(state
);
663 sendto_domain(state
, domain
);
666 NTSTATUS
winbindd_dual_pam_auth_cached(struct winbindd_domain
*domain
,
667 struct winbindd_cli_state
*state
,
668 NET_USER_INFO_3
**info3
)
670 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
671 uint16 max_allowed_bad_attempts
;
672 fstring name_domain
, name_user
;
674 enum SID_NAME_USE type
;
675 uchar new_nt_pass
[NT_HASH_LEN
];
676 const uint8
*cached_nt_pass
;
677 NET_USER_INFO_3
*my_info3
;
678 time_t kickoff_time
, must_change_time
;
684 DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
686 /* Parse domain and username */
688 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
691 if (!lookup_cached_name(state
->mem_ctx
,
696 DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
697 return NT_STATUS_NO_SUCH_USER
;
700 if (type
!= SID_NAME_USER
) {
701 DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type
)));
702 return NT_STATUS_LOGON_FAILURE
;
705 result
= winbindd_get_creds(domain
,
710 if (!NT_STATUS_IS_OK(result
)) {
711 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result
)));
717 E_md4hash(state
->request
.data
.auth
.pass
, new_nt_pass
);
719 dump_data(100, (const char *)new_nt_pass
, NT_HASH_LEN
);
720 dump_data(100, (const char *)cached_nt_pass
, NT_HASH_LEN
);
722 if (!memcmp(cached_nt_pass
, new_nt_pass
, NT_HASH_LEN
)) {
724 /* User *DOES* know the password, update logon_time and reset
727 my_info3
->user_flgs
|= LOGON_CACHED_ACCOUNT
;
729 if (my_info3
->acct_flags
& ACB_AUTOLOCK
) {
730 return NT_STATUS_ACCOUNT_LOCKED_OUT
;
733 if (my_info3
->acct_flags
& ACB_DISABLED
) {
734 return NT_STATUS_ACCOUNT_DISABLED
;
737 if (my_info3
->acct_flags
& ACB_WSTRUST
) {
738 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
;
741 if (my_info3
->acct_flags
& ACB_SVRTRUST
) {
742 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
;
745 if (my_info3
->acct_flags
& ACB_DOMTRUST
) {
746 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
;
749 if (!(my_info3
->acct_flags
& ACB_NORMAL
)) {
750 DEBUG(10,("winbindd_dual_pam_auth_cached: whats wrong with that one?: 0x%08x\n",
751 my_info3
->acct_flags
));
752 return NT_STATUS_LOGON_FAILURE
;
755 kickoff_time
= nt_time_to_unix(&my_info3
->kickoff_time
);
756 if (kickoff_time
!= 0 && time(NULL
) > kickoff_time
) {
757 return NT_STATUS_ACCOUNT_EXPIRED
;
760 must_change_time
= nt_time_to_unix(&my_info3
->pass_must_change_time
);
761 if (must_change_time
!= 0 && must_change_time
< time(NULL
)) {
762 return NT_STATUS_PASSWORD_EXPIRED
;
765 /* FIXME: we possibly should handle logon hours as well (does xp when
766 * offline?) see auth/auth_sam.c:sam_account_ok for details */
768 unix_to_nt_time(&my_info3
->logon_time
, time(NULL
));
769 my_info3
->bad_pw_count
= 0;
771 result
= winbindd_update_creds_by_info3(domain
,
773 state
->request
.data
.auth
.user
,
774 state
->request
.data
.auth
.pass
,
776 if (!NT_STATUS_IS_OK(result
)) {
777 DEBUG(1,("failed to update creds: %s\n", nt_errstr(result
)));
785 /* User does *NOT* know the correct password, modify info3 accordingly */
787 /* failure of this is not critical */
788 result
= get_max_bad_attempts_from_lockout_policy(domain
, state
->mem_ctx
, &max_allowed_bad_attempts
);
789 if (!NT_STATUS_IS_OK(result
)) {
790 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
791 "Won't be able to honour account lockout policies\n"));
794 /* increase counter */
795 my_info3
->bad_pw_count
++;
797 if (max_allowed_bad_attempts
== 0) {
802 if (my_info3
->bad_pw_count
>= max_allowed_bad_attempts
) {
804 uint32 password_properties
;
806 result
= get_pwd_properties(domain
, state
->mem_ctx
, &password_properties
);
807 if (!NT_STATUS_IS_OK(result
)) {
808 DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
811 if ((my_info3
->user_rid
!= DOMAIN_USER_RID_ADMIN
) ||
812 (password_properties
& DOMAIN_LOCKOUT_ADMINS
)) {
813 my_info3
->acct_flags
|= ACB_AUTOLOCK
;
818 result
= winbindd_update_creds_by_info3(domain
,
820 state
->request
.data
.auth
.user
,
824 if (!NT_STATUS_IS_OK(result
)) {
825 DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
829 return NT_STATUS_LOGON_FAILURE
;
832 NTSTATUS
winbindd_dual_pam_auth_kerberos(struct winbindd_domain
*domain
,
833 struct winbindd_cli_state
*state
,
834 NET_USER_INFO_3
**info3
)
836 struct winbindd_domain
*contact_domain
;
837 fstring name_domain
, name_user
;
840 DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
842 /* Parse domain and username */
844 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
846 /* what domain should we contact? */
849 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
850 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
851 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
852 result
= NT_STATUS_NO_SUCH_USER
;
857 if (is_myname(name_domain
)) {
858 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
859 result
= NT_STATUS_NO_SUCH_USER
;
863 contact_domain
= find_domain_from_name(name_domain
);
864 if (contact_domain
== NULL
) {
865 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
866 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
868 contact_domain
= find_our_domain();
872 set_dc_type_and_flags(contact_domain
);
874 if (!contact_domain
->active_directory
) {
875 DEBUG(3,("krb5 auth requested but domain is not Active Directory\n"));
876 return NT_STATUS_INVALID_LOGON_TYPE
;
879 result
= winbindd_raw_kerberos_login(contact_domain
, state
, info3
);
884 NTSTATUS
winbindd_dual_pam_auth_samlogon(struct winbindd_domain
*domain
,
885 struct winbindd_cli_state
*state
,
886 NET_USER_INFO_3
**info3
)
889 struct rpc_pipe_client
*netlogon_pipe
;
894 unsigned char local_lm_response
[24];
895 unsigned char local_nt_response
[24];
896 struct winbindd_domain
*contact_domain
;
897 fstring name_domain
, name_user
;
900 NET_USER_INFO_3
*my_info3
;
906 my_info3
= TALLOC_ZERO_P(state
->mem_ctx
, NET_USER_INFO_3
);
907 if (my_info3
== NULL
) {
908 return NT_STATUS_NO_MEMORY
;
912 DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
914 /* Parse domain and username */
916 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
918 /* do password magic */
921 generate_random_buffer(chal
, 8);
922 if (lp_client_ntlmv2_auth()) {
923 DATA_BLOB server_chal
;
924 DATA_BLOB names_blob
;
925 DATA_BLOB nt_response
;
926 DATA_BLOB lm_response
;
927 server_chal
= data_blob_talloc(state
->mem_ctx
, chal
, 8);
929 /* note that the 'workgroup' here is a best guess - we don't know
930 the server's domain at this point. The 'server name' is also
933 names_blob
= NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
935 if (!SMBNTLMv2encrypt(name_user
, name_domain
,
936 state
->request
.data
.auth
.pass
,
939 &lm_response
, &nt_response
, NULL
)) {
940 data_blob_free(&names_blob
);
941 data_blob_free(&server_chal
);
942 DEBUG(0, ("winbindd_pam_auth: SMBNTLMv2encrypt() failed!\n"));
943 result
= NT_STATUS_NO_MEMORY
;
946 data_blob_free(&names_blob
);
947 data_blob_free(&server_chal
);
948 lm_resp
= data_blob_talloc(state
->mem_ctx
, lm_response
.data
,
950 nt_resp
= data_blob_talloc(state
->mem_ctx
, nt_response
.data
,
952 data_blob_free(&lm_response
);
953 data_blob_free(&nt_response
);
956 if (lp_client_lanman_auth()
957 && SMBencrypt(state
->request
.data
.auth
.pass
,
959 local_lm_response
)) {
960 lm_resp
= data_blob_talloc(state
->mem_ctx
,
962 sizeof(local_lm_response
));
964 lm_resp
= data_blob(NULL
, 0);
966 SMBNTencrypt(state
->request
.data
.auth
.pass
,
970 nt_resp
= data_blob_talloc(state
->mem_ctx
,
972 sizeof(local_nt_response
));
975 /* what domain should we contact? */
978 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
979 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
980 state
->request
.data
.auth
.user
, name_domain
, name_user
, name_domain
));
981 result
= NT_STATUS_NO_SUCH_USER
;
986 if (is_myname(name_domain
)) {
987 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
988 result
= NT_STATUS_NO_SUCH_USER
;
992 contact_domain
= find_our_domain();
995 /* check authentication loop */
999 ZERO_STRUCTP(my_info3
);
1002 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1004 if (!NT_STATUS_IS_OK(result
)) {
1005 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
1009 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1012 contact_domain
->dcname
, /* server name */
1013 name_user
, /* user name */
1014 name_domain
, /* target domain */
1015 global_myname(), /* workstation */
1022 /* We have to try a second time as cm_connect_netlogon
1023 might not yet have noticed that the DC has killed
1026 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1031 /* if we get access denied, a possible cause was that we had
1032 and open connection to the DC, but someone changed our
1033 machine account password out from underneath us using 'net
1034 rpc changetrustpw' */
1036 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1037 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1038 "ACCESS_DENIED. Maybe the trust account "
1039 "password was changed and we didn't know it. "
1040 "Killing connections to domain %s\n",
1042 invalidate_cm_connection(&contact_domain
->conn
);
1046 } while ( (attempts
< 2) && retry
);
1053 enum winbindd_result
winbindd_dual_pam_auth(struct winbindd_domain
*domain
,
1054 struct winbindd_cli_state
*state
)
1056 NTSTATUS result
= NT_STATUS_LOGON_FAILURE
;
1057 fstring name_domain
, name_user
;
1058 NET_USER_INFO_3
*info3
= NULL
;
1060 /* Ensure null termination */
1061 state
->request
.data
.auth
.user
[sizeof(state
->request
.data
.auth
.user
)-1]='\0';
1063 /* Ensure null termination */
1064 state
->request
.data
.auth
.pass
[sizeof(state
->request
.data
.auth
.pass
)-1]='\0';
1066 DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state
->pid
,
1067 state
->request
.data
.auth
.user
));
1069 /* Parse domain and username */
1071 parse_domain_user(state
->request
.data
.auth
.user
, name_domain
, name_user
);
1073 DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain
->name
, domain
->online
? "online":"offline"));
1075 /* Check for Kerberos authentication */
1076 if (domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1078 result
= winbindd_dual_pam_auth_kerberos(domain
, state
, &info3
);
1080 if (NT_STATUS_IS_OK(result
)) {
1081 DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
1082 goto process_result
;
1084 DEBUG(10,("winbindd_dual_pam_auth_kerberos failed: %s\n", nt_errstr(result
)));
1087 if (NT_STATUS_EQUAL(result
, NT_STATUS_NO_LOGON_SERVERS
)) {
1088 DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
1089 domain
->online
= False
;
1092 if (state
->request
.flags
& WBFLAG_PAM_FALLBACK_AFTER_KRB5
) {
1093 DEBUG(3,("falling back to samlogon\n"));
1101 /* Check for Samlogon authentication */
1102 if (domain
->online
) {
1103 result
= winbindd_dual_pam_auth_samlogon(domain
, state
, &info3
);
1105 if (NT_STATUS_IS_OK(result
)) {
1106 DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
1107 goto process_result
;
1109 DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n", nt_errstr(result
)));
1110 if (domain
->online
) {
1111 /* We're still online - fail. */
1114 /* Else drop through and see if we can check offline.... */
1119 /* Check for Cached logons */
1120 if (!domain
->online
&& (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1121 lp_winbind_offline_logon()) {
1123 result
= winbindd_dual_pam_auth_cached(domain
, state
, &info3
);
1125 if (NT_STATUS_IS_OK(result
)) {
1126 DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
1127 goto process_result
;
1129 DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result
)));
1136 if (NT_STATUS_IS_OK(result
)) {
1140 netsamlogon_cache_store(name_user
, info3
);
1141 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), info3
);
1143 /* save name_to_sid info as early as possible */
1144 sid_compose(&user_sid
, &info3
->dom_sid
.sid
, info3
->user_rid
);
1145 cache_name2sid(domain
, name_domain
, name_user
, SID_NAME_USER
, &user_sid
);
1147 /* Check if the user is in the right group */
1149 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, info3
,
1150 state
->request
.data
.auth
.require_membership_of_sid
))) {
1151 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1152 state
->request
.data
.auth
.user
,
1153 state
->request
.data
.auth
.require_membership_of_sid
));
1157 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1158 result
= append_info3_as_ndr(state
->mem_ctx
, state
, info3
);
1159 if (!NT_STATUS_IS_OK(result
)) {
1160 DEBUG(10,("Failed to append INFO3 (NDR): %s\n", nt_errstr(result
)));
1165 if (state
->request
.flags
& WBFLAG_PAM_INFO3_TEXT
) {
1166 result
= append_info3_as_txt(state
->mem_ctx
, state
, info3
);
1167 if (!NT_STATUS_IS_OK(result
)) {
1168 DEBUG(10,("Failed to append INFO3 (TXT): %s\n", nt_errstr(result
)));
1174 if ((state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1175 lp_winbind_offline_logon()) {
1177 result
= winbindd_store_creds(domain
,
1179 state
->request
.data
.auth
.user
,
1180 state
->request
.data
.auth
.pass
,
1182 if (!NT_STATUS_IS_OK(result
)) {
1183 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result
)));
1189 result
= fillup_password_policy(domain
, state
);
1191 if (!NT_STATUS_IS_OK(result
)) {
1192 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result
)));
1199 /* give us a more useful (more correct?) error code */
1200 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1201 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1202 result
= NT_STATUS_NO_LOGON_SERVERS
;
1205 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1206 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1208 /* we might have given a more useful error above */
1209 if (!*state
->response
.data
.auth
.error_string
)
1210 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1211 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1213 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
1214 state
->request
.data
.auth
.user
,
1215 state
->response
.data
.auth
.nt_status_string
,
1216 state
->response
.data
.auth
.pam_error
));
1218 if ( NT_STATUS_IS_OK(result
) &&
1219 (state
->request
.flags
& WBFLAG_PAM_AFS_TOKEN
) ) {
1221 char *afsname
= talloc_strdup(state
->mem_ctx
,
1222 lp_afs_username_map());
1225 if (afsname
== NULL
) {
1229 afsname
= talloc_string_sub(state
->mem_ctx
,
1230 lp_afs_username_map(),
1232 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1234 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1241 sid_copy(&user_sid
, &info3
->dom_sid
.sid
);
1242 sid_append_rid(&user_sid
, info3
->user_rid
);
1243 sid_to_string(sidstr
, &user_sid
);
1244 afsname
= talloc_string_sub(state
->mem_ctx
, afsname
,
1248 if (afsname
== NULL
) {
1252 strlower_m(afsname
);
1254 DEBUG(10, ("Generating token for user %s\n", afsname
));
1256 cell
= strchr(afsname
, '@');
1265 /* Append an AFS token string */
1266 state
->response
.extra_data
=
1267 afs_createtoken_str(afsname
, cell
);
1269 if (state
->response
.extra_data
!= NULL
)
1270 state
->response
.length
+=
1271 strlen(state
->response
.extra_data
)+1;
1274 TALLOC_FREE(afsname
);
1277 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1281 /**********************************************************************
1282 Challenge Response Authentication Protocol
1283 **********************************************************************/
1285 void winbindd_pam_auth_crap(struct winbindd_cli_state
*state
)
1287 struct winbindd_domain
*domain
= NULL
;
1288 const char *domain_name
= NULL
;
1291 if (!state
->privileged
) {
1292 char *error_string
= NULL
;
1293 DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access "
1295 DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions "
1296 "on %s are set correctly.\n",
1297 get_winbind_priv_pipe_dir()));
1298 /* send a better message than ACCESS_DENIED */
1299 error_string
= talloc_asprintf(state
->mem_ctx
,
1300 "winbind client not authorized "
1301 "to use winbindd_pam_auth_crap."
1302 " Ensure permissions on %s "
1303 "are set correctly.",
1304 get_winbind_priv_pipe_dir());
1305 fstrcpy(state
->response
.data
.auth
.error_string
, error_string
);
1306 result
= NT_STATUS_ACCESS_DENIED
;
1310 /* Ensure null termination */
1311 state
->request
.data
.auth_crap
.user
1312 [sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1313 state
->request
.data
.auth_crap
.domain
1314 [sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1316 DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n",
1317 (unsigned long)state
->pid
,
1318 state
->request
.data
.auth_crap
.domain
,
1319 state
->request
.data
.auth_crap
.user
));
1321 if (*state
->request
.data
.auth_crap
.domain
!= '\0') {
1322 domain_name
= state
->request
.data
.auth_crap
.domain
;
1323 } else if (lp_winbind_use_default_domain()) {
1324 domain_name
= lp_workgroup();
1327 if (domain_name
!= NULL
)
1328 domain
= find_auth_domain(state
, domain_name
);
1330 if (domain
!= NULL
) {
1331 sendto_domain(state
, domain
);
1335 result
= NT_STATUS_NO_SUCH_USER
;
1338 set_auth_errors(&state
->response
, result
);
1339 DEBUG(5, ("CRAP authentication for %s\\%s returned %s (PAM: %d)\n",
1340 state
->request
.data
.auth_crap
.domain
,
1341 state
->request
.data
.auth_crap
.user
,
1342 state
->response
.data
.auth
.nt_status_string
,
1343 state
->response
.data
.auth
.pam_error
));
1344 request_error(state
);
1349 enum winbindd_result
winbindd_dual_pam_auth_crap(struct winbindd_domain
*domain
,
1350 struct winbindd_cli_state
*state
)
1353 NET_USER_INFO_3 info3
;
1354 struct rpc_pipe_client
*netlogon_pipe
;
1355 const char *name_user
= NULL
;
1356 const char *name_domain
= NULL
;
1357 const char *workstation
;
1358 struct winbindd_domain
*contact_domain
;
1362 DATA_BLOB lm_resp
, nt_resp
;
1364 /* This is child-only, so no check for privileged access is needed
1367 /* Ensure null termination */
1368 state
->request
.data
.auth_crap
.user
[sizeof(state
->request
.data
.auth_crap
.user
)-1]=0;
1369 state
->request
.data
.auth_crap
.domain
[sizeof(state
->request
.data
.auth_crap
.domain
)-1]=0;
1371 name_user
= state
->request
.data
.auth_crap
.user
;
1373 if (*state
->request
.data
.auth_crap
.domain
) {
1374 name_domain
= state
->request
.data
.auth_crap
.domain
;
1375 } else if (lp_winbind_use_default_domain()) {
1376 name_domain
= lp_workgroup();
1378 DEBUG(5,("no domain specified with username (%s) - failing auth\n",
1380 result
= NT_STATUS_NO_SUCH_USER
;
1384 DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state
->pid
,
1385 name_domain
, name_user
));
1387 if (*state
->request
.data
.auth_crap
.workstation
) {
1388 workstation
= state
->request
.data
.auth_crap
.workstation
;
1390 workstation
= global_myname();
1393 if (state
->request
.data
.auth_crap
.lm_resp_len
> sizeof(state
->request
.data
.auth_crap
.lm_resp
)
1394 || state
->request
.data
.auth_crap
.nt_resp_len
> sizeof(state
->request
.data
.auth_crap
.nt_resp
)) {
1395 DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
1396 state
->request
.data
.auth_crap
.lm_resp_len
,
1397 state
->request
.data
.auth_crap
.nt_resp_len
));
1398 result
= NT_STATUS_INVALID_PARAMETER
;
1402 lm_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.lm_resp
,
1403 state
->request
.data
.auth_crap
.lm_resp_len
);
1404 nt_resp
= data_blob_talloc(state
->mem_ctx
, state
->request
.data
.auth_crap
.nt_resp
,
1405 state
->request
.data
.auth_crap
.nt_resp_len
);
1407 /* what domain should we contact? */
1410 if (!(contact_domain
= find_domain_from_name(name_domain
))) {
1411 DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
1412 state
->request
.data
.auth_crap
.user
, name_domain
, name_user
, name_domain
));
1413 result
= NT_STATUS_NO_SUCH_USER
;
1417 if (is_myname(name_domain
)) {
1418 DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain
));
1419 result
= NT_STATUS_NO_SUCH_USER
;
1422 contact_domain
= find_our_domain();
1429 netlogon_pipe
= NULL
;
1430 result
= cm_connect_netlogon(contact_domain
, &netlogon_pipe
);
1432 if (!NT_STATUS_IS_OK(result
)) {
1433 DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
1434 nt_errstr(result
)));
1438 result
= rpccli_netlogon_sam_network_logon(netlogon_pipe
,
1440 state
->request
.data
.auth_crap
.logon_parameters
,
1441 contact_domain
->dcname
,
1444 /* Bug #3248 - found by Stefan Burkei. */
1445 workstation
, /* We carefully set this above so use it... */
1446 state
->request
.data
.auth_crap
.chal
,
1453 /* We have to try a second time as cm_connect_netlogon
1454 might not yet have noticed that the DC has killed
1457 if (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)) {
1462 /* if we get access denied, a possible cause was that we had and open
1463 connection to the DC, but someone changed our machine account password
1464 out from underneath us using 'net rpc changetrustpw' */
1466 if ( NT_STATUS_EQUAL(result
, NT_STATUS_ACCESS_DENIED
) ) {
1467 DEBUG(3,("winbindd_pam_auth: sam_logon returned "
1468 "ACCESS_DENIED. Maybe the trust account "
1469 "password was changed and we didn't know it. "
1470 "Killing connections to domain %s\n",
1472 invalidate_cm_connection(&contact_domain
->conn
);
1476 } while ( (attempts
< 2) && retry
);
1478 if (NT_STATUS_IS_OK(result
)) {
1480 netsamlogon_cache_store(name_user
, &info3
);
1481 wcache_invalidate_samlogon(find_domain_from_name(name_domain
), &info3
);
1483 /* Check if the user is in the right group */
1485 if (!NT_STATUS_IS_OK(result
= check_info3_in_group(state
->mem_ctx
, &info3
,
1486 state
->request
.data
.auth_crap
.require_membership_of_sid
))) {
1487 DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n",
1488 state
->request
.data
.auth_crap
.user
,
1489 state
->request
.data
.auth_crap
.require_membership_of_sid
));
1493 if (state
->request
.flags
& WBFLAG_PAM_INFO3_NDR
) {
1494 result
= append_info3_as_ndr(state
->mem_ctx
, state
, &info3
);
1495 } else if (state
->request
.flags
& WBFLAG_PAM_UNIX_NAME
) {
1496 /* ntlm_auth should return the unix username, per
1497 'winbind use default domain' settings and the like */
1499 fstring username_out
;
1500 const char *nt_username
, *nt_domain
;
1501 if (!(nt_username
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_user_name
)))) {
1502 /* If the server didn't give us one, just use the one we sent them */
1503 nt_username
= name_user
;
1506 if (!(nt_domain
= unistr2_tdup(state
->mem_ctx
, &(info3
.uni_logon_dom
)))) {
1507 /* If the server didn't give us one, just use the one we sent them */
1508 nt_domain
= name_domain
;
1511 fill_domain_username(username_out
, nt_domain
, nt_username
);
1513 DEBUG(5, ("Setting unix username to [%s]\n", username_out
));
1515 state
->response
.extra_data
= SMB_STRDUP(username_out
);
1516 if (!state
->response
.extra_data
) {
1517 result
= NT_STATUS_NO_MEMORY
;
1520 state
->response
.length
+= strlen(state
->response
.extra_data
)+1;
1523 if (state
->request
.flags
& WBFLAG_PAM_USER_SESSION_KEY
) {
1524 memcpy(state
->response
.data
.auth
.user_session_key
, info3
.user_sess_key
,
1525 sizeof(state
->response
.data
.auth
.user_session_key
) /* 16 */);
1527 if (state
->request
.flags
& WBFLAG_PAM_LMKEY
) {
1528 memcpy(state
->response
.data
.auth
.first_8_lm_hash
, info3
.lm_sess_key
,
1529 sizeof(state
->response
.data
.auth
.first_8_lm_hash
) /* 8 */);
1535 /* give us a more useful (more correct?) error code */
1536 if ((NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ||
1537 (NT_STATUS_EQUAL(result
, NT_STATUS_UNSUCCESSFUL
)))) {
1538 result
= NT_STATUS_NO_LOGON_SERVERS
;
1541 if (state
->request
.flags
& WBFLAG_PAM_NT_STATUS_SQUASH
) {
1542 result
= nt_status_squash(result
);
1545 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1546 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1548 /* we might have given a more useful error above */
1549 if (!*state
->response
.data
.auth
.error_string
) {
1550 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1552 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1554 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1555 ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
1558 state
->response
.data
.auth
.nt_status_string
,
1559 state
->response
.data
.auth
.pam_error
));
1561 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;
1564 /* Change a user password */
1566 void winbindd_pam_chauthtok(struct winbindd_cli_state
*state
)
1568 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1570 char *newpass
= NULL
;
1571 fstring domain
, user
;
1573 struct winbindd_domain
*contact_domain
;
1574 struct rpc_pipe_client
*cli
;
1575 BOOL got_info
= False
;
1576 SAM_UNK_INFO_1 info
;
1577 SAMR_CHANGE_REJECT reject
;
1579 DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state
->pid
,
1580 state
->request
.data
.chauthtok
.user
));
1584 parse_domain_user(state
->request
.data
.chauthtok
.user
, domain
, user
);
1586 if (!(contact_domain
= find_domain_from_name(domain
))) {
1587 DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
1588 state
->request
.data
.chauthtok
.user
, domain
, user
, domain
));
1589 result
= NT_STATUS_NO_SUCH_USER
;
1593 /* Change password */
1595 oldpass
= state
->request
.data
.chauthtok
.oldpass
;
1596 newpass
= state
->request
.data
.chauthtok
.newpass
;
1598 /* Get sam handle */
1600 result
= cm_connect_sam(contact_domain
, state
->mem_ctx
, &cli
,
1602 if (!NT_STATUS_IS_OK(result
)) {
1603 DEBUG(1, ("could not get SAM handle on DC for %s\n", domain
));
1607 result
= rpccli_samr_chgpasswd3(cli
, state
->mem_ctx
, user
, newpass
, oldpass
, &info
, &reject
);
1609 /* FIXME: need to check for other error codes ? */
1610 if (NT_STATUS_EQUAL(result
, NT_STATUS_PASSWORD_RESTRICTION
)) {
1612 state
->response
.data
.auth
.policy
.min_length_password
=
1613 info
.min_length_password
;
1614 state
->response
.data
.auth
.policy
.password_history
=
1615 info
.password_history
;
1616 state
->response
.data
.auth
.policy
.password_properties
=
1617 info
.password_properties
;
1618 state
->response
.data
.auth
.policy
.expire
=
1619 nt_time_to_unix_abs(&info
.expire
);
1620 state
->response
.data
.auth
.policy
.min_passwordage
=
1621 nt_time_to_unix_abs(&info
.min_passwordage
);
1623 state
->response
.data
.auth
.reject_reason
=
1624 reject
.reject_reason
;
1628 } else if (!NT_STATUS_IS_OK(result
)) {
1630 DEBUG(10,("Password change with chgpasswd3 failed with: %s, retrying chgpasswd_user\n",
1631 nt_errstr(result
)));
1633 state
->response
.data
.auth
.reject_reason
= 0;
1635 result
= rpccli_samr_chgpasswd_user(cli
, state
->mem_ctx
, user
, newpass
, oldpass
);
1639 if (NT_STATUS_IS_OK(result
) && (state
->request
.flags
& WBFLAG_PAM_CACHED_LOGIN
) &&
1640 lp_winbind_offline_logon()) {
1644 cred_ret
= winbindd_update_creds_by_name(contact_domain
,
1645 state
->mem_ctx
, user
,
1647 if (!NT_STATUS_IS_OK(cred_ret
)) {
1648 DEBUG(10,("Failed to store creds: %s\n", nt_errstr(cred_ret
)));
1649 goto process_result
; /* FIXME: hm, risking inconsistant cache ? */
1653 if (!NT_STATUS_IS_OK(result
) && !got_info
) {
1655 NTSTATUS policy_ret
;
1657 policy_ret
= fillup_password_policy(contact_domain
, state
);
1659 /* failure of this is non critical, it will just provide no
1660 * additional information to the client why the change has
1661 * failed - Guenther */
1663 if (!NT_STATUS_IS_OK(policy_ret
)) {
1664 DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret
)));
1665 goto process_result
;
1671 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1672 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1673 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1674 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1676 DEBUG(NT_STATUS_IS_OK(result
) ? 5 : 2,
1677 ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
1680 state
->response
.data
.auth
.nt_status_string
,
1681 state
->response
.data
.auth
.pam_error
));
1683 if (NT_STATUS_IS_OK(result
))
1686 request_error(state
);
1689 void winbindd_pam_logoff(struct winbindd_cli_state
*state
)
1691 struct winbindd_domain
*domain
;
1692 fstring name_domain
, user
;
1694 DEBUG(3, ("[%5lu]: pam logoff %s\n", (unsigned long)state
->pid
,
1695 state
->request
.data
.logoff
.user
));
1697 /* Ensure null termination */
1698 state
->request
.data
.logoff
.user
1699 [sizeof(state
->request
.data
.logoff
.user
)-1]='\0';
1701 state
->request
.data
.logoff
.krb5ccname
1702 [sizeof(state
->request
.data
.logoff
.krb5ccname
)-1]='\0';
1704 parse_domain_user(state
->request
.data
.logoff
.user
, name_domain
, user
);
1706 domain
= find_auth_domain(state
, name_domain
);
1708 if (domain
== NULL
) {
1709 set_auth_errors(&state
->response
, NT_STATUS_NO_SUCH_USER
);
1710 DEBUG(5, ("Pam Logoff for %s returned %s "
1712 state
->request
.data
.auth
.user
,
1713 state
->response
.data
.auth
.nt_status_string
,
1714 state
->response
.data
.auth
.pam_error
));
1715 request_error(state
);
1719 sendto_domain(state
, domain
);
1722 enum winbindd_result
winbindd_dual_pam_logoff(struct winbindd_domain
*domain
,
1723 struct winbindd_cli_state
*state
)
1725 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
1726 struct WINBINDD_CCACHE_ENTRY
*entry
;
1729 DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state
->pid
,
1730 state
->request
.data
.logoff
.user
));
1732 if (!(state
->request
.flags
& WBFLAG_PAM_KRB5
)) {
1733 result
= NT_STATUS_OK
;
1734 goto process_result
;
1739 /* what we need here is to find the corresponding krb5 ccache name *we*
1740 * created for a given username and destroy it (as the user who created it) */
1742 entry
= get_ccache_by_username(state
->request
.data
.logoff
.user
);
1743 if (entry
== NULL
) {
1744 DEBUG(10,("winbindd_pam_logoff: could not get ccname for user %s\n",
1745 state
->request
.data
.logoff
.user
));
1746 goto process_result
;
1749 DEBUG(10,("winbindd_pam_logoff: found ccache [%s]\n", entry
->ccname
));
1751 if (entry
->uid
< 0 || state
->request
.data
.logoff
.uid
< 0) {
1752 DEBUG(0,("winbindd_pam_logoff: invalid uid\n"));
1753 goto process_result
;
1756 if (entry
->uid
!= state
->request
.data
.logoff
.uid
) {
1757 DEBUG(0,("winbindd_pam_logoff: uid's differ: %d != %d\n",
1758 entry
->uid
, state
->request
.data
.logoff
.uid
));
1759 goto process_result
;
1762 if (!strcsequal(entry
->ccname
, state
->request
.data
.logoff
.krb5ccname
)) {
1763 DEBUG(0,("winbindd_pam_logoff: krb5ccnames differ: (daemon) %s != (client) %s\n",
1764 entry
->ccname
, state
->request
.data
.logoff
.krb5ccname
));
1765 goto process_result
;
1768 seteuid(entry
->uid
);
1770 ret
= ads_kdestroy(entry
->ccname
);
1775 DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n",
1776 entry
->ccname
, error_message(ret
)));
1778 DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n",
1779 entry
->ccname
, state
->request
.data
.logoff
.user
));
1780 remove_ccache_by_ccname(entry
->ccname
);
1783 result
= krb5_to_nt_status(ret
);
1785 result
= NT_STATUS_NOT_SUPPORTED
;
1789 state
->response
.data
.auth
.nt_status
= NT_STATUS_V(result
);
1790 fstrcpy(state
->response
.data
.auth
.nt_status_string
, nt_errstr(result
));
1791 fstrcpy(state
->response
.data
.auth
.error_string
, get_friendly_nt_error_msg(result
));
1792 state
->response
.data
.auth
.pam_error
= nt_status_to_pam(result
);
1794 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;