2 Unix SMB/CIFS implementation.
3 Password and authentication handling
4 Copyright (C) Andrew Bartlett 2001-2002
5 Copyright (C) Stefan Metzmacher 2005
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "../lib/util/tevent_ntstatus.h"
24 #include "../lib/util/dlinklist.h"
25 #include "auth/auth.h"
26 #include "auth/ntlm/auth_proto.h"
27 #include "param/param.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "libcli/wbclient/wbclient.h"
30 #include "lib/util/samba_modules.h"
31 #include "auth/credentials/credentials.h"
32 #include "system/kerberos.h"
33 #include "auth/kerberos/kerberos.h"
34 #include "auth/kerberos/kerberos_util.h"
36 static NTSTATUS auth_generate_session_info_wrapper(TALLOC_CTX *mem_ctx,
37 struct auth4_context *auth_context,
38 struct auth_user_info_dc *user_info_dc,
39 uint32_t session_info_flags,
40 struct auth_session_info **session_info);
42 /***************************************************************************
44 ***************************************************************************/
45 _PUBLIC_ NTSTATUS auth_context_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
47 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
48 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
50 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
51 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
56 /***************************************************************************
58 ***************************************************************************/
59 _PUBLIC_ bool auth_challenge_may_be_modified(struct auth4_context *auth_ctx)
61 return auth_ctx->challenge.may_be_modified;
64 /****************************************************************************
65 Try to get a challenge out of the various authentication modules.
66 Returns a const char of length 8 bytes.
67 ****************************************************************************/
68 _PUBLIC_ NTSTATUS auth_get_challenge(struct auth4_context *auth_ctx, uint8_t chal[8])
71 struct auth_method_context *method;
73 if (auth_ctx->challenge.data.length == 8) {
74 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
75 auth_ctx->challenge.set_by));
76 memcpy(chal, auth_ctx->challenge.data.data, 8);
80 for (method = auth_ctx->methods; method; method = method->next) {
81 nt_status = method->ops->get_challenge(method, auth_ctx, chal);
82 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
86 NT_STATUS_NOT_OK_RETURN(nt_status);
88 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
89 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
90 auth_ctx->challenge.set_by = method->ops->name;
95 if (!auth_ctx->challenge.set_by) {
96 generate_random_buffer(chal, 8);
98 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
99 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
100 auth_ctx->challenge.set_by = "random";
102 auth_ctx->challenge.may_be_modified = true;
105 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
106 auth_ctx->challenge.set_by));
111 /****************************************************************************
112 Used in the gensec_gssapi and gensec_krb5 server-side code, where the
113 PAC isn't available, and for tokenGroups in the DSDB stack.
115 Supply either a principal or a DN
116 ****************************************************************************/
117 static NTSTATUS auth_generate_session_info_principal(struct auth4_context *auth_ctx,
119 const char *principal,
120 struct ldb_dn *user_dn,
121 uint32_t session_info_flags,
122 struct auth_session_info **session_info)
125 struct auth_method_context *method;
126 struct auth_user_info_dc *user_info_dc;
128 for (method = auth_ctx->methods; method; method = method->next) {
129 if (!method->ops->get_user_info_dc_principal) {
133 nt_status = method->ops->get_user_info_dc_principal(mem_ctx, auth_ctx, principal, user_dn, &user_info_dc);
134 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
137 if (!NT_STATUS_IS_OK(nt_status)) {
141 nt_status = auth_generate_session_info_wrapper(mem_ctx, auth_ctx,
143 session_info_flags, session_info);
144 talloc_free(user_info_dc);
149 return NT_STATUS_NOT_IMPLEMENTED;
153 * Check a user's Plaintext, LM or NTLM password.
156 * Check a user's password, as given in the user_info struct and return various
157 * interesting details in the user_info_dc struct.
159 * The return value takes precedence over the contents of the user_info_dc
160 * struct. When the return is other than NT_STATUS_OK the contents
161 * of that structure is undefined.
163 * @param auth_ctx Supplies the challenges and some other data.
164 * Must be created with auth_context_create(), and the challenges should be
165 * filled in, either at creation or by calling the challenge geneation
166 * function auth_get_challenge().
168 * @param user_info Contains the user supplied components, including the passwords.
170 * @param mem_ctx The parent memory context for the user_info_dc structure
172 * @param user_info_dc If successful, contains information about the authentication,
173 * including a SAM_ACCOUNT struct describing the user.
175 * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
179 _PUBLIC_ NTSTATUS auth_check_password(struct auth4_context *auth_ctx,
181 const struct auth_usersupplied_info *user_info,
182 struct auth_user_info_dc **user_info_dc)
184 struct tevent_req *subreq;
185 struct tevent_context *ev;
189 /*TODO: create a new event context here! */
190 ev = auth_ctx->event_ctx;
192 subreq = auth_check_password_send(mem_ctx,
196 if (subreq == NULL) {
197 return NT_STATUS_NO_MEMORY;
200 ok = tevent_req_poll(subreq, ev);
202 return NT_STATUS_INTERNAL_ERROR;
205 status = auth_check_password_recv(subreq, mem_ctx, user_info_dc);
211 struct auth_check_password_state {
212 struct auth4_context *auth_ctx;
213 const struct auth_usersupplied_info *user_info;
214 struct auth_user_info_dc *user_info_dc;
215 struct auth_method_context *method;
218 static void auth_check_password_async_trigger(struct tevent_context *ev,
219 struct tevent_immediate *im,
222 * Check a user's Plaintext, LM or NTLM password.
225 * Check a user's password, as given in the user_info struct and return various
226 * interesting details in the user_info_dc struct.
228 * The return value takes precedence over the contents of the user_info_dc
229 * struct. When the return is other than NT_STATUS_OK the contents
230 * of that structure is undefined.
232 * @param mem_ctx The memory context the request should operate on
234 * @param ev The tevent context the request should operate on
236 * @param auth_ctx Supplies the challenges and some other data.
237 * Must be created with make_auth_context(), and the challenges should be
238 * filled in, either at creation or by calling the challenge geneation
239 * function auth_get_challenge().
241 * @param user_info Contains the user supplied components, including the passwords.
243 * @return The request handle or NULL on no memory error.
247 _PUBLIC_ struct tevent_req *auth_check_password_send(TALLOC_CTX *mem_ctx,
248 struct tevent_context *ev,
249 struct auth4_context *auth_ctx,
250 const struct auth_usersupplied_info *user_info)
252 struct tevent_req *req;
253 struct auth_check_password_state *state;
254 /* if all the modules say 'not for me' this is reasonable */
257 struct auth_usersupplied_info *user_info_tmp;
258 struct tevent_immediate *im;
260 DEBUG(3,("auth_check_password_send: "
261 "Checking password for unmapped user [%s]\\[%s]@[%s]\n",
262 user_info->client.domain_name, user_info->client.account_name,
263 user_info->workstation_name));
265 req = tevent_req_create(mem_ctx, &state,
266 struct auth_check_password_state);
271 state->auth_ctx = auth_ctx;
272 state->user_info = user_info;
274 if (!user_info->mapped_state) {
275 nt_status = map_user_info(auth_ctx->sam_ctx, req, lpcfg_workgroup(auth_ctx->lp_ctx),
276 user_info, &user_info_tmp);
277 if (tevent_req_nterror(req, nt_status)) {
278 return tevent_req_post(req, ev);
280 user_info = user_info_tmp;
281 state->user_info = user_info_tmp;
284 DEBUGADD(3,("auth_check_password_send: "
285 "mapped user is: [%s]\\[%s]@[%s]\n",
286 user_info->mapped.domain_name,
287 user_info->mapped.account_name,
288 user_info->workstation_name));
290 nt_status = auth_get_challenge(auth_ctx, chal);
291 if (tevent_req_nterror(req, nt_status)) {
292 DEBUG(0,("auth_check_password_send: "
293 "Invalid challenge (length %u) stored for "
294 "this auth context set_by %s - cannot continue: %s\n",
295 (unsigned)auth_ctx->challenge.data.length,
296 auth_ctx->challenge.set_by,
297 nt_errstr(nt_status)));
298 return tevent_req_post(req, ev);
301 if (auth_ctx->challenge.set_by) {
302 DEBUG(10,("auth_check_password_send: "
303 "auth_context challenge created by %s\n",
304 auth_ctx->challenge.set_by));
307 DEBUG(10, ("auth_check_password_send: challenge is: \n"));
308 dump_data(5, auth_ctx->challenge.data.data,
309 auth_ctx->challenge.data.length);
311 im = tevent_create_immediate(state);
312 if (tevent_req_nomem(im, req)) {
313 return tevent_req_post(req, ev);
316 tevent_schedule_immediate(im,
318 auth_check_password_async_trigger,
323 static void auth_check_password_async_trigger(struct tevent_context *ev,
324 struct tevent_immediate *im,
327 struct tevent_req *req =
328 talloc_get_type_abort(private_data, struct tevent_req);
329 struct auth_check_password_state *state =
330 tevent_req_data(req, struct auth_check_password_state);
332 struct auth_method_context *method;
334 status = NT_STATUS_OK;
336 for (method=state->auth_ctx->methods; method; method = method->next) {
338 /* we fill in state->method here so debug messages in
339 the callers know which method failed */
340 state->method = method;
342 /* check if the module wants to check the password */
343 status = method->ops->want_check(method, req, state->user_info);
344 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
345 DEBUG(11,("auth_check_password_send: "
346 "%s had nothing to say\n",
351 if (tevent_req_nterror(req, status)) {
355 status = method->ops->check_password(method,
358 &state->user_info_dc);
359 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
360 /* the backend has handled the request */
365 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
366 /* don't expose the NT_STATUS_NOT_IMPLEMENTED
368 status = NT_STATUS_NO_SUCH_USER;
371 if (tevent_req_nterror(req, status)) {
375 tevent_req_done(req);
379 * Check a user's Plaintext, LM or NTLM password.
380 * async receive function
382 * The return value takes precedence over the contents of the user_info_dc
383 * struct. When the return is other than NT_STATUS_OK the contents
384 * of that structure is undefined.
387 * @param req The async request state
389 * @param mem_ctx The parent memory context for the user_info_dc structure
391 * @param user_info_dc If successful, contains information about the authentication,
392 * including a SAM_ACCOUNT struct describing the user.
394 * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
398 _PUBLIC_ NTSTATUS auth_check_password_recv(struct tevent_req *req,
400 struct auth_user_info_dc **user_info_dc)
402 struct auth_check_password_state *state =
403 tevent_req_data(req, struct auth_check_password_state);
406 if (tevent_req_is_nterror(req, &status)) {
407 DEBUG(2,("auth_check_password_recv: "
408 "%s authentication for user [%s\\%s] "
409 "FAILED with error %s\n",
410 (state->method ? state->method->ops->name : "NO_METHOD"),
411 state->user_info->mapped.domain_name,
412 state->user_info->mapped.account_name,
414 tevent_req_received(req);
418 DEBUG(5,("auth_check_password_recv: "
419 "%s authentication for user [%s\\%s] succeeded\n",
420 state->method->ops->name,
421 state->user_info_dc->info->domain_name,
422 state->user_info_dc->info->account_name));
424 *user_info_dc = talloc_move(mem_ctx, &state->user_info_dc);
426 tevent_req_received(req);
430 /* Wrapper because we don't want to expose all callers to needing to
431 * know that session_info is generated from the main ldb, and because
432 * we need to break a depenency loop between the DCE/RPC layer and the
433 * generation of unix tokens via IRPC */
434 static NTSTATUS auth_generate_session_info_wrapper(TALLOC_CTX *mem_ctx,
435 struct auth4_context *auth_context,
436 struct auth_user_info_dc *user_info_dc,
437 uint32_t session_info_flags,
438 struct auth_session_info **session_info)
440 NTSTATUS status = auth_generate_session_info(mem_ctx, auth_context->lp_ctx,
441 auth_context->sam_ctx, user_info_dc,
442 session_info_flags, session_info);
443 if (!NT_STATUS_IS_OK(status)) {
447 if ((session_info_flags & AUTH_SESSION_INFO_UNIX_TOKEN)
448 && NT_STATUS_IS_OK(status)) {
449 struct wbc_context *wbc_ctx = wbc_init(auth_context,
450 auth_context->msg_ctx,
451 auth_context->event_ctx);
453 TALLOC_FREE(*session_info);
454 DEBUG(1, ("Cannot contact winbind to provide unix token\n"));
455 return NT_STATUS_INVALID_SERVER_STATE;
457 status = auth_session_info_fill_unix(wbc_ctx, auth_context->lp_ctx,
459 if (!NT_STATUS_IS_OK(status)) {
460 TALLOC_FREE(*session_info);
462 TALLOC_FREE(wbc_ctx);
467 /* Wrapper because we don't want to expose all callers to needing to
468 * know anything about the PAC or auth subsystem internal structures
469 * before we output a struct auth session_info */
470 static NTSTATUS auth_generate_session_info_pac(struct auth4_context *auth_ctx,
471 TALLOC_CTX *mem_ctx_out,
472 struct smb_krb5_context *smb_krb5_context,
474 const char *principal_name,
475 const struct tsocket_address *remote_address,
476 uint32_t session_info_flags,
477 struct auth_session_info **session_info)
480 struct auth_user_info_dc *user_info_dc;
484 return auth_generate_session_info_principal(auth_ctx, mem_ctx_out, principal_name,
485 NULL, session_info_flags, session_info);
488 mem_ctx = talloc_named(mem_ctx_out, 0, "gensec_gssapi_session_info context");
489 NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
491 status = kerberos_pac_blob_to_user_info_dc(mem_ctx,
493 smb_krb5_context->krb5_context,
494 &user_info_dc, NULL, NULL);
495 if (!NT_STATUS_IS_OK(status)) {
496 talloc_free(mem_ctx);
500 if (user_info_dc->info->authenticated) {
501 session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
504 status = auth_generate_session_info_wrapper(mem_ctx_out, auth_ctx,
506 session_info_flags, session_info);
507 talloc_free(mem_ctx);
511 /***************************************************************************
512 Make a auth_info struct for the auth subsystem
513 - Allow the caller to specify the methods to use, including optionally the SAM to use
514 ***************************************************************************/
515 _PUBLIC_ NTSTATUS auth_context_create_methods(TALLOC_CTX *mem_ctx, const char **methods,
516 struct tevent_context *ev,
517 struct imessaging_context *msg,
518 struct loadparm_context *lp_ctx,
519 struct ldb_context *sam_ctx,
520 struct auth4_context **auth_ctx)
523 struct auth4_context *ctx;
528 DEBUG(0,("auth_context_create: called with out event context\n"));
529 return NT_STATUS_INTERNAL_ERROR;
532 ctx = talloc_zero(mem_ctx, struct auth4_context);
533 NT_STATUS_HAVE_NO_MEMORY(ctx);
534 ctx->challenge.set_by = NULL;
535 ctx->challenge.may_be_modified = false;
536 ctx->challenge.data = data_blob(NULL, 0);
540 ctx->lp_ctx = lp_ctx;
543 ctx->sam_ctx = sam_ctx;
545 ctx->sam_ctx = samdb_connect(ctx, ctx->event_ctx, ctx->lp_ctx, system_session(ctx->lp_ctx), 0);
548 for (i=0; methods && methods[i] ; i++) {
549 struct auth_method_context *method;
551 method = talloc(ctx, struct auth_method_context);
552 NT_STATUS_HAVE_NO_MEMORY(method);
554 method->ops = auth_backend_byname(methods[i]);
556 DEBUG(1,("auth_context_create: failed to find method=%s\n",
558 return NT_STATUS_INTERNAL_ERROR;
560 method->auth_ctx = ctx;
562 DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
565 ctx->check_password = auth_check_password;
566 ctx->get_challenge = auth_get_challenge;
567 ctx->set_challenge = auth_context_set_challenge;
568 ctx->challenge_may_be_modified = auth_challenge_may_be_modified;
569 ctx->generate_session_info = auth_generate_session_info_wrapper;
570 ctx->generate_session_info_pac = auth_generate_session_info_pac;
577 const char **auth_methods_from_lp(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
579 char **auth_methods = NULL;
581 switch (lpcfg_server_role(lp_ctx)) {
582 case ROLE_STANDALONE:
583 auth_methods = str_list_make(mem_ctx, "anonymous sam_ignoredomain", NULL);
585 case ROLE_DOMAIN_MEMBER:
586 auth_methods = str_list_make(mem_ctx, "anonymous sam winbind", NULL);
588 case ROLE_DOMAIN_BDC:
589 case ROLE_DOMAIN_PDC:
590 auth_methods = str_list_make(mem_ctx, "anonymous sam_ignoredomain winbind", NULL);
593 return (const char **) auth_methods;
596 /***************************************************************************
597 Make a auth_info struct for the auth subsystem
598 - Uses default auth_methods, depending on server role and smb.conf settings
599 ***************************************************************************/
600 _PUBLIC_ NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx,
601 struct tevent_context *ev,
602 struct imessaging_context *msg,
603 struct loadparm_context *lp_ctx,
604 struct auth4_context **auth_ctx)
607 const char **auth_methods;
608 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
610 return NT_STATUS_NO_MEMORY;
613 auth_methods = auth_methods_from_lp(tmp_ctx, lp_ctx);
615 return NT_STATUS_INVALID_PARAMETER;
617 status = auth_context_create_methods(mem_ctx, auth_methods, ev, msg, lp_ctx, NULL, auth_ctx);
618 talloc_free(tmp_ctx);
622 /* the list of currently registered AUTH backends */
623 static struct auth_backend {
624 const struct auth_operations *ops;
626 static int num_backends;
629 register a AUTH backend.
631 The 'name' can be later used by other backends to find the operations
632 structure for this backend.
634 _PUBLIC_ NTSTATUS auth_register(const struct auth_operations *ops)
636 struct auth_operations *new_ops;
638 if (auth_backend_byname(ops->name) != NULL) {
639 /* its already registered! */
640 DEBUG(0,("AUTH backend '%s' already registered\n",
642 return NT_STATUS_OBJECT_NAME_COLLISION;
645 backends = talloc_realloc(talloc_autofree_context(), backends,
646 struct auth_backend, num_backends+1);
647 NT_STATUS_HAVE_NO_MEMORY(backends);
649 new_ops = (struct auth_operations *)talloc_memdup(backends, ops, sizeof(*ops));
650 NT_STATUS_HAVE_NO_MEMORY(new_ops);
651 new_ops->name = talloc_strdup(new_ops, ops->name);
652 NT_STATUS_HAVE_NO_MEMORY(new_ops->name);
654 backends[num_backends].ops = new_ops;
658 DEBUG(3,("AUTH backend '%s' registered\n",
665 return the operations structure for a named backend of the specified type
667 const struct auth_operations *auth_backend_byname(const char *name)
671 for (i=0;i<num_backends;i++) {
672 if (strcmp(backends[i].ops->name, name) == 0) {
673 return backends[i].ops;
681 return the AUTH interface version, and the size of some critical types
682 This can be used by backends to either detect compilation errors, or provide
683 multiple implementations for different smbd compilation options in one module
685 const struct auth_critical_sizes *auth_interface_version(void)
687 static const struct auth_critical_sizes critical_sizes = {
688 AUTH4_INTERFACE_VERSION,
689 sizeof(struct auth_operations),
690 sizeof(struct auth_method_context),
691 sizeof(struct auth4_context),
692 sizeof(struct auth_usersupplied_info),
693 sizeof(struct auth_user_info_dc)
696 return &critical_sizes;
699 _PUBLIC_ NTSTATUS auth4_init(void)
701 static bool initialized = false;
702 #define _MODULE_PROTO(init) extern NTSTATUS init(void);
703 STATIC_auth4_MODULES_PROTO;
704 init_module_fn static_init[] = { STATIC_auth4_MODULES };
706 if (initialized) return NT_STATUS_OK;
709 run_init_functions(static_init);