s3-dcerpc: Add sign/seal with gssapi
authorSimo Sorce <idra@samba.org>
Sat, 24 Jul 2010 17:02:57 +0000 (13:02 -0400)
committerSimo Sorce <idra@samba.org>
Wed, 28 Jul 2010 16:42:15 +0000 (12:42 -0400)
source3/configure.in
source3/librpc/rpc/dcerpc_gssapi.c
source3/librpc/rpc/dcerpc_gssapi.h
source3/librpc/rpc/dcerpc_helpers.c
source3/rpc_client/cli_pipe.c

index 905ad23efcc0d974c104b56d04de0619b1ae8b6c..4b92cd677e49547b8984710ff7432593fb6faf41 100644 (file)
@@ -3749,7 +3749,7 @@ if test x"$with_ads_support" != x"no"; then
 
   # now check for gssapi headers.  This is also done here to allow for
   # different kerberos include paths
-  AC_CHECK_HEADERS(gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h com_err.h)
+  AC_CHECK_HEADERS(gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h gssapi/gssapi_ext.h com_err.h)
 
   ##################################################################
   # we might need the k5crypto and com_err libraries on some systems
@@ -3774,6 +3774,7 @@ if test x"$with_ads_support" != x"no"; then
   # now see if we can find the gssapi libs in standard paths
   if test x"$have_gssapi" != x"yes"; then
      AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],have_gssapi=yes)
+     AC_CHECK_FUNC_EXT(gss_wrap_iov, $KRB5_LIBS)
   fi
 
   AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS)
index d4153692079cec14defb16e02a02261cfac0ca9e..8c0ad6a8b332550c2c7340ac7783624c2038f58c 100644 (file)
@@ -22,6 +22,7 @@
 #include "includes.h"
 #include <gssapi/gssapi.h>
 #include <gssapi/gssapi_krb5.h>
+#include <gssapi/gssapi_ext.h>
 #include "dcerpc_gssapi.h"
 
 #ifdef HAVE_GSSAPI_H
@@ -413,3 +414,224 @@ DATA_BLOB gse_get_session_key(struct gse_context *gse_ctx)
 }
 
 #endif /* HAVE_GSSAPI_H */
