r1605: GENSEC krb5 updates - fix a valgrind found uninitialised variable, and
[samba.git] / source / libcli / auth / spnego.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    RFC2478 Compliant SPNEGO implementation
5    
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com>      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    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_AUTH
29
30 enum spnego_state_position {
31         SPNEGO_SERVER_START,
32         SPNEGO_CLIENT_START,
33         SPNEGO_SERVER_TARG,
34         SPNEGO_CLIENT_TARG,
35         SPNEGO_FALLBACK,
36         SPNEGO_DONE
37 };
38
39 struct spnego_state {
40         TALLOC_CTX *mem_ctx;
41         uint_t ref_count;
42         enum spnego_message_type expected_packet;
43         enum spnego_state_position state_position;
44         struct gensec_security *sub_sec_security;
45 };
46
47 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
48 {
49         struct spnego_state *spnego_state;
50         TALLOC_CTX *mem_ctx = talloc_init("gensec_spnego_client_start");
51         if (!mem_ctx) {
52                 return NT_STATUS_NO_MEMORY;
53         }
54         spnego_state = talloc_p(mem_ctx, struct spnego_state);
55                 
56         if (!spnego_state) {
57                 return NT_STATUS_NO_MEMORY;
58         }
59
60         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
61         spnego_state->state_position = SPNEGO_CLIENT_START;
62         spnego_state->mem_ctx = mem_ctx;
63         spnego_state->sub_sec_security = NULL;
64
65         gensec_security->private_data = spnego_state;
66         return NT_STATUS_OK;
67 }
68
69 /*
70   wrappers for the spnego_*() functions
71 */
72 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, 
73                                      TALLOC_CTX *mem_ctx, 
74                                      uint8_t *data, size_t length, DATA_BLOB *sig)
75 {
76         struct spnego_state *spnego_state = gensec_security->private_data;
77
78         if (spnego_state->state_position != SPNEGO_DONE 
79             && spnego_state->state_position != SPNEGO_FALLBACK) {
80                 return NT_STATUS_INVALID_PARAMETER;
81         }
82         
83         return gensec_unseal_packet(spnego_state->sub_sec_security, 
84                                     mem_ctx, data, length, sig); 
85 }
86
87 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, 
88                                      TALLOC_CTX *mem_ctx, 
89                                      const uint8_t *data, size_t length, 
90                                      const DATA_BLOB *sig)
91 {
92         struct spnego_state *spnego_state = gensec_security->private_data;
93
94         return NT_STATUS_NOT_IMPLEMENTED;
95         if (spnego_state->state_position != SPNEGO_DONE 
96             && spnego_state->state_position != SPNEGO_FALLBACK) {
97                 return NT_STATUS_INVALID_PARAMETER;
98         }
99         
100         return gensec_check_packet(spnego_state->sub_sec_security, 
101                                 mem_ctx, data, length, sig);
102 }
103
104 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, 
105                                     TALLOC_CTX *mem_ctx, 
106                                     uint8_t *data, size_t length, 
107                                     DATA_BLOB *sig)
108 {
109         struct spnego_state *spnego_state = gensec_security->private_data;
110
111         return NT_STATUS_NOT_IMPLEMENTED;
112         if (spnego_state->state_position != SPNEGO_DONE 
113             && spnego_state->state_position != SPNEGO_FALLBACK) {
114                 return NT_STATUS_INVALID_PARAMETER;
115         }
116         
117         return gensec_seal_packet(spnego_state->sub_sec_security, 
118                                   mem_ctx, data, length, sig);
119 }
120
121 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, 
122                                     TALLOC_CTX *mem_ctx, 
123                                     const uint8_t *data, size_t length, 
124                                     DATA_BLOB *sig)
125 {
126         struct spnego_state *spnego_state = gensec_security->private_data;
127
128         if (spnego_state->state_position != SPNEGO_DONE 
129             && spnego_state->state_position != SPNEGO_FALLBACK) {
130                 return NT_STATUS_INVALID_PARAMETER;
131         }
132         
133         return gensec_sign_packet(spnego_state->sub_sec_security, 
134                                   mem_ctx, data, length, sig);
135 }
136
137 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, 
138                                     DATA_BLOB *session_key)
139 {
140         struct spnego_state *spnego_state = gensec_security->private_data;
141         if (!spnego_state->sub_sec_security) {
142                 return NT_STATUS_INVALID_PARAMETER;
143         }
144         
145         return gensec_session_key(spnego_state->sub_sec_security, 
146                                   session_key);
147 }
148
149 /** Fallback to another GENSEC mechanism, based on magic strings 
150  *
151  * This is the 'fallback' case, where we don't get SPNEGO, and have to
152  * try all the other options (and hope they all have a magic string
153  * they check)
154 */
155
156 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security, 
157                                                   struct spnego_state *spnego_state,
158                                                   TALLOC_CTX *out_mem_ctx, 
159                                                   const DATA_BLOB in, DATA_BLOB *out) 
160 {
161         int i;
162         int num_ops;
163         const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops);
164         for (i=0; i < num_ops; i++) {
165                 NTSTATUS nt_status;
166                 if (!all_ops[i]->oid) {
167                         continue;
168                 }
169                 nt_status = gensec_subcontext_start(gensec_security, 
170                                                     &spnego_state->sub_sec_security);
171                 if (!NT_STATUS_IS_OK(nt_status)) {
172                         return nt_status;
173                 }
174                 /* select the sub context */
175                 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
176                                                      all_ops[i]->oid);
177                 if (!NT_STATUS_IS_OK(nt_status)) {
178                         gensec_end(&spnego_state->sub_sec_security);
179                                         continue;
180                 }
181                 nt_status = gensec_update(spnego_state->sub_sec_security,
182                                                           out_mem_ctx, in, out);
183                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
184                         spnego_state->state_position = SPNEGO_FALLBACK;
185                         return nt_status;
186                 }
187                 gensec_end(&spnego_state->sub_sec_security);
188         }
189         DEBUG(1, ("Failed to parse SPNEGO request\n"));
190         return NT_STATUS_INVALID_PARAMETER;
191         
192 }
193
194 /** create a client netTokenInit 
195  *
196  * This is the case, where the client is the first one who sends data
197 */
198
199 static NTSTATUS gensec_spnego_client_netTokenInit(struct gensec_security *gensec_security, 
200                                                   struct spnego_state *spnego_state,
201                                                   TALLOC_CTX *out_mem_ctx, 
202                                                   const DATA_BLOB in, DATA_BLOB *out) 
203 {
204         NTSTATUS nt_status;
205         int i;
206         int num_ops;
207         char **mechTypes = NULL;
208         const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops);
209         DATA_BLOB null_data_blob = data_blob(NULL,0);
210         DATA_BLOB unwrapped_out = data_blob(NULL,0);
211
212         if (num_ops < 1) {
213                 DEBUG(1, ("no GENSEC backends available\n"));
214                 return NT_STATUS_INVALID_PARAMETER;
215         }
216
217         /* build a mechTypes list we want to offer */
218         for (i=0; i < num_ops; i++) {
219                 if (!all_ops[i]->oid) {
220                         continue;
221                 }
222
223                 /* skip SPNEGO itself */
224                 if (strcmp(OID_SPNEGO,all_ops[i]->oid)==0) {
225                         continue;
226                 }
227
228                 mechTypes = talloc_realloc_p(out_mem_ctx, mechTypes, char *, i+2);
229                 if (!mechTypes) {
230                         DEBUG(1, ("talloc_realloc_p(out_mem_ctx, mechTypes, char *, i+1) failed\n"));
231                         return NT_STATUS_NO_MEMORY;
232                 }
233
234                 mechTypes[i] = all_ops[i]->oid;
235                 mechTypes[i+1] = NULL;
236         }
237
238         if (!mechTypes) {
239                 DEBUG(1, ("no GENSEC OID backends available\n"));
240                 return NT_STATUS_INVALID_PARAMETER;
241         }
242
243         nt_status = gensec_subcontext_start(gensec_security, 
244                                             &spnego_state->sub_sec_security);
245         if (!NT_STATUS_IS_OK(nt_status)) {
246                 return nt_status;
247         }
248         /* select our preferred mech */
249         nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
250                                              mechTypes[0]);
251         if (!NT_STATUS_IS_OK(nt_status)) {
252                 gensec_end(&spnego_state->sub_sec_security);
253                         return nt_status;
254         }
255         nt_status = gensec_update(spnego_state->sub_sec_security,
256                                   out_mem_ctx, in, &unwrapped_out);
257         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
258                 struct spnego_data spnego_out;
259                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
260                 spnego_out.negTokenInit.mechTypes = mechTypes;
261                 spnego_out.negTokenInit.reqFlags = 0;
262                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
263                 spnego_out.negTokenInit.mechToken = unwrapped_out;
264                 
265                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
266                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
267                                 return NT_STATUS_INVALID_PARAMETER;
268                 }
269                 
270                 /* set next state */
271                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
272                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
273                 return nt_status;
274         }
275         gensec_end(&spnego_state->sub_sec_security);
276
277         DEBUG(1, ("Failed to setup SPNEGO netTokenInit request\n"));
278         return NT_STATUS_INVALID_PARAMETER;
279 }
280
281 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
282                                const DATA_BLOB in, DATA_BLOB *out) 
283 {
284         struct spnego_state *spnego_state = gensec_security->private_data;
285         DATA_BLOB null_data_blob = data_blob(NULL, 0);
286         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
287         struct spnego_data spnego_out;
288         struct spnego_data spnego;
289
290         ssize_t len;
291
292         if (!out_mem_ctx) {
293                 out_mem_ctx = spnego_state->mem_ctx;
294         }
295
296         /* and switch into the state machine */
297
298         switch (spnego_state->state_position) {
299         case SPNEGO_FALLBACK:
300                 return gensec_update(spnego_state->sub_sec_security,
301                                      out_mem_ctx, in, out);
302         case SPNEGO_SERVER_START:
303         {
304                 if (in.length) {
305                         len = spnego_read_data(in, &spnego);
306                         if (len == -1) {
307                                 return gensec_spnego_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out);
308                         } else {
309                                 /* client sent NegTargetInit */
310                         }
311                 } else {
312                         /* server needs to send NegTargetInit */
313                 }
314                 return NT_STATUS_INVALID_PARAMETER;
315         }
316         
317         case SPNEGO_CLIENT_START:
318         {
319                 /* The server offers a list of mechanisms */
320                 
321                 char **mechType;
322                 char *my_mechs[] = {NULL, NULL};
323                 int i;
324                 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
325
326                 if (!in.length) {
327                         /* client to produce negTokenInit */
328                         return gensec_spnego_client_netTokenInit(gensec_security, spnego_state, out_mem_ctx, in, out);
329                 }
330                 
331                 len = spnego_read_data(in, &spnego);
332                 
333                 if (len == -1) {
334                         DEBUG(1, ("Invalid SPNEGO request:\n"));
335                         dump_data(1, (const char *)in.data, in.length);
336                         return NT_STATUS_INVALID_PARAMETER;
337                 }
338                 
339                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
340                 if (spnego.type != spnego_state->expected_packet) {
341                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
342                                   spnego_state->expected_packet));
343                         dump_data(1, (const char *)in.data, in.length);
344                         spnego_free_data(&spnego);
345                         return NT_STATUS_INVALID_PARAMETER;
346                 }
347
348                 if (spnego.negTokenInit.targetPrincipal) {
349                         DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
350                         nt_status = gensec_set_target_principal(gensec_security, 
351                                                                 spnego.negTokenInit.targetPrincipal);
352                         if (!NT_STATUS_IS_OK(nt_status)) {
353                                 return nt_status;
354                         }
355                 }
356
357                 mechType = spnego.negTokenInit.mechTypes;
358                 for (i=0; mechType && mechType[i]; i++) {
359                         nt_status = gensec_subcontext_start(gensec_security,
360                                                             &spnego_state->sub_sec_security);
361                         if (!NT_STATUS_IS_OK(nt_status)) {
362                                 break;
363                         }
364                         /* select the sub context */
365                         nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
366                                                              mechType[i]);
367                         if (!NT_STATUS_IS_OK(nt_status)) {
368                                 gensec_end(&spnego_state->sub_sec_security);
369                                 continue;
370                         }
371                         
372                         if (i == 0) {
373                                 nt_status = gensec_update(spnego_state->sub_sec_security,
374                                                           out_mem_ctx, 
375                                                           spnego.negTokenInit.mechToken, 
376                                                           &unwrapped_out);
377                         } else {
378                                 /* only get the helping start blob for the first OID */
379                                 nt_status = gensec_update(spnego_state->sub_sec_security,
380                                                           out_mem_ctx, 
381                                                           null_data_blob, 
382                                                           &unwrapped_out);
383                         }
384                         if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
385                                 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
386                                           spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
387                                 gensec_end(&spnego_state->sub_sec_security);
388                         } else {
389                                 break;
390                         }
391                 }
392                 if (!mechType || !mechType[i]) {
393                         DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
394                 }
395                 
396                 spnego_free_data(&spnego);
397                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
398                         return nt_status;
399                 }
400                 
401                 /* compose reply */
402                 my_mechs[0] = spnego_state->sub_sec_security->ops->oid;
403                 
404                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
405                 spnego_out.negTokenInit.mechTypes = my_mechs;
406                 spnego_out.negTokenInit.reqFlags = 0;
407                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
408                 spnego_out.negTokenInit.mechToken = unwrapped_out;
409                 
410                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
411                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
412                                 return NT_STATUS_INVALID_PARAMETER;
413                 }
414                 
415                 /* set next state */
416                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
417                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
418                 
419                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
420         }
421         case SPNEGO_SERVER_TARG:
422         {
423                 NTSTATUS nt_status;
424                 if (!in.length) {
425                         return NT_STATUS_INVALID_PARAMETER;
426                 }
427                 
428                 len = spnego_read_data(in, &spnego);
429                 
430                 if (len == -1) {
431                         DEBUG(1, ("Invalid SPNEGO request:\n"));
432                         dump_data(1, (const char *)in.data, in.length);
433                         return NT_STATUS_INVALID_PARAMETER;
434                 }
435                 
436                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
437                 if (spnego.type != spnego_state->expected_packet) {
438                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
439                                   spnego_state->expected_packet));
440                         dump_data(1, (const char *)in.data, in.length);
441                         spnego_free_data(&spnego);
442                         return NT_STATUS_INVALID_PARAMETER;
443                 }
444                 
445                 nt_status = gensec_update(spnego_state->sub_sec_security,
446                                           out_mem_ctx, 
447                                           spnego.negTokenTarg.responseToken, 
448                                           &unwrapped_out);
449                 
450                 spnego_free_data(&spnego);
451                 
452                 /* compose reply */
453                 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
454                 spnego_out.negTokenTarg.supportedMech 
455                         = spnego_state->sub_sec_security->ops->oid;
456                 spnego_out.negTokenTarg.responseToken = unwrapped_out;
457                 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
458                 
459                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
460                         spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
461                         spnego_state->state_position = SPNEGO_SERVER_TARG;
462                 } else if (NT_STATUS_IS_OK(nt_status)) {
463                         spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
464                         spnego_state->state_position = SPNEGO_DONE;
465                 } else {
466                         spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
467                         DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
468                                   spnego_state->sub_sec_security->ops->name, 
469                                   nt_errstr(nt_status)));
470                         spnego_state->state_position = SPNEGO_DONE;
471                 }
472                 
473                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
474                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
475                         return NT_STATUS_INVALID_PARAMETER;
476                 }
477
478                 return nt_status;
479         }
480         case SPNEGO_CLIENT_TARG:
481         {
482                 NTSTATUS nt_status;
483                 if (!in.length) {
484                         return NT_STATUS_INVALID_PARAMETER;
485                 }
486                 
487                 len = spnego_read_data(in, &spnego);
488                 
489                 if (len == -1) {
490                         DEBUG(1, ("Invalid SPNEGO request:\n"));
491                         dump_data(1, (const char *)in.data, in.length);
492                         return NT_STATUS_INVALID_PARAMETER;
493                 }
494                 
495                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
496                 if (spnego.type != spnego_state->expected_packet) {
497                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
498                                   spnego_state->expected_packet));
499                         dump_data(1, (const char *)in.data, in.length);
500                         spnego_free_data(&spnego);
501                         return NT_STATUS_INVALID_PARAMETER;
502                 }
503         
504                 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
505                         return NT_STATUS_ACCESS_DENIED;
506                 }
507
508                 nt_status = gensec_update(spnego_state->sub_sec_security,
509                                           out_mem_ctx, 
510                                           spnego.negTokenTarg.responseToken, 
511                                           &unwrapped_out);
512                 
513                 
514                 if (NT_STATUS_IS_OK(nt_status) 
515                     && (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED)) {
516                         DEBUG(1,("gensec_update ok but not accepted\n"));
517                         nt_status = NT_STATUS_INVALID_PARAMETER;
518                 } 
519                 
520                 spnego_free_data(&spnego);
521
522                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
523                         /* compose reply */
524                         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
525                         spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
526                         spnego_out.negTokenTarg.supportedMech = NULL;
527                         spnego_out.negTokenTarg.responseToken = unwrapped_out;
528                         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
529                         
530                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
531                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
532                                 return NT_STATUS_INVALID_PARAMETER;
533                         }
534                 
535                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
536                 } else if (NT_STATUS_IS_OK(nt_status)) {
537                         /* all done - server has accepted, and we agree */
538                         
539                         if (unwrapped_out.length) {
540                                 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
541                                 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
542                                 spnego_out.negTokenTarg.supportedMech = NULL;
543                                 spnego_out.negTokenTarg.responseToken = unwrapped_out;
544                                 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
545                                 
546                                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
547                                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
548                                         return NT_STATUS_INVALID_PARAMETER;
549                                 }
550                         } else {
551                                 *out = null_data_blob;
552                         }
553
554                         spnego_state->state_position = SPNEGO_DONE;
555                 } else {
556                         DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
557                                   spnego_state->sub_sec_security->ops->name, 
558                                   nt_errstr(nt_status)));
559                 }
560                 return nt_status;
561         }
562         case SPNEGO_DONE:
563                 return NT_STATUS_OK;
564         }
565         return NT_STATUS_INVALID_PARAMETER;
566 }
567
568 static void gensec_spnego_end(struct gensec_security *gensec_security)
569 {
570         struct spnego_state *spnego_state = gensec_security->private_data;
571
572         if (spnego_state->sub_sec_security) {
573                 gensec_end(&spnego_state->sub_sec_security);
574         }
575
576         talloc_destroy(spnego_state->mem_ctx);
577
578         gensec_security->private_data = NULL;
579 }
580
581 static const struct gensec_security_ops gensec_spnego_security_ops = {
582         .name           = "spnego",
583         .sasl_name      = "GSS-SPNEGO",
584         .auth_type      = DCERPC_AUTH_TYPE_SPNEGO,
585         .oid            = OID_SPNEGO,
586         .client_start   = gensec_spnego_client_start,
587         .update         = gensec_spnego_update,
588         .seal_packet    = gensec_spnego_seal_packet,
589         .sign_packet    = gensec_spnego_sign_packet,
590         .check_packet   = gensec_spnego_check_packet,
591         .unseal_packet  = gensec_spnego_unseal_packet,
592         .session_key    = gensec_spnego_session_key,
593         .end            = gensec_spnego_end
594 };
595
596 NTSTATUS gensec_spnego_init(void)
597 {
598         NTSTATUS ret;
599         ret = register_backend("gensec", &gensec_spnego_security_ops);
600         if (!NT_STATUS_IS_OK(ret)) {
601                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
602                         gensec_spnego_security_ops.name));
603                 return ret;
604         }
605
606         return ret;
607 }