s3:librpc/gse: make use of add gssapi_get_sig_size() and gssapi_{seal,unseal,sign...
[Samba.git] / source3 / librpc / crypto / gse.c
blobbeebd14a7664e3e79d0c122079cfd1281c3bb90c
1 /*
2 * GSSAPI Security Extensions
3 * RPC Pipe client and server routines
4 * Copyright (C) Simo Sorce 2010.
5 * Copyright (C) Andrew Bartlett 2004-2011.
6 * Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2005
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 /* We support only GSSAPI/KRB5 here */
24 #include "includes.h"
25 #include "gse.h"
26 #include "libads/kerberos_proto.h"
27 #include "auth/common_auth.h"
28 #include "auth/gensec/gensec.h"
29 #include "auth/gensec/gensec_internal.h"
30 #include "auth/credentials/credentials.h"
31 #include "../librpc/gen_ndr/dcerpc.h"
33 #if defined(HAVE_KRB5)
35 #include "auth/kerberos/pac_utils.h"
36 #include "gse_krb5.h"
38 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min);
39 static size_t gensec_gse_sig_size(struct gensec_security *gensec_security,
40 size_t data_size);
42 struct gse_context {
43 gss_ctx_id_t gssapi_context;
44 gss_name_t server_name;
45 gss_name_t client_name;
46 OM_uint32 gss_want_flags, gss_got_flags;
47 size_t sig_size;
49 gss_cred_id_t delegated_cred_handle;
51 NTTIME expire_time;
53 /* gensec_gse only */
54 krb5_context k5ctx;
55 krb5_ccache ccache;
56 krb5_keytab keytab;
58 gss_OID_desc gss_mech;
59 gss_cred_id_t creds;
61 gss_OID ret_mech;
64 /* free non talloc dependent contexts */
65 static int gse_context_destructor(void *ptr)
67 struct gse_context *gse_ctx;
68 OM_uint32 gss_min;
70 gse_ctx = talloc_get_type_abort(ptr, struct gse_context);
71 if (gse_ctx->k5ctx) {
72 if (gse_ctx->ccache) {
73 krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache);
74 gse_ctx->ccache = NULL;
76 if (gse_ctx->keytab) {
77 krb5_kt_close(gse_ctx->k5ctx, gse_ctx->keytab);
78 gse_ctx->keytab = NULL;
80 krb5_free_context(gse_ctx->k5ctx);
81 gse_ctx->k5ctx = NULL;
83 if (gse_ctx->gssapi_context != GSS_C_NO_CONTEXT) {
84 (void)gss_delete_sec_context(&gss_min,
85 &gse_ctx->gssapi_context,
86 GSS_C_NO_BUFFER);
88 if (gse_ctx->server_name) {
89 (void)gss_release_name(&gss_min,
90 &gse_ctx->server_name);
92 if (gse_ctx->client_name) {
93 (void)gss_release_name(&gss_min,
94 &gse_ctx->client_name);
96 if (gse_ctx->creds) {
97 (void)gss_release_cred(&gss_min,
98 &gse_ctx->creds);
100 if (gse_ctx->delegated_cred_handle) {
101 (void)gss_release_cred(&gss_min,
102 &gse_ctx->delegated_cred_handle);
105 /* MIT and Heimdal differ as to if you can call
106 * gss_release_oid() on this OID, generated by
107 * gss_{accept,init}_sec_context(). However, as long as the
108 * oid is gss_mech_krb5 (which it always is at the moment),
109 * then this is a moot point, as both declare this particular
110 * OID static, and so no memory is lost. This assert is in
111 * place to ensure that the programmer who wishes to extend
112 * this code to EAP or other GSS mechanisms determines an
113 * implementation-dependent way of releasing any dynamically
114 * allocated OID */
115 SMB_ASSERT(smb_gss_oid_equal(&gse_ctx->gss_mech, GSS_C_NO_OID) ||
116 smb_gss_oid_equal(&gse_ctx->gss_mech, gss_mech_krb5));
118 return 0;
121 static NTSTATUS gse_context_init(TALLOC_CTX *mem_ctx,
122 bool do_sign, bool do_seal,
123 const char *ccache_name,
124 uint32_t add_gss_c_flags,
125 struct gse_context **_gse_ctx)
127 struct gse_context *gse_ctx;
128 krb5_error_code k5ret;
129 NTSTATUS status;
131 gse_ctx = talloc_zero(mem_ctx, struct gse_context);
132 if (!gse_ctx) {
133 return NT_STATUS_NO_MEMORY;
135 talloc_set_destructor((TALLOC_CTX *)gse_ctx, gse_context_destructor);
137 gse_ctx->expire_time = GENSEC_EXPIRE_TIME_INFINITY;
139 memcpy(&gse_ctx->gss_mech, gss_mech_krb5, sizeof(gss_OID_desc));
141 gse_ctx->gss_want_flags = GSS_C_MUTUAL_FLAG |
142 GSS_C_DELEG_FLAG |
143 GSS_C_DELEG_POLICY_FLAG |
144 GSS_C_REPLAY_FLAG |
145 GSS_C_SEQUENCE_FLAG;
146 if (do_sign) {
147 gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
149 if (do_seal) {
150 gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
151 gse_ctx->gss_want_flags |= GSS_C_CONF_FLAG;
154 gse_ctx->gss_want_flags |= add_gss_c_flags;
156 /* Initialize Kerberos Context */
157 initialize_krb5_error_table();
159 k5ret = krb5_init_context(&gse_ctx->k5ctx);
160 if (k5ret) {
161 DEBUG(0, ("Failed to initialize kerberos context! (%s)\n",
162 error_message(k5ret)));
163 status = NT_STATUS_INTERNAL_ERROR;
164 goto err_out;
167 if (!ccache_name) {
168 ccache_name = krb5_cc_default_name(gse_ctx->k5ctx);
170 k5ret = krb5_cc_resolve(gse_ctx->k5ctx, ccache_name,
171 &gse_ctx->ccache);
172 if (k5ret) {
173 DEBUG(1, ("Failed to resolve credential cache! (%s)\n",
174 error_message(k5ret)));
175 status = NT_STATUS_INTERNAL_ERROR;
176 goto err_out;
179 /* TODO: Should we enforce a enc_types list ?
180 ret = krb5_set_default_tgs_ktypes(gse_ctx->k5ctx, enc_types);
183 *_gse_ctx = gse_ctx;
184 return NT_STATUS_OK;
186 err_out:
187 TALLOC_FREE(gse_ctx);
188 return status;
191 static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
192 bool do_sign, bool do_seal,
193 const char *ccache_name,
194 const char *server,
195 const char *service,
196 const char *username,
197 const char *password,
198 uint32_t add_gss_c_flags,
199 struct gse_context **_gse_ctx)
201 struct gse_context *gse_ctx;
202 OM_uint32 gss_maj, gss_min;
203 gss_buffer_desc name_buffer = {0, NULL};
204 gss_OID_set_desc mech_set;
205 NTSTATUS status;
207 if (!server || !service) {
208 return NT_STATUS_INVALID_PARAMETER;
211 status = gse_context_init(mem_ctx, do_sign, do_seal,
212 ccache_name, add_gss_c_flags,
213 &gse_ctx);
214 if (!NT_STATUS_IS_OK(status)) {
215 return NT_STATUS_NO_MEMORY;
218 /* Guess the realm based on the supplied service, and avoid the GSS libs
219 doing DNS lookups which may fail.
221 TODO: Loop with the KDC on some more combinations (local
222 realm in particular), possibly falling back to
223 GSS_C_NT_HOSTBASED_SERVICE
225 name_buffer.value = kerberos_get_principal_from_service_hostname(
226 gse_ctx, service, server, lp_realm());
227 if (!name_buffer.value) {
228 status = NT_STATUS_NO_MEMORY;
229 goto err_out;
231 name_buffer.length = strlen((char *)name_buffer.value);
232 gss_maj = gss_import_name(&gss_min, &name_buffer,
233 GSS_C_NT_USER_NAME,
234 &gse_ctx->server_name);
235 if (gss_maj) {
236 DEBUG(0, ("gss_import_name failed for %s, with [%s]\n",
237 (char *)name_buffer.value,
238 gse_errstr(gse_ctx, gss_maj, gss_min)));
239 status = NT_STATUS_INTERNAL_ERROR;
240 goto err_out;
243 /* TODO: get krb5 ticket using username/password, if no valid
244 * one already available in ccache */
246 mech_set.count = 1;
247 mech_set.elements = &gse_ctx->gss_mech;
249 gss_maj = gss_acquire_cred(&gss_min,
250 GSS_C_NO_NAME,
251 GSS_C_INDEFINITE,
252 &mech_set,
253 GSS_C_INITIATE,
254 &gse_ctx->creds,
255 NULL, NULL);
256 if (gss_maj) {
257 DEBUG(0, ("gss_acquire_creds failed for %s, with [%s]\n",
258 (char *)name_buffer.value,
259 gse_errstr(gse_ctx, gss_maj, gss_min)));
260 status = NT_STATUS_INTERNAL_ERROR;
261 goto err_out;
264 *_gse_ctx = gse_ctx;
265 TALLOC_FREE(name_buffer.value);
266 return NT_STATUS_OK;
268 err_out:
269 TALLOC_FREE(name_buffer.value);
270 TALLOC_FREE(gse_ctx);
271 return status;
274 static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
275 struct gse_context *gse_ctx,
276 const DATA_BLOB *token_in,
277 DATA_BLOB *token_out)
279 OM_uint32 gss_maj, gss_min;
280 gss_buffer_desc in_data;
281 gss_buffer_desc out_data;
282 DATA_BLOB blob = data_blob_null;
283 NTSTATUS status;
284 OM_uint32 time_rec = 0;
285 struct timeval tv;
287 in_data.value = token_in->data;
288 in_data.length = token_in->length;
290 gss_maj = gss_init_sec_context(&gss_min,
291 gse_ctx->creds,
292 &gse_ctx->gssapi_context,
293 gse_ctx->server_name,
294 &gse_ctx->gss_mech,
295 gse_ctx->gss_want_flags,
296 0, GSS_C_NO_CHANNEL_BINDINGS,
297 &in_data, NULL, &out_data,
298 &gse_ctx->gss_got_flags, &time_rec);
299 switch (gss_maj) {
300 case GSS_S_COMPLETE:
301 /* we are done with it */
302 tv = timeval_current_ofs(time_rec, 0);
303 gse_ctx->expire_time = timeval_to_nttime(&tv);
305 status = NT_STATUS_OK;
306 break;
307 case GSS_S_CONTINUE_NEEDED:
308 /* we will need a third leg */
309 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
310 break;
311 default:
312 DEBUG(0, ("gss_init_sec_context failed with [%s]\n",
313 gse_errstr(talloc_tos(), gss_maj, gss_min)));
314 status = NT_STATUS_INTERNAL_ERROR;
315 goto done;
318 /* we may be told to return nothing */
319 if (out_data.length) {
320 blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
321 if (!blob.data) {
322 status = NT_STATUS_NO_MEMORY;
325 gss_maj = gss_release_buffer(&gss_min, &out_data);
328 done:
329 *token_out = blob;
330 return status;
333 static NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
334 bool do_sign, bool do_seal,
335 uint32_t add_gss_c_flags,
336 struct gse_context **_gse_ctx)
338 struct gse_context *gse_ctx;
339 OM_uint32 gss_maj, gss_min;
340 krb5_error_code ret;
341 NTSTATUS status;
343 status = gse_context_init(mem_ctx, do_sign, do_seal,
344 NULL, add_gss_c_flags, &gse_ctx);
345 if (!NT_STATUS_IS_OK(status)) {
346 return NT_STATUS_NO_MEMORY;
349 ret = gse_krb5_get_server_keytab(gse_ctx->k5ctx,
350 &gse_ctx->keytab);
351 if (ret) {
352 status = NT_STATUS_INTERNAL_ERROR;
353 goto done;
356 #ifdef HAVE_GSS_KRB5_IMPORT_CRED
358 /* This creates a GSSAPI cred_id_t with the keytab set */
359 gss_maj = gss_krb5_import_cred(&gss_min, NULL, NULL, gse_ctx->keytab,
360 &gse_ctx->creds);
362 if (gss_maj != 0
363 && gss_maj != (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME)) {
364 DEBUG(0, ("gss_krb5_import_cred failed with [%s]\n",
365 gse_errstr(gse_ctx, gss_maj, gss_min)));
366 status = NT_STATUS_INTERNAL_ERROR;
367 goto done;
369 /* This is the error the MIT krb5 1.9 gives when it
370 * implements the function, but we do not specify the
371 * principal. However, when we specify the principal
372 * as host$@REALM the GSS acceptor fails with 'wrong
373 * principal in request'. Work around the issue by
374 * falling back to the alternate approach below. */
375 } else if (gss_maj == (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME))
376 #endif
377 /* FIXME!!!
378 * This call sets the default keytab for the whole server, not
379 * just for this context. Need to find a way that does not alter
380 * the state of the whole server ... */
382 const char *ktname;
383 gss_OID_set_desc mech_set;
385 ret = smb_krb5_keytab_name(gse_ctx, gse_ctx->k5ctx,
386 gse_ctx->keytab, &ktname);
387 if (ret) {
388 status = NT_STATUS_INTERNAL_ERROR;
389 goto done;
392 ret = gsskrb5_register_acceptor_identity(ktname);
393 if (ret) {
394 status = NT_STATUS_INTERNAL_ERROR;
395 goto done;
398 mech_set.count = 1;
399 mech_set.elements = &gse_ctx->gss_mech;
401 gss_maj = gss_acquire_cred(&gss_min,
402 GSS_C_NO_NAME,
403 GSS_C_INDEFINITE,
404 &mech_set,
405 GSS_C_ACCEPT,
406 &gse_ctx->creds,
407 NULL, NULL);
409 if (gss_maj) {
410 DEBUG(0, ("gss_acquire_creds failed with [%s]\n",
411 gse_errstr(gse_ctx, gss_maj, gss_min)));
412 status = NT_STATUS_INTERNAL_ERROR;
413 goto done;
417 status = NT_STATUS_OK;
419 done:
420 if (!NT_STATUS_IS_OK(status)) {
421 TALLOC_FREE(gse_ctx);
424 *_gse_ctx = gse_ctx;
425 return status;
428 static NTSTATUS gse_get_server_auth_token(TALLOC_CTX *mem_ctx,
429 struct gse_context *gse_ctx,
430 const DATA_BLOB *token_in,
431 DATA_BLOB *token_out)
433 OM_uint32 gss_maj, gss_min;
434 gss_buffer_desc in_data;
435 gss_buffer_desc out_data;
436 DATA_BLOB blob = data_blob_null;
437 NTSTATUS status;
438 OM_uint32 time_rec = 0;
439 struct timeval tv;
441 in_data.value = token_in->data;
442 in_data.length = token_in->length;
444 gss_maj = gss_accept_sec_context(&gss_min,
445 &gse_ctx->gssapi_context,
446 gse_ctx->creds,
447 &in_data,
448 GSS_C_NO_CHANNEL_BINDINGS,
449 &gse_ctx->client_name,
450 &gse_ctx->ret_mech,
451 &out_data,
452 &gse_ctx->gss_got_flags,
453 &time_rec,
454 &gse_ctx->delegated_cred_handle);
455 switch (gss_maj) {
456 case GSS_S_COMPLETE:
457 /* we are done with it */
458 tv = timeval_current_ofs(time_rec, 0);
459 gse_ctx->expire_time = timeval_to_nttime(&tv);
461 status = NT_STATUS_OK;
462 break;
463 case GSS_S_CONTINUE_NEEDED:
464 /* we will need a third leg */
465 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
466 break;
467 default:
468 DEBUG(1, ("gss_accept_sec_context failed with [%s]\n",
469 gse_errstr(talloc_tos(), gss_maj, gss_min)));
471 if (gse_ctx->gssapi_context) {
472 gss_delete_sec_context(&gss_min,
473 &gse_ctx->gssapi_context,
474 GSS_C_NO_BUFFER);
477 status = NT_STATUS_LOGON_FAILURE;
478 goto done;
481 /* we may be told to return nothing */
482 if (out_data.length) {
483 blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
484 if (!blob.data) {
485 status = NT_STATUS_NO_MEMORY;
487 gss_maj = gss_release_buffer(&gss_min, &out_data);
491 done:
492 *token_out = blob;
493 return status;
496 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min)
498 OM_uint32 gss_min, gss_maj;
499 gss_buffer_desc msg_min;
500 gss_buffer_desc msg_maj;
501 OM_uint32 msg_ctx = 0;
503 char *errstr = NULL;
505 ZERO_STRUCT(msg_min);
506 ZERO_STRUCT(msg_maj);
508 gss_maj = gss_display_status(&gss_min, maj, GSS_C_GSS_CODE,
509 GSS_C_NO_OID, &msg_ctx, &msg_maj);
510 if (gss_maj) {
511 goto done;
513 errstr = talloc_strndup(mem_ctx,
514 (char *)msg_maj.value,
515 msg_maj.length);
516 if (!errstr) {
517 goto done;
519 gss_maj = gss_display_status(&gss_min, min, GSS_C_MECH_CODE,
520 (gss_OID)discard_const(gss_mech_krb5),
521 &msg_ctx, &msg_min);
522 if (gss_maj) {
523 goto done;
526 errstr = talloc_strdup_append_buffer(errstr, ": ");
527 if (!errstr) {
528 goto done;
530 errstr = talloc_strndup_append_buffer(errstr,
531 (char *)msg_min.value,
532 msg_min.length);
533 if (!errstr) {
534 goto done;
537 done:
538 if (msg_min.value) {
539 gss_maj = gss_release_buffer(&gss_min, &msg_min);
541 if (msg_maj.value) {
542 gss_maj = gss_release_buffer(&gss_min, &msg_maj);
544 return errstr;
547 static NTSTATUS gensec_gse_client_start(struct gensec_security *gensec_security)
549 struct gse_context *gse_ctx;
550 struct cli_credentials *creds = gensec_get_credentials(gensec_security);
551 NTSTATUS nt_status;
552 OM_uint32 want_flags = 0;
553 bool do_sign = false, do_seal = false;
554 const char *hostname = gensec_get_target_hostname(gensec_security);
555 const char *service = gensec_get_target_service(gensec_security);
556 const char *username = cli_credentials_get_username(creds);
557 const char *password = cli_credentials_get_password(creds);
559 if (!hostname) {
560 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
561 return NT_STATUS_INVALID_PARAMETER;
563 if (is_ipaddress(hostname)) {
564 DEBUG(2, ("Cannot do GSE to an IP address\n"));
565 return NT_STATUS_INVALID_PARAMETER;
567 if (strcmp(hostname, "localhost") == 0) {
568 DEBUG(2, ("GSE to 'localhost' does not make sense\n"));
569 return NT_STATUS_INVALID_PARAMETER;
572 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
573 do_sign = true;
575 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
576 do_seal = true;
578 if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
579 want_flags |= GSS_C_DCE_STYLE;
582 nt_status = gse_init_client(gensec_security, do_sign, do_seal, NULL,
583 hostname, service,
584 username, password, want_flags,
585 &gse_ctx);
586 if (!NT_STATUS_IS_OK(nt_status)) {
587 return nt_status;
589 gensec_security->private_data = gse_ctx;
590 return NT_STATUS_OK;
593 static NTSTATUS gensec_gse_server_start(struct gensec_security *gensec_security)
595 struct gse_context *gse_ctx;
596 NTSTATUS nt_status;
597 OM_uint32 want_flags = 0;
598 bool do_sign = false, do_seal = false;
600 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
601 do_sign = true;
603 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
604 do_seal = true;
606 if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
607 want_flags |= GSS_C_DCE_STYLE;
610 nt_status = gse_init_server(gensec_security, do_sign, do_seal, want_flags,
611 &gse_ctx);
612 if (!NT_STATUS_IS_OK(nt_status)) {
613 return nt_status;
615 gensec_security->private_data = gse_ctx;
616 return NT_STATUS_OK;
620 * Next state function for the GSE GENSEC mechanism
622 * @param gensec_gse_state GSE State
623 * @param mem_ctx The TALLOC_CTX for *out to be allocated on
624 * @param in The request, as a DATA_BLOB
625 * @param out The reply, as an talloc()ed DATA_BLOB, on *mem_ctx
626 * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
627 * or NT_STATUS_OK if the user is authenticated.
630 static NTSTATUS gensec_gse_update(struct gensec_security *gensec_security,
631 TALLOC_CTX *mem_ctx,
632 struct tevent_context *ev,
633 const DATA_BLOB in, DATA_BLOB *out)
635 NTSTATUS status;
636 struct gse_context *gse_ctx =
637 talloc_get_type_abort(gensec_security->private_data,
638 struct gse_context);
640 switch (gensec_security->gensec_role) {
641 case GENSEC_CLIENT:
642 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
643 &in, out);
644 break;
645 case GENSEC_SERVER:
646 status = gse_get_server_auth_token(mem_ctx, gse_ctx,
647 &in, out);
648 break;
650 if (!NT_STATUS_IS_OK(status)) {
651 return status;
654 return NT_STATUS_OK;
657 static NTSTATUS gensec_gse_wrap(struct gensec_security *gensec_security,
658 TALLOC_CTX *mem_ctx,
659 const DATA_BLOB *in,
660 DATA_BLOB *out)
662 struct gse_context *gse_ctx =
663 talloc_get_type_abort(gensec_security->private_data,
664 struct gse_context);
665 OM_uint32 maj_stat, min_stat;
666 gss_buffer_desc input_token, output_token;
667 int conf_state;
668 input_token.length = in->length;
669 input_token.value = in->data;
671 maj_stat = gss_wrap(&min_stat,
672 gse_ctx->gssapi_context,
673 gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
674 GSS_C_QOP_DEFAULT,
675 &input_token,
676 &conf_state,
677 &output_token);
678 if (GSS_ERROR(maj_stat)) {
679 DEBUG(0, ("gensec_gse_wrap: GSS Wrap failed: %s\n",
680 gse_errstr(talloc_tos(), maj_stat, min_stat)));
681 return NT_STATUS_ACCESS_DENIED;
684 *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
685 gss_release_buffer(&min_stat, &output_token);
687 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
688 && !conf_state) {
689 return NT_STATUS_ACCESS_DENIED;
691 return NT_STATUS_OK;
694 static NTSTATUS gensec_gse_unwrap(struct gensec_security *gensec_security,
695 TALLOC_CTX *mem_ctx,
696 const DATA_BLOB *in,
697 DATA_BLOB *out)
699 struct gse_context *gse_ctx =
700 talloc_get_type_abort(gensec_security->private_data,
701 struct gse_context);
702 OM_uint32 maj_stat, min_stat;
703 gss_buffer_desc input_token, output_token;
704 int conf_state;
705 gss_qop_t qop_state;
706 input_token.length = in->length;
707 input_token.value = in->data;
709 maj_stat = gss_unwrap(&min_stat,
710 gse_ctx->gssapi_context,
711 &input_token,
712 &output_token,
713 &conf_state,
714 &qop_state);
715 if (GSS_ERROR(maj_stat)) {
716 DEBUG(0, ("gensec_gse_unwrap: GSS UnWrap failed: %s\n",
717 gse_errstr(talloc_tos(), maj_stat, min_stat)));
718 return NT_STATUS_ACCESS_DENIED;
721 *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
722 gss_release_buffer(&min_stat, &output_token);
724 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
725 && !conf_state) {
726 return NT_STATUS_ACCESS_DENIED;
728 return NT_STATUS_OK;
731 static NTSTATUS gensec_gse_seal_packet(struct gensec_security *gensec_security,
732 TALLOC_CTX *mem_ctx,
733 uint8_t *data, size_t length,
734 const uint8_t *whole_pdu, size_t pdu_length,
735 DATA_BLOB *sig)
737 struct gse_context *gse_ctx =
738 talloc_get_type_abort(gensec_security->private_data,
739 struct gse_context);
740 bool hdr_signing = false;
741 size_t sig_size = 0;
742 NTSTATUS status;
744 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
745 hdr_signing = true;
748 sig_size = gensec_gse_sig_size(gensec_security, length);
750 status = gssapi_seal_packet(gse_ctx->gssapi_context,
751 &gse_ctx->gss_mech,
752 hdr_signing, sig_size,
753 data, length,
754 whole_pdu, pdu_length,
755 mem_ctx, sig);
756 if (!NT_STATUS_IS_OK(status)) {
757 DEBUG(0, ("gssapi_seal_packet(hdr_signing=%u,sig_size=%ju,"
758 "data=%ju,pdu=%ju) failed: %s\n",
759 hdr_signing, sig_size, length, pdu_length,
760 nt_errstr(status)));
761 return status;
764 return NT_STATUS_OK;
767 static NTSTATUS gensec_gse_unseal_packet(struct gensec_security *gensec_security,
768 uint8_t *data, size_t length,
769 const uint8_t *whole_pdu, size_t pdu_length,
770 const DATA_BLOB *sig)
772 struct gse_context *gse_ctx =
773 talloc_get_type_abort(gensec_security->private_data,
774 struct gse_context);
775 bool hdr_signing = false;
776 NTSTATUS status;
778 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
779 hdr_signing = true;
782 status = gssapi_unseal_packet(gse_ctx->gssapi_context,
783 &gse_ctx->gss_mech,
784 hdr_signing,
785 data, length,
786 whole_pdu, pdu_length,
787 sig);
788 if (!NT_STATUS_IS_OK(status)) {
789 DEBUG(0, ("gssapi_unseal_packet(hdr_signing=%u,sig_size=%ju,"
790 "data=%ju,pdu=%ju) failed: %s\n",
791 hdr_signing, sig->length, length, pdu_length,
792 nt_errstr(status)));
793 return status;
796 return NT_STATUS_OK;
799 static NTSTATUS gensec_gse_sign_packet(struct gensec_security *gensec_security,
800 TALLOC_CTX *mem_ctx,
801 const uint8_t *data, size_t length,
802 const uint8_t *whole_pdu, size_t pdu_length,
803 DATA_BLOB *sig)
805 struct gse_context *gse_ctx =
806 talloc_get_type_abort(gensec_security->private_data,
807 struct gse_context);
808 bool hdr_signing = false;
809 NTSTATUS status;
811 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
812 hdr_signing = true;
815 status = gssapi_sign_packet(gse_ctx->gssapi_context,
816 &gse_ctx->gss_mech,
817 hdr_signing,
818 data, length,
819 whole_pdu, pdu_length,
820 mem_ctx, sig);
821 if (!NT_STATUS_IS_OK(status)) {
822 DEBUG(0, ("gssapi_sign_packet(hdr_signing=%u,"
823 "data=%ju,pdu=%ju) failed: %s\n",
824 hdr_signing, length, pdu_length,
825 nt_errstr(status)));
826 return status;
829 return NT_STATUS_OK;
832 static NTSTATUS gensec_gse_check_packet(struct gensec_security *gensec_security,
833 const uint8_t *data, size_t length,
834 const uint8_t *whole_pdu, size_t pdu_length,
835 const DATA_BLOB *sig)
837 struct gse_context *gse_ctx =
838 talloc_get_type_abort(gensec_security->private_data,
839 struct gse_context);
840 bool hdr_signing = false;
841 NTSTATUS status;
843 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
844 hdr_signing = true;
847 status = gssapi_check_packet(gse_ctx->gssapi_context,
848 &gse_ctx->gss_mech,
849 hdr_signing,
850 data, length,
851 whole_pdu, pdu_length,
852 sig);
853 if (!NT_STATUS_IS_OK(status)) {
854 DEBUG(0, ("gssapi_check_packet(hdr_signing=%u,sig_size=%ju"
855 "data=%ju,pdu=%ju) failed: %s\n",
856 hdr_signing, sig->length, length, pdu_length,
857 nt_errstr(status)));
858 return status;
861 return NT_STATUS_OK;
864 /* Try to figure out what features we actually got on the connection */
865 static bool gensec_gse_have_feature(struct gensec_security *gensec_security,
866 uint32_t feature)
868 struct gse_context *gse_ctx =
869 talloc_get_type_abort(gensec_security->private_data,
870 struct gse_context);
872 if (feature & GENSEC_FEATURE_SIGN) {
873 return gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG;
875 if (feature & GENSEC_FEATURE_SEAL) {
876 return gse_ctx->gss_got_flags & GSS_C_CONF_FLAG;
878 if (feature & GENSEC_FEATURE_SESSION_KEY) {
879 /* Only for GSE/Krb5 */
880 if (smb_gss_oid_equal(gse_ctx->ret_mech, gss_mech_krb5)) {
881 return true;
884 if (feature & GENSEC_FEATURE_DCE_STYLE) {
885 return gse_ctx->gss_got_flags & GSS_C_DCE_STYLE;
887 if (feature & GENSEC_FEATURE_NEW_SPNEGO) {
888 NTSTATUS status;
889 uint32_t keytype;
891 if (!(gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG)) {
892 return false;
895 status = gssapi_get_session_key(talloc_tos(),
896 gse_ctx->gssapi_context, NULL, &keytype);
898 * We should do a proper sig on the mechListMic unless
899 * we know we have to be backwards compatible with
900 * earlier windows versions.
902 * Negotiating a non-krb5
903 * mech for example should be regarded as having
904 * NEW_SPNEGO
906 if (NT_STATUS_IS_OK(status)) {
907 switch (keytype) {
908 case ENCTYPE_DES_CBC_CRC:
909 case ENCTYPE_DES_CBC_MD5:
910 case ENCTYPE_ARCFOUR_HMAC:
911 case ENCTYPE_DES3_CBC_SHA1:
912 return false;
915 return true;
917 /* We can always do async (rather than strict request/reply) packets. */
918 if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
919 return true;
921 if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
922 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
923 return true;
926 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
927 return true;
930 return false;
932 return false;
935 static NTTIME gensec_gse_expire_time(struct gensec_security *gensec_security)
937 struct gse_context *gse_ctx =
938 talloc_get_type_abort(gensec_security->private_data,
939 struct gse_context);
941 return gse_ctx->expire_time;
945 * Extract the 'sesssion key' needed by SMB signing and ncacn_np
946 * (for encrypting some passwords).
948 * This breaks all the abstractions, but what do you expect...
950 static NTSTATUS gensec_gse_session_key(struct gensec_security *gensec_security,
951 TALLOC_CTX *mem_ctx,
952 DATA_BLOB *session_key)
954 struct gse_context *gse_ctx =
955 talloc_get_type_abort(gensec_security->private_data,
956 struct gse_context);
958 return gssapi_get_session_key(mem_ctx, gse_ctx->gssapi_context, session_key, NULL);
961 /* Get some basic (and authorization) information about the user on
962 * this session. This uses either the PAC (if present) or a local
963 * database lookup */
964 static NTSTATUS gensec_gse_session_info(struct gensec_security *gensec_security,
965 TALLOC_CTX *mem_ctx,
966 struct auth_session_info **_session_info)
968 struct gse_context *gse_ctx =
969 talloc_get_type_abort(gensec_security->private_data,
970 struct gse_context);
971 NTSTATUS nt_status;
972 TALLOC_CTX *tmp_ctx;
973 struct auth_session_info *session_info = NULL;
974 OM_uint32 maj_stat, min_stat;
975 DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
977 gss_buffer_desc name_token;
978 char *principal_string;
980 tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gse_session_info context");
981 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
983 maj_stat = gss_display_name(&min_stat,
984 gse_ctx->client_name,
985 &name_token,
986 NULL);
987 if (GSS_ERROR(maj_stat)) {
988 DEBUG(1, ("GSS display_name failed: %s\n",
989 gse_errstr(talloc_tos(), maj_stat, min_stat)));
990 talloc_free(tmp_ctx);
991 return NT_STATUS_FOOBAR;
994 principal_string = talloc_strndup(tmp_ctx,
995 (const char *)name_token.value,
996 name_token.length);
998 gss_release_buffer(&min_stat, &name_token);
1000 if (!principal_string) {
1001 talloc_free(tmp_ctx);
1002 return NT_STATUS_NO_MEMORY;
1005 nt_status = gssapi_obtain_pac_blob(tmp_ctx, gse_ctx->gssapi_context,
1006 gse_ctx->client_name,
1007 &pac_blob);
1009 /* IF we have the PAC - otherwise we need to get this
1010 * data from elsewere
1012 if (NT_STATUS_IS_OK(nt_status)) {
1013 pac_blob_ptr = &pac_blob;
1015 nt_status = gensec_generate_session_info_pac(tmp_ctx,
1016 gensec_security,
1017 NULL,
1018 pac_blob_ptr, principal_string,
1019 gensec_get_remote_address(gensec_security),
1020 &session_info);
1021 if (!NT_STATUS_IS_OK(nt_status)) {
1022 talloc_free(tmp_ctx);
1023 return nt_status;
1026 nt_status = gensec_gse_session_key(gensec_security, session_info,
1027 &session_info->session_key);
1028 if (!NT_STATUS_IS_OK(nt_status)) {
1029 talloc_free(tmp_ctx);
1030 return nt_status;
1033 *_session_info = talloc_move(mem_ctx, &session_info);
1034 talloc_free(tmp_ctx);
1036 return NT_STATUS_OK;
1039 static size_t gensec_gse_sig_size(struct gensec_security *gensec_security,
1040 size_t data_size)
1042 struct gse_context *gse_ctx =
1043 talloc_get_type_abort(gensec_security->private_data,
1044 struct gse_context);
1046 if (gse_ctx->sig_size > 0) {
1047 return gse_ctx->sig_size;
1050 gse_ctx->sig_size = gssapi_get_sig_size(gse_ctx->gssapi_context,
1051 &gse_ctx->gss_mech,
1052 gse_ctx->gss_want_flags,
1053 data_size);
1054 return gse_ctx->sig_size;
1057 static const char *gensec_gse_krb5_oids[] = {
1058 GENSEC_OID_KERBEROS5_OLD,
1059 GENSEC_OID_KERBEROS5,
1060 NULL
1063 const struct gensec_security_ops gensec_gse_krb5_security_ops = {
1064 .name = "gse_krb5",
1065 .auth_type = DCERPC_AUTH_TYPE_KRB5,
1066 .oid = gensec_gse_krb5_oids,
1067 .client_start = gensec_gse_client_start,
1068 .server_start = gensec_gse_server_start,
1069 .magic = gensec_magic_check_krb5_oid,
1070 .update = gensec_gse_update,
1071 .session_key = gensec_gse_session_key,
1072 .session_info = gensec_gse_session_info,
1073 .sig_size = gensec_gse_sig_size,
1074 .sign_packet = gensec_gse_sign_packet,
1075 .check_packet = gensec_gse_check_packet,
1076 .seal_packet = gensec_gse_seal_packet,
1077 .unseal_packet = gensec_gse_unseal_packet,
1078 .wrap = gensec_gse_wrap,
1079 .unwrap = gensec_gse_unwrap,
1080 .have_feature = gensec_gse_have_feature,
1081 .expire_time = gensec_gse_expire_time,
1082 .enabled = true,
1083 .kerberos = true,
1084 .priority = GENSEC_GSSAPI
1087 #endif /* HAVE_KRB5 */