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