r13903: Don't generate prototypes for modules and binaries in include/proto.h by
[samba.git] / source4 / 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 #include "auth/gensec/spnego_proto.h"
28
29 enum spnego_state_position {
30         SPNEGO_SERVER_START,
31         SPNEGO_CLIENT_START,
32         SPNEGO_SERVER_TARG,
33         SPNEGO_CLIENT_TARG,
34         SPNEGO_FALLBACK,
35         SPNEGO_DONE
36 };
37
38 struct spnego_state {
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, size_t data_size) 
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, data_size);
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         struct gensec_security_ops **all_ops
251                 = gensec_security_mechs(gensec_security, out_mem_ctx);
252         for (i=0; all_ops[i]; 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(gensec_security, 
319                                               out_mem_ctx, 
320                                               mechType,
321                                               GENSEC_OID_SPNEGO);
322         if (spnego_state->state_position == SPNEGO_SERVER_START) {
323                 for (i=0; all_sec && all_sec[i].op; i++) {
324                         /* optomisitic token */
325                         if (strcmp(all_sec[i].oid, mechType[0]) == 0) {
326                                 nt_status = gensec_subcontext_start(spnego_state,
327                                                                     gensec_security,
328                                                                     &spnego_state->sub_sec_security);
329                                 if (!NT_STATUS_IS_OK(nt_status)) {
330                                         return nt_status;
331                                 }
332                                 /* select the sub context */
333                                 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
334                                                                      all_sec[i].op);
335                                 if (!NT_STATUS_IS_OK(nt_status)) {
336                                         talloc_free(spnego_state->sub_sec_security);
337                                         spnego_state->sub_sec_security = NULL;
338                                         break;
339                                 }
340                                 
341                                 nt_status = gensec_update(spnego_state->sub_sec_security,
342                                                           out_mem_ctx, 
343                                                           unwrapped_in,
344                                                           unwrapped_out);
345                                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) || 
346                                     NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
347                                         /* Pretend we never started it (lets the first run find some incompatible demand) */
348                                         
349                                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n", 
350                                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
351                                         talloc_free(spnego_state->sub_sec_security);
352                                         spnego_state->sub_sec_security = NULL;
353                                         break;
354                                 }
355
356                                 spnego_state->neg_oid = all_sec[i].oid;
357                                 break;
358                         }
359                 }
360         }
361         
362         /* Having tried any optomisitc token from the client (if we
363          * were the server), if we didn't get anywhere, walk our list
364          * in our preference order */
365         
366         if (!spnego_state->sub_sec_security) {
367                 for (i=0; all_sec && all_sec[i].op; i++) {
368                         nt_status = gensec_subcontext_start(spnego_state,
369                                                             gensec_security,
370                                                             &spnego_state->sub_sec_security);
371                         if (!NT_STATUS_IS_OK(nt_status)) {
372                                 return nt_status;
373                         }
374                         /* select the sub context */
375                         nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
376                                                              all_sec[i].op);
377                         if (!NT_STATUS_IS_OK(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                         /* only get the helping start blob for the first OID */
386                         nt_status = gensec_update(spnego_state->sub_sec_security,
387                                                   out_mem_ctx, 
388                                                   null_data_blob, 
389                                                   unwrapped_out);
390
391                         /* it is likely that a NULL input token will
392                          * not be liked by most server mechs, but if
393                          * we are in the client, we want the first
394                          * update packet to be able to abort the use
395                          * of this mech */
396                         if (spnego_state->state_position != SPNEGO_SERVER_START) {
397                                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) || 
398                                     NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
399                                         /* Pretend we never started it (lets the first run find some incompatible demand) */
400                                         
401                                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n", 
402                                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
403                                         talloc_free(spnego_state->sub_sec_security);
404                                         spnego_state->sub_sec_security = NULL;
405                                         continue;
406                                 }
407                         }
408
409                         break;
410                 }
411         }
412
413         if (spnego_state->sub_sec_security) {
414                 /* it is likely that a NULL input token will
415                  * not be liked by most server mechs, but this
416                  * does the right thing in the CIFS client.
417                  * just push us along the merry-go-round
418                  * again, and hope for better luck next
419                  * time */
420                 
421                 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
422                         *unwrapped_out = data_blob(NULL, 0);
423                         nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
424                 }
425                 
426                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) 
427                     && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
428                     && !NT_STATUS_IS_OK(nt_status)) {
429                         DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
430                                   spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
431                         talloc_free(spnego_state->sub_sec_security);
432                         spnego_state->sub_sec_security = NULL;
433                         
434                         /* We started the mech correctly, and the
435                          * input from the other side was valid.
436                          * Return the error (say bad password, invalid
437                          * ticket) */
438                         return nt_status;
439                 }
440         
441                 
442                 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
443         }
444
445         DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
446         /* we could re-negotiate here, but it would only work
447          * if the client or server lied about what it could
448          * support the first time.  Lets keep this code to
449          * reality */
450
451         return NT_STATUS_INVALID_PARAMETER;
452 }
453
454 /** create a negTokenInit 
455  *
456  * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
457 */
458 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security, 
459                                                   struct spnego_state *spnego_state,
460                                                   TALLOC_CTX *out_mem_ctx, 
461                                                   const DATA_BLOB in, DATA_BLOB *out) 
462 {
463         int i;
464         NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
465         DATA_BLOB null_data_blob = data_blob(NULL,0);
466         const char **mechTypes = NULL;
467         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
468         const struct gensec_security_ops_wrapper *all_sec;
469         const char *principal = NULL;
470
471         mechTypes = gensec_security_oids(gensec_security, 
472                                          out_mem_ctx, GENSEC_OID_SPNEGO);
473
474         all_sec = gensec_security_by_oid_list(gensec_security, 
475                                               out_mem_ctx, 
476                                               mechTypes,
477                                               GENSEC_OID_SPNEGO);
478         for (i=0; all_sec && all_sec[i].op; i++) {
479                 struct spnego_data spnego_out;
480                 nt_status = gensec_subcontext_start(spnego_state,
481                                                     gensec_security,
482                                                     &spnego_state->sub_sec_security);
483                 if (!NT_STATUS_IS_OK(nt_status)) {
484                         return nt_status;
485                 }
486                 /* select the sub context */
487                 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
488                                                      all_sec[i].op);
489                 if (!NT_STATUS_IS_OK(nt_status)) {
490                         talloc_free(spnego_state->sub_sec_security);
491                         spnego_state->sub_sec_security = NULL;
492                         continue;
493                 }
494
495                 /* In the client, try and produce the first (optimistic) packet */
496                 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
497                         nt_status = gensec_update(spnego_state->sub_sec_security,
498                                                   out_mem_ctx, 
499                                                   null_data_blob,
500                                                   &unwrapped_out);
501                         
502                         if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
503                             && !NT_STATUS_IS_OK(nt_status)) {
504                                 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n", 
505                                           spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
506                                 talloc_free(spnego_state->sub_sec_security);
507                                 spnego_state->sub_sec_security = NULL;
508                                 /* Pretend we never started it (lets the first run find some incompatible demand) */
509                                 
510                                 continue;
511                         }
512                 }
513
514                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
515                 
516                 /* List the remaining mechs as options */
517                 spnego_out.negTokenInit.mechTypes = gensec_security_oids_from_ops_wrapped(out_mem_ctx, 
518                                                                                           &all_sec[i]);
519                 spnego_out.negTokenInit.reqFlags = 0;
520                 
521                 if (spnego_state->state_position == SPNEGO_SERVER_START) {
522                         /* server credentails */
523                         struct cli_credentials *creds = gensec_get_credentials(gensec_security);
524                         if (creds) {
525                                 principal = cli_credentials_get_principal(creds, out_mem_ctx);
526                         }
527                 }
528                 if (principal) {
529                         spnego_out.negTokenInit.mechListMIC
530                                 = data_blob_string_const(principal);
531                 } else {
532                         spnego_out.negTokenInit.mechListMIC = null_data_blob;
533                 }
534
535                 spnego_out.negTokenInit.mechToken = unwrapped_out;
536                 
537                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
538                         DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
539                                 return NT_STATUS_INVALID_PARAMETER;
540                 }
541                 
542                 /* set next state */
543                 spnego_state->neg_oid = all_sec[i].oid;
544                 
545                 if (NT_STATUS_IS_OK(nt_status)) {
546                         spnego_state->no_response_expected = True;
547                 }
548
549                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
550         } 
551         talloc_free(spnego_state->sub_sec_security);
552         spnego_state->sub_sec_security = NULL;
553
554         DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
555         return NT_STATUS_INVALID_PARAMETER;
556 }
557
558
559 /** create a server negTokenTarg 
560  *
561  * This is the case, where the client is the first one who sends data
562 */
563
564 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security, 
565                                                   struct spnego_state *spnego_state,
566                                                   TALLOC_CTX *out_mem_ctx, 
567                                                   NTSTATUS nt_status,
568                                                   const DATA_BLOB unwrapped_out, DATA_BLOB *out) 
569 {
570         struct spnego_data spnego_out;
571         DATA_BLOB null_data_blob = data_blob(NULL, 0);
572
573         /* compose reply */
574         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
575         spnego_out.negTokenTarg.responseToken = unwrapped_out;
576         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
577         spnego_out.negTokenTarg.supportedMech = NULL;
578
579         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {   
580                 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
581                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
582                 spnego_state->state_position = SPNEGO_SERVER_TARG;
583         } else if (NT_STATUS_IS_OK(nt_status)) {
584                 if (unwrapped_out.data) {
585                         spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
586                 }
587                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
588                 spnego_state->state_position = SPNEGO_DONE;
589         } else {
590                 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
591                 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
592                 spnego_state->state_position = SPNEGO_DONE;
593         }
594
595         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
596                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
597                 return NT_STATUS_INVALID_PARAMETER;
598         }
599
600         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
601
602         return nt_status;
603 }
604
605
606 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
607                                      const DATA_BLOB in, DATA_BLOB *out) 
608 {
609         struct spnego_state *spnego_state = gensec_security->private_data;
610         DATA_BLOB null_data_blob = data_blob(NULL, 0);
611         DATA_BLOB unwrapped_out = data_blob(NULL, 0);
612         struct spnego_data spnego_out;
613         struct spnego_data spnego;
614
615         ssize_t len;
616
617         *out = data_blob(NULL, 0);
618
619         if (!out_mem_ctx) {
620                 out_mem_ctx = spnego_state;
621         }
622
623         /* and switch into the state machine */
624
625         switch (spnego_state->state_position) {
626         case SPNEGO_FALLBACK:
627                 return gensec_update(spnego_state->sub_sec_security,
628                                      out_mem_ctx, in, out);
629         case SPNEGO_SERVER_START:
630         {
631                 NTSTATUS nt_status;
632                 if (in.length) {
633
634                         len = spnego_read_data(in, &spnego);
635                         if (len == -1) {
636                                 return gensec_spnego_server_try_fallback(gensec_security, spnego_state, 
637                                                                          out_mem_ctx, in, out);
638                         }
639                         /* client sent NegTargetInit, we send NegTokenTarg */
640
641                         /* OK, so it's real SPNEGO, check the packet's the one we expect */
642                         if (spnego.type != spnego_state->expected_packet) {
643                                 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
644                                           spnego_state->expected_packet));
645                                 dump_data(1, in.data, in.length);
646                                 spnego_free_data(&spnego);
647                                 return NT_STATUS_INVALID_PARAMETER;
648                         }
649                         
650                         nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
651                                                                      spnego_state,
652                                                                      out_mem_ctx, 
653                                                                      spnego.negTokenInit.mechTypes,
654                                                                      spnego.negTokenInit.mechToken, 
655                                                                      &unwrapped_out);
656                         
657                         nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
658                                                                       spnego_state,
659                                                                       out_mem_ctx,
660                                                                       nt_status,
661                                                                       unwrapped_out, 
662                                                                       out);
663                         
664                         spnego_free_data(&spnego);
665                         
666                         return nt_status;
667                 } else {
668                         nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state, 
669                                                                       out_mem_ctx, in, out);
670                         spnego_state->state_position = SPNEGO_SERVER_START;
671                         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
672                         return nt_status;
673                 }
674         }
675         
676         case SPNEGO_CLIENT_START:
677         {
678                 /* The server offers a list of mechanisms */
679                 
680                 const char *my_mechs[] = {NULL, NULL};
681                 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
682
683                 if (!in.length) {
684                         /* client to produce negTokenInit */
685                         nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state, 
686                                                                  out_mem_ctx, in, out);
687                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
688                         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
689                         return nt_status;
690                 }
691                 
692                 len = spnego_read_data(in, &spnego);
693                 
694                 if (len == -1) {
695                         DEBUG(1, ("Invalid SPNEGO request:\n"));
696                         dump_data(1, in.data, in.length);
697                         return NT_STATUS_INVALID_PARAMETER;
698                 }
699                 
700                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
701                 if (spnego.type != spnego_state->expected_packet) {
702                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
703                                   spnego_state->expected_packet));
704                         dump_data(1, in.data, in.length);
705                         spnego_free_data(&spnego);
706                         return NT_STATUS_INVALID_PARAMETER;
707                 }
708
709                 if (spnego.negTokenInit.targetPrincipal) {
710                         DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
711                         gensec_set_target_principal(gensec_security, spnego.negTokenInit.targetPrincipal);
712                 }
713
714                 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
715                                                              spnego_state,
716                                                              out_mem_ctx, 
717                                                              spnego.negTokenInit.mechTypes,
718                                                              spnego.negTokenInit.mechToken, 
719                                                              &unwrapped_out);
720
721                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
722                         spnego_free_data(&spnego);
723                         return nt_status;
724                 }
725
726                 my_mechs[0] = spnego_state->neg_oid;
727                 /* compose reply */
728                 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
729                 spnego_out.negTokenInit.mechTypes = my_mechs;
730                 spnego_out.negTokenInit.reqFlags = 0;
731                 spnego_out.negTokenInit.mechListMIC = null_data_blob;
732                 spnego_out.negTokenInit.mechToken = unwrapped_out;
733                 
734                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
735                         DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
736                                 return NT_STATUS_INVALID_PARAMETER;
737                 }
738                 
739                 /* set next state */
740                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
741                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
742
743                 if (NT_STATUS_IS_OK(nt_status)) {
744                         spnego_state->no_response_expected = True;
745                 }
746                 
747                 spnego_free_data(&spnego);
748                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
749         }
750         case SPNEGO_SERVER_TARG:
751         {
752                 NTSTATUS nt_status;
753                 if (!in.length) {
754                         return NT_STATUS_INVALID_PARAMETER;
755                 }
756                 
757                 len = spnego_read_data(in, &spnego);
758                 
759                 if (len == -1) {
760                         DEBUG(1, ("Invalid SPNEGO request:\n"));
761                         dump_data(1, in.data, in.length);
762                         return NT_STATUS_INVALID_PARAMETER;
763                 }
764                 
765                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
766                 if (spnego.type != spnego_state->expected_packet) {
767                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
768                                   spnego_state->expected_packet));
769                         dump_data(1, in.data, in.length);
770                         spnego_free_data(&spnego);
771                         return NT_STATUS_INVALID_PARAMETER;
772                 }
773
774                 if (!spnego_state->sub_sec_security) {
775                         DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
776                         spnego_free_data(&spnego);
777                         return NT_STATUS_INVALID_PARAMETER;
778                 }
779
780                 nt_status = gensec_update(spnego_state->sub_sec_security,
781                                           out_mem_ctx, 
782                                           spnego.negTokenTarg.responseToken,
783                                           &unwrapped_out);
784
785                 nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
786                                                               spnego_state,
787                                                               out_mem_ctx, 
788                                                               nt_status,
789                                                               unwrapped_out, 
790                                                               out);
791                 
792                 spnego_free_data(&spnego);
793                 
794                 return nt_status;
795         }
796         case SPNEGO_CLIENT_TARG:
797         {
798                 NTSTATUS nt_status;
799                 if (!in.length) {
800                         return NT_STATUS_INVALID_PARAMETER;
801                 }
802                 
803                 len = spnego_read_data(in, &spnego);
804                 
805                 if (len == -1) {
806                         DEBUG(1, ("Invalid SPNEGO request:\n"));
807                         dump_data(1, in.data, in.length);
808                         return NT_STATUS_INVALID_PARAMETER;
809                 }
810                 
811                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
812                 if (spnego.type != spnego_state->expected_packet) {
813                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, 
814                                   spnego_state->expected_packet));
815                         dump_data(1, in.data, in.length);
816                         spnego_free_data(&spnego);
817                         return NT_STATUS_INVALID_PARAMETER;
818                 }
819         
820                 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
821                         spnego_free_data(&spnego);
822                         return NT_STATUS_ACCESS_DENIED;
823                 }
824
825                 /* Server didn't like our choice of mech, and chose something else */
826                 if ((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) &&
827                     strcmp(spnego.negTokenTarg.supportedMech, spnego_state->neg_oid) != 0) {
828                         DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
829                                  gensec_get_name_by_oid(spnego.negTokenTarg.supportedMech), 
830                                  gensec_get_name_by_oid(spnego_state->neg_oid)));
831                         
832                         talloc_free(spnego_state->sub_sec_security);
833                         nt_status = gensec_subcontext_start(spnego_state,
834                                                             gensec_security,
835                                                             &spnego_state->sub_sec_security);
836                         if (!NT_STATUS_IS_OK(nt_status)) {
837                                 spnego_free_data(&spnego);
838                                 return nt_status;
839                         }
840                         /* select the sub context */
841                         nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
842                                                              spnego.negTokenTarg.supportedMech);
843                         if (!NT_STATUS_IS_OK(nt_status)) {
844                                 spnego_free_data(&spnego);
845                                 return nt_status;
846                         }
847
848                         nt_status = gensec_update(spnego_state->sub_sec_security,
849                                                   out_mem_ctx, 
850                                                   spnego.negTokenTarg.responseToken,
851                                                   &unwrapped_out);
852                         spnego_state->neg_oid = talloc_strdup(spnego_state, spnego.negTokenTarg.supportedMech);
853                 } else if (spnego_state->no_response_expected) {
854                         if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
855                                 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
856                                 nt_status = NT_STATUS_INVALID_PARAMETER;
857                         } else if (spnego.negTokenTarg.responseToken.length) {
858                                 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
859                                 nt_status = NT_STATUS_INVALID_PARAMETER;
860                         } else {
861                                 nt_status = NT_STATUS_OK;
862                         }
863                 } else {
864                         nt_status = gensec_update(spnego_state->sub_sec_security,
865                                                   out_mem_ctx, 
866                                                   spnego.negTokenTarg.responseToken, 
867                                                   &unwrapped_out);
868
869                         if (NT_STATUS_IS_OK(nt_status)) {
870                                 spnego_state->no_response_expected = True;
871                         }
872                 } 
873                 
874                 spnego_free_data(&spnego);
875
876                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
877                         && !NT_STATUS_IS_OK(nt_status)) {
878                         DEBUG(1, ("SPNEGO(%s) login failed: %s\n", 
879                                   spnego_state->sub_sec_security->ops->name, 
880                                   nt_errstr(nt_status)));
881                         return nt_status;
882                 }
883
884                 if (unwrapped_out.length) {
885                         /* compose reply */
886                         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
887                         spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
888                         spnego_out.negTokenTarg.supportedMech = NULL;
889                         spnego_out.negTokenTarg.responseToken = unwrapped_out;
890                         spnego_out.negTokenTarg.mechListMIC = null_data_blob;
891                         
892                         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
893                                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
894                                 return NT_STATUS_INVALID_PARAMETER;
895                         }
896                 
897                         spnego_state->state_position = SPNEGO_CLIENT_TARG;
898                         nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
899                 } else {
900
901                         /* all done - server has accepted, and we agree */
902                         *out = null_data_blob;
903
904                         if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
905                                 /* unless of course it did not accept */
906                                 DEBUG(1,("gensec_update ok but not accepted\n"));
907                                 nt_status = NT_STATUS_INVALID_PARAMETER;
908                         }
909                 
910                         spnego_state->state_position = SPNEGO_DONE;
911                 }
912
913                 return nt_status;
914         }
915         case SPNEGO_DONE:
916                 /* We should not be called after we are 'done' */
917                 return NT_STATUS_INVALID_PARAMETER;
918         }
919         return NT_STATUS_INVALID_PARAMETER;
920 }
921
922 static BOOL gensec_spnego_have_feature(struct gensec_security *gensec_security,
923                                        uint32_t feature) 
924 {
925         struct spnego_state *spnego_state = gensec_security->private_data;
926         if (!spnego_state->sub_sec_security) {
927                 return False;
928         }
929         
930         return gensec_have_feature(spnego_state->sub_sec_security, 
931                                    feature);
932 }
933
934 static const char *gensec_spnego_oids[] = { 
935         GENSEC_OID_SPNEGO,
936         NULL 
937 };
938
939 static const struct gensec_security_ops gensec_spnego_security_ops = {
940         .name           = "spnego",
941         .sasl_name      = "GSS-SPNEGO",
942         .auth_type      = DCERPC_AUTH_TYPE_SPNEGO,
943         .oid            = gensec_spnego_oids,
944         .client_start   = gensec_spnego_client_start,
945         .server_start   = gensec_spnego_server_start,
946         .update         = gensec_spnego_update,
947         .seal_packet    = gensec_spnego_seal_packet,
948         .sign_packet    = gensec_spnego_sign_packet,
949         .sig_size       = gensec_spnego_sig_size,
950         .check_packet   = gensec_spnego_check_packet,
951         .unseal_packet  = gensec_spnego_unseal_packet,
952         .wrap           = gensec_spnego_wrap,
953         .unwrap         = gensec_spnego_unwrap,
954         .session_key    = gensec_spnego_session_key,
955         .session_info   = gensec_spnego_session_info,
956         .have_feature   = gensec_spnego_have_feature,
957         .enabled        = True,
958 };
959
960 NTSTATUS gensec_spnego_init(void)
961 {
962         NTSTATUS ret;
963         ret = gensec_register(&gensec_spnego_security_ops);
964         if (!NT_STATUS_IS_OK(ret)) {
965                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
966                         gensec_spnego_security_ops.name));
967                 return ret;
968         }
969
970         return ret;
971 }