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