lib/util Rename run_init_functions -> samba_init_module_fns_run
[samba.git] / source4 / auth / ntlm / 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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include <tevent.h>
23 #include "../lib/util/tevent_ntstatus.h"
24 #include "../lib/util/dlinklist.h"
25 #include "auth/auth.h"
26 #include "auth/ntlm/auth_proto.h"
27 #include "param/param.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "libcli/wbclient/wbclient.h"
30 #include "lib/util/samba_modules.h"
31
32 /***************************************************************************
33  Set a fixed challenge
34 ***************************************************************************/
35 _PUBLIC_ NTSTATUS auth_context_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by) 
36 {
37         auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
38         NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
39
40         auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
41         NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
42
43         return NT_STATUS_OK;
44 }
45
46 /***************************************************************************
47  Set a fixed challenge
48 ***************************************************************************/
49 _PUBLIC_ bool auth_challenge_may_be_modified(struct auth4_context *auth_ctx)
50 {
51         return auth_ctx->challenge.may_be_modified;
52 }
53
54 /****************************************************************************
55  Try to get a challenge out of the various authentication modules.
56  Returns a const char of length 8 bytes.
57 ****************************************************************************/
58 _PUBLIC_ NTSTATUS auth_get_challenge(struct auth4_context *auth_ctx, uint8_t chal[8])
59 {
60         NTSTATUS nt_status;
61         struct auth_method_context *method;
62
63         if (auth_ctx->challenge.data.length == 8) {
64                 DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n", 
65                           auth_ctx->challenge.set_by));
66                 memcpy(chal, auth_ctx->challenge.data.data, 8);
67                 return NT_STATUS_OK;
68         }
69
70         for (method = auth_ctx->methods; method; method = method->next) {
71                 nt_status = method->ops->get_challenge(method, auth_ctx, chal);
72                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
73                         continue;
74                 }
75
76                 NT_STATUS_NOT_OK_RETURN(nt_status);
77
78                 auth_ctx->challenge.data        = data_blob_talloc(auth_ctx, chal, 8);
79                 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
80                 auth_ctx->challenge.set_by      = method->ops->name;
81
82                 break;
83         }
84
85         if (!auth_ctx->challenge.set_by) {
86                 generate_random_buffer(chal, 8);
87
88                 auth_ctx->challenge.data                = data_blob_talloc(auth_ctx, chal, 8);
89                 NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
90                 auth_ctx->challenge.set_by              = "random";
91
92                 auth_ctx->challenge.may_be_modified     = true;
93         }
94
95         DEBUG(10,("auth_get_challenge: challenge set by %s\n",
96                  auth_ctx->challenge.set_by));
97
98         return NT_STATUS_OK;
99 }
100
101 /****************************************************************************
102 Used in the gensec_gssapi and gensec_krb5 server-side code, where the
103 PAC isn't available, and for tokenGroups in the DSDB stack.
104
105  Supply either a principal or a DN
106 ****************************************************************************/
107 _PUBLIC_ NTSTATUS auth_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
108                                                  struct auth4_context *auth_ctx,
109                                                  const char *principal,
110                                                  struct ldb_dn *user_dn,
111                                                  struct auth_user_info_dc **user_info_dc)
112 {
113         NTSTATUS nt_status;
114         struct auth_method_context *method;
115
116         for (method = auth_ctx->methods; method; method = method->next) {
117                 if (!method->ops->get_user_info_dc_principal) {
118                         continue;
119                 }
120
121                 nt_status = method->ops->get_user_info_dc_principal(mem_ctx, auth_ctx, principal, user_dn, user_info_dc);
122                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
123                         continue;
124                 }
125
126                 return nt_status;
127         }
128
129         return NT_STATUS_NOT_IMPLEMENTED;
130 }
131
132 /**
133  * Check a user's Plaintext, LM or NTLM password.
134  * (sync version)
135  *
136  * Check a user's password, as given in the user_info struct and return various
137  * interesting details in the user_info_dc struct.
138  *
139  * The return value takes precedence over the contents of the user_info_dc
140  * struct.  When the return is other than NT_STATUS_OK the contents 
141  * of that structure is undefined.
142  *
143  * @param auth_ctx Supplies the challenges and some other data. 
144  *                  Must be created with auth_context_create(), and the challenges should be 
145  *                  filled in, either at creation or by calling the challenge geneation 
146  *                  function auth_get_challenge().  
147  *
148  * @param user_info Contains the user supplied components, including the passwords.
149  *
150  * @param mem_ctx The parent memory context for the user_info_dc structure
151  *
152  * @param user_info_dc If successful, contains information about the authentication,
153  *                    including a SAM_ACCOUNT struct describing the user.
154  *
155  * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
156  *
157  **/
158
159 _PUBLIC_ NTSTATUS auth_check_password(struct auth4_context *auth_ctx,
160                              TALLOC_CTX *mem_ctx,
161                              const struct auth_usersupplied_info *user_info, 
162                              struct auth_user_info_dc **user_info_dc)
163 {
164         struct tevent_req *subreq;
165         struct tevent_context *ev;
166         bool ok;
167         NTSTATUS status;
168
169         /*TODO: create a new event context here! */
170         ev = auth_ctx->event_ctx;
171
172         subreq = auth_check_password_send(mem_ctx,
173                                           ev,
174                                           auth_ctx,
175                                           user_info);
176         if (subreq == NULL) {
177                 return NT_STATUS_NO_MEMORY;
178         }
179
180         ok = tevent_req_poll(subreq, ev);
181         if (!ok) {
182                 return NT_STATUS_INTERNAL_ERROR;
183         }
184
185         status = auth_check_password_recv(subreq, mem_ctx, user_info_dc);
186         TALLOC_FREE(subreq);
187
188         return status;
189 }
190
191 struct auth_check_password_state {
192         struct auth4_context *auth_ctx;
193         const struct auth_usersupplied_info *user_info;
194         struct auth_user_info_dc *user_info_dc;
195         struct auth_method_context *method;
196 };
197
198 static void auth_check_password_async_trigger(struct tevent_context *ev,
199                                               struct tevent_immediate *im,
200                                               void *private_data);
201 /**
202  * Check a user's Plaintext, LM or NTLM password.
203  * async send hook
204  *
205  * Check a user's password, as given in the user_info struct and return various
206  * interesting details in the user_info_dc struct.
207  *
208  * The return value takes precedence over the contents of the user_info_dc
209  * struct.  When the return is other than NT_STATUS_OK the contents 
210  * of that structure is undefined.
211  *
212  * @param mem_ctx The memory context the request should operate on
213  *
214  * @param ev The tevent context the request should operate on
215  *
216  * @param auth_ctx Supplies the challenges and some other data. 
217  *                  Must be created with make_auth_context(), and the challenges should be 
218  *                  filled in, either at creation or by calling the challenge geneation 
219  *                  function auth_get_challenge().  
220  *
221  * @param user_info Contains the user supplied components, including the passwords.
222  *
223  * @return The request handle or NULL on no memory error.
224  *
225  **/
226
227 _PUBLIC_ struct tevent_req *auth_check_password_send(TALLOC_CTX *mem_ctx,
228                                 struct tevent_context *ev,
229                                 struct auth4_context *auth_ctx,
230                                 const struct auth_usersupplied_info *user_info)
231 {
232         struct tevent_req *req;
233         struct auth_check_password_state *state;
234         /* if all the modules say 'not for me' this is reasonable */
235         NTSTATUS nt_status;
236         uint8_t chal[8];
237         struct auth_usersupplied_info *user_info_tmp;
238         struct tevent_immediate *im;
239
240         DEBUG(3,("auth_check_password_send: "
241                  "Checking password for unmapped user [%s]\\[%s]@[%s]\n",
242                  user_info->client.domain_name, user_info->client.account_name,
243                  user_info->workstation_name));
244
245         req = tevent_req_create(mem_ctx, &state,
246                                 struct auth_check_password_state);
247         if (req == NULL) {
248                 return NULL;
249         }
250
251         state->auth_ctx         = auth_ctx;
252         state->user_info        = user_info;
253
254         if (!user_info->mapped_state) {
255                 nt_status = map_user_info(auth_ctx->sam_ctx, req, lpcfg_workgroup(auth_ctx->lp_ctx),
256                                           user_info, &user_info_tmp);
257                 if (tevent_req_nterror(req, nt_status)) {
258                         return tevent_req_post(req, ev);
259                 }
260                 user_info = user_info_tmp;
261                 state->user_info = user_info_tmp;
262         }
263
264         DEBUGADD(3,("auth_check_password_send: "
265                     "mapped user is: [%s]\\[%s]@[%s]\n",
266                     user_info->mapped.domain_name,
267                     user_info->mapped.account_name,
268                     user_info->workstation_name));
269
270         nt_status = auth_get_challenge(auth_ctx, chal);
271         if (tevent_req_nterror(req, nt_status)) {
272                 DEBUG(0,("auth_check_password_send: "
273                          "Invalid challenge (length %u) stored for "
274                          "this auth context set_by %s - cannot continue: %s\n",
275                         (unsigned)auth_ctx->challenge.data.length,
276                         auth_ctx->challenge.set_by,
277                         nt_errstr(nt_status)));
278                 return tevent_req_post(req, ev);
279         }
280
281         if (auth_ctx->challenge.set_by) {
282                 DEBUG(10,("auth_check_password_send: "
283                           "auth_context challenge created by %s\n",
284                           auth_ctx->challenge.set_by));
285         }
286
287         DEBUG(10, ("auth_check_password_send: challenge is: \n"));
288         dump_data(5, auth_ctx->challenge.data.data,
289                   auth_ctx->challenge.data.length);
290
291         im = tevent_create_immediate(state);
292         if (tevent_req_nomem(im, req)) {
293                 return tevent_req_post(req, ev);
294         }
295
296         tevent_schedule_immediate(im,
297                                   auth_ctx->event_ctx,
298                                   auth_check_password_async_trigger,
299                                   req);
300         return req;
301 }
302
303 static void auth_check_password_async_trigger(struct tevent_context *ev,
304                                               struct tevent_immediate *im,
305                                               void *private_data)
306 {
307         struct tevent_req *req =
308                 talloc_get_type_abort(private_data, struct tevent_req);
309         struct auth_check_password_state *state =
310                 tevent_req_data(req, struct auth_check_password_state);
311         NTSTATUS status;
312         struct auth_method_context *method;
313
314         status = NT_STATUS_OK;
315
316         for (method=state->auth_ctx->methods; method; method = method->next) {
317
318                 /* we fill in state->method here so debug messages in
319                    the callers know which method failed */
320                 state->method = method;
321
322                 /* check if the module wants to check the password */
323                 status = method->ops->want_check(method, req, state->user_info);
324                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
325                         DEBUG(11,("auth_check_password_send: "
326                                   "%s had nothing to say\n",
327                                   method->ops->name));
328                         continue;
329                 }
330
331                 if (tevent_req_nterror(req, status)) {
332                         return;
333                 }
334
335                 status = method->ops->check_password(method,
336                                                      state,
337                                                      state->user_info,
338                                                      &state->user_info_dc);
339                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
340                         /* the backend has handled the request */
341                         break;
342                 }
343         }
344
345         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
346                 /* don't expose the NT_STATUS_NOT_IMPLEMENTED
347                    internals */
348                 status = NT_STATUS_NO_SUCH_USER;
349         }
350
351         if (tevent_req_nterror(req, status)) {
352                 return;
353         }
354
355         tevent_req_done(req);
356 }
357
358 /**
359  * Check a user's Plaintext, LM or NTLM password.
360  * async receive function
361  *
362  * The return value takes precedence over the contents of the user_info_dc
363  * struct.  When the return is other than NT_STATUS_OK the contents 
364  * of that structure is undefined.
365  *
366  *
367  * @param req The async request state
368  *
369  * @param mem_ctx The parent memory context for the user_info_dc structure
370  *
371  * @param user_info_dc If successful, contains information about the authentication,
372  *                    including a SAM_ACCOUNT struct describing the user.
373  *
374  * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
375  *
376  **/
377
378 _PUBLIC_ NTSTATUS auth_check_password_recv(struct tevent_req *req,
379                                   TALLOC_CTX *mem_ctx,
380                                   struct auth_user_info_dc **user_info_dc)
381 {
382         struct auth_check_password_state *state =
383                 tevent_req_data(req, struct auth_check_password_state);
384         NTSTATUS status;
385
386         if (tevent_req_is_nterror(req, &status)) {
387                 DEBUG(2,("auth_check_password_recv: "
388                          "%s authentication for user [%s\\%s] "
389                          "FAILED with error %s\n",
390                          (state->method ? state->method->ops->name : "NO_METHOD"),
391                          state->user_info->mapped.domain_name,
392                          state->user_info->mapped.account_name,
393                          nt_errstr(status)));
394                 tevent_req_received(req);
395                 return status;
396         }
397
398         DEBUG(5,("auth_check_password_recv: "
399                  "%s authentication for user [%s\\%s] succeeded\n",
400                  state->method->ops->name,
401                  state->user_info_dc->info->domain_name,
402                  state->user_info_dc->info->account_name));
403
404         *user_info_dc = talloc_move(mem_ctx, &state->user_info_dc);
405
406         tevent_req_received(req);
407         return NT_STATUS_OK;
408 }
409
410 /* Wrapper because we don't want to expose all callers to needing to
411  * know that session_info is generated from the main ldb, and because
412  * we need to break a depenency loop between the DCE/RPC layer and the
413  * generation of unix tokens via IRPC */
414 static NTSTATUS auth_generate_session_info_wrapper(TALLOC_CTX *mem_ctx,
415                                                    struct auth4_context *auth_context,
416                                                    struct auth_user_info_dc *user_info_dc,
417                                                    uint32_t session_info_flags,
418                                                    struct auth_session_info **session_info)
419 {
420         NTSTATUS status = auth_generate_session_info(mem_ctx, auth_context->lp_ctx,
421                                                      auth_context->sam_ctx, user_info_dc,
422                                                      session_info_flags, session_info);
423
424         if ((session_info_flags & AUTH_SESSION_INFO_UNIX_TOKEN)
425             && NT_STATUS_IS_OK(status)) {
426                 struct wbc_context *wbc_ctx = wbc_init(auth_context,
427                                                        auth_context->msg_ctx,
428                                                        auth_context->event_ctx);
429                 if (!wbc_ctx) {
430                         TALLOC_FREE(*session_info);
431                         DEBUG(1, ("Cannot contact winbind to provide unix token\n"));
432                         return NT_STATUS_INVALID_SERVER_STATE;
433                 }
434                 status = auth_session_info_fill_unix(wbc_ctx, auth_context->lp_ctx,
435                                                      *session_info);
436                 if (!NT_STATUS_IS_OK(status)) {
437                         TALLOC_FREE(*session_info);
438                 }
439                 TALLOC_FREE(wbc_ctx);
440         }
441         return status;
442 }
443
444 /***************************************************************************
445  Make a auth_info struct for the auth subsystem
446  - Allow the caller to specify the methods to use, including optionally the SAM to use
447 ***************************************************************************/
448 _PUBLIC_ NTSTATUS auth_context_create_methods(TALLOC_CTX *mem_ctx, const char **methods, 
449                                               struct tevent_context *ev,
450                                               struct imessaging_context *msg,
451                                               struct loadparm_context *lp_ctx,
452                                               struct ldb_context *sam_ctx,
453                                               struct auth4_context **auth_ctx)
454 {
455         int i;
456         struct auth4_context *ctx;
457
458         auth4_init();
459
460         if (!ev) {
461                 DEBUG(0,("auth_context_create: called with out event context\n"));
462                 return NT_STATUS_INTERNAL_ERROR;
463         }
464
465         ctx = talloc(mem_ctx, struct auth4_context);
466         NT_STATUS_HAVE_NO_MEMORY(ctx);
467         ctx->challenge.set_by           = NULL;
468         ctx->challenge.may_be_modified  = false;
469         ctx->challenge.data             = data_blob(NULL, 0);
470         ctx->methods                    = NULL;
471         ctx->event_ctx                  = ev;
472         ctx->msg_ctx                    = msg;
473         ctx->lp_ctx                     = lp_ctx;
474
475         if (sam_ctx) {
476                 ctx->sam_ctx = sam_ctx;
477         } else {
478                 ctx->sam_ctx = samdb_connect(ctx, ctx->event_ctx, ctx->lp_ctx, system_session(ctx->lp_ctx), 0);
479         }
480
481         for (i=0; methods && methods[i] ; i++) {
482                 struct auth_method_context *method;
483
484                 method = talloc(ctx, struct auth_method_context);
485                 NT_STATUS_HAVE_NO_MEMORY(method);
486
487                 method->ops = auth_backend_byname(methods[i]);
488                 if (!method->ops) {
489                         DEBUG(1,("auth_context_create: failed to find method=%s\n",
490                                 methods[i]));
491                         return NT_STATUS_INTERNAL_ERROR;
492                 }
493                 method->auth_ctx        = ctx;
494                 method->depth           = i;
495                 DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
496         }
497
498         ctx->check_password = auth_check_password;
499         ctx->get_challenge = auth_get_challenge;
500         ctx->set_challenge = auth_context_set_challenge;
501         ctx->challenge_may_be_modified = auth_challenge_may_be_modified;
502         ctx->get_user_info_dc_principal = auth_get_user_info_dc_principal;
503         ctx->generate_session_info = auth_generate_session_info_wrapper;
504
505         *auth_ctx = ctx;
506
507         return NT_STATUS_OK;
508 }
509
510 const char **auth_methods_from_lp(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
511 {
512         char **auth_methods = NULL;
513
514         switch (lpcfg_server_role(lp_ctx)) {
515         case ROLE_STANDALONE:
516                 auth_methods = str_list_make(mem_ctx, "anonymous sam_ignoredomain", NULL);
517                 break;
518         case ROLE_DOMAIN_MEMBER:
519                 auth_methods = str_list_make(mem_ctx, "anonymous sam winbind", NULL);
520                 break;
521         case ROLE_DOMAIN_BDC:
522         case ROLE_DOMAIN_PDC:
523                 auth_methods = str_list_make(mem_ctx, "anonymous sam_ignoredomain winbind", NULL);
524                 break;
525         }
526         return (const char **) auth_methods;
527 }
528
529 /***************************************************************************
530  Make a auth_info struct for the auth subsystem
531  - Uses default auth_methods, depending on server role and smb.conf settings
532 ***************************************************************************/
533 _PUBLIC_ NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx,
534                              struct tevent_context *ev,
535                              struct imessaging_context *msg,
536                              struct loadparm_context *lp_ctx,
537                              struct auth4_context **auth_ctx)
538 {
539         NTSTATUS status;
540         const char **auth_methods;
541         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
542         if (!tmp_ctx) {
543                 return NT_STATUS_NO_MEMORY;
544         }
545
546         auth_methods = auth_methods_from_lp(tmp_ctx, lp_ctx);
547         if (!auth_methods) {
548                 return NT_STATUS_INVALID_PARAMETER;
549         }
550         status = auth_context_create_methods(mem_ctx, auth_methods, ev, msg, lp_ctx, NULL, auth_ctx);
551         talloc_free(tmp_ctx);
552         return status;
553 }
554
555 /* Create an auth context from an open LDB.
556
557    This allows us not to re-open the LDB when we need to do a some authentication logic (such as tokenGroups)
558
559  */
560 NTSTATUS auth_context_create_from_ldb(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, struct auth4_context **auth_ctx)
561 {
562         NTSTATUS status;
563         const char **auth_methods;
564         struct loadparm_context *lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"), struct loadparm_context);
565         struct tevent_context *ev = ldb_get_event_context(ldb);
566
567         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
568         if (!tmp_ctx) {
569                 return NT_STATUS_NO_MEMORY;
570         }
571
572         auth_methods = auth_methods_from_lp(tmp_ctx, lp_ctx);
573         if (!auth_methods) {
574                 return NT_STATUS_INVALID_PARAMETER;
575         }
576         status = auth_context_create_methods(mem_ctx, auth_methods, ev, NULL, lp_ctx, ldb, auth_ctx);
577         talloc_free(tmp_ctx);
578         return status;
579 }
580
581 /* the list of currently registered AUTH backends */
582 static struct auth_backend {
583         const struct auth_operations *ops;
584 } *backends = NULL;
585 static int num_backends;
586
587 /*
588   register a AUTH backend. 
589
590   The 'name' can be later used by other backends to find the operations
591   structure for this backend.
592 */
593 _PUBLIC_ NTSTATUS auth_register(const struct auth_operations *ops)
594 {
595         struct auth_operations *new_ops;
596         
597         if (auth_backend_byname(ops->name) != NULL) {
598                 /* its already registered! */
599                 DEBUG(0,("AUTH backend '%s' already registered\n", 
600                          ops->name));
601                 return NT_STATUS_OBJECT_NAME_COLLISION;
602         }
603
604         backends = talloc_realloc(talloc_autofree_context(), backends, 
605                                   struct auth_backend, num_backends+1);
606         NT_STATUS_HAVE_NO_MEMORY(backends);
607
608         new_ops = (struct auth_operations *)talloc_memdup(backends, ops, sizeof(*ops));
609         NT_STATUS_HAVE_NO_MEMORY(new_ops);
610         new_ops->name = talloc_strdup(new_ops, ops->name);
611         NT_STATUS_HAVE_NO_MEMORY(new_ops->name);
612
613         backends[num_backends].ops = new_ops;
614
615         num_backends++;
616
617         DEBUG(3,("AUTH backend '%s' registered\n", 
618                  ops->name));
619
620         return NT_STATUS_OK;
621 }
622
623 /*
624   return the operations structure for a named backend of the specified type
625 */
626 const struct auth_operations *auth_backend_byname(const char *name)
627 {
628         int i;
629
630         for (i=0;i<num_backends;i++) {
631                 if (strcmp(backends[i].ops->name, name) == 0) {
632                         return backends[i].ops;
633                 }
634         }
635
636         return NULL;
637 }
638
639 /*
640   return the AUTH interface version, and the size of some critical types
641   This can be used by backends to either detect compilation errors, or provide
642   multiple implementations for different smbd compilation options in one module
643 */
644 const struct auth_critical_sizes *auth_interface_version(void)
645 {
646         static const struct auth_critical_sizes critical_sizes = {
647                 AUTH4_INTERFACE_VERSION,
648                 sizeof(struct auth_operations),
649                 sizeof(struct auth_method_context),
650                 sizeof(struct auth4_context),
651                 sizeof(struct auth_usersupplied_info),
652                 sizeof(struct auth_user_info_dc)
653         };
654
655         return &critical_sizes;
656 }
657
658 _PUBLIC_ NTSTATUS auth4_init(void)
659 {
660         static bool initialized = false;
661 #define _MODULE_PROTO(init) extern NTSTATUS init(void);
662         STATIC_auth4_MODULES_PROTO;
663         samba_init_module_fn static_init[] = { STATIC_auth4_MODULES };
664         
665         if (initialized) return NT_STATUS_OK;
666         initialized = true;
667         
668         samba_init_module_fns_run(static_init);
669         
670         return NT_STATUS_OK;    
671 }