r2646: - use a talloc destructor to ensure that sockets from the new socket
[samba.git] / source4 / libcli / auth / gensec.c
1 /* 
2    Unix SMB/CIFS implementation.
3  
4    Generic Authentication Interface
5
6    Copyright (C) Andrew Tridgell 2003
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 /* the list of currently registered GENSEC backends */
27 const static struct gensec_security_ops **generic_security_ops;
28 static int gensec_num_backends;
29
30 static const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type)
31 {
32         int i;
33         for (i=0; i < gensec_num_backends; i++) {
34                 if (generic_security_ops[i]->auth_type == auth_type) {
35                         return generic_security_ops[i];
36                 }
37         }
38
39         return NULL;
40 }
41
42 static const struct gensec_security_ops *gensec_security_by_oid(const char *oid_string)
43 {
44         int i;
45         for (i=0; i < gensec_num_backends; i++) {
46                 if (generic_security_ops[i]->oid &&
47                     (strcmp(generic_security_ops[i]->oid, oid_string) == 0)) {
48                         return generic_security_ops[i];
49                 }
50         }
51
52         return NULL;
53 }
54
55 static const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_name)
56 {
57         int i;
58         for (i=0; i < gensec_num_backends; i++) {
59                 if (generic_security_ops[i]->sasl_name 
60                     && (strcmp(generic_security_ops[i]->sasl_name, sasl_name) == 0)) {
61                         return generic_security_ops[i];
62                 }
63         }
64
65         return NULL;
66 }
67
68 static const struct gensec_security_ops *gensec_security_by_name(const char *name)
69 {
70         int i;
71         for (i=0; i < gensec_num_backends; i++) {
72                 if (generic_security_ops[i]->name 
73                     && (strcmp(generic_security_ops[i]->name, name) == 0)) {
74                         return generic_security_ops[i];
75                 }
76         }
77
78         return NULL;
79 }
80
81 const struct gensec_security_ops **gensec_security_all(int *num_backends_out)
82 {
83         *num_backends_out = gensec_num_backends;
84         return generic_security_ops;
85 }
86
87 const char **gensec_security_oids(TALLOC_CTX *mem_ctx, const char *skip) 
88 {
89         int i, j = 0;
90         const char **oid_list;
91         int num_backends;
92         const struct gensec_security_ops **ops = gensec_security_all(&num_backends);
93         if (!ops) {
94                 return NULL;
95         }
96         oid_list = talloc_array_p(mem_ctx, const char *, num_backends + 1);
97         if (!oid_list) {
98                 return NULL;
99         }
100         
101         for (i=0; i<num_backends; i++) {
102                 if (!ops[i]->oid) {
103                         continue;
104                 }
105                 
106                 if (skip && strcmp(skip, ops[i]->oid)==0) {
107                         continue;
108                 }
109
110                 oid_list[j] = ops[i]->oid;
111                 j++;
112         }
113         oid_list[j] = NULL;
114         return oid_list;
115 }
116
117 /*
118   note that memory context is the parent context to hang this gensec context off. It may be NULL.
119 */
120 static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security) 
121 {
122         /* awaiting a correct fix from metze */
123         if (!gensec_init()) {
124                 return NT_STATUS_INTERNAL_ERROR;
125         }
126
127         (*gensec_security) = talloc_p(mem_ctx, struct gensec_security);
128         if (!(*gensec_security)) {
129                 return NT_STATUS_NO_MEMORY;
130         }
131
132         (*gensec_security)->ops = NULL;
133
134         ZERO_STRUCT((*gensec_security)->user);
135         ZERO_STRUCT((*gensec_security)->target);
136         ZERO_STRUCT((*gensec_security)->default_user);
137
138         (*gensec_security)->default_user.name = "";
139         (*gensec_security)->default_user.domain = talloc_strdup(*gensec_security, lp_workgroup());
140         (*gensec_security)->default_user.realm = talloc_strdup(*gensec_security, lp_realm());
141
142         (*gensec_security)->subcontext = False;
143         (*gensec_security)->want_features = 0;
144         return NT_STATUS_OK;
145 }
146
147 /** 
148  * Start a GENSEC subcontext, with a copy of the properties of the parent
149  *
150  * @note Used by SPENGO in particular, for the actual implementation mechanism
151  */
152
153 NTSTATUS gensec_subcontext_start(struct gensec_security *parent, 
154                                  struct gensec_security **gensec_security)
155 {
156         (*gensec_security) = talloc_p(parent, struct gensec_security);
157         if (!(*gensec_security)) {
158                 return NT_STATUS_NO_MEMORY;
159         }
160
161         (**gensec_security) = *parent;
162         (*gensec_security)->ops = NULL;
163         (*gensec_security)->private_data = NULL;
164
165         (*gensec_security)->subcontext = True;
166
167         return NT_STATUS_OK;
168 }
169
170 NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
171 {
172         NTSTATUS status;
173         status = gensec_start(mem_ctx, gensec_security);
174         if (!NT_STATUS_IS_OK(status)) {
175                 return status;
176         }
177         (*gensec_security)->gensec_role = GENSEC_CLIENT;
178         (*gensec_security)->password_callback = NULL;
179
180         ZERO_STRUCT((*gensec_security)->user);
181
182         return status;
183 }
184
185 NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
186 {
187         NTSTATUS status;
188         status = gensec_start(mem_ctx, gensec_security);
189         if (!NT_STATUS_IS_OK(status)) {
190                 return status;
191         }
192         (*gensec_security)->gensec_role = GENSEC_SERVER;
193
194         return status;
195 }
196
197 static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security) 
198 {
199         NTSTATUS status;
200         DEBUG(5, ("Starting GENSEC %smechanism %s\n", 
201                   gensec_security->subcontext ? "sub" : "", 
202                   gensec_security->ops->name));
203         switch (gensec_security->gensec_role) {
204         case GENSEC_CLIENT:
205                 if (gensec_security->ops->client_start) {
206                         status = gensec_security->ops->client_start(gensec_security);
207                         if (!NT_STATUS_IS_OK(status)) {
208                                 DEBUG(1, ("Faild to start GENSEC client mech %s: %s\n",
209                                           gensec_security->ops->name, nt_errstr(status))); 
210                         }
211                         return status;
212                 }
213         case GENSEC_SERVER:
214                 if (gensec_security->ops->server_start) {
215                         status = gensec_security->ops->server_start(gensec_security);
216                         if (!NT_STATUS_IS_OK(status)) {
217                                 DEBUG(1, ("Faild to start GENSEC server mech %s: %s\n",
218                                           gensec_security->ops->name, nt_errstr(status))); 
219                         }
220                         return status;
221                 }
222         }
223         return NT_STATUS_INVALID_PARAMETER;
224 }
225
226 /** 
227  * Start a GENSEC sub-mechanism by DCERPC allocated 'auth type' number 
228  */
229
230 NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security, 
231                                        uint8_t auth_type, uint8_t auth_level) 
232 {
233         gensec_security->ops = gensec_security_by_authtype(auth_type);
234         if (!gensec_security->ops) {
235                 DEBUG(3, ("Could not find GENSEC backend for auth_type=%d\n", (int)auth_type));
236                 return NT_STATUS_INVALID_PARAMETER;
237         }
238         if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
239                 gensec_want_feature(gensec_security, GENSEC_WANT_SIGN);
240         }
241         if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
242                 gensec_want_feature(gensec_security, GENSEC_WANT_SIGN);
243                 gensec_want_feature(gensec_security, GENSEC_WANT_SEAL);
244         }
245
246         return gensec_start_mech(gensec_security);
247 }
248
249 const char *gensec_get_name_by_authtype(uint8_t authtype) 
250 {
251         const struct gensec_security_ops *ops;
252         ops = gensec_security_by_authtype(authtype);
253         if (ops) {
254                 return ops->name;
255         }
256         return NULL;
257 }
258         
259
260 const char *gensec_get_name_by_oid(const char *oid_string) 
261 {
262         const struct gensec_security_ops *ops;
263         ops = gensec_security_by_oid(oid_string);
264         if (ops) {
265                 return ops->name;
266         }
267         return NULL;
268 }
269         
270
271 /** 
272  * Start a GENSEC sub-mechanism by OID, used in SPNEGO
273  *
274  * @note This should also be used when you wish to just start NLTMSSP (for example), as it uses a
275  *       well-known #define to hook it in.
276  */
277
278 NTSTATUS gensec_start_mech_by_oid(struct gensec_security *gensec_security, 
279                                   const char *mech_oid) 
280 {
281         gensec_security->ops = gensec_security_by_oid(mech_oid);
282         if (!gensec_security->ops) {
283                 DEBUG(3, ("Could not find GENSEC backend for oid=%s\n", mech_oid));
284                 return NT_STATUS_INVALID_PARAMETER;
285         }
286         return gensec_start_mech(gensec_security);
287 }
288
289 /** 
290  * Start a GENSEC sub-mechanism by a well know SASL name
291  *
292  */
293
294 NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security, 
295                                         const char *sasl_name) 
296 {
297         gensec_security->ops = gensec_security_by_sasl_name(sasl_name);
298         if (!gensec_security->ops) {
299                 DEBUG(3, ("Could not find GENSEC backend for sasl_name=%s\n", sasl_name));
300                 return NT_STATUS_INVALID_PARAMETER;
301         }
302         return gensec_start_mech(gensec_security);
303 }
304
305 /*
306   wrappers for the gensec function pointers
307 */
308 NTSTATUS gensec_unseal_packet(struct gensec_security *gensec_security, 
309                               TALLOC_CTX *mem_ctx, 
310                               uint8_t *data, size_t length, 
311                               const uint8_t *whole_pdu, size_t pdu_length, 
312                               DATA_BLOB *sig)
313 {
314         if (!gensec_security->ops->unseal_packet) {
315                 return NT_STATUS_NOT_IMPLEMENTED;
316         }
317         return gensec_security->ops->unseal_packet(gensec_security, mem_ctx, 
318                                                    data, length, 
319                                                    whole_pdu, pdu_length, 
320                                                    sig);
321 }
322
323 NTSTATUS gensec_check_packet(struct gensec_security *gensec_security, 
324                              TALLOC_CTX *mem_ctx, 
325                              const uint8_t *data, size_t length, 
326                              const uint8_t *whole_pdu, size_t pdu_length, 
327                              const DATA_BLOB *sig)
328 {
329         if (!gensec_security->ops->check_packet) {
330                 return NT_STATUS_NOT_IMPLEMENTED;
331         }
332         if (!(gensec_security->want_features & GENSEC_WANT_SIGN)) {
333                 return NT_STATUS_INVALID_PARAMETER;
334         }
335         
336         return gensec_security->ops->check_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
337 }
338
339 NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security, 
340                             TALLOC_CTX *mem_ctx, 
341                             uint8_t *data, size_t length, 
342                             const uint8_t *whole_pdu, size_t pdu_length, 
343                             DATA_BLOB *sig)
344 {
345         if (!gensec_security->ops->seal_packet) {
346                 return NT_STATUS_NOT_IMPLEMENTED;
347         }
348         if (!(gensec_security->want_features & GENSEC_WANT_SEAL)) {
349                 return NT_STATUS_INVALID_PARAMETER;
350         }
351
352         return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
353 }
354
355 NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security, 
356                             TALLOC_CTX *mem_ctx, 
357                             const uint8_t *data, size_t length, 
358                             const uint8_t *whole_pdu, size_t pdu_length, 
359                             DATA_BLOB *sig)
360 {
361         if (!gensec_security->ops->sign_packet) {
362                 return NT_STATUS_NOT_IMPLEMENTED;
363         }
364         if (!(gensec_security->want_features & GENSEC_WANT_SIGN)) {
365                 return NT_STATUS_INVALID_PARAMETER;
366         }
367         
368         return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
369 }
370
371 size_t gensec_sig_size(struct gensec_security *gensec_security) 
372 {
373         if (!gensec_security->ops->sig_size) {
374                 return 0;
375         }
376         if (!(gensec_security->want_features & GENSEC_WANT_SIGN)) {
377                 return 0;
378         }
379         
380         return gensec_security->ops->sig_size(gensec_security);
381 }
382
383 NTSTATUS gensec_session_key(struct gensec_security *gensec_security, 
384                             DATA_BLOB *session_key)
385 {
386         if (!gensec_security->ops->session_key) {
387                 return NT_STATUS_NOT_IMPLEMENTED;
388         }
389         if (!(gensec_security->want_features & GENSEC_WANT_SESSION_KEY)) {
390                 return NT_STATUS_INVALID_PARAMETER;
391         }
392         
393         return gensec_security->ops->session_key(gensec_security, session_key);
394 }
395
396 /** 
397  * Return the credentials of a logged on user, including session keys
398  * etc.
399  *
400  * Only valid after a successful authentication
401  *
402  * May only be called once per authentication.
403  *
404  */
405
406 NTSTATUS gensec_session_info(struct gensec_security *gensec_security, 
407                              struct auth_session_info **session_info)
408 {
409         if (!gensec_security->ops->session_info) {
410                 return NT_STATUS_NOT_IMPLEMENTED;
411         }
412         return gensec_security->ops->session_info(gensec_security, session_info);
413 }
414
415 /**
416  * Next state function for the GENSEC state machine
417  * 
418  * @param gensec_security GENSEC State
419  * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
420  * @param in The request, as a DATA_BLOB
421  * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
422  * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
423  *                or NT_STATUS_OK if the user is authenticated. 
424  */
425
426 NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
427                        const DATA_BLOB in, DATA_BLOB *out) 
428 {
429         return gensec_security->ops->update(gensec_security, out_mem_ctx, in, out);
430 }
431
432 void gensec_end(struct gensec_security **gensec_security)
433 {
434         if ((*gensec_security)->ops) {
435                 (*gensec_security)->ops->end(*gensec_security);
436         }
437         (*gensec_security)->private_data = NULL;
438
439         if (!(*gensec_security)->subcontext) {
440                 /* don't destory this if this is a subcontext - it belongs to the parent */
441                 talloc_free(*gensec_security);
442         }
443         gensec_security = NULL;
444 }
445
446 /** 
447  * Set the requirement for a certain feature on the connection
448  *
449  */
450
451 void gensec_want_feature(struct gensec_security *gensec_security,
452                          uint32 feature) 
453 {
454         gensec_security->want_features |= feature;
455 }
456
457 /** 
458  * Set a username on a GENSEC context - ensures it is talloc()ed 
459  *
460  */
461
462 NTSTATUS gensec_set_unparsed_username(struct gensec_security *gensec_security, const char *user) 
463 {
464         char *p;
465         char *u = talloc_strdup(gensec_security, user);
466         if (!u) {
467                 return NT_STATUS_NO_MEMORY;
468         }
469
470         p = strchr_m(user, '@');
471         
472         if (p) {
473                 *p = '\0';
474                 gensec_security->user.name = talloc_strdup(gensec_security, u);
475                 if (!gensec_security->user.name) {
476                         return NT_STATUS_NO_MEMORY;
477                 }
478                 
479                 gensec_security->user.realm = talloc_strdup(gensec_security, p+1);
480                 if (!gensec_security->user.realm) {
481                         return NT_STATUS_NO_MEMORY;
482                 }
483                 return NT_STATUS_OK;
484         } 
485
486         p = strchr_m(user, '\\');
487         if (!p) {
488                 p = strchr_m(user, '/');
489         }
490         
491         if (p) {
492                 *p = '\0';
493                 gensec_security->user.domain = talloc_strdup(gensec_security, u);
494                 if (!gensec_security->user.domain) {
495                         return NT_STATUS_NO_MEMORY;
496                 }
497                 gensec_security->user.name = talloc_strdup(gensec_security, p+1);
498                 if (!gensec_security->user.name) {
499                         return NT_STATUS_NO_MEMORY;
500                 }
501                 
502                 return NT_STATUS_OK;
503         } 
504         
505         gensec_security->user.name = u;
506         if (!gensec_security->user.name) {
507                 return NT_STATUS_NO_MEMORY;
508         }
509         return NT_STATUS_OK;
510 }
511
512 /** 
513  * Set a username on a GENSEC context - ensures it is talloc()ed 
514  *
515  */
516
517 NTSTATUS gensec_set_username(struct gensec_security *gensec_security, const char *user) 
518 {
519         gensec_security->user.name = talloc_strdup(gensec_security, user);
520         if (!gensec_security->user.name) {
521                 return NT_STATUS_NO_MEMORY;
522         }
523         return NT_STATUS_OK;
524 }
525
526 /** 
527  * Set a username on a GENSEC context - ensures it is talloc()ed 
528  *
529  */
530
531 const char *gensec_get_username(struct gensec_security *gensec_security) 
532 {
533         if (gensec_security->user.name) {
534                 return gensec_security->user.name;
535         }
536         return gensec_security->default_user.name;
537 }
538
539 /** 
540  * Set a domain on a GENSEC context - ensures it is talloc()ed 
541  *
542  */
543
544 NTSTATUS gensec_set_domain(struct gensec_security *gensec_security, const char *domain) 
545 {
546         gensec_security->user.domain = talloc_strdup(gensec_security, domain);
547         if (!gensec_security->user.domain) {
548                 return NT_STATUS_NO_MEMORY;
549         }
550         return NT_STATUS_OK;
551 }
552
553 /** 
554  * Return the NT domain for this GENSEC context
555  *
556  */
557
558 const char *gensec_get_domain(struct gensec_security *gensec_security) 
559 {
560         if (gensec_security->user.domain) {
561                 return gensec_security->user.domain;
562         } else if (gensec_security->user.realm) {
563                 return gensec_security->user.realm;
564         }
565         return gensec_security->default_user.domain;
566 }
567
568 /** 
569  * Set a kerberos realm on a GENSEC context - ensures it is talloc()ed 
570  *
571  */
572
573 NTSTATUS gensec_set_realm(struct gensec_security *gensec_security, const char *realm) 
574 {
575         gensec_security->user.realm = talloc_strdup(gensec_security, realm);
576         if (!gensec_security->user.realm) {
577                 return NT_STATUS_NO_MEMORY;
578         }
579         return NT_STATUS_OK;
580 }
581
582 /** 
583  * Return the Krb5 realm for this context
584  *
585  */
586
587 const char *gensec_get_realm(struct gensec_security *gensec_security) 
588 {
589         if (gensec_security->user.realm) {
590                 return gensec_security->user.realm;
591         } else if (gensec_security->user.domain) {
592                 return gensec_security->user.domain;
593         }
594         return gensec_security->default_user.realm;
595 }
596
597 /** 
598  * Return a kerberos principal for this context, if one has been set 
599  *
600  */
601
602 char *gensec_get_client_principal(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx) 
603 {
604         const char *realm = gensec_get_realm(gensec_security);
605         if (realm) {
606                 return talloc_asprintf(mem_ctx, "%s@%s", 
607                                        gensec_get_username(gensec_security), 
608                                        gensec_get_realm(gensec_security));
609         } else {
610                 return talloc_strdup(mem_ctx, gensec_get_username(gensec_security));
611         }
612 }
613
614 /** 
615  * Set the password outright on GENSEC context - ensures it is talloc()ed, and that we will
616  * not do a callback
617  *
618  */
619
620 NTSTATUS gensec_set_password(struct gensec_security *gensec_security,
621                              const char *password) 
622 {
623         gensec_security->user.password = talloc_strdup(gensec_security, password);
624         if (!gensec_security->user.password) {
625                 return NT_STATUS_NO_MEMORY;
626         }
627         return NT_STATUS_OK;
628 }
629
630 /** 
631  * Set the target principal name (if already known) on a GENSEC context - ensures it is talloc()ed 
632  *
633  */
634
635 NTSTATUS gensec_set_target_principal(struct gensec_security *gensec_security, const char *principal) 
636 {
637         gensec_security->target.principal = talloc_strdup(gensec_security, principal);
638         if (!gensec_security->target.principal) {
639                 return NT_STATUS_NO_MEMORY;
640         }
641         return NT_STATUS_OK;
642 }
643
644 /** 
645  * Set the target service (such as 'http' or 'host') on a GENSEC context - ensures it is talloc()ed 
646  *
647  */
648
649 NTSTATUS gensec_set_target_service(struct gensec_security *gensec_security, const char *service) 
650 {
651         gensec_security->target.service = talloc_strdup(gensec_security, service);
652         if (!gensec_security->target.service) {
653                 return NT_STATUS_NO_MEMORY;
654         }
655         return NT_STATUS_OK;
656 }
657
658 /** 
659  * Set the target hostname (suitable for kerberos resolutation) on a GENSEC context - ensures it is talloc()ed 
660  *
661  */
662
663 NTSTATUS gensec_set_target_hostname(struct gensec_security *gensec_security, const char *hostname) 
664 {
665         gensec_security->target.hostname = talloc_strdup(gensec_security, hostname);
666         if (!gensec_security->target.hostname) {
667                 return NT_STATUS_NO_MEMORY;
668         }
669         return NT_STATUS_OK;
670 }
671
672 const char *gensec_get_target_hostname(struct gensec_security *gensec_security) 
673 {
674         if (gensec_security->target.hostname) {
675                 return gensec_security->target.hostname;
676         }
677
678         /* TODO: Add a 'set sockaddr' call, and do a reverse lookup */
679         return NULL;
680 }
681
682 const char *gensec_get_target_service(struct gensec_security *gensec_security) 
683 {
684         if (gensec_security->target.service) {
685                 return gensec_security->target.service;
686         }
687
688         return "host";
689 }
690
691 /** 
692  * Set a password callback, if the gensec module we use demands a password
693  */
694
695 void gensec_set_password_callback(struct gensec_security *gensec_security, 
696                                   gensec_password_callback callback, void *callback_private_data) 
697 {
698         gensec_security->password_callback = callback;
699         gensec_security->password_callback_private = callback_private_data;
700 }
701
702 /**
703  * Get (or call back for) a password.
704  */
705
706 NTSTATUS gensec_get_password(struct gensec_security *gensec_security,
707                              TALLOC_CTX *mem_ctx, 
708                              char **password) 
709 {
710         if (gensec_security->user.password) {
711                 *password = talloc_strdup(mem_ctx, gensec_security->user.password);
712                 if (!*password) {
713                         return NT_STATUS_NO_MEMORY;
714                 } else {
715                         return NT_STATUS_OK;
716                 }
717         }
718         if (!gensec_security->password_callback) {
719                 return NT_STATUS_INVALID_PARAMETER;
720         }
721         return gensec_security->password_callback(gensec_security, mem_ctx, password);
722 }
723
724 /*
725   register a GENSEC backend. 
726
727   The 'name' can be later used by other backends to find the operations
728   structure for this backend.
729 */
730 static NTSTATUS gensec_register(const void *_ops)
731 {
732         const struct gensec_security_ops *ops = _ops;
733         
734         if (!lp_parm_bool(-1, "gensec", ops->name, True)) {
735                 DEBUG(2,("gensec subsystem %s is disabled\n", ops->name));
736                 return NT_STATUS_OK;
737         }
738
739         if (gensec_security_by_name(ops->name) != NULL) {
740                 /* its already registered! */
741                 DEBUG(0,("GENSEC backend '%s' already registered\n", 
742                          ops->name));
743                 return NT_STATUS_OBJECT_NAME_COLLISION;
744         }
745
746         generic_security_ops = Realloc(generic_security_ops, sizeof(generic_security_ops[0]) * (gensec_num_backends+1));
747         if (!generic_security_ops) {
748                 smb_panic("out of memory in gensec_register");
749         }
750
751         generic_security_ops[gensec_num_backends] = ops;
752
753         gensec_num_backends++;
754
755         DEBUG(3,("GENSEC backend '%s' registered\n", 
756                  ops->name));
757
758         return NT_STATUS_OK;
759 }
760
761 /*
762   return the GENSEC interface version, and the size of some critical types
763   This can be used by backends to either detect compilation errors, or provide
764   multiple implementations for different smbd compilation options in one module
765 */
766 const struct gensec_critical_sizes *gensec_interface_version(void)
767 {
768         static const struct gensec_critical_sizes critical_sizes = {
769                 GENSEC_INTERFACE_VERSION,
770                 sizeof(struct gensec_security_ops),
771                 sizeof(struct gensec_security),
772         };
773
774         return &critical_sizes;
775 }
776
777 /*
778   initialise the GENSEC subsystem
779 */
780 BOOL gensec_init(void)
781 {
782         static BOOL initialised;
783         NTSTATUS status;
784
785         /* this is *completely* the wrong way to do this */
786         if (initialised) {
787                 return True;
788         }
789
790         status = register_subsystem("gensec", gensec_register); 
791         if (!NT_STATUS_IS_OK(status)) {
792                 return False;
793         }
794
795         static_init_gensec;
796         gensec_dcerpc_schannel_init();
797
798         initialised = True;
799         DEBUG(3,("GENSEC subsystem version %d initialised\n", GENSEC_INTERFACE_VERSION));
800         return True;
801 }