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