r3453: - split out the auth and popt includes
[ira/wip.git] / source / auth / auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Password and authentication handling
4    Copyright (C) Andrew Bartlett         2001-2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "auth/auth.h"
23
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_AUTH
26
27 /****************************************************************************
28  Try to get a challenge out of the various authentication modules.
29  Returns a const char of length 8 bytes.
30 ****************************************************************************/
31
32 static const uint8_t *get_ntlm_challenge(struct auth_context *auth_context) 
33 {
34         DATA_BLOB challenge = data_blob(NULL, 0);
35         const char *challenge_set_by = NULL;
36         struct auth_methods *auth_method;
37         TALLOC_CTX *mem_ctx;
38
39         if (auth_context->challenge.length) {
40                 DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge by module %s (normal)\n", 
41                           auth_context->challenge_set_by));
42                 return auth_context->challenge.data;
43         }
44
45         auth_context->challenge_may_be_modified = False;
46
47         for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) {
48                 if (auth_method->get_chal == NULL) {
49                         DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name));
50                         continue;
51                 }
52
53                 DEBUG(5, ("auth_get_challenge: getting challenge from module %s\n", auth_method->name));
54                 if (challenge_set_by != NULL) {
55                         DEBUG(1, ("auth_get_challenge: CONFIGURATION ERROR: authentication method %s has already specified a challenge.  Challenge by %s ignored.\n", 
56                                   challenge_set_by, auth_method->name));
57                         continue;
58                 }
59
60                 mem_ctx = talloc_init("auth_get_challenge for module %s", auth_method->name);
61                 if (!mem_ctx) {
62                         smb_panic("talloc_init() failed!");
63                 }
64                 
65                 challenge = auth_method->get_chal(auth_context, &auth_method->private_data, mem_ctx);
66                 if (!challenge.length) {
67                         DEBUG(3, ("auth_get_challenge: getting challenge from authentication method %s FAILED.\n", 
68                                   auth_method->name));
69                 } else {
70                         DEBUG(5, ("auth_get_challenge: sucessfully got challenge from module %s\n", auth_method->name));
71                         auth_context->challenge = challenge;
72                         challenge_set_by = auth_method->name;
73                         auth_context->challenge_set_method = auth_method;
74                 }
75                 talloc_destroy(mem_ctx);
76         }
77         
78         if (!challenge_set_by) {
79                 uint8_t chal[8];
80                 
81                 generate_random_buffer(chal, sizeof(chal));
82                 auth_context->challenge = data_blob_talloc(auth_context, 
83                                                            chal, sizeof(chal));
84                 
85                 challenge_set_by = "random";
86                 auth_context->challenge_may_be_modified = True;
87         } 
88         
89         DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by));
90         DEBUG(5, ("challenge is: \n"));
91         dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
92         
93         SMB_ASSERT(auth_context->challenge.length == 8);
94
95         auth_context->challenge_set_by=challenge_set_by;
96
97         return auth_context->challenge.data;
98 }
99
100
101 /**
102  * Check user is in correct domain (if required)
103  *
104  * @param user Only used to fill in the debug message
105  * 
106  * @param domain The domain to be verified
107  *
108  * @return True if the user can connect with that domain, 
109  *         False otherwise.
110 **/
111
112 static BOOL check_domain_match(const char *user, const char *domain) 
113 {
114         /*
115          * If we aren't serving to trusted domains, we must make sure that
116          * the validation request comes from an account in the same domain
117          * as the Samba server
118          */
119
120         if (!lp_allow_trusted_domains() &&
121             !(strequal("", domain) || 
122               strequal(lp_workgroup(), domain) || 
123               is_myname(domain))) {
124                 DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain));
125                 return False;
126         } else {
127                 return True;
128         }
129 }
130
131 /**
132  * Check a user's Plaintext, LM or NTLM password.
133  *
134  * Check a user's password, as given in the user_info struct and return various
135  * interesting details in the server_info struct.
136  *
137  * The return value takes precedence over the contents of the server_info 
138  * struct.  When the return is other than NT_STATUS_OK the contents 
139  * of that structure is undefined.
140  *
141  * @param user_info Contains the user supplied components, including the passwords.
142  *                  Must be created with make_user_info() or one of its wrappers.
143  *
144  * @param auth_context Supplies the challenges and some other data. 
145  *                  Must be created with make_auth_context(), and the challenges should be 
146  *                  filled in, either at creation or by calling the challenge geneation 
147  *                  function auth_get_challenge().  
148  *
149  * @param server_info If successful, contains information about the authentication, 
150  *                    including a SAM_ACCOUNT struct describing the user.
151  *
152  * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
153  *
154  **/
155
156 static NTSTATUS check_ntlm_password(struct auth_context *auth_context,
157                                     const struct auth_usersupplied_info *user_info, 
158                                     TALLOC_CTX *out_mem_ctx, 
159                                     struct auth_serversupplied_info **server_info)
160 {
161         /* if all the modules say 'not for me' this is reasonable */
162         NTSTATUS nt_status = NT_STATUS_NO_SUCH_USER;
163         struct auth_methods *auth_method;
164         TALLOC_CTX *mem_ctx;
165
166         if (!user_info || !auth_context || !server_info)
167                 return NT_STATUS_LOGON_FAILURE;
168
169         DEBUG(3, ("check_ntlm_password:  Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", 
170                   user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str));
171
172         DEBUG(3, ("check_ntlm_password:  mapped user is: [%s]\\[%s]@[%s]\n", 
173                   user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
174
175         if (auth_context->challenge.length == 0) {
176                 /* get a challenge, if we have not asked for one yet */
177                 get_ntlm_challenge(auth_context);
178         }
179
180         if (auth_context->challenge.length != 8) {
181                 DEBUG(0, ("check_ntlm_password:  Invalid challenge stored for this auth context - cannot continue\n"));
182                 return NT_STATUS_LOGON_FAILURE;
183         }
184
185         if (auth_context->challenge_set_by)
186                 DEBUG(10, ("check_ntlm_password: auth_context challenge created by %s\n",
187                                         auth_context->challenge_set_by));
188
189         DEBUG(10, ("challenge is: \n"));
190         dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
191
192 #ifdef DEBUG_PASSWORD
193         DEBUG(100, ("user_info has passwords of length %d and %d\n", 
194                     user_info->lm_resp.length, user_info->nt_resp.length));
195         DEBUG(100, ("lm:\n"));
196         dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length);
197         DEBUG(100, ("nt:\n"));
198         dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length);
199 #endif
200
201         /* This needs to be sorted:  If it doesn't match, what should we do? */
202         if (!check_domain_match(user_info->smb_name.str, user_info->domain.str))
203                 return NT_STATUS_LOGON_FAILURE;
204
205         for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) {
206                 NTSTATUS result;
207                 
208                 mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name, 
209                                             user_info->domain.str, user_info->smb_name.str);
210
211                 result = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info);
212
213                 /* check if the module did anything */
214                 if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ) {
215                         DEBUG(10,("check_ntlm_password: %s had nothing to say\n", auth_method->name));
216                         talloc_destroy(mem_ctx);
217                         continue;
218                 }
219
220                 nt_status = result;
221
222                 if (NT_STATUS_IS_OK(nt_status)) {
223                         DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] succeeded\n", 
224                                   auth_method->name, user_info->smb_name.str));
225                         
226                         /* Give the server info to the client to hold onto */
227                         talloc_reference(out_mem_ctx, *server_info);
228                 } else {
229                         DEBUG(5, ("check_ntlm_password: %s authentication for user [%s] FAILED with error %s\n", 
230                                   auth_method->name, user_info->smb_name.str, nt_errstr(nt_status)));
231                 }
232
233                 talloc_destroy(mem_ctx);
234
235                 if ( NT_STATUS_IS_OK(nt_status))
236                 {
237                                 break;                  
238                 }
239         }
240
241         if (NT_STATUS_IS_OK(nt_status)) {
242                 if (NT_STATUS_IS_OK(nt_status)) {
243                         DEBUG((*server_info)->guest ? 5 : 2, 
244                               ("check_ntlm_password:  %sauthentication for user [%s] -> [%s] succeeded\n", 
245                                (*server_info)->guest ? "guest " : "", 
246                                user_info->smb_name.str, 
247                                user_info->internal_username.str));
248                 }
249         }
250
251         if (!NT_STATUS_IS_OK(nt_status)) {
252                 DEBUG(2, ("check_ntlm_password:  Authentication for user [%s] -> [%s] FAILED with error %s\n", 
253                           user_info->smb_name.str, user_info->internal_username.str, 
254                           nt_errstr(nt_status)));
255                 ZERO_STRUCTP(server_info);
256         }
257         return nt_status;
258 }
259
260 /***************************************************************************
261  Clear out a auth_context, and destroy the attached TALLOC_CTX
262 ***************************************************************************/
263
264 void free_auth_context(struct auth_context **auth_context)
265 {
266         struct auth_methods *auth_method;
267         
268         if (*auth_context) {
269                 /* Free private data of context's authentication methods */
270                 for (auth_method = (*auth_context)->auth_method_list; auth_method; auth_method = auth_method->next) {
271                         if (auth_method->free_private_data) {
272                                 auth_method->free_private_data (&auth_method->private_data);
273                                 auth_method->private_data = NULL;
274                         }
275                 }
276
277                 talloc_free(*auth_context);
278                 *auth_context = NULL;
279         }
280 }
281
282 /***************************************************************************
283  Make a auth_info struct
284 ***************************************************************************/
285
286 static NTSTATUS make_auth_context(TALLOC_CTX *mem_ctx, struct auth_context **auth_context) 
287 {
288         *auth_context = talloc_p(mem_ctx, struct auth_context);
289         if (!*auth_context) {
290                 DEBUG(0,("make_auth_context: talloc failed!\n"));
291                 return NT_STATUS_NO_MEMORY;
292         }
293         ZERO_STRUCTP(*auth_context);
294
295         (*auth_context)->check_ntlm_password = check_ntlm_password;
296         (*auth_context)->get_ntlm_challenge = get_ntlm_challenge;
297         
298         return NT_STATUS_OK;
299 }
300
301 /***************************************************************************
302  Make a auth_info struct for the auth subsystem
303 ***************************************************************************/
304
305 static NTSTATUS make_auth_context_text_list(TALLOC_CTX *mem_ctx, 
306                                             struct auth_context **auth_context, char **text_list) 
307 {
308         struct auth_methods *list = NULL;
309         struct auth_methods *t = NULL;
310         NTSTATUS nt_status;
311
312         if (!text_list) {
313                 DEBUG(2,("make_auth_context_text_list: No auth method list!?\n"));
314                 return NT_STATUS_UNSUCCESSFUL;
315         }
316         
317         if (!NT_STATUS_IS_OK(nt_status = make_auth_context(mem_ctx, auth_context)))
318                 return nt_status;
319         
320         for (;*text_list; text_list++) {
321                 char *module_name = smb_xstrdup(*text_list);
322                 char *module_params = NULL;
323                 char *p;
324                 const struct auth_operations *ops;
325
326                 DEBUG(5,("make_auth_context_text_list: Attempting to find an auth method to match %s\n",
327                                         *text_list));
328
329                 p = strchr(module_name, ':');
330                 if (p) {
331                         *p = 0;
332                         module_params = p+1;
333                         trim_string(module_params, " ", " ");
334                 }
335
336                 trim_string(module_name, " ", " ");
337
338                 ops = auth_backend_byname(module_name);
339                 if (!ops) {
340                         DEBUG(5,("make_auth_context_text_list: Found auth method %s\n", *text_list));
341                         SAFE_FREE(module_name);
342                         break;
343                 }
344
345                 if (NT_STATUS_IS_OK(ops->init(*auth_context, module_params, &t))) {
346                         DEBUG(5,("make_auth_context_text_list: auth method %s has a valid init\n",
347                                                 *text_list));
348                         DLIST_ADD_END(list, t, struct auth_methods *);
349                 } else {
350                         DEBUG(0,("make_auth_context_text_list: auth method %s did not correctly init\n",
351                                                 *text_list));
352                 }
353                 SAFE_FREE(module_name);
354         }
355         
356         (*auth_context)->auth_method_list = list;
357         
358         return nt_status;
359 }
360
361 /***************************************************************************
362  Make a auth_context struct for the auth subsystem
363 ***************************************************************************/
364
365 NTSTATUS make_auth_context_subsystem(TALLOC_CTX *mem_ctx, struct auth_context **auth_context) 
366 {
367         char **auth_method_list = NULL; 
368         NTSTATUS nt_status;
369
370         if (lp_auth_methods() && !str_list_copy(&auth_method_list, lp_auth_methods())) {
371                 return NT_STATUS_NO_MEMORY;
372         }
373
374         nt_status = make_auth_context_text_list(mem_ctx, auth_context, auth_method_list);
375         if (!NT_STATUS_IS_OK(nt_status)) {
376                 str_list_free(&auth_method_list);
377                 return nt_status;
378         }
379         
380         str_list_free(&auth_method_list);
381         return nt_status;
382 }
383
384 /***************************************************************************
385  Make a auth_info struct with a fixed challenge
386 ***************************************************************************/
387
388 NTSTATUS make_auth_context_fixed(TALLOC_CTX *mem_ctx, 
389                                  struct auth_context **auth_context, uint8_t chal[8]) 
390 {
391         NTSTATUS nt_status;
392         if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(mem_ctx, auth_context))) {
393                 return nt_status;
394         }
395         
396         (*auth_context)->challenge = data_blob_talloc(*auth_context, chal, 8);
397         (*auth_context)->challenge_set_by = "fixed";
398         return nt_status;
399 }
400
401 /* the list of currently registered AUTH backends */
402 static struct {
403         const struct auth_operations *ops;
404 } *backends = NULL;
405 static int num_backends;
406
407 /*
408   register a AUTH backend. 
409
410   The 'name' can be later used by other backends to find the operations
411   structure for this backend.
412 */
413 static NTSTATUS auth_register(const void *_ops)
414 {
415         const struct auth_operations *ops = _ops;
416         struct auth_operations *new_ops;
417         
418         if (auth_backend_byname(ops->name) != NULL) {
419                 /* its already registered! */
420                 DEBUG(0,("AUTH backend '%s' already registered\n", 
421                          ops->name));
422                 return NT_STATUS_OBJECT_NAME_COLLISION;
423         }
424
425         backends = Realloc(backends, sizeof(backends[0]) * (num_backends+1));
426         if (!backends) {
427                 smb_panic("out of memory in auth_register");
428         }
429
430         new_ops = smb_xmemdup(ops, sizeof(*ops));
431         new_ops->name = smb_xstrdup(ops->name);
432
433         backends[num_backends].ops = new_ops;
434
435         num_backends++;
436
437         DEBUG(3,("AUTH backend '%s' registered\n", 
438                  ops->name));
439
440         return NT_STATUS_OK;
441 }
442
443 /*
444   return the operations structure for a named backend of the specified type
445 */
446 const struct auth_operations *auth_backend_byname(const char *name)
447 {
448         int i;
449
450         for (i=0;i<num_backends;i++) {
451                 if (strcmp(backends[i].ops->name, name) == 0) {
452                         return backends[i].ops;
453                 }
454         }
455
456         return NULL;
457 }
458
459 /*
460   return the AUTH interface version, and the size of some critical types
461   This can be used by backends to either detect compilation errors, or provide
462   multiple implementations for different smbd compilation options in one module
463 */
464 const struct auth_critical_sizes *auth_interface_version(void)
465 {
466         static const struct auth_critical_sizes critical_sizes = {
467                 AUTH_INTERFACE_VERSION,
468                 sizeof(struct auth_operations),
469                 sizeof(struct auth_methods),
470                 sizeof(struct auth_context),
471                 sizeof(struct auth_usersupplied_info),
472                 sizeof(struct auth_serversupplied_info),
473                 sizeof(struct auth_str),
474         };
475
476         return &critical_sizes;
477 }
478
479 /*
480   initialise the AUTH subsystem
481 */
482 BOOL auth_init(void)
483 {
484         NTSTATUS status;
485         
486         /* ugly cludge, to go away */
487         static BOOL initialised;
488
489         if (initialised) {
490                 return True;
491         }
492
493         status = register_subsystem("auth", auth_register); 
494         if (!NT_STATUS_IS_OK(status)) {
495                 return False;
496         }
497
498         /* FIXME: Perhaps panic if a basic backend, such as SAM, fails to initialise? */
499         static_init_auth;
500
501         initialised = True;
502         DEBUG(3,("AUTH subsystem version %d initialised\n", AUTH_INTERFACE_VERSION));
503         return True;
504 }
505
506 NTSTATUS server_service_auth_init(void)
507 {
508         return NT_STATUS_OK;    
509 }