diff options
Diffstat (limited to 'auth/auth_kerb_sspi.c')
| -rw-r--r-- | auth/auth_kerb_sspi.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/auth/auth_kerb_sspi.c b/auth/auth_kerb_sspi.c new file mode 100644 index 000000000000..ba023cb23002 --- /dev/null +++ b/auth/auth_kerb_sspi.c @@ -0,0 +1,268 @@ +/* Copyright 2010 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "auth_kerb.h" +#include "serf.h" + +#ifdef SERF_USE_SSPI +#include <apr.h> +#include <apr_strings.h> + +#define SECURITY_WIN32 +#include <sspi.h> + +/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */ +#ifndef SEC_E_MUTUAL_AUTH_FAILED +#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L) +#endif + +struct serf__kerb_context_t +{ + CredHandle sspi_credentials; + CtxtHandle sspi_context; + BOOL initalized; +}; + +/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped + * to our own codes and some to Win32 error codes: + * http://support.microsoft.com/kb/113996 + */ +static apr_status_t +map_sspi_status(SECURITY_STATUS sspi_status) +{ + switch(sspi_status) + { + case SEC_E_INSUFFICIENT_MEMORY: + return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES); + case SEC_E_INVALID_HANDLE: + return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE); + case SEC_E_UNSUPPORTED_FUNCTION: + return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION); + case SEC_E_TARGET_UNKNOWN: + return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH); + case SEC_E_INTERNAL_ERROR: + return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR); + case SEC_E_SECPKG_NOT_FOUND: + case SEC_E_BAD_PKGID: + return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE); + case SEC_E_NO_IMPERSONATION: + return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE); + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS); + case SEC_E_UNTRUSTED_ROOT: + return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE); + case SEC_E_WRONG_PRINCIPAL: + return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME); + case SEC_E_MUTUAL_AUTH_FAILED: + return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED); + case SEC_E_TIME_SKEW: + return APR_FROM_OS_ERROR(ERROR_TIME_SKEW); + default: + return SERF_ERROR_AUTHN_FAILED; + } +} + +/* Cleans the SSPI context object, when the pool used to create it gets + cleared or destroyed. */ +static apr_status_t +cleanup_ctx(void *data) +{ + serf__kerb_context_t *ctx = data; + + if (SecIsValidHandle(&ctx->sspi_context)) { + DeleteSecurityContext(&ctx->sspi_context); + SecInvalidateHandle(&ctx->sspi_context); + } + + if (SecIsValidHandle(&ctx->sspi_credentials)) { + FreeCredentialsHandle(&ctx->sspi_context); + SecInvalidateHandle(&ctx->sspi_context); + } + + return APR_SUCCESS; +} + +static apr_status_t +cleanup_sec_buffer(void *data) +{ + FreeContextBuffer(data); + + return APR_SUCCESS; +} + +apr_status_t +serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p, + apr_pool_t *scratch_pool, + apr_pool_t *result_pool) +{ + SECURITY_STATUS sspi_status; + serf__kerb_context_t *ctx; + + ctx = apr_pcalloc(result_pool, sizeof(*ctx)); + + SecInvalidateHandle(&ctx->sspi_context); + SecInvalidateHandle(&ctx->sspi_credentials); + ctx->initalized = FALSE; + + apr_pool_cleanup_register(result_pool, ctx, + cleanup_ctx, + apr_pool_cleanup_null); + + sspi_status = AcquireCredentialsHandle( + NULL, "Negotiate", SECPKG_CRED_OUTBOUND, + NULL, NULL, NULL, NULL, + &ctx->sspi_credentials, NULL); + + if (FAILED(sspi_status)) { + return map_sspi_status(sspi_status); + } + + *ctx_p = ctx; + + return APR_SUCCESS; +} + +static apr_status_t +get_canonical_hostname(const char **canonname, + const char *hostname, + apr_pool_t *pool) +{ + struct addrinfo hints; + struct addrinfo *addrinfo; + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + + if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) { + return apr_get_netos_error(); + } + + if (addrinfo) { + *canonname = apr_pstrdup(pool, addrinfo->ai_canonname); + } + else { + *canonname = apr_pstrdup(pool, hostname); + } + + freeaddrinfo(addrinfo); + return APR_SUCCESS; +} + +apr_status_t +serf__kerb_reset_sec_context(serf__kerb_context_t *ctx) +{ + if (SecIsValidHandle(&ctx->sspi_context)) { + DeleteSecurityContext(&ctx->sspi_context); + SecInvalidateHandle(&ctx->sspi_context); + } + + ctx->initalized = FALSE; + + return APR_SUCCESS; +} + +apr_status_t +serf__kerb_init_sec_context(serf__kerb_context_t *ctx, + const char *service, + const char *hostname, + serf__kerb_buffer_t *input_buf, + serf__kerb_buffer_t *output_buf, + apr_pool_t *scratch_pool, + apr_pool_t *result_pool + ) +{ + SECURITY_STATUS status; + ULONG actual_attr; + SecBuffer sspi_in_buffer; + SecBufferDesc sspi_in_buffer_desc; + SecBuffer sspi_out_buffer; + SecBufferDesc sspi_out_buffer_desc; + char *target_name; + apr_status_t apr_status; + const char *canonname; + + apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool); + if (apr_status) { + return apr_status; + } + target_name = apr_pstrcat(scratch_pool, service, "/", canonname, NULL); + + /* Prepare input buffer description. */ + sspi_in_buffer.BufferType = SECBUFFER_TOKEN; + sspi_in_buffer.pvBuffer = input_buf->value; + sspi_in_buffer.cbBuffer = input_buf->length; + + sspi_in_buffer_desc.cBuffers = 1; + sspi_in_buffer_desc.pBuffers = &sspi_in_buffer; + sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION; + + /* Output buffers. Output buffer will be allocated by system. */ + sspi_out_buffer.BufferType = SECBUFFER_TOKEN; + sspi_out_buffer.pvBuffer = NULL; + sspi_out_buffer.cbBuffer = 0; + + sspi_out_buffer_desc.cBuffers = 1; + sspi_out_buffer_desc.pBuffers = &sspi_out_buffer; + sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION; + + status = InitializeSecurityContext( + &ctx->sspi_credentials, + ctx->initalized ? &ctx->sspi_context : NULL, + target_name, + ISC_REQ_ALLOCATE_MEMORY + | ISC_REQ_MUTUAL_AUTH + | ISC_REQ_CONFIDENTIALITY, + 0, /* Reserved1 */ + SECURITY_NETWORK_DREP, + &sspi_in_buffer_desc, + 0, /* Reserved2 */ + &ctx->sspi_context, + &sspi_out_buffer_desc, + &actual_attr, + NULL); + + if (sspi_out_buffer.cbBuffer > 0) { + apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer, + cleanup_sec_buffer, + apr_pool_cleanup_null); + } + + ctx->initalized = TRUE; + + /* Finish authentication if SSPI requires so. */ + if (status == SEC_I_COMPLETE_NEEDED + || status == SEC_I_COMPLETE_AND_CONTINUE) + { + CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc); + } + + output_buf->value = sspi_out_buffer.pvBuffer; + output_buf->length = sspi_out_buffer.cbBuffer; + + switch(status) { + case SEC_I_COMPLETE_AND_CONTINUE: + case SEC_I_CONTINUE_NEEDED: + return APR_EAGAIN; + + case SEC_I_COMPLETE_NEEDED: + case SEC_E_OK: + return APR_SUCCESS; + + default: + return map_sspi_status(status); + } +} + +#endif /* SERF_USE_SSPI */ |
