From 7eaa15af2c5b544946bfb2b8c522ba9677527972 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Sat, 24 Jul 2010 13:02:57 -0400 Subject: s3-dcerpc: Add sign/seal with gssapi --- source3/configure.in | 3 +- source3/librpc/rpc/dcerpc_gssapi.c | 222 ++++++++++++++++++++++++++++++++++++ source3/librpc/rpc/dcerpc_gssapi.h | 10 ++ source3/librpc/rpc/dcerpc_helpers.c | 86 ++++++++++++++ source3/rpc_client/cli_pipe.c | 18 ++- 5 files changed, 332 insertions(+), 7 deletions(-) (limited to 'source3') diff --git a/source3/configure.in b/source3/configure.in index 905ad23efc..4b92cd677e 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -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) diff --git a/source3/librpc/rpc/dcerpc_gssapi.c b/source3/librpc/rpc/dcerpc_gssapi.c index d415369207..8c0ad6a8b3 100644 --- a/source3/librpc/rpc/dcerpc_gssapi.c +++ b/source3/librpc/rpc/dcerpc_gssapi.c @@ -22,6 +22,7 @@ #include "includes.h" #include #include +#include #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 */ diff --git a/source3/librpc/rpc/dcerpc_gssapi.h b/source3/librpc/rpc/dcerpc_gssapi.h index ea44e9e383..6367990ac1 100644 --- a/source3/librpc/rpc/dcerpc_gssapi.h +++ b/source3/librpc/rpc/dcerpc_gssapi.h @@ -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_ */ diff --git a/source3/librpc/rpc/dcerpc_helpers.c b/source3/librpc/rpc/dcerpc_helpers.c index be076d8645..4dc3d7f81f 100644 --- a/source3/librpc/rpc/dcerpc_helpers.c +++ b/source3/librpc/rpc/dcerpc_helpers.c @@ -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", diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index 4a3229d7cc..8588875506 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -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; -- cgit