r17270: split the logic of saying this auth backend wants to handle this
[jelmer/samba4-debian.git] / source / auth / auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Password and authentication handling
4    Copyright (C) Andrew Bartlett         2001-2002
5    Copyright (C) Stefan Metzmacher       2005
6    
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23 #include "dlinklist.h"
24 #include "lib/ldb/include/ldb.h"
25 #include "auth/auth.h"
26 #include "lib/events/events.h"
27 #include "build.h"
28
29 /***************************************************************************
30  Set a fixed challenge
31 ***************************************************************************/
32 NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by) 
33 {
34         auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
35         NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
36
37         auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
38         NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
39
40         return NT_STATUS_OK;
41 }
42
43 /***************************************************************************
44  Set a fixed challenge
45 ***************************************************************************/
46 BOOL auth_challenge_may_be_modified(struct auth_context *auth_ctx) 
47 {
48         return auth_ctx->challenge.may_be_modified;
49 }
50
51 /****************************************************************************
52  Try to get a challenge out of the various authentication modules.
53  Returns a const char of length 8 bytes.
54 ****************************************************************************/
55 NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
56 {
57         NTSTATUS nt_status;
58         struct auth_method_context *method;
59
60         if (auth_ctx->challenge.data.length) {
61                 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n", 
62                           auth_ctx->challenge.set_by));
63                 *_chal = auth_ctx->challenge.data.data;
64                 return NT_STATUS_OK;
65         }
66
67         for (method = auth_ctx->methods; method; method = method->next) {
68                 DATA_BLOB challenge = data_blob(NULL,0);
69
70                 nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
71                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
72                         continue;
73                 }
74
75                 NT_STATUS_NOT_OK_RETURN(nt_status);
76
77                 if (challenge.length != 8) {
78                         DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
79                                 (unsigned)challenge.length, method->ops->name));
80                         return NT_STATUS_INTERNAL_ERROR;
81                 }
82
83                 auth_ctx->challenge.data        = challenge;
84                 auth_ctx->challenge.set_by      = method->ops->name;
85
86                 break;
87         }
88
89         if (!auth_ctx->challenge.set_by) {
90                 uint8_t chal[8];
91                 generate_random_buffer(chal, 8);
92
93                 auth_ctx->challenge.data                = data_blob_talloc(auth_ctx, chal, 8);
94                 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
95                 auth_ctx->challenge.set_by              = "random";
96
97                 auth_ctx->challenge.may_be_modified     = True;
98         }
99
100         DEBUG(10,("auth_get_challenge: challenge set by %s\n",
101                  auth_ctx->challenge.set_by));
102
103         *_chal = auth_ctx->challenge.data.data;
104         return NT_STATUS_OK;
105 }
106
107 /**
108  * Check a user's Plaintext, LM or NTLM password.
109  *
110  * Check a user's password, as given in the user_info struct and return various
111  * interesting details in the server_info struct.
112  *
113  * The return value takes precedence over the contents of the server_info 
114  * struct.  When the return is other than NT_STATUS_OK the contents 
115  * of that structure is undefined.
116  *
117  * @param user_info Contains the user supplied components, including the passwords.
118  *
119  * @param auth_context Supplies the challenges and some other data. 
120  *                  Must be created with make_auth_context(), and the challenges should be 
121  *                  filled in, either at creation or by calling the challenge geneation 
122  *                  function auth_get_challenge().  
123  *
124  * @param server_info If successful, contains information about the authentication, 
125  *                    including a SAM_ACCOUNT struct describing the user.
126  *
127  * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
128  *
129  **/
130
131 NTSTATUS auth_check_password(struct auth_context *auth_ctx,
132                              TALLOC_CTX *mem_ctx,
133                              const struct auth_usersupplied_info *user_info, 
134                              struct auth_serversupplied_info **server_info)
135 {
136         /* if all the modules say 'not for me' this is reasonable */
137         NTSTATUS nt_status;
138         struct auth_method_context *method;
139         const char *method_name = "NO METHOD";
140         const uint8_t *challenge;
141         struct auth_usersupplied_info *user_info_tmp; 
142
143         DEBUG(3,   ("auth_check_password:  Checking password for unmapped user [%s]\\[%s]@[%s]\n", 
144                     user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
145
146         if (!user_info->mapped_state) {
147                 nt_status = map_user_info(mem_ctx, user_info, &user_info_tmp);
148                 if (!NT_STATUS_IS_OK(nt_status)) {
149                         return nt_status;
150                 }
151                 user_info = user_info_tmp;
152         }
153
154         DEBUGADD(3,("auth_check_password:  mapped user is: [%s]\\[%s]@[%s]\n", 
155                     user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
156
157         nt_status = auth_get_challenge(auth_ctx, &challenge);
158
159         if (!NT_STATUS_IS_OK(nt_status)) {
160                 DEBUG(0, ("auth_check_password:  Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
161                         (unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
162                 return nt_status;
163         }
164
165         if (auth_ctx->challenge.set_by) {
166                 DEBUG(10, ("auth_check_password: auth_context challenge created by %s\n",
167                                         auth_ctx->challenge.set_by));
168         }
169
170         DEBUG(10, ("challenge is: \n"));
171         dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
172
173         nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
174         for (method = auth_ctx->methods; method; method = method->next) {
175                 NTSTATUS result;
176
177                 /* check if the module wants to chek the password */
178                 result = method->ops->want_check(method, mem_ctx, user_info);
179                 if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
180                         DEBUG(11,("auth_check_password: %s had nothing to say\n", method->ops->name));
181                         continue;
182                 }
183
184                 method_name = method->ops->name;
185                 nt_status = result;
186
187                 if (!NT_STATUS_IS_OK(nt_status)) break;
188
189                 nt_status = method->ops->check_password(method, mem_ctx, user_info, server_info);
190                 break;
191         }
192
193         if (!NT_STATUS_IS_OK(nt_status)) {
194                 DEBUG(2,("auth_check_password: %s authentication for user [%s\\%s] FAILED with error %s\n", 
195                          method_name, user_info->mapped.domain_name, user_info->mapped.account_name, 
196                          nt_errstr(nt_status)));
197                 return nt_status;
198         }
199
200         DEBUG(5,("auth_check_password: %s authentication for user [%s\\%s] succeeded\n",
201                  method_name, (*server_info)->domain_name, (*server_info)->account_name));
202
203         return nt_status;
204 }
205
206 /***************************************************************************
207  Make a auth_info struct for the auth subsystem
208 ***************************************************************************/
209 NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx, const char **methods, 
210                              struct auth_context **auth_ctx,
211                              struct event_context *ev) 
212 {
213         int i;
214         struct auth_context *ctx;
215
216         if (!methods) {
217                 DEBUG(0,("auth_context_create: No auth method list!?\n"));
218                 return NT_STATUS_INTERNAL_ERROR;
219         }
220
221         ctx = talloc(mem_ctx, struct auth_context);
222         NT_STATUS_HAVE_NO_MEMORY(ctx);
223         ctx->challenge.set_by           = NULL;
224         ctx->challenge.may_be_modified  = False;
225         ctx->challenge.data             = data_blob(NULL, 0);
226         ctx->methods                    = NULL;
227         
228         if (ev == NULL) {
229                 ev = event_context_init(ctx);
230                 if (ev == NULL) {
231                         talloc_free(ctx);
232                         return NT_STATUS_NO_MEMORY;
233                 }
234         }
235
236         ctx->event_ctx = ev;
237
238         for (i=0; methods[i] ; i++) {
239                 struct auth_method_context *method;
240
241                 method = talloc(ctx, struct auth_method_context);
242                 NT_STATUS_HAVE_NO_MEMORY(method);
243
244                 method->ops = auth_backend_byname(methods[i]);
245                 if (!method->ops) {
246                         DEBUG(1,("auth_context_create: failed to find method=%s\n",
247                                 methods[i]));
248                         return NT_STATUS_INTERNAL_ERROR;
249                 }
250                 method->auth_ctx        = ctx;
251                 method->depth           = i;
252                 DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
253         }
254
255         if (!ctx->methods) {
256                 return NT_STATUS_INTERNAL_ERROR;
257         }
258
259         *auth_ctx = ctx;
260
261         return NT_STATUS_OK;
262 }
263
264 /* the list of currently registered AUTH backends */
265 static struct auth_backend {
266         const struct auth_operations *ops;
267 } *backends = NULL;
268 static int num_backends;
269
270 /*
271   register a AUTH backend. 
272
273   The 'name' can be later used by other backends to find the operations
274   structure for this backend.
275 */
276 NTSTATUS auth_register(const void *_ops)
277 {
278         const struct auth_operations *ops = _ops;
279         struct auth_operations *new_ops;
280         
281         if (auth_backend_byname(ops->name) != NULL) {
282                 /* its already registered! */
283                 DEBUG(0,("AUTH backend '%s' already registered\n", 
284                          ops->name));
285                 return NT_STATUS_OBJECT_NAME_COLLISION;
286         }
287
288         backends = realloc_p(backends, struct auth_backend, num_backends+1);
289         if (!backends) {
290                 return NT_STATUS_NO_MEMORY;
291         }
292
293         new_ops = smb_xmemdup(ops, sizeof(*ops));
294         new_ops->name = smb_xstrdup(ops->name);
295
296         backends[num_backends].ops = new_ops;
297
298         num_backends++;
299
300         DEBUG(3,("AUTH backend '%s' registered\n", 
301                  ops->name));
302
303         return NT_STATUS_OK;
304 }
305
306 /*
307   return the operations structure for a named backend of the specified type
308 */
309 const struct auth_operations *auth_backend_byname(const char *name)
310 {
311         int i;
312
313         for (i=0;i<num_backends;i++) {
314                 if (strcmp(backends[i].ops->name, name) == 0) {
315                         return backends[i].ops;
316                 }
317         }
318
319         return NULL;
320 }
321
322 /*
323   return the AUTH interface version, and the size of some critical types
324   This can be used by backends to either detect compilation errors, or provide
325   multiple implementations for different smbd compilation options in one module
326 */
327 const struct auth_critical_sizes *auth_interface_version(void)
328 {
329         static const struct auth_critical_sizes critical_sizes = {
330                 AUTH_INTERFACE_VERSION,
331                 sizeof(struct auth_operations),
332                 sizeof(struct auth_method_context),
333                 sizeof(struct auth_context),
334                 sizeof(struct auth_usersupplied_info),
335                 sizeof(struct auth_serversupplied_info)
336         };
337
338         return &critical_sizes;
339 }
340
341 NTSTATUS auth_init(void)
342 {
343         static BOOL initialized = False;
344
345         init_module_fn static_init[] = STATIC_auth_MODULES;
346         init_module_fn *shared_init;
347         
348         if (initialized) return NT_STATUS_OK;
349         initialized = True;
350         
351         shared_init = load_samba_modules(NULL, "auth");
352
353         run_init_functions(static_init);
354         run_init_functions(shared_init);
355
356         talloc_free(shared_init);
357         
358         return NT_STATUS_OK;    
359 }
360
361 NTSTATUS server_service_auth_init(void)
362 {
363         return auth_init();
364 }