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