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