+
+#ifdef HAVE_GSS_WRAP_IOV
+
+size_t gse_get_signature_length(struct gse_context *gse_ctx,
+                               int seal, size_t payload_size)
+{
+       OM_uint32 gss_min, gss_maj;
+       gss_iov_buffer_desc iov[2];
+       uint8_t fakebuf[payload_size];
+       int sealed;
+
+       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 = fakebuf;
+       iov[1].buffer.length = payload_size;
+
+       gss_maj = gss_wrap_iov_length(&gss_min, gse_ctx->gss_ctx,
+                                       seal, GSS_C_QOP_DEFAULT,
+                                       &sealed, iov, 2);
+       if (gss_maj) {
+               DEBUG(0, ("gss_wrap_iov_length failed with [%s]\n",
+                         gse_errstr(talloc_tos(), gss_maj, gss_min)));
+               return 0;
+       }
+
+       return iov[0].buffer.length;
+}
+
+NTSTATUS gse_seal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                 DATA_BLOB *data, DATA_BLOB *signature)
+{
+       OM_uint32 gss_min, gss_maj;
+       gss_iov_buffer_desc iov[2];
+       int req_seal = 1; /* setting to 1 means we request sign+seal */
+       int sealed;
+       NTSTATUS status;
+
+       /* allocate the memory ourselves so we do not need to talloc_memdup */
+       signature->length = gse_get_signature_length(gse_ctx, 1, data->length);
+       if (!signature->length) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+       signature->data = talloc_size(mem_ctx, signature->length);
+       if (!signature->data) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+       iov[0].buffer.value = signature->data;
+       iov[0].buffer.length = signature->length;
+
+       /* data is encrypted in place, which is ok */
+       iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+       iov[1].buffer.value = data->data;
+       iov[1].buffer.length = data->length;
+
+       gss_maj = gss_wrap_iov(&gss_min, gse_ctx->gss_ctx,
+                               req_seal, GSS_C_QOP_DEFAULT,
+                               &sealed, iov, 2);
+       if (gss_maj) {
+               DEBUG(0, ("gss_wrap_iov failed with [%s]\n",
+                         gse_errstr(talloc_tos(), gss_maj, gss_min)));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
+       if (!sealed) {
+               DEBUG(0, ("gss_wrap_iov says data was not sealed!\n"));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
+       status = NT_STATUS_OK;
+
+       DEBUG(10, ("Sealed %d bytes, and got %d bytes header/signature.\n",
+                  (int)iov[1].buffer.length, (int)iov[0].buffer.length));
+
+done:
+       return status;
+}
+
+NTSTATUS gse_unseal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                   DATA_BLOB *data, DATA_BLOB *signature)
+{
+       OM_uint32 gss_min, gss_maj;
+       gss_iov_buffer_desc iov[2];
+       int sealed;
+       NTSTATUS status;
+
+       iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+       iov[0].buffer.value = signature->data;
+       iov[0].buffer.length = signature->length;
+
+       /* data is decrypted in place, which is ok */
+       iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+       iov[1].buffer.value = data->data;
+       iov[1].buffer.length = data->length;
+
+       gss_maj = gss_unwrap_iov(&gss_min, gse_ctx->gss_ctx,
+                                &sealed, NULL, iov, 2);
+       if (gss_maj) {
+               DEBUG(0, ("gss_unwrap_iov failed with [%s]\n",
+                         gse_errstr(talloc_tos(), gss_maj, gss_min)));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
+       if (!sealed) {
+               DEBUG(0, ("gss_unwrap_iov says data is not sealed!\n"));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
+       status = NT_STATUS_OK;
+
+       DEBUG(10, ("Unsealed %d bytes, with %d bytes header/signature.\n",
+                  (int)iov[1].buffer.length, (int)iov[0].buffer.length));
+
+done:
+       return status;
+}
+
+NTSTATUS gse_sign(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                 DATA_BLOB *data, DATA_BLOB *signature)
+{
+       OM_uint32 gss_min, gss_maj;
+       gss_buffer_desc in_data = { 0, NULL };
+       gss_buffer_desc out_data = { 0, NULL};
+       NTSTATUS status;
+
+       in_data.value = data->data;
+       in_data.length = data->length;
+
+       gss_maj = gss_get_mic(&gss_min, gse_ctx->gss_ctx,
+                             GSS_C_QOP_DEFAULT,
+                             &in_data, &out_data);
+       if (gss_maj) {
+               DEBUG(0, ("gss_get_mic failed with [%s]\n",
+                         gse_errstr(talloc_tos(), gss_maj, gss_min)));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
+       *signature = data_blob_talloc(mem_ctx,
+                                       out_data.value, out_data.length);
+       if (!signature->data) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       status = NT_STATUS_OK;
+
+done:
+       if (out_data.value) {
+               gss_maj = gss_release_buffer(&gss_min, &out_data);
+       }
+       return status;
+}
+
+NTSTATUS gse_sigcheck(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                     DATA_BLOB *data, DATA_BLOB *signature)
+{
+       OM_uint32 gss_min, gss_maj;
+       gss_buffer_desc in_data = { 0, NULL };
+       gss_buffer_desc in_token = { 0, NULL};
+       NTSTATUS status;
+
+       in_data.value = data->data;
+       in_data.length = data->length;
+       in_token.value = signature->data;
+       in_token.length = signature->length;
+
+       gss_maj = gss_verify_mic(&gss_min, gse_ctx->gss_ctx,
+                                &in_data, &in_token, NULL);
+       if (gss_maj) {
+               DEBUG(0, ("gss_verify_mic failed with [%s]\n",
+                         gse_errstr(talloc_tos(), gss_maj, gss_min)));
+               status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
+       status = NT_STATUS_OK;
+
+done:
+       return status;
+}
+
+#else /* HAVE_GSS_WRAP_IOV */
+
+size_t gse_get_signature_length(struct gse_context *gse_ctx,
+                               int seal, size_t payload_size)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS gse_seal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                 DATA_BLOB *data, DATA_BLOB *signature)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS gse_unseal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                   DATA_BLOB *data, DATA_BLOB *signature)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS gse_sign(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                 DATA_BLOB *data, DATA_BLOB *signature)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS gse_sigcheck(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                     DATA_BLOB *data, DATA_BLOB *signature)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+#endif /* HAVE_GSS_WRAP_IOV */
index ea44e9e383eb43d01b334c1c59d487cf46baaaa0..6367990ac106579e0cb55f97647d69353a8b5c5a 100644 (file)
@@ -45,4 +45,14 @@ NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
 bool gse_require_more_processing(struct gse_context *gse_ctx);
 DATA_BLOB gse_get_session_key(struct gse_context *gse_ctx);
 
+size_t gse_get_signature_length(struct gse_context *gse_ctx,
+                               int seal, size_t payload_size);
+NTSTATUS gse_seal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                 DATA_BLOB *data, DATA_BLOB *signature);
+NTSTATUS gse_unseal(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                   DATA_BLOB *data, DATA_BLOB *signature);
+NTSTATUS gse_sign(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                 DATA_BLOB *data, DATA_BLOB *signature);
+NTSTATUS gse_sigcheck(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx,
+                     DATA_BLOB *data, DATA_BLOB *signature);
 #endif /* _CLI_PIPE_GSSAPI_H_ */
index be076d86453f1944f76ca7b6ddb8ad447ad0bdc8..4dc3d7f81f2ebdede9c3f0ac7ce6b8a8919a063c 100644 (file)
@@ -26,6 +26,7 @@
 #include "../libcli/auth/spnego.h"
 #include "../libcli/auth/ntlmssp.h"
 #include "ntlmssp_wrap.h"
+#include "librpc/rpc/dcerpc_gssapi.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_RPC_PARSE
@@ -371,6 +372,55 @@ static NTSTATUS add_schannel_auth_footer(struct schannel_state *sas,
        return NT_STATUS_OK;
 }
 
+/*******************************************************************
+ Create and add the gssapi sign/seal auth data.
+ ********************************************************************/
+
+static NTSTATUS add_gssapi_auth_footer(struct gse_context *gse_ctx,
+                                       enum dcerpc_AuthLevel auth_level,
+                                       DATA_BLOB *rpc_out)
+{
+       DATA_BLOB data;
+       DATA_BLOB auth_blob;
+       NTSTATUS status;
+
+       if (!gse_ctx) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       data.data = rpc_out->data + DCERPC_RESPONSE_LENGTH;
+       data.length = rpc_out->length - DCERPC_RESPONSE_LENGTH
+                                       - DCERPC_AUTH_TRAILER_LENGTH;
+
+       switch (auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               status = gse_seal(talloc_tos(), gse_ctx, &data, &auth_blob);
+               break;
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               status = gse_sign(talloc_tos(), gse_ctx, &data, &auth_blob);
+               break;
+       default:
+               status = NT_STATUS_INTERNAL_ERROR;
+               break;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("Failed to process packet: %s\n",
+                         nt_errstr(status)));
+               return status;
+       }
+
+       /* Finally attach the blob. */
+       if (!data_blob_append(NULL, rpc_out,
+                               auth_blob.data, auth_blob.length)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       data_blob_free(&auth_blob);
+
+       return NT_STATUS_OK;
+}
+
 /**
 * @brief   Append an auth footer according to what is the current mechanism
 *
@@ -443,6 +493,11 @@ NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth,
                                                  auth->auth_level,
                                                  rpc_out);
                break;
+       case DCERPC_AUTH_TYPE_KRB5:
+               status = add_gssapi_auth_footer(auth->a_u.gssapi_state,
+                                               auth->auth_level,
+                                               rpc_out);
+               break;
        default:
                status = NT_STATUS_INVALID_PARAMETER;
                break;
@@ -617,6 +672,37 @@ NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth,
                }
                break;
 
+       case DCERPC_AUTH_TYPE_KRB5:
+
+               DEBUG(10, ("KRB5 auth\n"));
+
+               switch (auth->auth_level) {
+               case DCERPC_AUTH_LEVEL_PRIVACY:
+                       status = gse_unseal(pkt, auth->a_u.gssapi_state,
+                                           &data, &auth_info.credentials);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return status;
+                       }
+                       memcpy(pkt_trailer->data, data.data, data.length);
+                       break;
+
+               case DCERPC_AUTH_LEVEL_INTEGRITY:
+                       /* TODO: pass in full_pkt when
+                        * DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN is set */
+                       status = gse_sigcheck(pkt, auth->a_u.gssapi_state,
+                                             &data, &auth_info.credentials);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return status;
+                       }
+                       break;
+
+               default:
+                       DEBUG(0, ("Invalid auth level, "
+                                 "failed to process packet auth.\n"));
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               break;
+
        default:
                DEBUG(0, ("process_request_pdu: "
                          "unknown auth type %u set.\n",
index 4a3229d7cc16b65df78662ac1b07943cf408ac7e..85888755069da4b85363ea4dc72827ef036de612 100644 (file)
@@ -1257,6 +1257,7 @@ static NTSTATUS calculate_data_len_tosend(struct rpc_pipe_client *cli,
                                          uint32_t *p_ss_padding)
 {
        uint32_t data_space, data_len;
+       size_t max_len;
 
        switch (cli->auth->auth_level) {
        case DCERPC_AUTH_LEVEL_NONE:
@@ -1272,6 +1273,10 @@ static NTSTATUS calculate_data_len_tosend(struct rpc_pipe_client *cli,
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
        case DCERPC_AUTH_LEVEL_PRIVACY:
+               max_len = cli->max_xmit_frag
+                               - DCERPC_REQUEST_LENGTH
+                               - DCERPC_AUTH_TRAILER_LENGTH;
+
                /* Treat the same for all authenticated rpc requests. */
                switch(cli->auth->auth_type) {
                case DCERPC_AUTH_TYPE_SPNEGO:
@@ -1280,7 +1285,7 @@ static NTSTATUS calculate_data_len_tosend(struct rpc_pipe_client *cli,
                                *p_auth_len = NTLMSSP_SIG_SIZE;
                                break;
                        case PIPE_AUTH_TYPE_SPNEGO_KRB5:
-                               *p_auth_len = 0; /* no signing */
+                               *p_auth_len = 0; /* TODO */
                                break;
                        default:
                                return NT_STATUS_INVALID_PARAMETER;
@@ -1292,16 +1297,17 @@ static NTSTATUS calculate_data_len_tosend(struct rpc_pipe_client *cli,
                        *p_auth_len = NL_AUTH_SIGNATURE_SIZE;
                        break;
                case DCERPC_AUTH_TYPE_KRB5:
-                       *p_auth_len = 0; /* no signing */
+                       *p_auth_len = gse_get_signature_length(
+                                       cli->auth->a_u.gssapi_state,
+                                       (cli->auth->auth_level ==
+                                               DCERPC_AUTH_LEVEL_PRIVACY),
+                                       max_len);
                        break;
                default:
                        return NT_STATUS_INVALID_PARAMETER;
                }
 
-               data_space = cli->max_xmit_frag
-                               - DCERPC_REQUEST_LENGTH
-                               - DCERPC_AUTH_TRAILER_LENGTH
-                               - *p_auth_len;
+               data_space = max_len - *p_auth_len;
 
                data_len = MIN(data_space, data_left);
                *p_ss_padding = 0;