r4413: login failure doesn't warrant a level 1 debug (its filling my logs during...
[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 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                 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid) == 0) {
222                         continue;
223                 }
224
225                 nt_status = gensec_subcontext_start(spnego_state, 
226                                                     gensec_security, 
227                                                     &spnego_state->sub_sec_security);
228                 if (!NT_STATUS_IS_OK(nt_status)) {
229                         return nt_status;
230                 }
231                 /* select the sub context */
232                 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
233                                                      all_ops[i]->oid);
234                 if (!NT_STATUS_IS_OK(nt_status)) {
235                         talloc_free(spnego_state->sub_sec_security);
236                         spnego_state->sub_sec_security = NULL;
237                         continue;
238                 }
239                 nt_status = gensec_update(spnego_state->sub_sec_security,
240                                                           out_mem_ctx, in, out);
241                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
242                         spnego_state->state_position = SPNEGO_FALLBACK;
243                         return nt_status;
244                 }
245                 talloc_free(spnego_state->sub_sec_security);
246                 spnego_state->sub_sec_security = NULL;
247         }
248         DEBUG(1, ("Failed to parse SPNEGO request\n"));
249         return NT_STATUS_INVALID_PARAMETER;
250         
251 }
252
253 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
254                                                  struct spnego_state *spnego_state, 
255                                                  TALLOC_CTX *out_mem_ctx, 
256                                                  const char **mechType,
257                                                  const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) 
258 {
259         int i;
260         NTSTATUS nt_status;
261         DATA_BLOB null_data_blob = data_blob(NULL,0);
262
263         for (i=0; mechType && mechType[i]; i++) {
264                 nt_status = gensec_subcontext_start(spnego_state,
265                                                     gensec_security,
266                                                     &spnego_state->sub_sec_security);
267                 if (!NT_STATUS_IS_OK(nt_status)) {
268                         break;
269                 }
270                 /* select the sub context */
271                 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
272                                                      mechType[i]);
273                 if (!NT_STATUS_IS_OK(nt_status)) {
274                         talloc_free(spnego_state->sub_sec_security);
275                         spnego_state->sub_sec_security = NULL;
276                         continue;
277                 }
278                 
279                 if (i == 0) {
280                         nt_status = gensec_update(spnego_state->sub_sec_security,
281                                                   out_mem_ctx, 
282                                                   unwrapped_in,
283                                                   unwrapped_out);
284                 } else {
285                         /* only get the helping start blob for the first OID */
286                         nt_status = gensec_update(spnego_state->sub_sec_security,
287                                                   out_mem_ctx, 
288                                                   null_data_blob, 
289                                                   unwrapped_out);
290                 }
291                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
292                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
293                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
294                         talloc_free(spnego_state->sub_sec_security);
295                         spnego_state->sub_sec_security = NULL;
296                 }
297                 return nt_status;
298         }
299         if (!mechType || !mechType[i]) {
300                 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
301         }
302         return NT_STATUS_INVALID_PARAMETER;
303 }
304
305 /** create a client negTokenInit 
306  *
307  * This is the case, where the client is the first one who sends data
308 */
309
310 static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security, 
311                                                   struct spnego_state *spnego_state,
312                                                   TALLOC_CTX *out_mem_ctx, 
313                                                   const DATA_BLOB in, DATA_BLOB *out) 
314 {
315         DATA_BLOB null_data_blob = data_blob(NULL,0);
316         NTSTATUS nt_status;
317         const char **mechTypes = NULL;
318         DATA_BLOB unwrapped_out = data_blob(NULL,0);
319
320         mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO);
321
322         if (!mechTypes) {
323                 DEBUG(1, ("no GENSEC OID backends available\n"));
324                 return NT_STATUS_INVALID_PARAMETER;
325         }
326
327         nt_status = gensec_subcontext_start(spnego_state, 
328                                             gensec_security, 
329                                             &spnego_state->sub_sec_security);
330         if (!NT_STATUS_IS_OK(nt_status)) {
331                 return nt_status;
332         }
333         /* select our preferred mech */
334         nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
335                                              mechTypes[0]);
336         if (!NT_STATUS_IS_OK(nt_status)) {
337                 talloc_free(spnego_state->sub_sec_security);
338                 spnego_state->sub_sec_security = NULL;
339                 return nt_status;
340         }
341         nt_status = gensec_update(spnego_state->sub_sec_security,
342                                   out_mem_ctx, in, &unwrapped_out);
343         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
344                 struct spnego_data spnego_out;
345                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
346                 spnego_out.negTokenInit.mechTypes = mechTypes;
347                 spnego_out.negTokenInit.reqFlags = 0;
348                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
349                 spnego_out.negTokenInit.mechToken = unwrapped_out;
350                 
351                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
352                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
353                                 return NT_STATUS_INVALID_PARAMETER;
354                 }
355                 
356                 /* set next state */
357                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
358                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
359                 return nt_status;
360         }
361         talloc_free(spnego_state->sub_sec_security);
362         spnego_state->sub_sec_security = NULL;
363
364         DEBUG(1, ("Failed to setup SPNEGO netTokenInit request\n"));
365         return NT_STATUS_INVALID_PARAMETER;
366 }
367
368
369 /** create a client negTokenTarg 
370  *
371  * This is the case, where the client is the first one who sends data
372 */
373
374 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security, 
375                                                   struct spnego_state *spnego_state,
376                                                   TALLOC_CTX *out_mem_ctx, 
377                                                   NTSTATUS nt_status,
378                                                   const DATA_BLOB unwrapped_out, DATA_BLOB *out) 
379 {
380         struct spnego_data spnego_out;
381         DATA_BLOB null_data_blob = data_blob(NULL, 0);
382
383         /* compose reply */
384         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
385         spnego_out.negTokenTarg.responseToken = unwrapped_out;
386         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
387         spnego_out.negTokenTarg.supportedMech = NULL;
388
389         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
390                 spnego_out.negTokenTarg.supportedMech
391                         = spnego_state->sub_sec_security->ops->oid;
392                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
393                 spnego_state->state_position = SPNEGO_SERVER_TARG;
394         } else if (NT_STATUS_IS_OK(nt_status)) {
395                 if (unwrapped_out.data) {
396                         spnego_out.negTokenTarg.supportedMech
397                                 = spnego_state->sub_sec_security->ops->oid;
398                 }
399                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
400                 spnego_state->state_position = SPNEGO_DONE;
401         } else {
402                 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
403                 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
404                 spnego_state->state_position = SPNEGO_DONE;
405         }
406
407         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
408                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
409                 return NT_STATUS_INVALID_PARAMETER;
410         }
411
412         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
413
414         return nt_status;
415 }
416
417
418 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
419                                      const DATA_BLOB in, DATA_BLOB *out) 
420 {
421         struct spnego_state *spnego_state = gensec_security->private_data;
422         DATA_BLOB null_data_blob = data_blob(NULL, 0);
423         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
424         struct spnego_data spnego_out;
425         struct spnego_data spnego;
426
427         ssize_t len;
428
429         *out = data_blob(NULL, 0);
430
431         if (!out_mem_ctx) {
432                 out_mem_ctx = spnego_state;
433         }
434
435         /* and switch into the state machine */
436
437         switch (spnego_state->state_position) {
438         case SPNEGO_FALLBACK:
439                 return gensec_update(spnego_state->sub_sec_security,
440                                      out_mem_ctx, in, out);
441         case SPNEGO_SERVER_START:
442         {
443                 if (in.length) {
444                         NTSTATUS nt_status;
445
446                         len = spnego_read_data(in, &spnego);
447                         if (len == -1) {
448                                 return gensec_spnego_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out);
449                         }
450                         /* client sent NegTargetInit, we send NegTokenTarg */
451
452                         /* OK, so it's real SPNEGO, check the packet's the one we expect */
453                         if (spnego.type != spnego_state->expected_packet) {
454                                 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
455                                           spnego_state->expected_packet));
456                                 dump_data(1, in.data, in.length);
457                                 spnego_free_data(&spnego);
458                                 return NT_STATUS_INVALID_PARAMETER;
459                         }
460                         
461                         nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
462                                                                      spnego_state,
463                                                                      out_mem_ctx, 
464                                                                      spnego.negTokenInit.mechTypes,
465                                                                      spnego.negTokenInit.mechToken, 
466                                                                      &unwrapped_out);
467                         
468                         nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
469                                                                       spnego_state,
470                                                                       out_mem_ctx,
471                                                                       nt_status,
472                                                                       unwrapped_out, 
473                                                                       out);
474                         
475                         spnego_free_data(&spnego);
476                         
477                         return nt_status;
478                 } else {
479                         const char **mechlist = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO);
480
481                         spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
482                         spnego_out.negTokenInit.mechTypes = mechlist;
483                         spnego_out.negTokenInit.reqFlags = 0;
484                         spnego_out.negTokenInit.mechListMIC
485                                 = data_blob_string_const(gensec_get_target_principal(gensec_security));
486                         spnego_out.negTokenInit.mechToken = unwrapped_out;
487                         
488                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
489                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
490                                 return NT_STATUS_INVALID_PARAMETER;
491                         }
492                         
493                         /* set next state */
494                         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
495                         spnego_state->state_position = SPNEGO_SERVER_TARG;
496                         
497                         return NT_STATUS_MORE_PROCESSING_REQUIRED;
498                 }
499         }
500         
501         case SPNEGO_CLIENT_START:
502         {
503                 /* The server offers a list of mechanisms */
504                 
505                 const char *my_mechs[] = {NULL, NULL};
506                 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
507
508                 if (!in.length) {
509                         /* client to produce negTokenInit */
510                         return gensec_spnego_client_negTokenInit(gensec_security, spnego_state, 
511                                                                  out_mem_ctx, in, out);
512                 }
513                 
514                 len = spnego_read_data(in, &spnego);
515                 
516                 if (len == -1) {
517                         DEBUG(1, ("Invalid SPNEGO request:\n"));
518                         dump_data(1, in.data, in.length);
519                         return NT_STATUS_INVALID_PARAMETER;
520                 }
521                 
522                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
523                 if (spnego.type != spnego_state->expected_packet) {
524                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
525                                   spnego_state->expected_packet));
526                         dump_data(1, in.data, in.length);
527                         spnego_free_data(&spnego);
528                         return NT_STATUS_INVALID_PARAMETER;
529                 }
530
531                 if (spnego.negTokenInit.targetPrincipal) {
532                         DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
533                         nt_status = gensec_set_target_principal(gensec_security, 
534                                                                 spnego.negTokenInit.targetPrincipal);
535                         if (!NT_STATUS_IS_OK(nt_status)) {
536                                 spnego_free_data(&spnego);
537                                 return nt_status;
538                         }
539                 }
540
541                 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
542                                                              spnego_state,
543                                                              out_mem_ctx, 
544                                                              spnego.negTokenInit.mechTypes,
545                                                              spnego.negTokenInit.mechToken, 
546                                                              &unwrapped_out);
547
548                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
549                         spnego_free_data(&spnego);
550                         return nt_status;
551                 }
552
553                 /* compose reply */
554                 my_mechs[0] = spnego_state->sub_sec_security->ops->oid;
555                 
556                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
557                 spnego_out.negTokenInit.mechTypes = my_mechs;
558                 spnego_out.negTokenInit.reqFlags = 0;
559                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
560                 spnego_out.negTokenInit.mechToken = unwrapped_out;
561                 
562                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
563                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
564                                 return NT_STATUS_INVALID_PARAMETER;
565                 }
566                 
567                 /* set next state */
568                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
569                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
570                 
571                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
572         }
573         case SPNEGO_SERVER_TARG:
574         {
575                 NTSTATUS nt_status;
576                 if (!in.length) {
577                         return NT_STATUS_INVALID_PARAMETER;
578                 }
579                 
580                 len = spnego_read_data(in, &spnego);
581                 
582                 if (len == -1) {
583                         DEBUG(1, ("Invalid SPNEGO request:\n"));
584                         dump_data(1, in.data, in.length);
585                         return NT_STATUS_INVALID_PARAMETER;
586                 }
587                 
588                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
589                 if (spnego.type != spnego_state->expected_packet) {
590                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
591                                   spnego_state->expected_packet));
592                         dump_data(1, in.data, in.length);
593                         spnego_free_data(&spnego);
594                         return NT_STATUS_INVALID_PARAMETER;
595                 }
596
597                 nt_status = gensec_update(spnego_state->sub_sec_security,
598                                           out_mem_ctx, 
599                                           spnego.negTokenTarg.responseToken,
600                                           &unwrapped_out);
601
602                 nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
603                                                               spnego_state,
604                                                               out_mem_ctx, 
605                                                               nt_status,
606                                                               unwrapped_out, 
607                                                               out);
608
609                 spnego_free_data(&spnego);
610                 
611                 return nt_status;
612         }
613         case SPNEGO_CLIENT_TARG:
614         {
615                 NTSTATUS nt_status;
616                 if (!in.length) {
617                         return NT_STATUS_INVALID_PARAMETER;
618                 }
619                 
620                 len = spnego_read_data(in, &spnego);
621                 
622                 if (len == -1) {
623                         DEBUG(1, ("Invalid SPNEGO request:\n"));
624                         dump_data(1, in.data, in.length);
625                         return NT_STATUS_INVALID_PARAMETER;
626                 }
627                 
628                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
629                 if (spnego.type != spnego_state->expected_packet) {
630                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
631                                   spnego_state->expected_packet));
632                         dump_data(1, in.data, in.length);
633                         spnego_free_data(&spnego);
634                         return NT_STATUS_INVALID_PARAMETER;
635                 }
636         
637                 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
638                         return NT_STATUS_ACCESS_DENIED;
639                 }
640
641                 nt_status = gensec_update(spnego_state->sub_sec_security,
642                                           out_mem_ctx, 
643                                           spnego.negTokenTarg.responseToken, 
644                                           &unwrapped_out);
645                 
646                 
647                 if (NT_STATUS_IS_OK(nt_status) 
648                     && (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED)) {
649                         DEBUG(1,("gensec_update ok but not accepted\n"));
650                         nt_status = NT_STATUS_INVALID_PARAMETER;
651                 } 
652                 
653                 spnego_free_data(&spnego);
654
655                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
656                         /* compose reply */
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                 
668                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
669                 } else if (NT_STATUS_IS_OK(nt_status)) {
670                         /* all done - server has accepted, and we agree */
671                         
672                         if (unwrapped_out.length) {
673                                 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
674                                 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
675                                 spnego_out.negTokenTarg.supportedMech = NULL;
676                                 spnego_out.negTokenTarg.responseToken = unwrapped_out;
677                                 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
678                                 
679                                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
680                                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
681                                         return NT_STATUS_INVALID_PARAMETER;
682                                 }
683                         } else {
684                                 *out = null_data_blob;
685                         }
686
687                         spnego_state->state_position = SPNEGO_DONE;
688                 } else {
689                         DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
690                                   spnego_state->sub_sec_security->ops->name, 
691                                   nt_errstr(nt_status)));
692                 }
693                 return nt_status;
694         }
695         case SPNEGO_DONE:
696                 return NT_STATUS_OK;
697         }
698         return NT_STATUS_INVALID_PARAMETER;
699 }
700
701 static const struct gensec_security_ops gensec_spnego_security_ops = {
702         .name           = "spnego",
703         .sasl_name      = "GSS-SPNEGO",
704         .auth_type      = DCERPC_AUTH_TYPE_SPNEGO,
705         .oid            = GENSEC_OID_SPNEGO,
706         .client_start   = gensec_spnego_client_start,
707         .server_start   = gensec_spnego_server_start,
708         .update         = gensec_spnego_update,
709         .seal_packet    = gensec_spnego_seal_packet,
710         .sign_packet    = gensec_spnego_sign_packet,
711         .sig_size       = gensec_spnego_sig_size,
712         .check_packet   = gensec_spnego_check_packet,
713         .unseal_packet  = gensec_spnego_unseal_packet,
714         .session_key    = gensec_spnego_session_key,
715         .session_info   = gensec_spnego_session_info,
716 };
717
718 NTSTATUS gensec_spnego_init(void)
719 {
720         NTSTATUS ret;
721         ret = gensec_register(&gensec_spnego_security_ops);
722         if (!NT_STATUS_IS_OK(ret)) {
723                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
724                         gensec_spnego_security_ops.name));
725                 return ret;
726         }
727
728         return ret;
729 }