r9927: Extend copyright for all the hard work I've done this year.
[samba.git] / source / auth / gensec / 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-2005
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 enum spnego_state_position {
29         SPNEGO_SERVER_START,
30         SPNEGO_CLIENT_START,
31         SPNEGO_SERVER_TARG,
32         SPNEGO_CLIENT_TARG,
33         SPNEGO_FALLBACK,
34         SPNEGO_DONE
35 };
36
37 struct spnego_state {
38         uint_t ref_count;
39         enum spnego_message_type expected_packet;
40         enum spnego_state_position state_position;
41         struct gensec_security *sub_sec_security;
42         BOOL no_response_expected;
43
44         const char *neg_oid;
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(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         spnego_state->no_response_expected = False;
61
62         gensec_security->private_data = spnego_state;
63         return NT_STATUS_OK;
64 }
65
66 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
67 {
68         struct spnego_state *spnego_state;
69
70         spnego_state = talloc(gensec_security, struct spnego_state);            
71         if (!spnego_state) {
72                 return NT_STATUS_NO_MEMORY;
73         }
74
75         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
76         spnego_state->state_position = SPNEGO_SERVER_START;
77         spnego_state->sub_sec_security = NULL;
78         spnego_state->no_response_expected = False;
79
80         gensec_security->private_data = spnego_state;
81         return NT_STATUS_OK;
82 }
83
84 /*
85   wrappers for the spnego_*() functions
86 */
87 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, 
88                                             TALLOC_CTX *mem_ctx, 
89                                             uint8_t *data, size_t length, 
90                                             const uint8_t *whole_pdu, size_t pdu_length, 
91                                             const DATA_BLOB *sig)
92 {
93         struct spnego_state *spnego_state = gensec_security->private_data;
94
95         if (spnego_state->state_position != SPNEGO_DONE 
96             && spnego_state->state_position != SPNEGO_FALLBACK) {
97                 return NT_STATUS_INVALID_PARAMETER;
98         }
99         
100         return gensec_unseal_packet(spnego_state->sub_sec_security, 
101                                     mem_ctx, 
102                                     data, length, 
103                                     whole_pdu, pdu_length,
104                                     sig); 
105 }
106
107 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, 
108                                            TALLOC_CTX *mem_ctx, 
109                                            const uint8_t *data, size_t length, 
110                                            const uint8_t *whole_pdu, size_t pdu_length, 
111                                            const DATA_BLOB *sig)
112 {
113         struct spnego_state *spnego_state = gensec_security->private_data;
114
115         if (spnego_state->state_position != SPNEGO_DONE 
116             && spnego_state->state_position != SPNEGO_FALLBACK) {
117                 return NT_STATUS_INVALID_PARAMETER;
118         }
119         
120         return gensec_check_packet(spnego_state->sub_sec_security, 
121                                    mem_ctx, 
122                                    data, length, 
123                                    whole_pdu, pdu_length,
124                                    sig);
125 }
126
127 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, 
128                                           TALLOC_CTX *mem_ctx, 
129                                           uint8_t *data, size_t length, 
130                                           const uint8_t *whole_pdu, size_t pdu_length, 
131                                           DATA_BLOB *sig)
132 {
133         struct spnego_state *spnego_state = gensec_security->private_data;
134
135         if (spnego_state->state_position != SPNEGO_DONE 
136             && spnego_state->state_position != SPNEGO_FALLBACK) {
137                 return NT_STATUS_INVALID_PARAMETER;
138         }
139         
140         return gensec_seal_packet(spnego_state->sub_sec_security, 
141                                   mem_ctx, 
142                                   data, length, 
143                                   whole_pdu, pdu_length,
144                                   sig);
145 }
146
147 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, 
148                                           TALLOC_CTX *mem_ctx, 
149                                           const uint8_t *data, size_t length, 
150                                           const uint8_t *whole_pdu, size_t pdu_length, 
151                                           DATA_BLOB *sig)
152 {
153         struct spnego_state *spnego_state = gensec_security->private_data;
154
155         if (spnego_state->state_position != SPNEGO_DONE 
156             && spnego_state->state_position != SPNEGO_FALLBACK) {
157                 return NT_STATUS_INVALID_PARAMETER;
158         }
159         
160         return gensec_sign_packet(spnego_state->sub_sec_security, 
161                                   mem_ctx, 
162                                   data, length, 
163                                   whole_pdu, pdu_length,
164                                   sig);
165 }
166
167 static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security, 
168                                    TALLOC_CTX *mem_ctx, 
169                                    const DATA_BLOB *in, 
170                                    DATA_BLOB *out)
171 {
172         struct spnego_state *spnego_state = gensec_security->private_data;
173
174         if (spnego_state->state_position != SPNEGO_DONE 
175             && spnego_state->state_position != SPNEGO_FALLBACK) {
176                 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
177                 return NT_STATUS_INVALID_PARAMETER;
178         }
179         
180         return gensec_wrap(spnego_state->sub_sec_security, 
181                            mem_ctx, in, out);
182 }
183
184 static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security, 
185                                      TALLOC_CTX *mem_ctx, 
186                                      const DATA_BLOB *in, 
187                                      DATA_BLOB *out)
188 {
189         struct spnego_state *spnego_state = gensec_security->private_data;
190
191         if (spnego_state->state_position != SPNEGO_DONE 
192             && spnego_state->state_position != SPNEGO_FALLBACK) {
193                 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
194                 return NT_STATUS_INVALID_PARAMETER;
195         }
196         
197         return gensec_unwrap(spnego_state->sub_sec_security, 
198                              mem_ctx, in, out);
199 }
200
201 static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security) 
202 {
203         struct spnego_state *spnego_state = gensec_security->private_data;
204
205         if (spnego_state->state_position != SPNEGO_DONE 
206             && spnego_state->state_position != SPNEGO_FALLBACK) {
207                 return 0;
208         }
209         
210         return gensec_sig_size(spnego_state->sub_sec_security);
211 }
212
213 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, 
214                                           DATA_BLOB *session_key)
215 {
216         struct spnego_state *spnego_state = gensec_security->private_data;
217         if (!spnego_state->sub_sec_security) {
218                 return NT_STATUS_INVALID_PARAMETER;
219         }
220         
221         return gensec_session_key(spnego_state->sub_sec_security, 
222                                   session_key);
223 }
224
225 static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
226                                                                       struct auth_session_info **session_info) 
227 {
228         struct spnego_state *spnego_state = gensec_security->private_data;
229         if (!spnego_state->sub_sec_security) {
230                 return NT_STATUS_INVALID_PARAMETER;
231         }
232         
233         return gensec_session_info(spnego_state->sub_sec_security, 
234                                    session_info);
235 }
236
237 /** Fallback to another GENSEC mechanism, based on magic strings 
238  *
239  * This is the 'fallback' case, where we don't get SPNEGO, and have to
240  * try all the other options (and hope they all have a magic string
241  * they check)
242 */
243
244 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security, 
245                                                   struct spnego_state *spnego_state,
246                                                   TALLOC_CTX *out_mem_ctx, 
247                                                   const DATA_BLOB in, DATA_BLOB *out) 
248 {
249         int i,j;
250         int num_ops;
251         const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops);
252         for (i=0; i < num_ops; i++) {
253                 BOOL is_spnego;
254                 NTSTATUS nt_status;
255                 if (!all_ops[i]->oid) {
256                         continue;
257                 }
258
259                 is_spnego = False;
260                 for (j=0; all_ops[i]->oid[j]; j++) {
261                         if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
262                                 is_spnego = True;
263                         }
264                 }
265                 if (is_spnego) {
266                         continue;
267                 }
268
269                 if (!all_ops[i]->magic) {
270                         continue;
271                 }
272
273                 nt_status = all_ops[i]->magic(gensec_security, &in);
274                 if (!NT_STATUS_IS_OK(nt_status)) {
275                         continue;
276                 }
277
278                 spnego_state->state_position = SPNEGO_FALLBACK;
279
280                 nt_status = gensec_subcontext_start(spnego_state, 
281                                                     gensec_security, 
282                                                     &spnego_state->sub_sec_security);
283
284                 if (!NT_STATUS_IS_OK(nt_status)) {
285                         return nt_status;
286                 }
287                 /* select the sub context */
288                 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
289                                                      all_ops[i]);
290                 if (!NT_STATUS_IS_OK(nt_status)) {
291                         return nt_status;
292                 }
293                 nt_status = gensec_update(spnego_state->sub_sec_security,
294                                           out_mem_ctx, in, out);
295                 return nt_status;
296         }
297         DEBUG(1, ("Failed to parse SPNEGO request\n"));
298         return NT_STATUS_INVALID_PARAMETER;
299         
300 }
301
302 /* 
303    Parse the netTokenInit, either from the client, to the server, or
304    from the server to the client.
305 */
306
307 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
308                                                  struct spnego_state *spnego_state, 
309                                                  TALLOC_CTX *out_mem_ctx, 
310                                                  const char **mechType,
311                                                  const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) 
312 {
313         int i;
314         NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
315         DATA_BLOB null_data_blob = data_blob(NULL,0);
316
317         const struct gensec_security_ops_wrapper *all_sec
318                 = gensec_security_by_oid_list(out_mem_ctx, 
319                                               mechType,
320                                               GENSEC_OID_SPNEGO);
321         for (i=0; all_sec && all_sec[i].op; i++) {
322                 nt_status = gensec_subcontext_start(spnego_state,
323                                                     gensec_security,
324                                                     &spnego_state->sub_sec_security);
325                 if (!NT_STATUS_IS_OK(nt_status)) {
326                         return nt_status;
327                 }
328                 /* select the sub context */
329                 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
330                                                      all_sec[i].op);
331                 if (!NT_STATUS_IS_OK(nt_status)) {
332                         talloc_free(spnego_state->sub_sec_security);
333                         spnego_state->sub_sec_security = NULL;
334                         continue;
335                 }
336
337                 if ((i == 0) && (strcmp(all_sec[0].oid, mechType[0]) == 0)) {
338                         nt_status = gensec_update(spnego_state->sub_sec_security,
339                                                   out_mem_ctx, 
340                                                   unwrapped_in,
341                                                   unwrapped_out);
342                 } else {
343                         /* only get the helping start blob for the first OID */
344                         nt_status = gensec_update(spnego_state->sub_sec_security,
345                                                   out_mem_ctx, 
346                                                   null_data_blob, 
347                                                   unwrapped_out);
348                         /* it is likely that a NULL input token will
349                          * not be liked by most mechs, so just push us
350                          * along the merry-go-round again, and hope
351                          * for better luck next time */
352
353                         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
354                                 *unwrapped_out = data_blob(NULL, 0);
355                                 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
356                         }
357                 }
358                         
359                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) 
360                     && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
361                     && !NT_STATUS_IS_OK(nt_status)) {
362                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
363                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
364                         talloc_free(spnego_state->sub_sec_security);
365                         spnego_state->sub_sec_security = NULL;
366
367                         /* We started the mech correctly, and the
368                          * input from the other side was valid.
369                          * Return the error (say bad password, invalid
370                          * ticket) */
371                         return nt_status;
372
373                 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
374                         /* Pretend we never started it (lets the first run find some incompatible demand) */
375
376                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n", 
377                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
378                         talloc_free(spnego_state->sub_sec_security);
379                         spnego_state->sub_sec_security = NULL;
380                         continue;
381                 }
382
383                 spnego_state->neg_oid = all_sec[i].oid;
384
385                 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
386         }
387
388         DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
389         /* we could re-negotiate here, but it would only work
390          * if the client or server lied about what it could
391          * support the first time.  Lets keep this code to
392          * reality */
393
394         return NT_STATUS_INVALID_PARAMETER;
395 }
396
397 /** create a negTokenInit 
398  *
399  * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
400 */
401 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security, 
402                                                   struct spnego_state *spnego_state,
403                                                   TALLOC_CTX *out_mem_ctx, 
404                                                   const DATA_BLOB in, DATA_BLOB *out) 
405 {
406         int i;
407         NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
408         DATA_BLOB null_data_blob = data_blob(NULL,0);
409         const char **mechTypes = NULL;
410         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
411         const struct gensec_security_ops_wrapper *all_sec;
412
413         mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO);
414
415         all_sec = gensec_security_by_oid_list(out_mem_ctx, 
416                                               mechTypes,
417                                               GENSEC_OID_SPNEGO);
418         for (i=0; all_sec && all_sec[i].op; i++) {
419                 struct spnego_data spnego_out;
420                 nt_status = gensec_subcontext_start(spnego_state,
421                                                     gensec_security,
422                                                     &spnego_state->sub_sec_security);
423                 if (!NT_STATUS_IS_OK(nt_status)) {
424                         return nt_status;
425                 }
426                 /* select the sub context */
427                 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
428                                                      all_sec[i].op);
429                 if (!NT_STATUS_IS_OK(nt_status)) {
430                         talloc_free(spnego_state->sub_sec_security);
431                         spnego_state->sub_sec_security = NULL;
432                         continue;
433                 }
434
435                 /* In the client, try and produce the first (optimistic) packet */
436                 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
437                         nt_status = gensec_update(spnego_state->sub_sec_security,
438                                                   out_mem_ctx, 
439                                                   null_data_blob,
440                                                   &unwrapped_out);
441                         
442                         if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
443                             && !NT_STATUS_IS_OK(nt_status)) {
444                                 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n", 
445                                           spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
446                                 talloc_free(spnego_state->sub_sec_security);
447                                 spnego_state->sub_sec_security = NULL;
448                                 /* Pretend we never started it (lets the first run find some incompatible demand) */
449                                 
450                                 continue;
451                         }
452                 }
453
454                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
455                 
456                 /* List the remaining mechs as options */
457                 spnego_out.negTokenInit.mechTypes = gensec_security_oids_from_ops_wrapped(out_mem_ctx, 
458                                                                                           &all_sec[i]);
459                 spnego_out.negTokenInit.reqFlags = 0;
460                 
461                 if (spnego_state->state_position == SPNEGO_SERVER_START) {
462                         spnego_out.negTokenInit.mechListMIC
463                                 = data_blob_string_const(talloc_asprintf(out_mem_ctx, "%s$@%s", lp_netbios_name(), lp_realm()));
464                 } else {
465                         spnego_out.negTokenInit.mechListMIC = null_data_blob;
466                 }
467                 spnego_out.negTokenInit.mechToken = unwrapped_out;
468                 
469                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
470                         DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
471                                 return NT_STATUS_INVALID_PARAMETER;
472                 }
473                 
474                 /* set next state */
475                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
476                 
477                 if (NT_STATUS_IS_OK(nt_status)) {
478                         spnego_state->no_response_expected = True;
479                 }
480
481                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
482         } 
483         talloc_free(spnego_state->sub_sec_security);
484         spnego_state->sub_sec_security = NULL;
485
486         DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
487         return NT_STATUS_INVALID_PARAMETER;
488 }
489
490
491 /** create a client negTokenTarg 
492  *
493  * This is the case, where the client is the first one who sends data
494 */
495
496 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security, 
497                                                   struct spnego_state *spnego_state,
498                                                   TALLOC_CTX *out_mem_ctx, 
499                                                   NTSTATUS nt_status,
500                                                   const DATA_BLOB unwrapped_out, DATA_BLOB *out) 
501 {
502         struct spnego_data spnego_out;
503         DATA_BLOB null_data_blob = data_blob(NULL, 0);
504
505         /* compose reply */
506         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
507         spnego_out.negTokenTarg.responseToken = unwrapped_out;
508         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
509         spnego_out.negTokenTarg.supportedMech = NULL;
510
511         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {   
512                 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
513                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
514                 spnego_state->state_position = SPNEGO_SERVER_TARG;
515         } else if (NT_STATUS_IS_OK(nt_status)) {
516                 if (unwrapped_out.data) {
517                         spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
518                 }
519                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
520                 spnego_state->state_position = SPNEGO_DONE;
521         } else {
522                 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
523                 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
524                 spnego_state->state_position = SPNEGO_DONE;
525         }
526
527         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
528                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
529                 return NT_STATUS_INVALID_PARAMETER;
530         }
531
532         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
533
534         return nt_status;
535 }
536
537
538 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
539                                      const DATA_BLOB in, DATA_BLOB *out) 
540 {
541         struct spnego_state *spnego_state = gensec_security->private_data;
542         DATA_BLOB null_data_blob = data_blob(NULL, 0);
543         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
544         struct spnego_data spnego_out;
545         struct spnego_data spnego;
546
547         ssize_t len;
548
549         *out = data_blob(NULL, 0);
550
551         if (!out_mem_ctx) {
552                 out_mem_ctx = spnego_state;
553         }
554
555         /* and switch into the state machine */
556
557         switch (spnego_state->state_position) {
558         case SPNEGO_FALLBACK:
559                 return gensec_update(spnego_state->sub_sec_security,
560                                      out_mem_ctx, in, out);
561         case SPNEGO_SERVER_START:
562         {
563                 NTSTATUS nt_status;
564                 if (in.length) {
565
566                         len = spnego_read_data(in, &spnego);
567                         if (len == -1) {
568                                 return gensec_spnego_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out);
569                         }
570                         /* client sent NegTargetInit, we send NegTokenTarg */
571
572                         /* OK, so it's real SPNEGO, check the packet's the one we expect */
573                         if (spnego.type != spnego_state->expected_packet) {
574                                 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
575                                           spnego_state->expected_packet));
576                                 dump_data(1, in.data, in.length);
577                                 spnego_free_data(&spnego);
578                                 return NT_STATUS_INVALID_PARAMETER;
579                         }
580                         
581                         nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
582                                                                      spnego_state,
583                                                                      out_mem_ctx, 
584                                                                      spnego.negTokenInit.mechTypes,
585                                                                      spnego.negTokenInit.mechToken, 
586                                                                      &unwrapped_out);
587                         
588                         nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
589                                                                       spnego_state,
590                                                                       out_mem_ctx,
591                                                                       nt_status,
592                                                                       unwrapped_out, 
593                                                                       out);
594                         
595                         spnego_free_data(&spnego);
596                         
597                         return nt_status;
598                 } else {
599                         nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state, 
600                                                                  out_mem_ctx, in, out);
601                         spnego_state->state_position = SPNEGO_SERVER_TARG;
602                         return nt_status;
603                 }
604         }
605         
606         case SPNEGO_CLIENT_START:
607         {
608                 /* The server offers a list of mechanisms */
609                 
610                 const char *my_mechs[] = {NULL, NULL};
611                 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
612
613                 if (!in.length) {
614                         /* client to produce negTokenInit */
615                         nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state, 
616                                                                  out_mem_ctx, in, out);
617                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
618                         return nt_status;
619                 }
620                 
621                 len = spnego_read_data(in, &spnego);
622                 
623                 if (len == -1) {
624                         DEBUG(1, ("Invalid SPNEGO request:\n"));
625                         dump_data(1, in.data, in.length);
626                         return NT_STATUS_INVALID_PARAMETER;
627                 }
628                 
629                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
630                 if (spnego.type != spnego_state->expected_packet) {
631                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
632                                   spnego_state->expected_packet));
633                         dump_data(1, in.data, in.length);
634                         spnego_free_data(&spnego);
635                         return NT_STATUS_INVALID_PARAMETER;
636                 }
637
638                 if (spnego.negTokenInit.targetPrincipal) {
639                         DEBUG(5, ("Server claims it's principal name is %s (ignored)\n", spnego.negTokenInit.targetPrincipal));
640                 }
641
642                 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
643                                                              spnego_state,
644                                                              out_mem_ctx, 
645                                                              spnego.negTokenInit.mechTypes,
646                                                              spnego.negTokenInit.mechToken, 
647                                                              &unwrapped_out);
648
649                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
650                         spnego_free_data(&spnego);
651                         return nt_status;
652                 }
653
654                 my_mechs[0] = spnego_state->neg_oid;
655                 /* compose reply */
656                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
657                 spnego_out.negTokenInit.mechTypes = my_mechs;
658                 spnego_out.negTokenInit.reqFlags = 0;
659                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
660                 spnego_out.negTokenInit.mechToken = unwrapped_out;
661                 
662                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
663                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
664                                 return NT_STATUS_INVALID_PARAMETER;
665                 }
666                 
667                 /* set next state */
668                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
669                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
670
671                 if (NT_STATUS_IS_OK(nt_status)) {
672                         spnego_state->no_response_expected = True;
673                 }
674                 
675                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
676         }
677         case SPNEGO_SERVER_TARG:
678         {
679                 NTSTATUS nt_status;
680                 if (!in.length) {
681                         return NT_STATUS_INVALID_PARAMETER;
682                 }
683                 
684                 len = spnego_read_data(in, &spnego);
685                 
686                 if (len == -1) {
687                         DEBUG(1, ("Invalid SPNEGO request:\n"));
688                         dump_data(1, in.data, in.length);
689                         return NT_STATUS_INVALID_PARAMETER;
690                 }
691                 
692                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
693                 if (spnego.type != spnego_state->expected_packet) {
694                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
695                                   spnego_state->expected_packet));
696                         dump_data(1, in.data, in.length);
697                         spnego_free_data(&spnego);
698                         return NT_STATUS_INVALID_PARAMETER;
699                 }
700
701                 if (!spnego_state->sub_sec_security) {
702                         DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
703                         return NT_STATUS_INVALID_PARAMETER;
704                 }
705
706                 nt_status = gensec_update(spnego_state->sub_sec_security,
707                                           out_mem_ctx, 
708                                           spnego.negTokenTarg.responseToken,
709                                           &unwrapped_out);
710
711                 nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
712                                                               spnego_state,
713                                                               out_mem_ctx, 
714                                                               nt_status,
715                                                               unwrapped_out, 
716                                                               out);
717
718                 spnego_free_data(&spnego);
719                 
720                 return nt_status;
721         }
722         case SPNEGO_CLIENT_TARG:
723         {
724                 NTSTATUS nt_status;
725                 if (!in.length) {
726                         return NT_STATUS_INVALID_PARAMETER;
727                 }
728                 
729                 len = spnego_read_data(in, &spnego);
730                 
731                 if (len == -1) {
732                         DEBUG(1, ("Invalid SPNEGO request:\n"));
733                         dump_data(1, in.data, in.length);
734                         return NT_STATUS_INVALID_PARAMETER;
735                 }
736                 
737                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
738                 if (spnego.type != spnego_state->expected_packet) {
739                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
740                                   spnego_state->expected_packet));
741                         dump_data(1, in.data, in.length);
742                         spnego_free_data(&spnego);
743                         return NT_STATUS_INVALID_PARAMETER;
744                 }
745         
746                 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
747                         return NT_STATUS_ACCESS_DENIED;
748                 }
749
750                 if (spnego_state->no_response_expected) {
751                         if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
752                                 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
753                                 nt_status = NT_STATUS_INVALID_PARAMETER;
754                         } else if (spnego.negTokenTarg.responseToken.length) {
755                                 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
756                                 nt_status = NT_STATUS_INVALID_PARAMETER;
757                         } else {
758                                 nt_status = NT_STATUS_OK;
759                         }
760                 } else {
761                         nt_status = gensec_update(spnego_state->sub_sec_security,
762                                                   out_mem_ctx, 
763                                                   spnego.negTokenTarg.responseToken, 
764                                                   &unwrapped_out);
765
766                         if (NT_STATUS_IS_OK(nt_status)) {
767                                 spnego_state->no_response_expected = True;
768                         }
769                 } 
770                 
771                 spnego_free_data(&spnego);
772
773                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
774                         && !NT_STATUS_IS_OK(nt_status)) {
775                         DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
776                                   spnego_state->sub_sec_security->ops->name, 
777                                   nt_errstr(nt_status)));
778                         return nt_status;
779                 }
780
781                 if (unwrapped_out.length) {
782                         /* compose reply */
783                         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
784                         spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
785                         spnego_out.negTokenTarg.supportedMech = NULL;
786                         spnego_out.negTokenTarg.responseToken = unwrapped_out;
787                         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
788                         
789                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
790                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
791                                 return NT_STATUS_INVALID_PARAMETER;
792                         }
793                 
794                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
795                         nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
796                 } else {
797
798                         /* all done - server has accepted, and we agree */
799                         *out = null_data_blob;
800
801                         if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
802                                 /* unless of course it did not accept */
803                                 DEBUG(1,("gensec_update ok but not accepted\n"));
804                                 nt_status = NT_STATUS_INVALID_PARAMETER;
805                         }
806                 }
807                 
808                 spnego_state->state_position = SPNEGO_DONE;
809
810                 return nt_status;
811         }
812         case SPNEGO_DONE:
813                 return NT_STATUS_OK;
814         }
815         return NT_STATUS_INVALID_PARAMETER;
816 }
817
818 static BOOL gensec_spnego_have_feature(struct gensec_security *gensec_security,
819                                        uint32_t feature) 
820 {
821         struct spnego_state *spnego_state = gensec_security->private_data;
822         if (!spnego_state->sub_sec_security) {
823                 return False;
824         }
825         
826         return gensec_have_feature(spnego_state->sub_sec_security, 
827                                    feature);
828 }
829
830 static const char *gensec_spnego_oids[] = { 
831         GENSEC_OID_SPNEGO,
832         NULL 
833 };
834
835 static const struct gensec_security_ops gensec_spnego_security_ops = {
836         .name           = "spnego",
837         .sasl_name      = "GSS-SPNEGO",
838         .auth_type      = DCERPC_AUTH_TYPE_SPNEGO,
839         .oid            = gensec_spnego_oids,
840         .client_start   = gensec_spnego_client_start,
841         .server_start   = gensec_spnego_server_start,
842         .update         = gensec_spnego_update,
843         .seal_packet    = gensec_spnego_seal_packet,
844         .sign_packet    = gensec_spnego_sign_packet,
845         .sig_size       = gensec_spnego_sig_size,
846         .check_packet   = gensec_spnego_check_packet,
847         .unseal_packet  = gensec_spnego_unseal_packet,
848         .wrap           = gensec_spnego_wrap,
849         .unwrap         = gensec_spnego_unwrap,
850         .session_key    = gensec_spnego_session_key,
851         .session_info   = gensec_spnego_session_info,
852         .have_feature   = gensec_spnego_have_feature,
853         .enabled        = True
854 };
855
856 NTSTATUS gensec_spnego_init(void)
857 {
858         NTSTATUS ret;
859         ret = gensec_register(&gensec_spnego_security_ops);
860         if (!NT_STATUS_IS_OK(ret)) {
861                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
862                         gensec_spnego_security_ops.name));
863                 return ret;
864         }
865
866         return ret;
867 }