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