r2645: converted the NTLMSSP code to the new style of talloc
[samba.git] / source4 / 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         uint_t ref_count;
41         enum spnego_message_type expected_packet;
42         enum spnego_state_position state_position;
43         struct gensec_security *sub_sec_security;
44 };
45
46 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
47 {
48         struct spnego_state *spnego_state;
49
50         spnego_state = talloc_p(gensec_security, struct spnego_state);          
51         if (!spnego_state) {
52                 return NT_STATUS_NO_MEMORY;
53         }
54
55         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
56         spnego_state->state_position = SPNEGO_CLIENT_START;
57         spnego_state->sub_sec_security = NULL;
58
59         gensec_security->private_data = spnego_state;
60         return NT_STATUS_OK;
61 }
62
63 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
64 {
65         struct spnego_state *spnego_state;
66
67         spnego_state = talloc_p(gensec_security, struct spnego_state);          
68         if (!spnego_state) {
69                 return NT_STATUS_NO_MEMORY;
70         }
71
72         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
73         spnego_state->state_position = SPNEGO_SERVER_START;
74         spnego_state->sub_sec_security = NULL;
75
76         gensec_security->private_data = spnego_state;
77         return NT_STATUS_OK;
78 }
79
80 /*
81   wrappers for the spnego_*() functions
82 */
83 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, 
84                                             TALLOC_CTX *mem_ctx, 
85                                             uint8_t *data, size_t length, 
86                                             const uint8_t *whole_pdu, size_t pdu_length, 
87                                             DATA_BLOB *sig)
88 {
89         struct spnego_state *spnego_state = gensec_security->private_data;
90
91         if (spnego_state->state_position != SPNEGO_DONE 
92             && spnego_state->state_position != SPNEGO_FALLBACK) {
93                 return NT_STATUS_INVALID_PARAMETER;
94         }
95         
96         return gensec_unseal_packet(spnego_state->sub_sec_security, 
97                                     mem_ctx, 
98                                     data, length, 
99                                     whole_pdu, pdu_length,
100                                     sig); 
101 }
102
103 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, 
104                                            TALLOC_CTX *mem_ctx, 
105                                            const uint8_t *data, size_t length, 
106                                            const uint8_t *whole_pdu, size_t pdu_length, 
107                                            const 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_check_packet(spnego_state->sub_sec_security, 
118                                    mem_ctx, 
119                                    data, length, 
120                                    whole_pdu, pdu_length,
121                                    sig);
122 }
123
124 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, 
125                                           TALLOC_CTX *mem_ctx, 
126                                           uint8_t *data, size_t length, 
127                                           const uint8_t *whole_pdu, size_t pdu_length, 
128                                           DATA_BLOB *sig)
129 {
130         struct spnego_state *spnego_state = gensec_security->private_data;
131
132         return NT_STATUS_NOT_IMPLEMENTED;
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) 
283                     && (!NT_STATUS_IS_OK(nt_status))) {
284                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
285                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
286                                 gensec_end(&spnego_state->sub_sec_security);
287                 } else {
288                         break;
289                 }
290         }
291         if (!mechType || !mechType[i]) {
292                 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
293         }
294         return nt_status;
295 }
296
297 /** create a client negTokenInit 
298  *
299  * This is the case, where the client is the first one who sends data
300 */
301
302 static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security, 
303                                                   struct spnego_state *spnego_state,
304                                                   TALLOC_CTX *out_mem_ctx, 
305                                                   const DATA_BLOB in, DATA_BLOB *out) 
306 {
307         DATA_BLOB null_data_blob = data_blob(NULL,0);
308         NTSTATUS nt_status;
309         const char **mechTypes = NULL;
310         DATA_BLOB unwrapped_out = data_blob(NULL,0);
311
312         mechTypes = gensec_security_oids(out_mem_ctx, OID_SPNEGO);
313
314         if (!mechTypes) {
315                 DEBUG(1, ("no GENSEC OID backends available\n"));
316                 return NT_STATUS_INVALID_PARAMETER;
317         }
318
319         nt_status = gensec_subcontext_start(gensec_security, 
320                                             &spnego_state->sub_sec_security);
321         if (!NT_STATUS_IS_OK(nt_status)) {
322                 return nt_status;
323         }
324         /* select our preferred mech */
325         nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
326                                              mechTypes[0]);
327         if (!NT_STATUS_IS_OK(nt_status)) {
328                 gensec_end(&spnego_state->sub_sec_security);
329                         return nt_status;
330         }
331         nt_status = gensec_update(spnego_state->sub_sec_security,
332                                   out_mem_ctx, in, &unwrapped_out);
333         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
334                 struct spnego_data spnego_out;
335                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
336                 spnego_out.negTokenInit.mechTypes = mechTypes;
337                 spnego_out.negTokenInit.reqFlags = 0;
338                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
339                 spnego_out.negTokenInit.mechToken = unwrapped_out;
340                 
341                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
342                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
343                                 return NT_STATUS_INVALID_PARAMETER;
344                 }
345                 
346                 /* set next state */
347                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
348                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
349                 return nt_status;
350         }
351         gensec_end(&spnego_state->sub_sec_security);
352
353         DEBUG(1, ("Failed to setup SPNEGO netTokenInit request\n"));
354         return NT_STATUS_INVALID_PARAMETER;
355 }
356
357
358 /** create a client negTokenTarg 
359  *
360  * This is the case, where the client is the first one who sends data
361 */
362
363 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security, 
364                                                   struct spnego_state *spnego_state,
365                                                   TALLOC_CTX *out_mem_ctx, 
366                                                   NTSTATUS nt_status,
367                                                   const DATA_BLOB unwrapped_out, DATA_BLOB *out) 
368 {
369         struct spnego_data spnego_out;
370         DATA_BLOB null_data_blob = data_blob(NULL, 0);
371
372         /* compose reply */
373         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
374         spnego_out.negTokenTarg.supportedMech 
375                 = spnego_state->sub_sec_security->ops->oid;
376         spnego_out.negTokenTarg.responseToken = unwrapped_out;
377         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
378         
379         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
380                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
381                 spnego_state->state_position = SPNEGO_SERVER_TARG;
382         } else if (NT_STATUS_IS_OK(nt_status)) {
383                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
384                 spnego_state->state_position = SPNEGO_DONE;
385         } else {
386                 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
387                 DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
388                           spnego_state->sub_sec_security->ops->name, 
389                           nt_errstr(nt_status)));
390                 spnego_state->state_position = SPNEGO_DONE;
391         }
392         
393         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
394                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
395                 return NT_STATUS_INVALID_PARAMETER;
396         }
397
398         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
399
400         return nt_status;
401 }
402
403
404 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
405                                      const DATA_BLOB in, DATA_BLOB *out) 
406 {
407         struct spnego_state *spnego_state = gensec_security->private_data;
408         DATA_BLOB null_data_blob = data_blob(NULL, 0);
409         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
410         struct spnego_data spnego_out;
411         struct spnego_data spnego;
412
413         ssize_t len;
414
415         *out = data_blob(NULL, 0);
416
417         if (!out_mem_ctx) {
418                 out_mem_ctx = spnego_state;
419         }
420
421         /* and switch into the state machine */
422
423         switch (spnego_state->state_position) {
424         case SPNEGO_FALLBACK:
425                 return gensec_update(spnego_state->sub_sec_security,
426                                      out_mem_ctx, in, out);
427         case SPNEGO_SERVER_START:
428         {
429                 if (in.length) {
430                         NTSTATUS nt_status;
431
432                         len = spnego_read_data(in, &spnego);
433                         if (len == -1) {
434                                 return gensec_spnego_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out);
435                         }
436                         /* client sent NegTargetInit, we send NegTokenTarg */
437
438                         /* OK, so it's real SPNEGO, check the packet's the one we expect */
439                         if (spnego.type != spnego_state->expected_packet) {
440                                 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
441                                           spnego_state->expected_packet));
442                                 dump_data(1, (const char *)in.data, in.length);
443                                 spnego_free_data(&spnego);
444                                 return NT_STATUS_INVALID_PARAMETER;
445                         }
446                         
447                         nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
448                                                                      spnego_state,
449                                                                      out_mem_ctx, 
450                                                                      spnego.negTokenInit.mechTypes,
451                                                                      spnego.negTokenInit.mechToken, 
452                                                                      &unwrapped_out);
453                         
454                         nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
455                                                                       spnego_state,
456                                                                       out_mem_ctx,
457                                                                       nt_status,
458                                                                       unwrapped_out, 
459                                                                       out);
460                         
461                         spnego_free_data(&spnego);
462                         
463                         return nt_status;
464                 } else {
465                         const char **mechlist = gensec_security_oids(out_mem_ctx, OID_SPNEGO);
466
467                         spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
468                         spnego_out.negTokenInit.mechTypes = mechlist;
469                         spnego_out.negTokenInit.reqFlags = 0;
470                         spnego_out.negTokenInit.mechListMIC = null_data_blob;
471                         spnego_out.negTokenInit.mechToken = unwrapped_out;
472                         
473                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
474                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
475                                 return NT_STATUS_INVALID_PARAMETER;
476                         }
477                         
478                         /* set next state */
479                         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
480                         spnego_state->state_position = SPNEGO_SERVER_TARG;
481                         
482                         return NT_STATUS_MORE_PROCESSING_REQUIRED;
483                 }
484         }
485         
486         case SPNEGO_CLIENT_START:
487         {
488                 /* The server offers a list of mechanisms */
489                 
490                 char *my_mechs[] = {NULL, NULL};
491                 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
492
493                 if (!in.length) {
494                         /* client to produce negTokenInit */
495                         return gensec_spnego_client_negTokenInit(gensec_security, spnego_state, out_mem_ctx, in, out);
496                 }
497                 
498                 len = spnego_read_data(in, &spnego);
499                 
500                 if (len == -1) {
501                         DEBUG(1, ("Invalid SPNEGO request:\n"));
502                         dump_data(1, (const char *)in.data, in.length);
503                         return NT_STATUS_INVALID_PARAMETER;
504                 }
505                 
506                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
507                 if (spnego.type != spnego_state->expected_packet) {
508                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
509                                   spnego_state->expected_packet));
510                         dump_data(1, (const char *)in.data, in.length);
511                         spnego_free_data(&spnego);
512                         return NT_STATUS_INVALID_PARAMETER;
513                 }
514
515                 if (spnego.negTokenInit.targetPrincipal) {
516                         DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
517                         nt_status = gensec_set_target_principal(gensec_security, 
518                                                                 spnego.negTokenInit.targetPrincipal);
519                         if (!NT_STATUS_IS_OK(nt_status)) {
520                                 spnego_free_data(&spnego);
521                                 return nt_status;
522                         }
523                 }
524
525                 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
526                                                              spnego_state,
527                                                              out_mem_ctx, 
528                                                              spnego.negTokenInit.mechTypes,
529                                                              spnego.negTokenInit.mechToken, 
530                                                              &unwrapped_out);
531
532                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
533                         spnego_free_data(&spnego);
534                         return nt_status;
535                 }
536
537                 /* compose reply */
538                 my_mechs[0] = spnego_state->sub_sec_security->ops->oid;
539                 
540                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
541                 spnego_out.negTokenInit.mechTypes = my_mechs;
542                 spnego_out.negTokenInit.reqFlags = 0;
543                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
544                 spnego_out.negTokenInit.mechToken = unwrapped_out;
545                 
546                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
547                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
548                                 return NT_STATUS_INVALID_PARAMETER;
549                 }
550                 
551                 /* set next state */
552                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
553                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
554                 
555                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
556         }
557         case SPNEGO_SERVER_TARG:
558         {
559                 NTSTATUS nt_status;
560                 if (!in.length) {
561                         return NT_STATUS_INVALID_PARAMETER;
562                 }
563                 
564                 len = spnego_read_data(in, &spnego);
565                 
566                 if (len == -1) {
567                         DEBUG(1, ("Invalid SPNEGO request:\n"));
568                         dump_data(1, (const char *)in.data, in.length);
569                         return NT_STATUS_INVALID_PARAMETER;
570                 }
571                 
572                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
573                 if (spnego.type != spnego_state->expected_packet) {
574                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
575                                   spnego_state->expected_packet));
576                         dump_data(1, (const char *)in.data, in.length);
577                         spnego_free_data(&spnego);
578                         return NT_STATUS_INVALID_PARAMETER;
579                 }
580
581                 nt_status = gensec_update(spnego_state->sub_sec_security,
582                                           out_mem_ctx, 
583                                           spnego.negTokenTarg.responseToken,
584                                           &unwrapped_out);
585
586                 nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
587                                                               spnego_state,
588                                                               out_mem_ctx, 
589                                                               nt_status,
590                                                               unwrapped_out, 
591                                                               out);
592
593                 spnego_free_data(&spnego);
594                 
595                 return nt_status;
596         }
597         case SPNEGO_CLIENT_TARG:
598         {
599                 NTSTATUS nt_status;
600                 if (!in.length) {
601                         return NT_STATUS_INVALID_PARAMETER;
602                 }
603                 
604                 len = spnego_read_data(in, &spnego);
605                 
606                 if (len == -1) {
607                         DEBUG(1, ("Invalid SPNEGO request:\n"));
608                         dump_data(1, (const char *)in.data, in.length);
609                         return NT_STATUS_INVALID_PARAMETER;
610                 }
611                 
612                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
613                 if (spnego.type != spnego_state->expected_packet) {
614                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
615                                   spnego_state->expected_packet));
616                         dump_data(1, (const char *)in.data, in.length);
617                         spnego_free_data(&spnego);
618                         return NT_STATUS_INVALID_PARAMETER;
619                 }
620         
621                 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
622                         return NT_STATUS_ACCESS_DENIED;
623                 }
624
625                 nt_status = gensec_update(spnego_state->sub_sec_security,
626                                           out_mem_ctx, 
627                                           spnego.negTokenTarg.responseToken, 
628                                           &unwrapped_out);
629                 
630                 
631                 if (NT_STATUS_IS_OK(nt_status) 
632                     && (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED)) {
633                         DEBUG(1,("gensec_update ok but not accepted\n"));
634                         nt_status = NT_STATUS_INVALID_PARAMETER;
635                 } 
636                 
637                 spnego_free_data(&spnego);
638
639                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
640                         /* compose reply */
641                         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
642                         spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
643                         spnego_out.negTokenTarg.supportedMech = NULL;
644                         spnego_out.negTokenTarg.responseToken = unwrapped_out;
645                         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
646                         
647                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
648                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
649                                 return NT_STATUS_INVALID_PARAMETER;
650                         }
651                 
652                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
653                 } else if (NT_STATUS_IS_OK(nt_status)) {
654                         /* all done - server has accepted, and we agree */
655                         
656                         if (unwrapped_out.length) {
657                                 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
658                                 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
659                                 spnego_out.negTokenTarg.supportedMech = NULL;
660                                 spnego_out.negTokenTarg.responseToken = unwrapped_out;
661                                 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
662                                 
663                                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
664                                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
665                                         return NT_STATUS_INVALID_PARAMETER;
666                                 }
667                         } else {
668                                 *out = null_data_blob;
669                         }
670
671                         spnego_state->state_position = SPNEGO_DONE;
672                 } else {
673                         DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
674                                   spnego_state->sub_sec_security->ops->name, 
675                                   nt_errstr(nt_status)));
676                 }
677                 return nt_status;
678         }
679         case SPNEGO_DONE:
680                 return NT_STATUS_OK;
681         }
682         return NT_STATUS_INVALID_PARAMETER;
683 }
684
685 static void gensec_spnego_end(struct gensec_security *gensec_security)
686 {
687         struct spnego_state *spnego_state = gensec_security->private_data;
688
689         if (spnego_state->sub_sec_security) {
690                 gensec_end(&spnego_state->sub_sec_security);
691         }
692
693         talloc_free(spnego_state);
694
695         gensec_security->private_data = NULL;
696 }
697
698 static const struct gensec_security_ops gensec_spnego_security_ops = {
699         .name           = "spnego",
700         .sasl_name      = "GSS-SPNEGO",
701         .auth_type      = DCERPC_AUTH_TYPE_SPNEGO,
702         .oid            = OID_SPNEGO,
703         .client_start   = gensec_spnego_client_start,
704         .server_start   = gensec_spnego_server_start,
705         .update         = gensec_spnego_update,
706         .seal_packet    = gensec_spnego_seal_packet,
707         .sign_packet    = gensec_spnego_sign_packet,
708         .sig_size       = gensec_spnego_sig_size,
709         .check_packet   = gensec_spnego_check_packet,
710         .unseal_packet  = gensec_spnego_unseal_packet,
711         .session_key    = gensec_spnego_session_key,
712         .session_info   = gensec_spnego_session_info,
713         .end            = gensec_spnego_end
714 };
715
716 NTSTATUS gensec_spnego_init(void)
717 {
718         NTSTATUS ret;
719         ret = register_backend("gensec", &gensec_spnego_security_ops);
720         if (!NT_STATUS_IS_OK(ret)) {
721                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
722                         gensec_spnego_security_ops.name));
723                 return ret;
724         }
725
726         return ret;
727 }