2 Unix SMB/CIFS implementation.
4 Winbind authentication mechnism
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Andrew Bartlett 2001 - 2002
8 Copyright (C) Stefan Metzmacher 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "../lib/util/tevent_ntstatus.h"
27 #include "auth/auth.h"
28 #include "auth/ntlm/auth_proto.h"
29 #include "librpc/gen_ndr/ndr_winbind_c.h"
30 #include "lib/messaging/irpc.h"
31 #include "param/param.h"
32 #include "nsswitch/libwbclient/wbclient.h"
33 #include "auth/auth_sam_reply.h"
34 #include "libcli/security/security.h"
35 #include "dsdb/samdb/samdb.h"
36 #include "auth/auth_sam.h"
38 _PUBLIC_ NTSTATUS
auth4_winbind_init(TALLOC_CTX
*);
40 static NTSTATUS
winbind_want_check(struct auth_method_context
*ctx
,
42 const struct auth_usersupplied_info
*user_info
)
44 if (!user_info
->mapped
.account_name
|| !*user_info
->mapped
.account_name
) {
45 return NT_STATUS_NOT_IMPLEMENTED
;
48 /* TODO: maybe limit the user scope to remote users only */
52 static NTSTATUS
winbind_rodc_want_check(struct auth_method_context
*ctx
,
54 const struct auth_usersupplied_info
*user_info
)
59 if (!user_info
->mapped
.account_name
|| !*user_info
->mapped
.account_name
) {
60 return NT_STATUS_NOT_IMPLEMENTED
;
63 if (ctx
->auth_ctx
->sam_ctx
== NULL
) {
64 DBG_ERR("ctx->auth_ctx->sam_ctx == NULL, don't check.\n");
65 return NT_STATUS_NOT_IMPLEMENTED
;
68 ret
= samdb_rodc(ctx
->auth_ctx
->sam_ctx
, &am_rodc
);
69 if (ret
!= LDB_SUCCESS
) {
70 DBG_ERR("samdb_rodc() failed %d %s, don't check.\n",
71 ret
, ldb_errstring(ctx
->auth_ctx
->sam_ctx
));
72 return NT_STATUS_NOT_IMPLEMENTED
;
77 * We don't support trusts yet and we
78 * don't want to add them using the
79 * semi-async irpc call that uses
80 * a nested event loop.
82 return NT_STATUS_NOT_IMPLEMENTED
;
86 * We're a RODC, so we forward the request to our winbind.
87 * As the RODC is not yet production ready anyway, we keep
88 * the semi-async behavior with nested event loops in order
89 * to keep autobuild happy.
94 struct winbind_check_password_state
{
95 struct auth_method_context
*ctx
;
96 const struct auth_usersupplied_info
*user_info
;
97 struct winbind_SamLogon req
;
98 struct auth_user_info_dc
*user_info_dc
;
102 static void winbind_check_password_done(struct tevent_req
*subreq
);
105 Authenticate a user with a challenge/response
106 using IRPC to the winbind task
108 static struct tevent_req
*winbind_check_password_send(TALLOC_CTX
*mem_ctx
,
109 struct tevent_context
*ev
,
110 struct auth_method_context
*ctx
,
111 const struct auth_usersupplied_info
*user_info
)
113 struct tevent_req
*req
= NULL
;
114 struct winbind_check_password_state
*state
= NULL
;
116 struct dcerpc_binding_handle
*irpc_handle
;
117 const struct auth_usersupplied_info
*user_info_new
;
118 struct netr_IdentityInfo
*identity_info
;
119 struct imessaging_context
*msg_ctx
;
120 struct tevent_req
*subreq
= NULL
;
122 req
= tevent_req_create(mem_ctx
, &state
,
123 struct winbind_check_password_state
);
128 state
->user_info
= user_info
;
129 state
->authoritative
= true;
131 msg_ctx
= imessaging_client_init(state
, ctx
->auth_ctx
->lp_ctx
, ev
);
132 if (msg_ctx
== NULL
) {
133 DEBUG(1, ("imessaging_init failed\n"));
134 tevent_req_nterror(req
, NT_STATUS_INVALID_SERVER_STATE
);
135 return tevent_req_post(req
, ev
);
138 irpc_handle
= irpc_binding_handle_by_name(state
, msg_ctx
,
141 if (irpc_handle
== NULL
) {
142 DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
143 "no winbind_server running!\n",
144 user_info
->client
.domain_name
, user_info
->client
.account_name
));
145 tevent_req_nterror(req
, NT_STATUS_NO_LOGON_SERVERS
);
146 return tevent_req_post(req
, ev
);
150 * 120 seconds should be enough even for trusted domains.
152 * Currently winbindd has a much lower limit.
153 * And tests with Windows RODCs show that it
154 * returns NO_LOGON_SERVERS after 90-100 seconds
155 * if it can't reach any RWDC.
157 dcerpc_binding_handle_set_timeout(irpc_handle
, 120);
159 if (user_info
->flags
& USER_INFO_INTERACTIVE_LOGON
) {
160 struct netr_PasswordInfo
*password_info
;
162 status
= encrypt_user_info(state
, ctx
->auth_ctx
, AUTH_PASSWORD_HASH
,
163 user_info
, &user_info_new
);
164 if (tevent_req_nterror(req
, status
)) {
165 return tevent_req_post(req
, ev
);
167 user_info
= user_info_new
;
169 password_info
= talloc_zero(state
, struct netr_PasswordInfo
);
170 if (tevent_req_nomem(password_info
, req
)) {
171 return tevent_req_post(req
, ev
);
174 password_info
->lmpassword
= *user_info
->password
.hash
.lanman
;
175 password_info
->ntpassword
= *user_info
->password
.hash
.nt
;
177 identity_info
= &password_info
->identity_info
;
178 state
->req
.in
.logon_level
= 1;
179 state
->req
.in
.logon
.password
= password_info
;
181 struct netr_NetworkInfo
*network_info
;
184 status
= encrypt_user_info(state
, ctx
->auth_ctx
, AUTH_PASSWORD_RESPONSE
,
185 user_info
, &user_info_new
);
186 if (tevent_req_nterror(req
, status
)) {
187 return tevent_req_post(req
, ev
);
189 user_info
= user_info_new
;
191 network_info
= talloc_zero(state
, struct netr_NetworkInfo
);
192 if (tevent_req_nomem(network_info
, req
)) {
193 return tevent_req_post(req
, ev
);
196 status
= auth_get_challenge(ctx
->auth_ctx
, chal
);
197 if (tevent_req_nterror(req
, status
)) {
198 return tevent_req_post(req
, ev
);
201 memcpy(network_info
->challenge
, chal
, sizeof(network_info
->challenge
));
203 network_info
->nt
.length
= user_info
->password
.response
.nt
.length
;
204 network_info
->nt
.data
= user_info
->password
.response
.nt
.data
;
206 network_info
->lm
.length
= user_info
->password
.response
.lanman
.length
;
207 network_info
->lm
.data
= user_info
->password
.response
.lanman
.data
;
209 identity_info
= &network_info
->identity_info
;
210 state
->req
.in
.logon_level
= 2;
211 state
->req
.in
.logon
.network
= network_info
;
214 identity_info
->domain_name
.string
= user_info
->client
.domain_name
;
215 identity_info
->parameter_control
= user_info
->logon_parameters
; /* see MSV1_0_* */
216 identity_info
->logon_id_low
= 0;
217 identity_info
->logon_id_high
= 0;
218 identity_info
->account_name
.string
= user_info
->client
.account_name
;
219 identity_info
->workstation
.string
= user_info
->workstation_name
;
221 state
->req
.in
.validation_level
= 3;
223 subreq
= dcerpc_winbind_SamLogon_r_send(state
, ev
, irpc_handle
,
225 if (tevent_req_nomem(subreq
, req
)) {
226 return tevent_req_post(req
, ev
);
228 tevent_req_set_callback(subreq
,
229 winbind_check_password_done
,
235 static void winbind_check_password_done(struct tevent_req
*subreq
)
237 struct tevent_req
*req
=
238 tevent_req_callback_data(subreq
,
240 struct winbind_check_password_state
*state
=
242 struct winbind_check_password_state
);
243 struct auth_method_context
*ctx
= state
->ctx
;
244 const struct auth_usersupplied_info
*user_info
= state
->user_info
;
245 const char *account_name
= user_info
->mapped
.account_name
;
246 struct ldb_dn
*domain_dn
= NULL
;
247 struct ldb_message
*msg
= NULL
;
248 const char *p
= NULL
;
251 status
= dcerpc_winbind_SamLogon_r_recv(subreq
, state
);
252 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
)) {
253 status
= NT_STATUS_NO_LOGON_SERVERS
;
256 if (tevent_req_nterror(req
, status
)) {
260 status
= state
->req
.out
.result
;
261 if (!NT_STATUS_IS_OK(status
)) {
262 if (!state
->req
.out
.authoritative
) {
263 state
->authoritative
= false;
265 tevent_req_nterror(req
, status
);
270 * At best, reset the badPwdCount to 0 if the account exists.
271 * This means that lockouts happen at a badPwdCount earlier than
272 * normal, but makes it more fault tolerant.
274 p
= strchr_m(account_name
, '@');
276 const char *nt4_domain
= NULL
;
277 const char *nt4_account
= NULL
;
279 status
= crack_name_to_nt4_name(state
,
280 ctx
->auth_ctx
->sam_ctx
,
281 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL
,
283 &nt4_domain
, &nt4_account
);
284 if (NT_STATUS_IS_OK(status
) &&
285 lpcfg_is_mydomain(ctx
->auth_ctx
->lp_ctx
, nt4_domain
))
287 account_name
= nt4_account
;
291 domain_dn
= ldb_get_default_basedn(ctx
->auth_ctx
->sam_ctx
);
292 if (domain_dn
!= NULL
) {
293 status
= authsam_search_account(state
, ctx
->auth_ctx
->sam_ctx
,
294 account_name
, domain_dn
, &msg
);
295 if (NT_STATUS_IS_OK(status
)) {
296 authsam_logon_success_accounting(
297 ctx
->auth_ctx
->sam_ctx
, msg
,
299 user_info
->flags
& USER_INFO_INTERACTIVE_LOGON
,
304 status
= make_user_info_dc_netlogon_validation(state
,
305 user_info
->client
.account_name
,
306 state
->req
.in
.validation_level
,
307 &state
->req
.out
.validation
,
308 true, /* This user was authenticated */
309 &state
->user_info_dc
);
310 if (tevent_req_nterror(req
, status
)) {
314 tevent_req_done(req
);
317 static NTSTATUS
winbind_check_password_recv(struct tevent_req
*req
,
319 struct auth_user_info_dc
**user_info_dc
,
320 bool *pauthoritative
)
322 struct winbind_check_password_state
*state
=
324 struct winbind_check_password_state
);
325 NTSTATUS status
= NT_STATUS_OK
;
327 *pauthoritative
= state
->authoritative
;
329 if (tevent_req_is_nterror(req
, &status
)) {
330 tevent_req_received(req
);
334 *user_info_dc
= talloc_move(mem_ctx
, &state
->user_info_dc
);
336 tevent_req_received(req
);
341 Authenticate a user with a challenge/response
342 using the samba3 winbind protocol via libwbclient
344 static NTSTATUS
winbind_check_password_wbclient(struct auth_method_context
*ctx
,
346 const struct auth_usersupplied_info
*user_info
,
347 struct auth_user_info_dc
**user_info_dc
,
350 struct wbcAuthUserParams params
;
351 struct wbcAuthUserInfo
*info
= NULL
;
352 struct wbcAuthErrorInfo
*err
= NULL
;
355 struct netr_SamInfo6
*info6
= NULL
;
356 union netr_Validation validation
;
358 /* Send off request */
359 const struct auth_usersupplied_info
*user_info_temp
;
360 nt_status
= encrypt_user_info(mem_ctx
, ctx
->auth_ctx
,
361 AUTH_PASSWORD_RESPONSE
,
362 user_info
, &user_info_temp
);
363 if (!NT_STATUS_IS_OK(nt_status
)) {
366 user_info
= user_info_temp
;
369 ZERO_STRUCT(validation
);
370 /*params.flags = WBFLAG_PAM_INFO3_NDR;*/
372 params
.parameter_control
= user_info
->logon_parameters
;
373 params
.parameter_control
|= WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT
|
374 WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT
;
375 params
.level
= WBC_AUTH_USER_LEVEL_RESPONSE
;
377 params
.account_name
= user_info
->client
.account_name
;
378 params
.domain_name
= user_info
->client
.domain_name
;
379 params
.workstation_name
= user_info
->workstation_name
;
381 DEBUG(5,("looking up %s@%s logging in from %s\n",
382 params
.account_name
, params
.domain_name
,
383 params
.workstation_name
));
385 memcpy(params
.password
.response
.challenge
,
386 ctx
->auth_ctx
->challenge
.data
.data
,
387 sizeof(params
.password
.response
.challenge
));
389 params
.password
.response
.lm_length
=
390 user_info
->password
.response
.lanman
.length
;
391 params
.password
.response
.nt_length
=
392 user_info
->password
.response
.nt
.length
;
394 params
.password
.response
.lm_data
=
395 user_info
->password
.response
.lanman
.data
;
396 params
.password
.response
.nt_data
=
397 user_info
->password
.response
.nt
.data
;
399 wbc_status
= wbcAuthenticateUserEx(¶ms
, &info
, &err
);
400 if (wbc_status
== WBC_ERR_AUTH_ERROR
) {
402 DEBUG(1, ("error was %s (0x%08x)\nerror message was '%s'\n",
403 err
->nt_string
, err
->nt_status
, err
->display_string
));
404 nt_status
= NT_STATUS(err
->nt_status
);
407 nt_status
= NT_STATUS_LOGON_FAILURE
;
409 NT_STATUS_NOT_OK_RETURN(nt_status
);
410 } else if (!WBC_ERROR_IS_OK(wbc_status
)) {
411 DEBUG(1, ("wbcAuthenticateUserEx: failed with %u - %s\n",
412 wbc_status
, wbcErrorString(wbc_status
)));
414 DEBUG(1, ("error was %s (0x%08x)\nerror message was '%s'\n",
415 err
->nt_string
, err
->nt_status
, err
->display_string
));
417 return NT_STATUS_LOGON_FAILURE
;
419 info6
= wbcAuthUserInfo_to_netr_SamInfo6(mem_ctx
, info
);
422 DEBUG(1, ("wbcAuthUserInfo_to_netr_SamInfo6 failed\n"));
423 return NT_STATUS_NO_MEMORY
;
426 validation
.sam6
= info6
;
427 nt_status
= make_user_info_dc_netlogon_validation(mem_ctx
,
428 user_info
->client
.account_name
,
430 true, /* This user was authenticated */
436 static const struct auth_operations winbind_ops
= {
438 .want_check
= winbind_want_check
,
439 .check_password_send
= winbind_check_password_send
,
440 .check_password_recv
= winbind_check_password_recv
443 static const struct auth_operations winbind_rodc_ops
= {
444 .name
= "winbind_rodc",
445 .want_check
= winbind_rodc_want_check
,
446 .check_password_send
= winbind_check_password_send
,
447 .check_password_recv
= winbind_check_password_recv
450 static const struct auth_operations winbind_wbclient_ops
= {
451 .name
= "winbind_wbclient",
452 .want_check
= winbind_want_check
,
453 .check_password
= winbind_check_password_wbclient
456 _PUBLIC_ NTSTATUS
auth4_winbind_init(TALLOC_CTX
*ctx
)
460 ret
= auth_register(ctx
, &winbind_ops
);
461 if (!NT_STATUS_IS_OK(ret
)) {
462 DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
466 ret
= auth_register(ctx
, &winbind_rodc_ops
);
467 if (!NT_STATUS_IS_OK(ret
)) {
468 DEBUG(0,("Failed to register 'winbind_rodc' auth backend!\n"));
472 ret
= auth_register(ctx
, &winbind_wbclient_ops
);
473 if (!NT_STATUS_IS_OK(ret
)) {
474 DEBUG(0,("Failed to register 'winbind_wbclient' auth backend!\n"));