summaryrefslogtreecommitdiffstats
path: root/contrib/serf/auth/auth_spnego_sspi.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/serf/auth/auth_spnego_sspi.c')
-rw-r--r--contrib/serf/auth/auth_spnego_sspi.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/contrib/serf/auth/auth_spnego_sspi.c b/contrib/serf/auth/auth_spnego_sspi.c
new file mode 100644
index 0000000..ef13428
--- /dev/null
+++ b/contrib/serf/auth/auth_spnego_sspi.c
@@ -0,0 +1,297 @@
+/* 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_spnego.h"
+#include "serf.h"
+#include "serf_private.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__spnego_context_t
+{
+ CredHandle sspi_credentials;
+ CtxtHandle sspi_context;
+ BOOL initalized;
+ apr_pool_t *pool;
+
+ /* Service Principal Name (SPN) used for authentication. */
+ const char *target_name;
+
+ /* One of SERF_AUTHN_* authentication types.*/
+ int authn_type;
+};
+
+/* 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__spnego_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__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
+ const serf__authn_scheme_t *scheme,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ SECURITY_STATUS sspi_status;
+ serf__spnego_context_t *ctx;
+ const char *sspi_package;
+
+ ctx = apr_pcalloc(result_pool, sizeof(*ctx));
+
+ SecInvalidateHandle(&ctx->sspi_context);
+ SecInvalidateHandle(&ctx->sspi_credentials);
+ ctx->initalized = FALSE;
+ ctx->pool = result_pool;
+ ctx->target_name = NULL;
+ ctx->authn_type = scheme->type;
+
+ apr_pool_cleanup_register(result_pool, ctx,
+ cleanup_ctx,
+ apr_pool_cleanup_null);
+
+ if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
+ sspi_package = "Negotiate";
+ else
+ sspi_package = "NTLM";
+
+ sspi_status = AcquireCredentialsHandle(
+ NULL, sspi_package, 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__spnego_reset_sec_context(serf__spnego_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__spnego_init_sec_context(serf__spnego_context_t *ctx,
+ const char *service,
+ const char *hostname,
+ serf__spnego_buffer_t *input_buf,
+ serf__spnego_buffer_t *output_buf,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_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;
+ apr_status_t apr_status;
+ const char *canonname;
+
+ if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
+ apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
+ if (apr_status) {
+ return apr_status;
+ }
+
+ ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
+ NULL);
+
+ serf__log(AUTH_VERBOSE, __FILE__,
+ "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
+ }
+ else if (ctx->authn_type == SERF_AUTHN_NTLM)
+ {
+ /* Target name is not used for NTLM authentication. */
+ ctx->target_name = 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,
+ ctx->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 */
OpenPOWER on IntegriCloud