gssapi: remove unused function argument
[metze/samba/wip.git] / source3 / librpc / crypto / gse.c
1 /*
2  *  GSSAPI Security Extensions
3  *  RPC Pipe client and server routines
4  *  Copyright (C) Simo Sorce 2010.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* We support only GSSAPI/KRB5 here */
21
22 #include "includes.h"
23 #include "gse.h"
24
25 #if defined(HAVE_KRB5) && defined(HAVE_GSSAPI_GSSAPI_EXT_H) && defined(HAVE_GSS_WRAP_IOV)
26
27 #include "smb_krb5.h"
28 #include "gse_krb5.h"
29
30 #include <gssapi/gssapi.h>
31 #include <gssapi/gssapi_krb5.h>
32 #include <gssapi/gssapi_ext.h>
33
34 #ifndef GSS_KRB5_INQ_SSPI_SESSION_KEY_OID
35 #define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11
36 #define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05"
37 #endif
38
39 gss_OID_desc gse_sesskey_inq_oid = {
40         GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH,
41         (void *)GSS_KRB5_INQ_SSPI_SESSION_KEY_OID
42 };
43
44 #ifndef GSS_KRB5_SESSION_KEY_ENCTYPE_OID
45 #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10
46 #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID  "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04"
47 #endif
48
49 gss_OID_desc gse_sesskeytype_oid = {
50         GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH,
51         (void *)GSS_KRB5_SESSION_KEY_ENCTYPE_OID
52 };
53
54 #define GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID_LENGTH 12
55 /*                                          EXTRACTION OID                                 AUTHZ ID */
56 #define GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a" "\x01"
57
58 gss_OID_desc gse_authz_data_oid = {
59         GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID_LENGTH,
60         (void *)GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID
61 };
62
63 #ifndef GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID
64 #define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH 11
65 #define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0c"
66 #endif
67
68 gss_OID_desc gse_authtime_oid = {
69         GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH,
70         (void *)GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID
71 };
72
73 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min);
74
75 struct gse_context {
76         krb5_context k5ctx;
77         krb5_ccache ccache;
78         krb5_keytab keytab;
79
80         gss_ctx_id_t gss_ctx;
81
82         gss_OID_desc gss_mech;
83         OM_uint32 gss_c_flags;
84         gss_cred_id_t creds;
85         gss_name_t server_name;
86
87         gss_OID ret_mech;
88         OM_uint32 ret_flags;
89         gss_cred_id_t delegated_creds;
90         gss_name_t client_name;
91
92         bool more_processing;
93         bool authenticated;
94 };
95
96 /* free non talloc dependent contexts */
97 static int gse_context_destructor(void *ptr)
98 {
99         struct gse_context *gse_ctx;
100         OM_uint32 gss_min, gss_maj;
101
102         gse_ctx = talloc_get_type_abort(ptr, struct gse_context);
103         if (gse_ctx->k5ctx) {
104                 if (gse_ctx->ccache) {
105                         krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache);
106                         gse_ctx->ccache = NULL;
107                 }
108                 if (gse_ctx->keytab) {
109                         krb5_kt_close(gse_ctx->k5ctx, gse_ctx->keytab);
110                         gse_ctx->keytab = NULL;
111                 }
112                 krb5_free_context(gse_ctx->k5ctx);
113                 gse_ctx->k5ctx = NULL;
114         }
115         if (gse_ctx->gss_ctx != GSS_C_NO_CONTEXT) {
116                 gss_maj = gss_delete_sec_context(&gss_min,
117                                                  &gse_ctx->gss_ctx,
118                                                  GSS_C_NO_BUFFER);
119         }
120         if (gse_ctx->server_name) {
121                 gss_maj = gss_release_name(&gss_min,
122                                            &gse_ctx->server_name);
123         }
124         if (gse_ctx->client_name) {
125                 gss_maj = gss_release_name(&gss_min,
126                                            &gse_ctx->client_name);
127         }
128         if (gse_ctx->creds) {
129                 gss_maj = gss_release_cred(&gss_min,
130                                            &gse_ctx->creds);
131         }
132         if (gse_ctx->delegated_creds) {
133                 gss_maj = gss_release_cred(&gss_min,
134                                            &gse_ctx->delegated_creds);
135         }
136         if (gse_ctx->ret_mech) {
137                 gss_maj = gss_release_oid(&gss_min,
138                                           &gse_ctx->ret_mech);
139         }
140         return 0;
141 }
142
143 static NTSTATUS gse_context_init(TALLOC_CTX *mem_ctx,
144                                  bool do_sign, bool do_seal,
145                                  const char *ccache_name,
146                                  uint32_t add_gss_c_flags,
147                                  struct gse_context **_gse_ctx)
148 {
149         struct gse_context *gse_ctx;
150         krb5_error_code k5ret;
151         NTSTATUS status;
152
153         gse_ctx = talloc_zero(mem_ctx, struct gse_context);
154         if (!gse_ctx) {
155                 return NT_STATUS_NO_MEMORY;
156         }
157         talloc_set_destructor((TALLOC_CTX *)gse_ctx, gse_context_destructor);
158
159         memcpy(&gse_ctx->gss_mech, gss_mech_krb5, sizeof(gss_OID_desc));
160
161         gse_ctx->gss_c_flags = GSS_C_MUTUAL_FLAG |
162                                 GSS_C_DELEG_FLAG |
163                                 GSS_C_DELEG_POLICY_FLAG |
164                                 GSS_C_REPLAY_FLAG |
165                                 GSS_C_SEQUENCE_FLAG;
166         if (do_sign) {
167                 gse_ctx->gss_c_flags |= GSS_C_INTEG_FLAG;
168         }
169         if (do_seal) {
170                 gse_ctx->gss_c_flags |= GSS_C_CONF_FLAG;
171         }
172
173         gse_ctx->gss_c_flags |= add_gss_c_flags;
174
175         /* Initialize Kerberos Context */
176         initialize_krb5_error_table();
177
178         k5ret = krb5_init_context(&gse_ctx->k5ctx);
179         if (k5ret) {
180                 DEBUG(0, ("Failed to initialize kerberos context! (%s)\n",
181                           error_message(k5ret)));
182                 status = NT_STATUS_INTERNAL_ERROR;
183                 goto err_out;
184         }
185
186         if (!ccache_name) {
187                 ccache_name = krb5_cc_default_name(gse_ctx->k5ctx);
188         }
189         k5ret = krb5_cc_resolve(gse_ctx->k5ctx, ccache_name,
190                                 &gse_ctx->ccache);
191         if (k5ret) {
192                 DEBUG(1, ("Failed to resolve credential cache! (%s)\n",
193                           error_message(k5ret)));
194                 status = NT_STATUS_INTERNAL_ERROR;
195                 goto err_out;
196         }
197
198         /* TODO: Should we enforce a enc_types list ?
199         ret = krb5_set_default_tgs_ktypes(gse_ctx->k5ctx, enc_types);
200         */
201
202         *_gse_ctx = gse_ctx;
203         return NT_STATUS_OK;
204
205 err_out:
206         TALLOC_FREE(gse_ctx);
207         return status;
208 }
209
210 NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
211                           bool do_sign, bool do_seal,
212                           const char *ccache_name,
213                           const char *server,
214                           const char *service,
215                           const char *username,
216                           const char *password,
217                           uint32_t add_gss_c_flags,
218                           struct gse_context **_gse_ctx)
219 {
220         struct gse_context *gse_ctx;
221         OM_uint32 gss_maj, gss_min;
222         gss_buffer_desc name_buffer = {0, NULL};
223         gss_OID_set_desc mech_set;
224         NTSTATUS status;
225
226         if (!server || !service) {
227                 return NT_STATUS_INVALID_PARAMETER;
228         }
229
230         status = gse_context_init(mem_ctx, do_sign, do_seal,
231                                   ccache_name, add_gss_c_flags,
232                                   &gse_ctx);
233         if (!NT_STATUS_IS_OK(status)) {
234                 return NT_STATUS_NO_MEMORY;
235         }
236
237         name_buffer.value = talloc_asprintf(gse_ctx,
238                                             "%s@%s", service, server);
239         if (!name_buffer.value) {
240                 status = NT_STATUS_NO_MEMORY;
241                 goto err_out;
242         }
243         name_buffer.length = strlen((char *)name_buffer.value);
244         gss_maj = gss_import_name(&gss_min, &name_buffer,
245                                   GSS_C_NT_HOSTBASED_SERVICE,
246                                   &gse_ctx->server_name);
247         if (gss_maj) {
248                 DEBUG(0, ("gss_import_name failed for %s, with [%s]\n",
249                           (char *)name_buffer.value,
250                           gse_errstr(gse_ctx, gss_maj, gss_min)));
251                 status = NT_STATUS_INTERNAL_ERROR;
252                 goto err_out;
253         }
254
255         /* TODO: get krb5 ticket using username/password, if no valid
256          * one already available in ccache */
257
258         mech_set.count = 1;
259         mech_set.elements = &gse_ctx->gss_mech;
260
261         gss_maj = gss_acquire_cred(&gss_min,
262                                    GSS_C_NO_NAME,
263                                    GSS_C_INDEFINITE,
264                                    &mech_set,
265                                    GSS_C_INITIATE,
266                                    &gse_ctx->creds,
267                                    NULL, NULL);
268         if (gss_maj) {
269                 DEBUG(0, ("gss_acquire_creds failed for %s, with [%s]\n",
270                           (char *)name_buffer.value,
271                           gse_errstr(gse_ctx, gss_maj, gss_min)));
272                 status = NT_STATUS_INTERNAL_ERROR;
273                 goto err_out;
274         }
275
276         *_gse_ctx = gse_ctx;
277         TALLOC_FREE(name_buffer.value);
278         return NT_STATUS_OK;
279
280 err_out:
281         TALLOC_FREE(name_buffer.value);
282         TALLOC_FREE(gse_ctx);
283         return status;
284 }
285
286 NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
287                                    struct gse_context *gse_ctx,
288                                    DATA_BLOB *token_in,
289                                    DATA_BLOB *token_out)
290 {
291         OM_uint32 gss_maj, gss_min;
292         gss_buffer_desc in_data;
293         gss_buffer_desc out_data;
294         DATA_BLOB blob = data_blob_null;
295         NTSTATUS status;
296
297         in_data.value = token_in->data;
298         in_data.length = token_in->length;
299
300         gss_maj = gss_init_sec_context(&gss_min,
301                                         gse_ctx->creds,
302                                         &gse_ctx->gss_ctx,
303                                         gse_ctx->server_name,
304                                         &gse_ctx->gss_mech,
305                                         gse_ctx->gss_c_flags,
306                                         0, GSS_C_NO_CHANNEL_BINDINGS,
307                                         &in_data, NULL, &out_data,
308                                         NULL, NULL);
309         switch (gss_maj) {
310         case GSS_S_COMPLETE:
311                 /* we are done with it */
312                 gse_ctx->more_processing = false;
313                 status = NT_STATUS_OK;
314                 break;
315         case GSS_S_CONTINUE_NEEDED:
316                 /* we will need a third leg */
317                 gse_ctx->more_processing = true;
318                 /* status = NT_STATUS_MORE_PROCESSING_REQUIRED; */
319                 status = NT_STATUS_OK;
320                 break;
321         default:
322                 DEBUG(0, ("gss_init_sec_context failed with [%s]\n",
323                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
324                 status = NT_STATUS_INTERNAL_ERROR;
325                 goto done;
326         }
327
328         blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
329         if (!blob.data) {
330                 status = NT_STATUS_NO_MEMORY;
331         }
332
333         gss_maj = gss_release_buffer(&gss_min, &out_data);
334
335 done:
336         *token_out = blob;
337         return status;
338 }
339
340 NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
341                          bool do_sign, bool do_seal,
342                          uint32_t add_gss_c_flags,
343                          const char *keytab_name,
344                          struct gse_context **_gse_ctx)
345 {
346         struct gse_context *gse_ctx;
347         OM_uint32 gss_maj, gss_min;
348         gss_OID_set_desc mech_set;
349         krb5_error_code ret;
350         const char *ktname;
351         NTSTATUS status;
352
353         status = gse_context_init(mem_ctx, do_sign, do_seal,
354                                   NULL, add_gss_c_flags, &gse_ctx);
355         if (!NT_STATUS_IS_OK(status)) {
356                 return NT_STATUS_NO_MEMORY;
357         }
358
359         if (!keytab_name) {
360                 ret = gse_krb5_get_server_keytab(gse_ctx->k5ctx,
361                                                  &gse_ctx->keytab);
362                 if (ret) {
363                         status = NT_STATUS_INTERNAL_ERROR;
364                         goto done;
365                 }
366                 ret = smb_krb5_keytab_name(gse_ctx, gse_ctx->k5ctx,
367                                            gse_ctx->keytab, &ktname);
368                 if (ret) {
369                         status = NT_STATUS_INTERNAL_ERROR;
370                         goto done;
371                 }
372         } else {
373                 ktname = keytab_name;
374         }
375
376         /* FIXME!!!
377          * This call sets the default keytab for the whole server, not
378          * just for this context. Need to find a way that does not alter
379          * the state of the whole server ... */
380         ret = gsskrb5_register_acceptor_identity(ktname);
381         if (ret) {
382                 status = NT_STATUS_INTERNAL_ERROR;
383                 goto done;
384         }
385
386         mech_set.count = 1;
387         mech_set.elements = &gse_ctx->gss_mech;
388
389         gss_maj = gss_acquire_cred(&gss_min,
390                                    GSS_C_NO_NAME,
391                                    GSS_C_INDEFINITE,
392                                    &mech_set,
393                                    GSS_C_ACCEPT,
394                                    &gse_ctx->creds,
395                                    NULL, NULL);
396         if (gss_maj) {
397                 DEBUG(0, ("gss_acquire_creds failed with [%s]\n",
398                           gse_errstr(gse_ctx, gss_maj, gss_min)));
399                 status = NT_STATUS_INTERNAL_ERROR;
400                 goto done;
401         }
402
403         status = NT_STATUS_OK;
404
405 done:
406         if (!NT_STATUS_IS_OK(status)) {
407                 TALLOC_FREE(gse_ctx);
408         }
409
410         *_gse_ctx = gse_ctx;
411         return status;
412 }
413
414 NTSTATUS gse_get_server_auth_token(TALLOC_CTX *mem_ctx,
415                                    struct gse_context *gse_ctx,
416                                    DATA_BLOB *token_in,
417                                    DATA_BLOB *token_out)
418 {
419         OM_uint32 gss_maj, gss_min;
420         gss_buffer_desc in_data;
421         gss_buffer_desc out_data;
422         DATA_BLOB blob = data_blob_null;
423         NTSTATUS status;
424
425         in_data.value = token_in->data;
426         in_data.length = token_in->length;
427
428         gss_maj = gss_accept_sec_context(&gss_min,
429                                          &gse_ctx->gss_ctx,
430                                          gse_ctx->creds,
431                                          &in_data,
432                                          GSS_C_NO_CHANNEL_BINDINGS,
433                                          &gse_ctx->client_name,
434                                          &gse_ctx->ret_mech,
435                                          &out_data,
436                                          &gse_ctx->ret_flags, NULL,
437                                          &gse_ctx->delegated_creds);
438         switch (gss_maj) {
439         case GSS_S_COMPLETE:
440                 /* we are done with it */
441                 gse_ctx->more_processing = false;
442                 gse_ctx->authenticated = true;
443                 status = NT_STATUS_OK;
444                 break;
445         case GSS_S_CONTINUE_NEEDED:
446                 /* we will need a third leg */
447                 gse_ctx->more_processing = true;
448                 /* status = NT_STATUS_MORE_PROCESSING_REQUIRED; */
449                 status = NT_STATUS_OK;
450                 break;
451         default:
452                 DEBUG(0, ("gss_init_sec_context failed with [%s]\n",
453                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
454
455                 if (gse_ctx->gss_ctx) {
456                         gss_delete_sec_context(&gss_min,
457                                                 &gse_ctx->gss_ctx,
458                                                 GSS_C_NO_BUFFER);
459                 }
460
461                 status = NT_STATUS_INTERNAL_ERROR;
462                 goto done;
463         }
464
465         /* we may be told to return nothing */
466         if (out_data.length) {
467                 blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
468                 if (!blob.data) {
469                         status = NT_STATUS_NO_MEMORY;
470                 }
471                 gss_maj = gss_release_buffer(&gss_min, &out_data);
472         }
473
474
475 done:
476         *token_out = blob;
477         return status;
478 }
479
480 NTSTATUS gse_verify_server_auth_flags(struct gse_context *gse_ctx)
481 {
482         if (!gse_ctx->authenticated) {
483                 return NT_STATUS_INVALID_HANDLE;
484         }
485
486         if (memcmp(gse_ctx->ret_mech,
487                    gss_mech_krb5, sizeof(gss_OID_desc)) != 0) {
488                 return NT_STATUS_ACCESS_DENIED;
489         }
490
491         /* GSS_C_MUTUAL_FLAG */
492         if (gse_ctx->gss_c_flags & GSS_C_MUTUAL_FLAG) {
493                 if (!(gse_ctx->ret_flags & GSS_C_MUTUAL_FLAG)) {
494                         return NT_STATUS_ACCESS_DENIED;
495                 }
496         }
497
498         /* GSS_C_DELEG_FLAG */
499         /* GSS_C_DELEG_POLICY_FLAG */
500         /* GSS_C_REPLAY_FLAG */
501         /* GSS_C_SEQUENCE_FLAG */
502
503         /* GSS_C_INTEG_FLAG */
504         if (gse_ctx->gss_c_flags & GSS_C_INTEG_FLAG) {
505                 if (!(gse_ctx->ret_flags & GSS_C_INTEG_FLAG)) {
506                         return NT_STATUS_ACCESS_DENIED;
507                 }
508         }
509
510         /* GSS_C_CONF_FLAG */
511         if (gse_ctx->gss_c_flags & GSS_C_CONF_FLAG) {
512                 if (!(gse_ctx->ret_flags & GSS_C_CONF_FLAG)) {
513                         return NT_STATUS_ACCESS_DENIED;
514                 }
515         }
516
517         return NT_STATUS_OK;
518 }
519
520 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min)
521 {
522         OM_uint32 gss_min, gss_maj;
523         gss_buffer_desc msg_min;
524         gss_buffer_desc msg_maj;
525         OM_uint32 msg_ctx = 0;
526
527         char *errstr = NULL;
528
529         ZERO_STRUCT(msg_min);
530         ZERO_STRUCT(msg_maj);
531
532         gss_maj = gss_display_status(&gss_min, maj, GSS_C_GSS_CODE,
533                                      GSS_C_NO_OID, &msg_ctx, &msg_maj);
534         if (gss_maj) {
535                 goto done;
536         }
537         gss_maj = gss_display_status(&gss_min, min, GSS_C_MECH_CODE,
538                                      (gss_OID)discard_const(gss_mech_krb5),
539                                      &msg_ctx, &msg_min);
540         if (gss_maj) {
541                 goto done;
542         }
543
544         errstr = talloc_strndup(mem_ctx,
545                                 (char *)msg_maj.value,
546                                         msg_maj.length);
547         if (!errstr) {
548                 goto done;
549         }
550         errstr = talloc_strdup_append_buffer(errstr, ": ");
551         if (!errstr) {
552                 goto done;
553         }
554         errstr = talloc_strndup_append_buffer(errstr,
555                                                 (char *)msg_min.value,
556                                                         msg_min.length);
557         if (!errstr) {
558                 goto done;
559         }
560
561 done:
562         if (msg_min.value) {
563                 gss_maj = gss_release_buffer(&gss_min, &msg_min);
564         }
565         if (msg_maj.value) {
566                 gss_maj = gss_release_buffer(&gss_min, &msg_maj);
567         }
568         return errstr;
569 }
570
571 bool gse_require_more_processing(struct gse_context *gse_ctx)
572 {
573         return gse_ctx->more_processing;
574 }
575
576 DATA_BLOB gse_get_session_key(TALLOC_CTX *mem_ctx,
577                                 struct gse_context *gse_ctx)
578 {
579         OM_uint32 gss_min, gss_maj;
580         gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;
581         DATA_BLOB ret;
582
583         gss_maj = gss_inquire_sec_context_by_oid(
584                                 &gss_min, gse_ctx->gss_ctx,
585                                 &gse_sesskey_inq_oid, &set);
586         if (gss_maj) {
587                 DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n",
588                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
589                 return data_blob_null;
590         }
591
592         if ((set == GSS_C_NO_BUFFER_SET) ||
593             (set->count != 2) ||
594             (memcmp(set->elements[1].value,
595                     gse_sesskeytype_oid.elements,
596                     gse_sesskeytype_oid.length) != 0)) {
597                 DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown "
598                           "OID for data in results:\n"));
599                 dump_data(1, (uint8_t *)set->elements[1].value,
600                              set->elements[1].length);
601                 return data_blob_null;
602         }
603
604         ret = data_blob_talloc(mem_ctx, set->elements[0].value,
605                                         set->elements[0].length);
606
607         gss_maj = gss_release_buffer_set(&gss_min, &set);
608         return ret;
609 }
610
611 NTSTATUS gse_get_client_name(struct gse_context *gse_ctx,
612                              TALLOC_CTX *mem_ctx, char **cli_name)
613 {
614         OM_uint32 gss_min, gss_maj;
615         gss_buffer_desc name_buffer;
616
617         if (!gse_ctx->authenticated) {
618                 return NT_STATUS_ACCESS_DENIED;
619         }
620
621         if (!gse_ctx->client_name) {
622                 return NT_STATUS_NOT_FOUND;
623         }
624
625         /* TODO: check OID matches KRB5 Principal Name OID ? */
626
627         gss_maj = gss_display_name(&gss_min,
628                                    gse_ctx->client_name,
629                                    &name_buffer, NULL);
630         if (gss_maj) {
631                 DEBUG(0, ("gss_display_name failed [%s]\n",
632                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
633                 return NT_STATUS_INTERNAL_ERROR;
634         }
635
636         *cli_name = talloc_strndup(talloc_tos(),
637                                         (char *)name_buffer.value,
638                                         name_buffer.length);
639
640         gss_maj = gss_release_buffer(&gss_min, &name_buffer);
641
642         if (!*cli_name) {
643                 return NT_STATUS_NO_MEMORY;
644         }
645
646         return NT_STATUS_OK;
647 }
648
649 NTSTATUS gse_get_authz_data(struct gse_context *gse_ctx,
650                             TALLOC_CTX *mem_ctx, DATA_BLOB *pac)
651 {
652         OM_uint32 gss_min, gss_maj;
653         gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;
654
655         if (!gse_ctx->authenticated) {
656                 return NT_STATUS_ACCESS_DENIED;
657         }
658
659         gss_maj = gss_inquire_sec_context_by_oid(
660                                 &gss_min, gse_ctx->gss_ctx,
661                                 &gse_authz_data_oid, &set);
662         if (gss_maj) {
663                 DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n",
664                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
665                 return NT_STATUS_NOT_FOUND;
666         }
667
668         if (set == GSS_C_NO_BUFFER_SET) {
669                 DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown "
670                           "data in results.\n"));
671                 return NT_STATUS_INTERNAL_ERROR;
672         }
673
674         /* for now we just hope it is the first value */
675         *pac = data_blob_talloc(mem_ctx,
676                                 set->elements[0].value,
677                                 set->elements[0].length);
678
679         gss_maj = gss_release_buffer_set(&gss_min, &set);
680
681         return NT_STATUS_OK;
682 }
683
684 NTSTATUS gse_get_authtime(struct gse_context *gse_ctx, time_t *authtime)
685 {
686         OM_uint32 gss_min, gss_maj;
687         gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;
688         int32_t tkttime;
689
690         if (!gse_ctx->authenticated) {
691                 return NT_STATUS_ACCESS_DENIED;
692         }
693
694         gss_maj = gss_inquire_sec_context_by_oid(
695                                 &gss_min, gse_ctx->gss_ctx,
696                                 &gse_authtime_oid, &set);
697         if (gss_maj) {
698                 DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n",
699                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
700                 return NT_STATUS_NOT_FOUND;
701         }
702
703         if ((set == GSS_C_NO_BUFFER_SET) || (set->count != 1) != 0) {
704                 DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown "
705                           "data in results.\n"));
706                 return NT_STATUS_INTERNAL_ERROR;
707         }
708
709         if (set->elements[0].length != sizeof(int32_t)) {
710                 DEBUG(0, ("Invalid authtime size!\n"));
711                 return NT_STATUS_INTERNAL_ERROR;
712         }
713
714         tkttime = *((int32_t *)set->elements[0].value);
715
716         gss_maj = gss_release_buffer_set(&gss_min, &set);
717
718         *authtime = (time_t)tkttime;
719         return NT_STATUS_OK;
720 }
721
722 size_t gse_get_signature_length(struct gse_context *gse_ctx,
723                                 int seal, size_t payload_size)
724 {
725         OM_uint32 gss_min, gss_maj;
726         gss_iov_buffer_desc iov[2];
727         uint8_t fakebuf[payload_size];
728         int sealed;
729
730         iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
731         iov[0].buffer.value = NULL;
732         iov[0].buffer.length = 0;
733         iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
734         iov[1].buffer.value = fakebuf;
735         iov[1].buffer.length = payload_size;
736
737         gss_maj = gss_wrap_iov_length(&gss_min, gse_ctx->gss_ctx,
738                                         seal, GSS_C_QOP_DEFAULT,
739                                         &sealed, iov, 2);
740         if (gss_maj) {
741                 DEBUG(0, ("gss_wrap_iov_length failed with [%s]\n",
742                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
743                 return 0;
744         }
745
746         return iov[0].buffer.length;
747 }
748
749 NTSTATUS gse_seal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
750                   DATA_BLOB *data, DATA_BLOB *signature)
751 {
752         OM_uint32 gss_min, gss_maj;
753         gss_iov_buffer_desc iov[2];
754         int req_seal = 1; /* setting to 1 means we request sign+seal */
755         int sealed;
756         NTSTATUS status;
757
758         /* allocate the memory ourselves so we do not need to talloc_memdup */
759         signature->length = gse_get_signature_length(gse_ctx, 1, data->length);
760         if (!signature->length) {
761                 return NT_STATUS_INTERNAL_ERROR;
762         }
763         signature->data = (uint8_t *)talloc_size(mem_ctx, signature->length);
764         if (!signature->data) {
765                 return NT_STATUS_NO_MEMORY;
766         }
767         iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
768         iov[0].buffer.value = signature->data;
769         iov[0].buffer.length = signature->length;
770
771         /* data is encrypted in place, which is ok */
772         iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
773         iov[1].buffer.value = data->data;
774         iov[1].buffer.length = data->length;
775
776         gss_maj = gss_wrap_iov(&gss_min, gse_ctx->gss_ctx,
777                                 req_seal, GSS_C_QOP_DEFAULT,
778                                 &sealed, iov, 2);
779         if (gss_maj) {
780                 DEBUG(0, ("gss_wrap_iov failed with [%s]\n",
781                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
782                 status = NT_STATUS_ACCESS_DENIED;
783                 goto done;
784         }
785
786         if (!sealed) {
787                 DEBUG(0, ("gss_wrap_iov says data was not sealed!\n"));
788                 status = NT_STATUS_ACCESS_DENIED;
789                 goto done;
790         }
791
792         status = NT_STATUS_OK;
793
794         DEBUG(10, ("Sealed %d bytes, and got %d bytes header/signature.\n",
795                    (int)iov[1].buffer.length, (int)iov[0].buffer.length));
796
797 done:
798         return status;
799 }
800
801 NTSTATUS gse_unseal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
802                     DATA_BLOB *data, DATA_BLOB *signature)
803 {
804         OM_uint32 gss_min, gss_maj;
805         gss_iov_buffer_desc iov[2];
806         int sealed;
807         NTSTATUS status;
808
809         iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
810         iov[0].buffer.value = signature->data;
811         iov[0].buffer.length = signature->length;
812
813         /* data is decrypted in place, which is ok */
814         iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
815         iov[1].buffer.value = data->data;
816         iov[1].buffer.length = data->length;
817
818         gss_maj = gss_unwrap_iov(&gss_min, gse_ctx->gss_ctx,
819                                  &sealed, NULL, iov, 2);
820         if (gss_maj) {
821                 DEBUG(0, ("gss_unwrap_iov failed with [%s]\n",
822                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
823                 status = NT_STATUS_ACCESS_DENIED;
824                 goto done;
825         }
826
827         if (!sealed) {
828                 DEBUG(0, ("gss_unwrap_iov says data is not sealed!\n"));
829                 status = NT_STATUS_ACCESS_DENIED;
830                 goto done;
831         }
832
833         status = NT_STATUS_OK;
834
835         DEBUG(10, ("Unsealed %d bytes, with %d bytes header/signature.\n",
836                    (int)iov[1].buffer.length, (int)iov[0].buffer.length));
837
838 done:
839         return status;
840 }
841
842 NTSTATUS gse_sign(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
843                   DATA_BLOB *data, DATA_BLOB *signature)
844 {
845         OM_uint32 gss_min, gss_maj;
846         gss_buffer_desc in_data = { 0, NULL };
847         gss_buffer_desc out_data = { 0, NULL};
848         NTSTATUS status;
849
850         in_data.value = data->data;
851         in_data.length = data->length;
852
853         gss_maj = gss_get_mic(&gss_min, gse_ctx->gss_ctx,
854                               GSS_C_QOP_DEFAULT,
855                               &in_data, &out_data);
856         if (gss_maj) {
857                 DEBUG(0, ("gss_get_mic failed with [%s]\n",
858                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
859                 status = NT_STATUS_ACCESS_DENIED;
860                 goto done;
861         }
862
863         *signature = data_blob_talloc(mem_ctx,
864                                         out_data.value, out_data.length);
865         if (!signature->data) {
866                 status = NT_STATUS_NO_MEMORY;
867                 goto done;
868         }
869
870         status = NT_STATUS_OK;
871
872 done:
873         if (out_data.value) {
874                 gss_maj = gss_release_buffer(&gss_min, &out_data);
875         }
876         return status;
877 }
878
879 NTSTATUS gse_sigcheck(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
880                       DATA_BLOB *data, DATA_BLOB *signature)
881 {
882         OM_uint32 gss_min, gss_maj;
883         gss_buffer_desc in_data = { 0, NULL };
884         gss_buffer_desc in_token = { 0, NULL};
885         NTSTATUS status;
886
887         in_data.value = data->data;
888         in_data.length = data->length;
889         in_token.value = signature->data;
890         in_token.length = signature->length;
891
892         gss_maj = gss_verify_mic(&gss_min, gse_ctx->gss_ctx,
893                                  &in_data, &in_token, NULL);
894         if (gss_maj) {
895                 DEBUG(0, ("gss_verify_mic failed with [%s]\n",
896                           gse_errstr(talloc_tos(), gss_maj, gss_min)));
897                 status = NT_STATUS_ACCESS_DENIED;
898                 goto done;
899         }
900
901         status = NT_STATUS_OK;
902
903 done:
904         return status;
905 }
906
907 #else
908
909 NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
910                           bool do_sign, bool do_seal,
911                           const char *ccache_name,
912                           const char *server,
913                           const char *service,
914                           const char *username,
915                           const char *password,
916                           uint32_t add_gss_c_flags,
917                           struct gse_context **_gse_ctx)
918 {
919         return NT_STATUS_NOT_IMPLEMENTED;
920 }
921
922 NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
923                                    struct gse_context *gse_ctx,
924                                    DATA_BLOB *token_in,
925                                    DATA_BLOB *token_out)
926 {
927         return NT_STATUS_NOT_IMPLEMENTED;
928 }
929
930 NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
931                          bool do_sign, bool do_seal,
932                          uint32_t add_gss_c_flags,
933                          const char *keytab,
934                          struct gse_context **_gse_ctx)
935 {
936         return NT_STATUS_NOT_IMPLEMENTED;
937 }
938
939 NTSTATUS gse_get_server_auth_token(TALLOC_CTX *mem_ctx,
940                                    struct gse_context *gse_ctx,
941                                    DATA_BLOB *token_in,
942                                    DATA_BLOB *token_out)
943 {
944         return NT_STATUS_NOT_IMPLEMENTED;
945 }
946
947 NTSTATUS gse_verify_server_auth_flags(struct gse_context *gse_ctx)
948 {
949         return NT_STATUS_NOT_IMPLEMENTED;
950 }
951
952 bool gse_require_more_processing(struct gse_context *gse_ctx)
953 {
954         return false;
955 }
956
957 DATA_BLOB gse_get_session_key(TALLOC_CTX *mem_ctx,
958                               struct gse_context *gse_ctx)
959 {
960         return data_blob_null;
961 }
962
963 NTSTATUS gse_get_client_name(struct gse_context *gse_ctx,
964                              TALLOC_CTX *mem_ctx, char **cli_name)
965 {
966         return NT_STATUS_NOT_IMPLEMENTED;
967 }
968
969 NTSTATUS gse_get_authz_data(struct gse_context *gse_ctx,
970                             TALLOC_CTX *mem_ctx, DATA_BLOB *pac)
971 {
972         return NT_STATUS_NOT_IMPLEMENTED;
973 }
974
975 NTSTATUS gse_get_authtime(struct gse_context *gse_ctx, time_t *authtime)
976 {
977         return NT_STATUS_NOT_IMPLEMENTED;
978 }
979
980 size_t gse_get_signature_length(struct gse_context *gse_ctx,
981                                 int seal, size_t payload_size)
982 {
983         return 0;
984 }
985
986 NTSTATUS gse_seal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
987                   DATA_BLOB *data, DATA_BLOB *signature)
988 {
989         return NT_STATUS_NOT_IMPLEMENTED;
990 }
991
992 NTSTATUS gse_unseal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
993                     DATA_BLOB *data, DATA_BLOB *signature)
994 {
995         return NT_STATUS_NOT_IMPLEMENTED;
996 }
997
998 NTSTATUS gse_sign(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
999                   DATA_BLOB *data, DATA_BLOB *signature)
1000 {
1001         return NT_STATUS_NOT_IMPLEMENTED;
1002 }
1003
1004 NTSTATUS gse_sigcheck(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
1005                       DATA_BLOB *data, DATA_BLOB *signature)
1006 {
1007         return NT_STATUS_NOT_IMPLEMENTED;
1008 }
1009
1010 #endif /* HAVE_KRB5 && HAVE_GSSAPI_EXT_H && HAVE_GSS_WRAP_IOV */