r18549: move gcc version check to libreplace and reorder the tests a bit
[Samba.git] / source / auth / auth.c
blobf79e2f7bbded602b5d830469ee60f51839c28102
1 /*
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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
23 #include "lib/util/dlinklist.h"
24 #include "auth/auth.h"
25 #include "lib/events/events.h"
26 #include "build.h"
28 /***************************************************************************
29 Set a fixed challenge
30 ***************************************************************************/
31 NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by)
33 auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
34 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
36 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
37 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
39 return NT_STATUS_OK;
42 /***************************************************************************
43 Set a fixed challenge
44 ***************************************************************************/
45 BOOL auth_challenge_may_be_modified(struct auth_context *auth_ctx)
47 return auth_ctx->challenge.may_be_modified;
50 /****************************************************************************
51 Try to get a challenge out of the various authentication modules.
52 Returns a const char of length 8 bytes.
53 ****************************************************************************/
54 NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
56 NTSTATUS nt_status;
57 struct auth_method_context *method;
59 if (auth_ctx->challenge.data.length) {
60 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
61 auth_ctx->challenge.set_by));
62 *_chal = auth_ctx->challenge.data.data;
63 return NT_STATUS_OK;
66 for (method = auth_ctx->methods; method; method = method->next) {
67 DATA_BLOB challenge = data_blob(NULL,0);
69 nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
70 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
71 continue;
74 NT_STATUS_NOT_OK_RETURN(nt_status);
76 if (challenge.length != 8) {
77 DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
78 (unsigned)challenge.length, method->ops->name));
79 return NT_STATUS_INTERNAL_ERROR;
82 auth_ctx->challenge.data = challenge;
83 auth_ctx->challenge.set_by = method->ops->name;
85 break;
88 if (!auth_ctx->challenge.set_by) {
89 uint8_t chal[8];
90 generate_random_buffer(chal, 8);
92 auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
93 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
94 auth_ctx->challenge.set_by = "random";
96 auth_ctx->challenge.may_be_modified = True;
99 DEBUG(10,("auth_get_challenge: challenge set by %s\n",
100 auth_ctx->challenge.set_by));
102 *_chal = auth_ctx->challenge.data.data;
103 return NT_STATUS_OK;
106 struct auth_check_password_sync_state {
107 BOOL finished;
108 NTSTATUS status;
109 struct auth_serversupplied_info *server_info;
112 static void auth_check_password_sync_callback(struct auth_check_password_request *req,
113 void *private_data)
115 struct auth_check_password_sync_state *s = talloc_get_type(private_data,
116 struct auth_check_password_sync_state);
118 s->finished = True;
119 s->status = auth_check_password_recv(req, s, &s->server_info);
123 * Check a user's Plaintext, LM or NTLM password.
124 * (sync version)
126 * Check a user's password, as given in the user_info struct and return various
127 * interesting details in the server_info struct.
129 * The return value takes precedence over the contents of the server_info
130 * struct. When the return is other than NT_STATUS_OK the contents
131 * of that structure is undefined.
133 * @param auth_ctx Supplies the challenges and some other data.
134 * Must be created with auth_context_create(), and the challenges should be
135 * filled in, either at creation or by calling the challenge geneation
136 * function auth_get_challenge().
138 * @param user_info Contains the user supplied components, including the passwords.
140 * @param mem_ctx The parent memory context for the server_info structure
142 * @param server_info If successful, contains information about the authentication,
143 * including a SAM_ACCOUNT struct describing the user.
145 * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
149 NTSTATUS auth_check_password(struct auth_context *auth_ctx,
150 TALLOC_CTX *mem_ctx,
151 const struct auth_usersupplied_info *user_info,
152 struct auth_serversupplied_info **server_info)
154 struct auth_check_password_sync_state *sync_state;
155 NTSTATUS status;
157 sync_state = talloc_zero(auth_ctx, struct auth_check_password_sync_state);
158 NT_STATUS_HAVE_NO_MEMORY(sync_state);
160 auth_check_password_send(auth_ctx, user_info, auth_check_password_sync_callback, sync_state);
162 while (!sync_state->finished) {
163 event_loop_once(auth_ctx->event_ctx);
166 status = sync_state->status;
168 if (NT_STATUS_IS_OK(status)) {
169 *server_info = talloc_steal(mem_ctx, sync_state->server_info);
172 talloc_free(sync_state);
173 return status;
176 struct auth_check_password_request {
177 struct auth_context *auth_ctx;
178 const struct auth_usersupplied_info *user_info;
179 struct auth_serversupplied_info *server_info;
180 struct auth_method_context *method;
181 NTSTATUS status;
182 struct {
183 void (*fn)(struct auth_check_password_request *req, void *private_data);
184 void *private_data;
185 } callback;
188 static void auth_check_password_async_timed_handler(struct event_context *ev, struct timed_event *te,
189 struct timeval t, void *ptr)
191 struct auth_check_password_request *req = talloc_get_type(ptr, struct auth_check_password_request);
192 req->status = req->method->ops->check_password(req->method, req, req->user_info, &req->server_info);
193 req->callback.fn(req, req->callback.private_data);
197 * Check a user's Plaintext, LM or NTLM password.
198 * async send hook
200 * Check a user's password, as given in the user_info struct and return various
201 * interesting details in the server_info struct.
203 * The return value takes precedence over the contents of the server_info
204 * struct. When the return is other than NT_STATUS_OK the contents
205 * of that structure is undefined.
207 * @param auth_ctx Supplies the challenges and some other data.
208 * Must be created with make_auth_context(), and the challenges should be
209 * filled in, either at creation or by calling the challenge geneation
210 * function auth_get_challenge().
212 * @param user_info Contains the user supplied components, including the passwords.
214 * @param callback A callback function which will be called when the operation is finished.
215 * The callback function needs to call auth_check_password_recv() to get the return values
217 * @param private_data A private pointer which will ba passed to the callback function
221 void auth_check_password_send(struct auth_context *auth_ctx,
222 const struct auth_usersupplied_info *user_info,
223 void (*callback)(struct auth_check_password_request *req, void *private_data),
224 void *private_data)
226 /* if all the modules say 'not for me' this is reasonable */
227 NTSTATUS nt_status;
228 struct auth_method_context *method;
229 const uint8_t *challenge;
230 struct auth_usersupplied_info *user_info_tmp;
231 struct auth_check_password_request *req = NULL;
233 DEBUG(3, ("auth_check_password_send: Checking password for unmapped user [%s]\\[%s]@[%s]\n",
234 user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
236 req = talloc_zero(auth_ctx, struct auth_check_password_request);
237 if (!req) {
238 callback(NULL, private_data);
239 return;
241 req->auth_ctx = auth_ctx;
242 req->user_info = user_info;
243 req->callback.fn = callback;
244 req->callback.private_data = private_data;
246 if (!user_info->mapped_state) {
247 nt_status = map_user_info(req, user_info, &user_info_tmp);
248 if (!NT_STATUS_IS_OK(nt_status)) goto failed;
249 user_info = user_info_tmp;
250 req->user_info = user_info_tmp;
253 DEBUGADD(3,("auth_check_password_send: mapped user is: [%s]\\[%s]@[%s]\n",
254 user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
256 nt_status = auth_get_challenge(auth_ctx, &challenge);
257 if (!NT_STATUS_IS_OK(nt_status)) {
258 DEBUG(0, ("auth_check_password_send: Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
259 (unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
260 goto failed;
263 if (auth_ctx->challenge.set_by) {
264 DEBUG(10, ("auth_check_password_send: auth_context challenge created by %s\n",
265 auth_ctx->challenge.set_by));
268 DEBUG(10, ("auth_check_password_send: challenge is: \n"));
269 dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
271 nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
272 for (method = auth_ctx->methods; method; method = method->next) {
273 NTSTATUS result;
274 struct timed_event *te = NULL;
276 /* check if the module wants to chek the password */
277 result = method->ops->want_check(method, req, user_info);
278 if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
279 DEBUG(11,("auth_check_password_send: %s had nothing to say\n", method->ops->name));
280 continue;
283 nt_status = result;
284 req->method = method;
286 if (!NT_STATUS_IS_OK(nt_status)) break;
288 te = event_add_timed(auth_ctx->event_ctx, req,
289 timeval_zero(),
290 auth_check_password_async_timed_handler, req);
291 if (!te) {
292 nt_status = NT_STATUS_NO_MEMORY;
293 goto failed;
295 return;
298 failed:
299 req->status = nt_status;
300 req->callback.fn(req, req->callback.private_data);
304 * Check a user's Plaintext, LM or NTLM password.
305 * async receive function
307 * The return value takes precedence over the contents of the server_info
308 * struct. When the return is other than NT_STATUS_OK the contents
309 * of that structure is undefined.
312 * @param req The async auth_check_password state, passes to the callers callback function
314 * @param mem_ctx The parent memory context for the server_info structure
316 * @param server_info If successful, contains information about the authentication,
317 * including a SAM_ACCOUNT struct describing the user.
319 * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
323 NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
324 TALLOC_CTX *mem_ctx,
325 struct auth_serversupplied_info **server_info)
327 NTSTATUS status;
329 NT_STATUS_HAVE_NO_MEMORY(req);
331 if (NT_STATUS_IS_OK(req->status)) {
332 DEBUG(5,("auth_check_password_recv: %s authentication for user [%s\\%s] succeeded\n",
333 req->method->ops->name, req->server_info->domain_name, req->server_info->account_name));
335 *server_info = talloc_steal(mem_ctx, req->server_info);
336 } else {
337 DEBUG(2,("auth_check_password_recv: %s authentication for user [%s\\%s] FAILED with error %s\n",
338 (req->method ? req->method->ops->name : "NO_METHOD"),
339 req->user_info->mapped.domain_name,
340 req->user_info->mapped.account_name,
341 nt_errstr(req->status)));
344 status = req->status;
345 talloc_free(req);
346 return status;
349 /***************************************************************************
350 Make a auth_info struct for the auth subsystem
351 ***************************************************************************/
352 NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx, const char **methods,
353 struct event_context *ev,
354 struct messaging_context *msg,
355 struct auth_context **auth_ctx)
357 int i;
358 struct auth_context *ctx;
360 if (!methods) {
361 DEBUG(0,("auth_context_create: No auth method list!?\n"));
362 return NT_STATUS_INTERNAL_ERROR;
365 if (!ev) {
366 DEBUG(0,("auth_context_create: called with out event context\n"));
367 return NT_STATUS_INTERNAL_ERROR;
370 if (!msg) {
371 DEBUG(0,("auth_context_create: called with out messaging context\n"));
372 return NT_STATUS_INTERNAL_ERROR;
375 ctx = talloc(mem_ctx, struct auth_context);
376 NT_STATUS_HAVE_NO_MEMORY(ctx);
377 ctx->challenge.set_by = NULL;
378 ctx->challenge.may_be_modified = False;
379 ctx->challenge.data = data_blob(NULL, 0);
380 ctx->methods = NULL;
381 ctx->event_ctx = ev;
382 ctx->msg_ctx = msg;
384 for (i=0; methods[i] ; i++) {
385 struct auth_method_context *method;
387 method = talloc(ctx, struct auth_method_context);
388 NT_STATUS_HAVE_NO_MEMORY(method);
390 method->ops = auth_backend_byname(methods[i]);
391 if (!method->ops) {
392 DEBUG(1,("auth_context_create: failed to find method=%s\n",
393 methods[i]));
394 return NT_STATUS_INTERNAL_ERROR;
396 method->auth_ctx = ctx;
397 method->depth = i;
398 DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
401 if (!ctx->methods) {
402 return NT_STATUS_INTERNAL_ERROR;
405 *auth_ctx = ctx;
407 return NT_STATUS_OK;
410 /* the list of currently registered AUTH backends */
411 static struct auth_backend {
412 const struct auth_operations *ops;
413 } *backends = NULL;
414 static int num_backends;
417 register a AUTH backend.
419 The 'name' can be later used by other backends to find the operations
420 structure for this backend.
422 NTSTATUS auth_register(const void *_ops)
424 const struct auth_operations *ops = _ops;
425 struct auth_operations *new_ops;
427 if (auth_backend_byname(ops->name) != NULL) {
428 /* its already registered! */
429 DEBUG(0,("AUTH backend '%s' already registered\n",
430 ops->name));
431 return NT_STATUS_OBJECT_NAME_COLLISION;
434 backends = realloc_p(backends, struct auth_backend, num_backends+1);
435 if (!backends) {
436 return NT_STATUS_NO_MEMORY;
439 new_ops = smb_xmemdup(ops, sizeof(*ops));
440 new_ops->name = smb_xstrdup(ops->name);
442 backends[num_backends].ops = new_ops;
444 num_backends++;
446 DEBUG(3,("AUTH backend '%s' registered\n",
447 ops->name));
449 return NT_STATUS_OK;
453 return the operations structure for a named backend of the specified type
455 const struct auth_operations *auth_backend_byname(const char *name)
457 int i;
459 for (i=0;i<num_backends;i++) {
460 if (strcmp(backends[i].ops->name, name) == 0) {
461 return backends[i].ops;
465 return NULL;
469 return the AUTH interface version, and the size of some critical types
470 This can be used by backends to either detect compilation errors, or provide
471 multiple implementations for different smbd compilation options in one module
473 const struct auth_critical_sizes *auth_interface_version(void)
475 static const struct auth_critical_sizes critical_sizes = {
476 AUTH_INTERFACE_VERSION,
477 sizeof(struct auth_operations),
478 sizeof(struct auth_method_context),
479 sizeof(struct auth_context),
480 sizeof(struct auth_usersupplied_info),
481 sizeof(struct auth_serversupplied_info)
484 return &critical_sizes;
487 NTSTATUS auth_init(void)
489 static BOOL initialized = False;
491 init_module_fn static_init[] = STATIC_auth_MODULES;
492 init_module_fn *shared_init;
494 if (initialized) return NT_STATUS_OK;
495 initialized = True;
497 shared_init = load_samba_modules(NULL, "auth");
499 run_init_functions(static_init);
500 run_init_functions(shared_init);
502 talloc_free(shared_init);
504 return NT_STATUS_OK;
507 NTSTATUS server_service_auth_init(void)
509 return auth_init();