summaryrefslogtreecommitdiff
path: root/auth/kerberos
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2008-09-25 08:34:48 +0200
committerAndrew Bartlett <abartlet@samba.org>2015-06-24 01:03:16 +0200
commit8a4c0abb3eaf1ae80d1ce476cc123c5a195cd15d (patch)
tree77f58fe30027e392ede00692ddf83c5d9f3fccab /auth/kerberos
parentc245d4f33e233f16aafb29a1737f8f1fa96724d7 (diff)
downloadsamba-8a4c0abb3eaf1ae80d1ce476cc123c5a195cd15d.tar.gz
auth/kerberos: add gssapi_get_sig_size() and gssapi_{seal,unseal,sign,check}_packet() helper functions
These make use of gss_[un]wrap_iov[_length]() where required and support header signing. Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'auth/kerberos')
-rw-r--r--auth/kerberos/gssapi_helper.c395
-rw-r--r--auth/kerberos/gssapi_helper.h55
-rwxr-xr-xauth/kerberos/wscript_build2
3 files changed, 451 insertions, 1 deletions
diff --git a/auth/kerberos/gssapi_helper.c b/auth/kerberos/gssapi_helper.c
new file mode 100644
index 00000000000..b7ffb6ca9ff
--- /dev/null
+++ b/auth/kerberos/gssapi_helper.c
@@ -0,0 +1,395 @@
+/*
+ Unix SMB/CIFS implementation.
+ GSSAPI helper functions
+
+ Copyright (C) Stefan Metzmacher 2008,2015
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/gssapi.h"
+#include "auth/kerberos/pac_utils.h"
+#include "auth/kerberos/gssapi_helper.h"
+
+size_t gssapi_get_sig_size(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ uint32_t gss_want_flags,
+ size_t data_size)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ size_t sig_size = 0;
+
+ if (gss_want_flags & GSS_C_CONF_FLAG) {
+ OM_uint32 min_stat, maj_stat;
+ bool want_sealing = true;
+ int sealed = 0;
+ gss_iov_buffer_desc iov[2];
+
+ if (!(gss_want_flags & GSS_C_DCE_STYLE)) {
+ TALLOC_FREE(frame);
+ return 0;
+ }
+
+ /*
+ * gss_wrap_iov_length() only needs the type and length
+ */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[0].buffer.value = NULL;
+ iov[0].buffer.length = 0;
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[1].buffer.value = NULL;
+ iov[1].buffer.length = data_size;
+
+ maj_stat = gss_wrap_iov_length(&min_stat,
+ gssapi_context,
+ want_sealing,
+ GSS_C_QOP_DEFAULT,
+ &sealed,
+ iov, ARRAY_SIZE(iov));
+ if (maj_stat) {
+ DEBUG(0, ("gss_wrap_iov_length failed with [%s]\n",
+ gssapi_error_string(frame,
+ maj_stat,
+ min_stat,
+ mech)));
+ TALLOC_FREE(frame);
+ return 0;
+ }
+
+ sig_size = iov[0].buffer.length;
+ } else if (gss_want_flags & GSS_C_INTEG_FLAG) {
+ NTSTATUS status;
+ uint32_t keytype;
+
+ status = gssapi_get_session_key(frame,
+ gssapi_context,
+ NULL, &keytype);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return 0;
+ }
+
+ switch (keytype) {
+ case ENCTYPE_DES_CBC_MD5:
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_ARCFOUR_HMAC:
+ case ENCTYPE_ARCFOUR_HMAC_EXP:
+ sig_size = 37;
+ break;
+ default:
+ sig_size = 28;
+ break;
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return sig_size;
+}
+
+NTSTATUS gssapi_seal_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing, size_t sig_size,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *sig)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_iov_buffer_desc iov[4];
+ int req_seal = 1;
+ int sealed = 0;
+ const uint8_t *pre_sign_ptr = NULL;
+ size_t pre_sign_len = 0;
+ const uint8_t *post_sign_ptr = NULL;
+ size_t post_sign_len = 0;
+
+ if (hdr_signing) {
+ const uint8_t *de = data + length;
+ const uint8_t *we = whole_pdu + pdu_length;
+
+ if (data < whole_pdu) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (de > we) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ pre_sign_len = data - whole_pdu;
+ if (pre_sign_len > 0) {
+ pre_sign_ptr = whole_pdu;
+ }
+ post_sign_len = we - de;
+ if (post_sign_len > 0) {
+ post_sign_ptr = de;
+ }
+ }
+
+ sig->length = sig_size;
+ if (sig->length == 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sig->data = talloc_zero_array(mem_ctx, uint8_t, sig->length);
+ if (sig->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[0].buffer.length = sig->length;
+ iov[0].buffer.value = sig->data;
+
+ if (pre_sign_ptr != NULL) {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[1].buffer.length = pre_sign_len;
+ iov[1].buffer.value = discard_const(pre_sign_ptr);
+ } else {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ iov[1].buffer.length = 0;
+ iov[1].buffer.value = NULL;
+ }
+
+ /* data is encrypted in place, which is ok */
+ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[2].buffer.length = length;
+ iov[2].buffer.value = data;
+
+ if (post_sign_ptr != NULL) {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[3].buffer.length = post_sign_len;
+ iov[3].buffer.value = discard_const(post_sign_ptr);
+ } else {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ iov[3].buffer.length = 0;
+ iov[3].buffer.value = NULL;
+ }
+
+ maj_stat = gss_wrap_iov(&min_stat,
+ gssapi_context,
+ req_seal,
+ GSS_C_QOP_DEFAULT,
+ &sealed,
+ iov, ARRAY_SIZE(iov));
+ if (GSS_ERROR(maj_stat)) {
+ char *error_string = gssapi_error_string(mem_ctx,
+ maj_stat,
+ min_stat,
+ mech);
+ DEBUG(1, ("gss_wrap_iov failed: %s\n", error_string));
+ talloc_free(error_string);
+ data_blob_free(sig);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (req_seal == 1 && sealed == 0) {
+ DEBUG(0, ("gss_wrap_iov says data was not sealed!\n"));
+ data_blob_free(sig);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ dump_data_pw("gssapi_seal_packet: sig\n", sig->data, sig->length);
+ dump_data_pw("gssapi_seal_packet: sealed\n", data, length);
+
+ DEBUG(10, ("Sealed %d bytes, and got %d bytes header/signature.\n",
+ (int)iov[2].buffer.length, (int)iov[0].buffer.length));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gssapi_unseal_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_iov_buffer_desc iov[4];
+ gss_qop_t qop_state;
+ int sealed = 0;
+ const uint8_t *pre_sign_ptr = NULL;
+ size_t pre_sign_len = 0;
+ const uint8_t *post_sign_ptr = NULL;
+ size_t post_sign_len = 0;
+
+ if (hdr_signing) {
+ const uint8_t *de = data + length;
+ const uint8_t *we = whole_pdu + pdu_length;
+
+ if (data < whole_pdu) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (de > we) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ pre_sign_len = data - whole_pdu;
+ if (pre_sign_len > 0) {
+ pre_sign_ptr = whole_pdu;
+ }
+ post_sign_len = we - de;
+ if (post_sign_len > 0) {
+ post_sign_ptr = de;
+ }
+ }
+
+ dump_data_pw("gssapi_unseal_packet: sig\n", sig->data, sig->length);
+ dump_data_pw("gssapi_unseal_packet: sealed\n", data, length);
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[0].buffer.length = sig->length;
+ iov[0].buffer.value = sig->data;
+
+ if (pre_sign_ptr != NULL) {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[1].buffer.length = pre_sign_len;
+ iov[1].buffer.value = discard_const(pre_sign_ptr);
+ } else {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ iov[1].buffer.length = 0;
+ iov[1].buffer.value = NULL;
+ }
+
+ /* data is encrypted in place, which is ok */
+ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[2].buffer.length = length;
+ iov[2].buffer.value = data;
+
+ if (post_sign_ptr != NULL) {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[3].buffer.length = post_sign_len;
+ iov[3].buffer.value = discard_const(post_sign_ptr);
+ } else {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ iov[3].buffer.length = 0;
+ iov[3].buffer.value = NULL;
+ }
+
+ maj_stat = gss_unwrap_iov(&min_stat,
+ gssapi_context,
+ &sealed,
+ &qop_state,
+ iov, ARRAY_SIZE(iov));
+ if (GSS_ERROR(maj_stat)) {
+ char *error_string = gssapi_error_string(NULL,
+ maj_stat,
+ min_stat,
+ mech);
+ DEBUG(1, ("gss_unwrap_iov failed: %s\n", error_string));
+ talloc_free(error_string);
+
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (sealed == 0) {
+ DEBUG(0, ("gss_unwrap_iov says data was not sealed!\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ DEBUG(10, ("Unsealed %d bytes, with %d bytes header/signature.\n",
+ (int)iov[2].buffer.length, (int)iov[0].buffer.length));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gssapi_sign_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *sig)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token, output_token;
+
+ if (hdr_signing) {
+ input_token.length = pdu_length;
+ input_token.value = discard_const_p(uint8_t *, whole_pdu);
+ } else {
+ input_token.length = length;
+ input_token.value = discard_const_p(uint8_t *, data);
+ }
+
+ maj_stat = gss_get_mic(&min_stat,
+ gssapi_context,
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ &output_token);
+ if (GSS_ERROR(maj_stat)) {
+ char *error_string = gssapi_error_string(mem_ctx,
+ maj_stat,
+ min_stat,
+ mech);
+ DEBUG(1, ("GSS GetMic failed: %s\n", error_string));
+ talloc_free(error_string);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, output_token.length);
+ gss_release_buffer(&min_stat, &output_token);
+ if (sig->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dump_data_pw("gssapi_sign_packet: sig\n", sig->data, sig->length);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gssapi_check_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token;
+ gss_buffer_desc input_message;
+ gss_qop_t qop_state;
+
+ dump_data_pw("gssapi_check_packet: sig\n", sig->data, sig->length);
+
+ if (hdr_signing) {
+ input_message.length = pdu_length;
+ input_message.value = discard_const(whole_pdu);
+ } else {
+ input_message.length = length;
+ input_message.value = discard_const(data);
+ }
+
+ input_token.length = sig->length;
+ input_token.value = sig->data;
+
+ maj_stat = gss_verify_mic(&min_stat,
+ gssapi_context,
+ &input_message,
+ &input_token,
+ &qop_state);
+ if (GSS_ERROR(maj_stat)) {
+ char *error_string = gssapi_error_string(NULL,
+ maj_stat,
+ min_stat,
+ mech);
+ DEBUG(1, ("GSS VerifyMic failed: %s\n", error_string));
+ talloc_free(error_string);
+
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/auth/kerberos/gssapi_helper.h b/auth/kerberos/gssapi_helper.h
new file mode 100644
index 00000000000..f40adf1540c
--- /dev/null
+++ b/auth/kerberos/gssapi_helper.h
@@ -0,0 +1,55 @@
+/*
+ Unix SMB/CIFS implementation.
+ GSSAPI helper functions
+
+ Copyright (C) Stefan Metzmacher 2008,2015
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef AUTH_KERBEROS_GSSAPI_HELPER_H
+#define AUTH_KERBEROS_GSSAPI_HELPER_H 1
+
+size_t gssapi_get_sig_size(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ uint32_t gss_want_flags,
+ size_t data_size);
+NTSTATUS gssapi_seal_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing, size_t sig_size,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *sig);
+NTSTATUS gssapi_unseal_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig);
+NTSTATUS gssapi_sign_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *sig);
+NTSTATUS gssapi_check_packet(gss_ctx_id_t gssapi_context,
+ const gss_OID mech,
+ bool hdr_signing,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig);
+
+#endif /* AUTH_KERBEROS_GSSAPI_HELPER_H */
diff --git a/auth/kerberos/wscript_build b/auth/kerberos/wscript_build
index 97b8879c8ef..1fa1b51138d 100755
--- a/auth/kerberos/wscript_build
+++ b/auth/kerberos/wscript_build
@@ -1,4 +1,4 @@
#!/usr/bin/env python
bld.SAMBA_SUBSYSTEM('KRB5_PAC',
- source='gssapi_pac.c kerberos_pac.c',
+ source='gssapi_pac.c kerberos_pac.c gssapi_helper.c',
deps='gssapi_krb5 ndr-krb5pac krb5samba')