1253893Speter/* Copyright 2010 Justin Erenkrantz and Greg Stein 2253893Speter * 3253893Speter * Licensed under the Apache License, Version 2.0 (the "License"); 4253893Speter * you may not use this file except in compliance with the License. 5253893Speter * You may obtain a copy of the License at 6253893Speter * 7253893Speter * http://www.apache.org/licenses/LICENSE-2.0 8253893Speter * 9253893Speter * Unless required by applicable law or agreed to in writing, software 10253893Speter * distributed under the License is distributed on an "AS IS" BASIS, 11253893Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12253893Speter * See the License for the specific language governing permissions and 13253893Speter * limitations under the License. 14253893Speter */ 15253893Speter 16253893Speter#include "auth_spnego.h" 17253893Speter#include "serf.h" 18253893Speter#include "serf_private.h" 19253893Speter 20253893Speter#ifdef SERF_USE_SSPI 21253893Speter#include <apr.h> 22253893Speter#include <apr_strings.h> 23253893Speter 24253893Speter#define SECURITY_WIN32 25253893Speter#include <sspi.h> 26253893Speter 27253893Speter/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */ 28253893Speter#ifndef SEC_E_MUTUAL_AUTH_FAILED 29253893Speter#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L) 30253893Speter#endif 31253893Speter 32253893Speterstruct serf__spnego_context_t 33253893Speter{ 34253893Speter CredHandle sspi_credentials; 35253893Speter CtxtHandle sspi_context; 36253893Speter BOOL initalized; 37253893Speter apr_pool_t *pool; 38253893Speter 39253893Speter /* Service Principal Name (SPN) used for authentication. */ 40253893Speter const char *target_name; 41253893Speter 42253893Speter /* One of SERF_AUTHN_* authentication types.*/ 43253893Speter int authn_type; 44253893Speter}; 45253893Speter 46253893Speter/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped 47253893Speter * to our own codes and some to Win32 error codes: 48253893Speter * http://support.microsoft.com/kb/113996 49253893Speter */ 50253893Speterstatic apr_status_t 51253893Spetermap_sspi_status(SECURITY_STATUS sspi_status) 52253893Speter{ 53253893Speter switch(sspi_status) 54253893Speter { 55253893Speter case SEC_E_INSUFFICIENT_MEMORY: 56253893Speter return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES); 57253893Speter case SEC_E_INVALID_HANDLE: 58253893Speter return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE); 59253893Speter case SEC_E_UNSUPPORTED_FUNCTION: 60253893Speter return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION); 61253893Speter case SEC_E_TARGET_UNKNOWN: 62253893Speter return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH); 63253893Speter case SEC_E_INTERNAL_ERROR: 64253893Speter return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR); 65253893Speter case SEC_E_SECPKG_NOT_FOUND: 66253893Speter case SEC_E_BAD_PKGID: 67253893Speter return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE); 68253893Speter case SEC_E_NO_IMPERSONATION: 69253893Speter return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE); 70253893Speter case SEC_E_NO_AUTHENTICATING_AUTHORITY: 71253893Speter return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS); 72253893Speter case SEC_E_UNTRUSTED_ROOT: 73253893Speter return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE); 74253893Speter case SEC_E_WRONG_PRINCIPAL: 75253893Speter return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME); 76253893Speter case SEC_E_MUTUAL_AUTH_FAILED: 77253893Speter return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED); 78253893Speter case SEC_E_TIME_SKEW: 79253893Speter return APR_FROM_OS_ERROR(ERROR_TIME_SKEW); 80253893Speter default: 81253893Speter return SERF_ERROR_AUTHN_FAILED; 82253893Speter } 83253893Speter} 84253893Speter 85253893Speter/* Cleans the SSPI context object, when the pool used to create it gets 86253893Speter cleared or destroyed. */ 87253893Speterstatic apr_status_t 88253893Spetercleanup_ctx(void *data) 89253893Speter{ 90253893Speter serf__spnego_context_t *ctx = data; 91253893Speter 92253893Speter if (SecIsValidHandle(&ctx->sspi_context)) { 93253893Speter DeleteSecurityContext(&ctx->sspi_context); 94253893Speter SecInvalidateHandle(&ctx->sspi_context); 95253893Speter } 96253893Speter 97253893Speter if (SecIsValidHandle(&ctx->sspi_credentials)) { 98289166Speter FreeCredentialsHandle(&ctx->sspi_credentials); 99289166Speter SecInvalidateHandle(&ctx->sspi_credentials); 100253893Speter } 101253893Speter 102253893Speter return APR_SUCCESS; 103253893Speter} 104253893Speter 105253893Speterstatic apr_status_t 106253893Spetercleanup_sec_buffer(void *data) 107253893Speter{ 108253893Speter FreeContextBuffer(data); 109253893Speter 110253893Speter return APR_SUCCESS; 111253893Speter} 112253893Speter 113253893Speterapr_status_t 114253893Speterserf__spnego_create_sec_context(serf__spnego_context_t **ctx_p, 115253893Speter const serf__authn_scheme_t *scheme, 116253893Speter apr_pool_t *result_pool, 117253893Speter apr_pool_t *scratch_pool) 118253893Speter{ 119253893Speter SECURITY_STATUS sspi_status; 120253893Speter serf__spnego_context_t *ctx; 121253893Speter const char *sspi_package; 122253893Speter 123253893Speter ctx = apr_pcalloc(result_pool, sizeof(*ctx)); 124253893Speter 125253893Speter SecInvalidateHandle(&ctx->sspi_context); 126253893Speter SecInvalidateHandle(&ctx->sspi_credentials); 127253893Speter ctx->initalized = FALSE; 128253893Speter ctx->pool = result_pool; 129253893Speter ctx->target_name = NULL; 130253893Speter ctx->authn_type = scheme->type; 131253893Speter 132253893Speter apr_pool_cleanup_register(result_pool, ctx, 133253893Speter cleanup_ctx, 134253893Speter apr_pool_cleanup_null); 135253893Speter 136253893Speter if (ctx->authn_type == SERF_AUTHN_NEGOTIATE) 137253893Speter sspi_package = "Negotiate"; 138253893Speter else 139253893Speter sspi_package = "NTLM"; 140253893Speter 141253893Speter sspi_status = AcquireCredentialsHandle( 142253893Speter NULL, sspi_package, SECPKG_CRED_OUTBOUND, 143253893Speter NULL, NULL, NULL, NULL, 144253893Speter &ctx->sspi_credentials, NULL); 145253893Speter 146253893Speter if (FAILED(sspi_status)) { 147253893Speter return map_sspi_status(sspi_status); 148253893Speter } 149253893Speter 150253893Speter *ctx_p = ctx; 151253893Speter 152253893Speter return APR_SUCCESS; 153253893Speter} 154253893Speter 155253893Speterstatic apr_status_t 156253893Speterget_canonical_hostname(const char **canonname, 157253893Speter const char *hostname, 158253893Speter apr_pool_t *pool) 159253893Speter{ 160253893Speter struct addrinfo hints; 161253893Speter struct addrinfo *addrinfo; 162253893Speter 163253893Speter ZeroMemory(&hints, sizeof(hints)); 164253893Speter hints.ai_flags = AI_CANONNAME; 165253893Speter 166253893Speter if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) { 167253893Speter return apr_get_netos_error(); 168253893Speter } 169253893Speter 170253893Speter if (addrinfo) { 171253893Speter *canonname = apr_pstrdup(pool, addrinfo->ai_canonname); 172253893Speter } 173253893Speter else { 174253893Speter *canonname = apr_pstrdup(pool, hostname); 175253893Speter } 176253893Speter 177253893Speter freeaddrinfo(addrinfo); 178253893Speter return APR_SUCCESS; 179253893Speter} 180253893Speter 181253893Speterapr_status_t 182253893Speterserf__spnego_reset_sec_context(serf__spnego_context_t *ctx) 183253893Speter{ 184253893Speter if (SecIsValidHandle(&ctx->sspi_context)) { 185253893Speter DeleteSecurityContext(&ctx->sspi_context); 186253893Speter SecInvalidateHandle(&ctx->sspi_context); 187253893Speter } 188253893Speter 189253893Speter ctx->initalized = FALSE; 190253893Speter 191253893Speter return APR_SUCCESS; 192253893Speter} 193253893Speter 194253893Speterapr_status_t 195262339Speterserf__spnego_init_sec_context(serf_connection_t *conn, 196262339Speter serf__spnego_context_t *ctx, 197253893Speter const char *service, 198253893Speter const char *hostname, 199253893Speter serf__spnego_buffer_t *input_buf, 200253893Speter serf__spnego_buffer_t *output_buf, 201253893Speter apr_pool_t *result_pool, 202253893Speter apr_pool_t *scratch_pool 203253893Speter ) 204253893Speter{ 205253893Speter SECURITY_STATUS status; 206253893Speter ULONG actual_attr; 207253893Speter SecBuffer sspi_in_buffer; 208253893Speter SecBufferDesc sspi_in_buffer_desc; 209253893Speter SecBuffer sspi_out_buffer; 210253893Speter SecBufferDesc sspi_out_buffer_desc; 211253893Speter apr_status_t apr_status; 212253893Speter const char *canonname; 213253893Speter 214253893Speter if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) { 215253893Speter apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool); 216253893Speter if (apr_status) { 217253893Speter return apr_status; 218253893Speter } 219253893Speter 220253893Speter ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname, 221253893Speter NULL); 222253893Speter 223262339Speter serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 224262339Speter "Using SPN '%s' for '%s'\n", ctx->target_name, hostname); 225253893Speter } 226253893Speter else if (ctx->authn_type == SERF_AUTHN_NTLM) 227253893Speter { 228253893Speter /* Target name is not used for NTLM authentication. */ 229253893Speter ctx->target_name = NULL; 230253893Speter } 231253893Speter 232253893Speter /* Prepare input buffer description. */ 233253893Speter sspi_in_buffer.BufferType = SECBUFFER_TOKEN; 234253893Speter sspi_in_buffer.pvBuffer = input_buf->value; 235253893Speter sspi_in_buffer.cbBuffer = input_buf->length; 236253893Speter 237253893Speter sspi_in_buffer_desc.cBuffers = 1; 238253893Speter sspi_in_buffer_desc.pBuffers = &sspi_in_buffer; 239253893Speter sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION; 240253893Speter 241253893Speter /* Output buffers. Output buffer will be allocated by system. */ 242253893Speter sspi_out_buffer.BufferType = SECBUFFER_TOKEN; 243253893Speter sspi_out_buffer.pvBuffer = NULL; 244253893Speter sspi_out_buffer.cbBuffer = 0; 245253893Speter 246253893Speter sspi_out_buffer_desc.cBuffers = 1; 247253893Speter sspi_out_buffer_desc.pBuffers = &sspi_out_buffer; 248253893Speter sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION; 249253893Speter 250253893Speter status = InitializeSecurityContext( 251253893Speter &ctx->sspi_credentials, 252253893Speter ctx->initalized ? &ctx->sspi_context : NULL, 253253893Speter ctx->target_name, 254253893Speter ISC_REQ_ALLOCATE_MEMORY 255253893Speter | ISC_REQ_MUTUAL_AUTH 256253893Speter | ISC_REQ_CONFIDENTIALITY, 257253893Speter 0, /* Reserved1 */ 258253893Speter SECURITY_NETWORK_DREP, 259253893Speter &sspi_in_buffer_desc, 260253893Speter 0, /* Reserved2 */ 261253893Speter &ctx->sspi_context, 262253893Speter &sspi_out_buffer_desc, 263253893Speter &actual_attr, 264253893Speter NULL); 265253893Speter 266253893Speter if (sspi_out_buffer.cbBuffer > 0) { 267253893Speter apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer, 268253893Speter cleanup_sec_buffer, 269253893Speter apr_pool_cleanup_null); 270253893Speter } 271253893Speter 272253893Speter ctx->initalized = TRUE; 273253893Speter 274253893Speter /* Finish authentication if SSPI requires so. */ 275253893Speter if (status == SEC_I_COMPLETE_NEEDED 276253893Speter || status == SEC_I_COMPLETE_AND_CONTINUE) 277253893Speter { 278253893Speter CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc); 279253893Speter } 280253893Speter 281253893Speter output_buf->value = sspi_out_buffer.pvBuffer; 282253893Speter output_buf->length = sspi_out_buffer.cbBuffer; 283253893Speter 284253893Speter switch(status) { 285253893Speter case SEC_I_COMPLETE_AND_CONTINUE: 286253893Speter case SEC_I_CONTINUE_NEEDED: 287253893Speter return APR_EAGAIN; 288253893Speter 289253893Speter case SEC_I_COMPLETE_NEEDED: 290253893Speter case SEC_E_OK: 291253893Speter return APR_SUCCESS; 292253893Speter 293253893Speter default: 294253893Speter return map_sspi_status(status); 295253893Speter } 296253893Speter} 297253893Speter 298253893Speter#endif /* SERF_USE_SSPI */ 299