r3737: - Get rid of the register_subsystem() and register_backend() functions.
[jelmer/samba4-debian.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 #include "auth/auth.h"
27 #include "asn_1.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_AUTH
31
32 enum spnego_state_position {
33         SPNEGO_SERVER_START,
34         SPNEGO_CLIENT_START,
35         SPNEGO_SERVER_TARG,
36         SPNEGO_CLIENT_TARG,
37         SPNEGO_FALLBACK,
38         SPNEGO_DONE
39 };
40
41 struct spnego_state {
42         uint_t ref_count;
43         enum spnego_message_type expected_packet;
44         enum spnego_state_position state_position;
45         struct gensec_security *sub_sec_security;
46 };
47
48 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
49 {
50         struct spnego_state *spnego_state;
51
52         spnego_state = talloc_p(gensec_security, struct spnego_state);          
53         if (!spnego_state) {
54                 return NT_STATUS_NO_MEMORY;
55         }
56
57         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
58         spnego_state->state_position = SPNEGO_CLIENT_START;
59         spnego_state->sub_sec_security = NULL;
60
61         gensec_security->private_data = spnego_state;
62         return NT_STATUS_OK;
63 }
64
65 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
66 {
67         struct spnego_state *spnego_state;
68
69         spnego_state = talloc_p(gensec_security, struct spnego_state);          
70         if (!spnego_state) {
71                 return NT_STATUS_NO_MEMORY;
72         }
73
74         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
75         spnego_state->state_position = SPNEGO_SERVER_START;
76         spnego_state->sub_sec_security = NULL;
77
78         gensec_security->private_data = spnego_state;
79         return NT_STATUS_OK;
80 }
81
82 /*
83   wrappers for the spnego_*() functions
84 */
85 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, 
86                                             TALLOC_CTX *mem_ctx, 
87                                             uint8_t *data, size_t length, 
88                                             const uint8_t *whole_pdu, size_t pdu_length, 
89                                             DATA_BLOB *sig)
90 {
91         struct spnego_state *spnego_state = gensec_security->private_data;
92
93         if (spnego_state->state_position != SPNEGO_DONE 
94             && spnego_state->state_position != SPNEGO_FALLBACK) {
95                 return NT_STATUS_INVALID_PARAMETER;
96         }
97         
98         return gensec_unseal_packet(spnego_state->sub_sec_security, 
99                                     mem_ctx, 
100                                     data, length, 
101                                     whole_pdu, pdu_length,
102                                     sig); 
103 }
104
105 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, 
106                                            TALLOC_CTX *mem_ctx, 
107                                            const uint8_t *data, size_t length, 
108                                            const uint8_t *whole_pdu, size_t pdu_length, 
109                                            const DATA_BLOB *sig)
110 {
111         struct spnego_state *spnego_state = gensec_security->private_data;
112
113         if (spnego_state->state_position != SPNEGO_DONE 
114             && spnego_state->state_position != SPNEGO_FALLBACK) {
115                 return NT_STATUS_INVALID_PARAMETER;
116         }
117         
118         return gensec_check_packet(spnego_state->sub_sec_security, 
119                                    mem_ctx, 
120                                    data, length, 
121                                    whole_pdu, pdu_length,
122                                    sig);
123 }
124
125 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, 
126                                           TALLOC_CTX *mem_ctx, 
127                                           uint8_t *data, size_t length, 
128                                           const uint8_t *whole_pdu, size_t pdu_length, 
129                                           DATA_BLOB *sig)
130 {
131         struct spnego_state *spnego_state = gensec_security->private_data;
132
133         if (spnego_state->state_position != SPNEGO_DONE 
134             && spnego_state->state_position != SPNEGO_FALLBACK) {
135                 return NT_STATUS_INVALID_PARAMETER;
136         }
137         
138         return gensec_seal_packet(spnego_state->sub_sec_security, 
139                                   mem_ctx, 
140                                   data, length, 
141                                   whole_pdu, pdu_length,
142                                   sig);
143 }
144
145 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, 
146                                           TALLOC_CTX *mem_ctx, 
147                                           const uint8_t *data, size_t length, 
148                                           const uint8_t *whole_pdu, size_t pdu_length, 
149                                           DATA_BLOB *sig)
150 {
151         struct spnego_state *spnego_state = gensec_security->private_data;
152
153         if (spnego_state->state_position != SPNEGO_DONE 
154             && spnego_state->state_position != SPNEGO_FALLBACK) {
155                 return NT_STATUS_INVALID_PARAMETER;
156         }
157         
158         return gensec_sign_packet(spnego_state->sub_sec_security, 
159                                   mem_ctx, 
160                                   data, length, 
161                                   whole_pdu, pdu_length,
162                                   sig);
163 }
164
165 static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security) 
166 {
167         struct spnego_state *spnego_state = gensec_security->private_data;
168
169         if (spnego_state->state_position != SPNEGO_DONE 
170             && spnego_state->state_position != SPNEGO_FALLBACK) {
171                 return 0;
172         }
173         
174         return gensec_sig_size(spnego_state->sub_sec_security);
175 }
176
177 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, 
178                                           DATA_BLOB *session_key)
179 {
180         struct spnego_state *spnego_state = gensec_security->private_data;
181         if (!spnego_state->sub_sec_security) {
182                 return NT_STATUS_INVALID_PARAMETER;
183         }
184         
185         return gensec_session_key(spnego_state->sub_sec_security, 
186                                   session_key);
187 }
188
189 static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
190                                                                       struct auth_session_info **session_info) 
191 {
192         struct spnego_state *spnego_state = gensec_security->private_data;
193         if (!spnego_state->sub_sec_security) {
194                 return NT_STATUS_INVALID_PARAMETER;
195         }
196         
197         return gensec_session_info(spnego_state->sub_sec_security, 
198                                    session_info);
199 }
200
201 /** Fallback to another GENSEC mechanism, based on magic strings 
202  *
203  * This is the 'fallback' case, where we don't get SPNEGO, and have to
204  * try all the other options (and hope they all have a magic string
205  * they check)
206 */
207
208 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security, 
209                                                   struct spnego_state *spnego_state,
210                                                   TALLOC_CTX *out_mem_ctx, 
211                                                   const DATA_BLOB in, DATA_BLOB *out) 
212 {
213         int i;
214         int num_ops;
215         const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops);
216         for (i=0; i < num_ops; i++) {
217                 NTSTATUS nt_status;
218                 if (!all_ops[i]->oid) {
219                         continue;
220                 }
221                 nt_status = gensec_subcontext_start(gensec_security, 
222                                                     &spnego_state->sub_sec_security);
223                 if (!NT_STATUS_IS_OK(nt_status)) {
224                         return nt_status;
225                 }
226                 /* select the sub context */
227                 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
228                                                      all_ops[i]->oid);
229                 if (!NT_STATUS_IS_OK(nt_status)) {
230                         gensec_end(&spnego_state->sub_sec_security);
231                                         continue;
232                 }
233                 nt_status = gensec_update(spnego_state->sub_sec_security,
234                                                           out_mem_ctx, in, out);
235                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
236                         spnego_state->state_position = SPNEGO_FALLBACK;
237                         return nt_status;
238                 }
239                 gensec_end(&spnego_state->sub_sec_security);
240         }
241         DEBUG(1, ("Failed to parse SPNEGO request\n"));
242         return NT_STATUS_INVALID_PARAMETER;
243         
244 }
245
246 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
247                                                  struct spnego_state *spnego_state, 
248                                                  TALLOC_CTX *out_mem_ctx, 
249                                                  const char **mechType,
250                                                  const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) 
251 {
252         int i;
253         NTSTATUS nt_status;
254         DATA_BLOB null_data_blob = data_blob(NULL,0);
255
256         for (i=0; mechType && mechType[i]; i++) {
257                 nt_status = gensec_subcontext_start(gensec_security,
258                                                     &spnego_state->sub_sec_security);
259                 if (!NT_STATUS_IS_OK(nt_status)) {
260                         break;
261                 }
262                 /* select the sub context */
263                 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
264                                                      mechType[i]);
265                 if (!NT_STATUS_IS_OK(nt_status)) {
266                         gensec_end(&spnego_state->sub_sec_security);
267                         continue;
268                 }
269                 
270                 if (i == 0) {
271                         nt_status = gensec_update(spnego_state->sub_sec_security,
272                                                   out_mem_ctx, 
273                                                   unwrapped_in,
274                                                   unwrapped_out);
275                 } else {
276                         /* only get the helping start blob for the first OID */
277                         nt_status = gensec_update(spnego_state->sub_sec_security,
278                                                   out_mem_ctx, 
279                                                   null_data_blob, 
280                                                   unwrapped_out);
281                 }
282                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
283                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
284                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
285                         gensec_end(&spnego_state->sub_sec_security);
286                 }
287                 return nt_status;
288         }
289         if (!mechType || !mechType[i]) {
290                 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
291         }
292         return NT_STATUS_INVALID_PARAMETER;
293 }
294
295 /** create a client negTokenInit 
296  *
297  * This is the case, where the client is the first one who sends data
298 */
299
300 static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security, 
301                                                   struct spnego_state *spnego_state,
302                                                   TALLOC_CTX *out_mem_ctx, 
303                                                   const DATA_BLOB in, DATA_BLOB *out) 
304 {
305         DATA_BLOB null_data_blob = data_blob(NULL,0);
306         NTSTATUS nt_status;
307         const char **mechTypes = NULL;
308         DATA_BLOB unwrapped_out = data_blob(NULL,0);
309
310         mechTypes = gensec_security_oids(out_mem_ctx, OID_SPNEGO);
311
312         if (!mechTypes) {
313                 DEBUG(1, ("no GENSEC OID backends available\n"));
314                 return NT_STATUS_INVALID_PARAMETER;
315         }
316
317         nt_status = gensec_subcontext_start(gensec_security, 
318                                             &spnego_state->sub_sec_security);
319         if (!NT_STATUS_IS_OK(nt_status)) {
320                 return nt_status;
321         }
322         /* select our preferred mech */
323         nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
324                                              mechTypes[0]);
325         if (!NT_STATUS_IS_OK(nt_status)) {
326                 gensec_end(&spnego_state->sub_sec_security);
327                         return nt_status;
328         }
329         nt_status = gensec_update(spnego_state->sub_sec_security,
330                                   out_mem_ctx, in, &unwrapped_out);
331         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
332                 struct spnego_data spnego_out;
333                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
334                 spnego_out.negTokenInit.mechTypes = mechTypes;
335                 spnego_out.negTokenInit.reqFlags = 0;
336                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
337                 spnego_out.negTokenInit.mechToken = unwrapped_out;
338                 
339                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
340                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
341                                 return NT_STATUS_INVALID_PARAMETER;
342                 }
343                 
344                 /* set next state */
345                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
346                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
347                 return nt_status;
348         }
349         gensec_end(&spnego_state->sub_sec_security);
350
351         DEBUG(1, ("Failed to setup SPNEGO netTokenInit request\n"));
352         return NT_STATUS_INVALID_PARAMETER;
353 }
354
355
356 /** create a client negTokenTarg 
357  *
358  * This is the case, where the client is the first one who sends data
359 */
360
361 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security, 
362                                                   struct spnego_state *spnego_state,
363                                                   TALLOC_CTX *out_mem_ctx, 
364                                                   NTSTATUS nt_status,
365                                                   const DATA_BLOB unwrapped_out, DATA_BLOB *out) 
366 {
367         struct spnego_data spnego_out;
368         DATA_BLOB null_data_blob = data_blob(NULL, 0);
369
370         /* compose reply */
371         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
372         spnego_out.negTokenTarg.responseToken = unwrapped_out;
373         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
374         
375         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
376                 spnego_out.negTokenTarg.supportedMech 
377                         = spnego_state->sub_sec_security->ops->oid;
378                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
379                 spnego_state->state_position = SPNEGO_SERVER_TARG;
380         } else if (NT_STATUS_IS_OK(nt_status)) {
381                 spnego_out.negTokenTarg.supportedMech = NULL;
382                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
383                 spnego_state->state_position = SPNEGO_DONE;
384         } else {
385                 spnego_out.negTokenTarg.supportedMech = NULL;
386                 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
387                 DEBUG(1, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
388                 spnego_state->state_position = SPNEGO_DONE;
389         }
390         
391         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
392                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
393                 return NT_STATUS_INVALID_PARAMETER;
394         }
395
396         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
397
398         return nt_status;
399 }
400
401
402 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
403                                      const DATA_BLOB in, DATA_BLOB *out) 
404 {
405         struct spnego_state *spnego_state = gensec_security->private_data;
406         DATA_BLOB null_data_blob = data_blob(NULL, 0);
407         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
408         struct spnego_data spnego_out;
409         struct spnego_data spnego;
410
411         ssize_t len;
412
413         *out = data_blob(NULL, 0);
414
415         if (!out_mem_ctx) {
416                 out_mem_ctx = spnego_state;
417         }
418
419         /* and switch into the state machine */
420
421         switch (spnego_state->state_position) {
422         case SPNEGO_FALLBACK:
423                 return gensec_update(spnego_state->sub_sec_security,
424                                      out_mem_ctx, in, out);
425         case SPNEGO_SERVER_START:
426         {
427                 if (in.length) {
428                         NTSTATUS nt_status;
429
430                         len = spnego_read_data(in, &spnego);
431                         if (len == -1) {
432                                 return gensec_spnego_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out);
433                         }
434                         /* client sent NegTargetInit, we send NegTokenTarg */
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_spnego_parse_negTokenInit(gensec_security,
446                                                                      spnego_state,
447                                                                      out_mem_ctx, 
448                                                                      spnego.negTokenInit.mechTypes,
449                                                                      spnego.negTokenInit.mechToken, 
450                                                                      &unwrapped_out);
451                         
452                         nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
453                                                                       spnego_state,
454                                                                       out_mem_ctx,
455                                                                       nt_status,
456                                                                       unwrapped_out, 
457                                                                       out);
458                         
459                         spnego_free_data(&spnego);
460                         
461                         return nt_status;
462                 } else {
463                         const char **mechlist = gensec_security_oids(out_mem_ctx, OID_SPNEGO);
464
465                         spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
466                         spnego_out.negTokenInit.mechTypes = mechlist;
467                         spnego_out.negTokenInit.reqFlags = 0;
468                         spnego_out.negTokenInit.mechListMIC = null_data_blob;
469                         spnego_out.negTokenInit.mechToken = unwrapped_out;
470                         
471                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
472                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
473                                 return NT_STATUS_INVALID_PARAMETER;
474                         }
475                         
476                         /* set next state */
477                         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
478                         spnego_state->state_position = SPNEGO_SERVER_TARG;
479                         
480                         return NT_STATUS_MORE_PROCESSING_REQUIRED;
481                 }
482         }
483         
484         case SPNEGO_CLIENT_START:
485         {
486                 /* The server offers a list of mechanisms */
487                 
488                 const char *my_mechs[] = {NULL, NULL};
489                 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
490
491                 if (!in.length) {
492                         /* client to produce negTokenInit */
493                         return gensec_spnego_client_negTokenInit(gensec_security, spnego_state, out_mem_ctx, in, out);
494                 }
495                 
496                 len = spnego_read_data(in, &spnego);
497                 
498                 if (len == -1) {
499                         DEBUG(1, ("Invalid SPNEGO request:\n"));
500                         dump_data(1, (const char *)in.data, in.length);
501                         return NT_STATUS_INVALID_PARAMETER;
502                 }
503                 
504                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
505                 if (spnego.type != spnego_state->expected_packet) {
506                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
507                                   spnego_state->expected_packet));
508                         dump_data(1, (const char *)in.data, in.length);
509                         spnego_free_data(&spnego);
510                         return NT_STATUS_INVALID_PARAMETER;
511                 }
512
513                 if (spnego.negTokenInit.targetPrincipal) {
514                         DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
515                         nt_status = gensec_set_target_principal(gensec_security, 
516                                                                 spnego.negTokenInit.targetPrincipal);
517                         if (!NT_STATUS_IS_OK(nt_status)) {
518                                 spnego_free_data(&spnego);
519                                 return nt_status;
520                         }
521                 }
522
523                 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
524                                                              spnego_state,
525                                                              out_mem_ctx, 
526                                                              spnego.negTokenInit.mechTypes,
527                                                              spnego.negTokenInit.mechToken, 
528                                                              &unwrapped_out);
529
530                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
531                         spnego_free_data(&spnego);
532                         return nt_status;
533                 }
534
535                 /* compose reply */
536                 my_mechs[0] = spnego_state->sub_sec_security->ops->oid;
537                 
538                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
539                 spnego_out.negTokenInit.mechTypes = my_mechs;
540                 spnego_out.negTokenInit.reqFlags = 0;
541                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
542                 spnego_out.negTokenInit.mechToken = unwrapped_out;
543                 
544                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
545                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
546                                 return NT_STATUS_INVALID_PARAMETER;
547                 }
548                 
549                 /* set next state */
550                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
551                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
552                 
553                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
554         }
555         case SPNEGO_SERVER_TARG:
556         {
557                 NTSTATUS nt_status;
558                 if (!in.length) {
559                         return NT_STATUS_INVALID_PARAMETER;
560                 }
561                 
562                 len = spnego_read_data(in, &spnego);
563                 
564                 if (len == -1) {
565                         DEBUG(1, ("Invalid SPNEGO request:\n"));
566                         dump_data(1, (const char *)in.data, in.length);
567                         return NT_STATUS_INVALID_PARAMETER;
568                 }
569                 
570                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
571                 if (spnego.type != spnego_state->expected_packet) {
572                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
573                                   spnego_state->expected_packet));
574                         dump_data(1, (const char *)in.data, in.length);
575                         spnego_free_data(&spnego);
576                         return NT_STATUS_INVALID_PARAMETER;
577                 }
578
579                 nt_status = gensec_update(spnego_state->sub_sec_security,
580                                           out_mem_ctx, 
581                                           spnego.negTokenTarg.responseToken,
582                                           &unwrapped_out);
583
584                 nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
585                                                               spnego_state,
586                                                               out_mem_ctx, 
587                                                               nt_status,
588                                                               unwrapped_out, 
589                                                               out);
590
591                 spnego_free_data(&spnego);
592                 
593                 return nt_status;
594         }
595         case SPNEGO_CLIENT_TARG:
596         {
597                 NTSTATUS nt_status;
598                 if (!in.length) {
599                         return NT_STATUS_INVALID_PARAMETER;
600                 }
601                 
602                 len = spnego_read_data(in, &spnego);
603                 
604                 if (len == -1) {
605                         DEBUG(1, ("Invalid SPNEGO request:\n"));
606                         dump_data(1, (const char *)in.data, in.length);
607                         return NT_STATUS_INVALID_PARAMETER;
608                 }
609                 
610                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
611                 if (spnego.type != spnego_state->expected_packet) {
612                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
613                                   spnego_state->expected_packet));
614                         dump_data(1, (const char *)in.data, in.length);
615                         spnego_free_data(&spnego);
616                         return NT_STATUS_INVALID_PARAMETER;
617                 }
618         
619                 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
620                         return NT_STATUS_ACCESS_DENIED;
621                 }
622
623                 nt_status = gensec_update(spnego_state->sub_sec_security,
624                                           out_mem_ctx, 
625                                           spnego.negTokenTarg.responseToken, 
626                                           &unwrapped_out);
627                 
628                 
629                 if (NT_STATUS_IS_OK(nt_status) 
630                     && (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED)) {
631                         DEBUG(1,("gensec_update ok but not accepted\n"));
632                         nt_status = NT_STATUS_INVALID_PARAMETER;
633                 } 
634                 
635                 spnego_free_data(&spnego);
636
637                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
638                         /* compose reply */
639                         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
640                         spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
641                         spnego_out.negTokenTarg.supportedMech = NULL;
642                         spnego_out.negTokenTarg.responseToken = unwrapped_out;
643                         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
644                         
645                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
646                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
647                                 return NT_STATUS_INVALID_PARAMETER;
648                         }
649                 
650                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
651                 } else if (NT_STATUS_IS_OK(nt_status)) {
652                         /* all done - server has accepted, and we agree */
653                         
654                         if (unwrapped_out.length) {
655                                 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
656                                 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
657                                 spnego_out.negTokenTarg.supportedMech = NULL;
658                                 spnego_out.negTokenTarg.responseToken = unwrapped_out;
659                                 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
660                                 
661                                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
662                                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
663                                         return NT_STATUS_INVALID_PARAMETER;
664                                 }
665                         } else {
666                                 *out = null_data_blob;
667                         }
668
669                         spnego_state->state_position = SPNEGO_DONE;
670                 } else {
671                         DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
672                                   spnego_state->sub_sec_security->ops->name, 
673                                   nt_errstr(nt_status)));
674                 }
675                 return nt_status;
676         }
677         case SPNEGO_DONE:
678                 return NT_STATUS_OK;
679         }
680         return NT_STATUS_INVALID_PARAMETER;
681 }
682
683 static void gensec_spnego_end(struct gensec_security *gensec_security)
684 {
685         struct spnego_state *spnego_state = gensec_security->private_data;
686
687         if (spnego_state->sub_sec_security) {
688                 gensec_end(&spnego_state->sub_sec_security);
689         }
690
691         talloc_free(spnego_state);
692
693         gensec_security->private_data = NULL;
694 }
695
696 static const struct gensec_security_ops gensec_spnego_security_ops = {
697         .name           = "spnego",
698         .sasl_name      = "GSS-SPNEGO",
699         .auth_type      = DCERPC_AUTH_TYPE_SPNEGO,
700         .oid            = OID_SPNEGO,
701         .client_start   = gensec_spnego_client_start,
702         .server_start   = gensec_spnego_server_start,
703         .update         = gensec_spnego_update,
704         .seal_packet    = gensec_spnego_seal_packet,
705         .sign_packet    = gensec_spnego_sign_packet,
706         .sig_size       = gensec_spnego_sig_size,
707         .check_packet   = gensec_spnego_check_packet,
708         .unseal_packet  = gensec_spnego_unseal_packet,
709         .session_key    = gensec_spnego_session_key,
710         .session_info   = gensec_spnego_session_info,
711         .end            = gensec_spnego_end
712 };
713
714 NTSTATUS gensec_spnego_init(void)
715 {
716         NTSTATUS ret;
717         ret = gensec_register(&gensec_spnego_security_ops);
718         if (!NT_STATUS_IS_OK(ret)) {
719                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
720                         gensec_spnego_security_ops.name));
721                 return ret;
722         }
723
724         return ret;
725 }