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