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