auth_spnego_sspi.c revision 262339
1139969Simp/* Copyright 2010 Justin Erenkrantz and Greg Stein 21556Srgrimes * 31556Srgrimes * Licensed under the Apache License, Version 2.0 (the "License"); 41556Srgrimes * you may not use this file except in compliance with the License. 51556Srgrimes * You may obtain a copy of the License at 61556Srgrimes * 71556Srgrimes * http://www.apache.org/licenses/LICENSE-2.0 81556Srgrimes * 91556Srgrimes * Unless required by applicable law or agreed to in writing, software 101556Srgrimes * distributed under the License is distributed on an "AS IS" BASIS, 111556Srgrimes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 121556Srgrimes * See the License for the specific language governing permissions and 131556Srgrimes * limitations under the License. 141556Srgrimes */ 151556Srgrimes 161556Srgrimes#include "auth_spnego.h" 171556Srgrimes#include "serf.h" 181556Srgrimes#include "serf_private.h" 191556Srgrimes 201556Srgrimes#ifdef SERF_USE_SSPI 211556Srgrimes#include <apr.h> 221556Srgrimes#include <apr_strings.h> 231556Srgrimes 241556Srgrimes#define SECURITY_WIN32 251556Srgrimes#include <sspi.h> 261556Srgrimes 271556Srgrimes/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */ 281556Srgrimes#ifndef SEC_E_MUTUAL_AUTH_FAILED 29219154Sjilles#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L) 30219154Sjilles#endif 31219154Sjilles 32219154Sjillesstruct serf__spnego_context_t 331556Srgrimes{ 34114433Sobrien CredHandle sspi_credentials; 351556Srgrimes CtxtHandle sspi_context; 3620416Ssteve BOOL initalized; 371556Srgrimes apr_pool_t *pool; 381556Srgrimes 391556Srgrimes /* Service Principal Name (SPN) used for authentication. */ 401556Srgrimes const char *target_name; 411556Srgrimes 4236012Scharnier /* One of SERF_AUTHN_* authentication types.*/ 43114433Sobrien int authn_type; 4436012Scharnier}; 4599109Sobrien 4699109Sobrien/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped 471556Srgrimes * to our own codes and some to Win32 error codes: 481556Srgrimes * http://support.microsoft.com/kb/113996 491556Srgrimes */ 501556Srgrimesstatic apr_status_t 511556Srgrimesmap_sspi_status(SECURITY_STATUS sspi_status) 521556Srgrimes{ 531556Srgrimes switch(sspi_status) 541556Srgrimes { 551556Srgrimes case SEC_E_INSUFFICIENT_MEMORY: 56216629Sjilles return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES); 57216629Sjilles case SEC_E_INVALID_HANDLE: 58216629Sjilles return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE); 59216629Sjilles case SEC_E_UNSUPPORTED_FUNCTION: 60216629Sjilles return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION); 61216629Sjilles case SEC_E_TARGET_UNKNOWN: 62127005Sjmallett return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH); 63127005Sjmallett case SEC_E_INTERNAL_ERROR: 64127005Sjmallett return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR); 65127005Sjmallett case SEC_E_SECPKG_NOT_FOUND: 661556Srgrimes case SEC_E_BAD_PKGID: 671556Srgrimes return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE); 6890110Simp case SEC_E_NO_IMPERSONATION: 691556Srgrimes return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE); 70315722Sbdrewery case SEC_E_NO_AUTHENTICATING_AUTHORITY: 71315722Sbdrewery return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS); 72315722Sbdrewery case SEC_E_UNTRUSTED_ROOT: 731556Srgrimes return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE); 741556Srgrimes case SEC_E_WRONG_PRINCIPAL: 751556Srgrimes return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME); 761556Srgrimes case SEC_E_MUTUAL_AUTH_FAILED: 771556Srgrimes return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED); 7820416Ssteve case SEC_E_TIME_SKEW: 7920416Ssteve return APR_FROM_OS_ERROR(ERROR_TIME_SKEW); 8020416Ssteve default: 8120416Ssteve return SERF_ERROR_AUTHN_FAILED; 8220416Ssteve } 8320416Ssteve} 8420416Ssteve 8520416Ssteve/* Cleans the SSPI context object, when the pool used to create it gets 8620416Ssteve cleared or destroyed. */ 8720416Sstevestatic apr_status_t 8820416Sstevecleanup_ctx(void *data) 8928554Sjlemon{ 90216629Sjilles serf__spnego_context_t *ctx = data; 9120416Ssteve 9220416Ssteve if (SecIsValidHandle(&ctx->sspi_context)) { 93125156Snjl DeleteSecurityContext(&ctx->sspi_context); 9420416Ssteve SecInvalidateHandle(&ctx->sspi_context); 9520416Ssteve } 96216629Sjilles 9720416Ssteve if (SecIsValidHandle(&ctx->sspi_credentials)) { 9820416Ssteve FreeCredentialsHandle(&ctx->sspi_context); 99216629Sjilles SecInvalidateHandle(&ctx->sspi_context); 1001556Srgrimes } 1011556Srgrimes 10220416Ssteve return APR_SUCCESS; 10320416Ssteve} 10420416Ssteve 10520416Sstevestatic apr_status_t 10620416Sstevecleanup_sec_buffer(void *data) 10720416Ssteve{ 10820416Ssteve FreeContextBuffer(data); 10920416Ssteve 11020416Ssteve return APR_SUCCESS; 11120416Ssteve} 11220416Ssteve 11320416Ssteveapr_status_t 11498158Stjrserf__spnego_create_sec_context(serf__spnego_context_t **ctx_p, 1151556Srgrimes const serf__authn_scheme_t *scheme, 1161556Srgrimes apr_pool_t *result_pool, 11720416Ssteve apr_pool_t *scratch_pool) 1181556Srgrimes{ 1191556Srgrimes SECURITY_STATUS sspi_status; 1201556Srgrimes serf__spnego_context_t *ctx; 12128554Sjlemon const char *sspi_package; 122216629Sjilles 123204308Skib ctx = apr_pcalloc(result_pool, sizeof(*ctx)); 1241556Srgrimes 1251556Srgrimes SecInvalidateHandle(&ctx->sspi_context); 1261556Srgrimes SecInvalidateHandle(&ctx->sspi_credentials); 12720416Ssteve ctx->initalized = FALSE; 1281556Srgrimes ctx->pool = result_pool; 1291556Srgrimes ctx->target_name = NULL; 13098158Stjr ctx->authn_type = scheme->type; 13198158Stjr 13298158Stjr apr_pool_cleanup_register(result_pool, ctx, 13320416Ssteve cleanup_ctx, 1341556Srgrimes apr_pool_cleanup_null); 1351556Srgrimes 13620416Ssteve if (ctx->authn_type == SERF_AUTHN_NEGOTIATE) 137216629Sjilles sspi_package = "Negotiate"; 138216629Sjilles else 139216629Sjilles sspi_package = "NTLM"; 140216629Sjilles 141216629Sjilles sspi_status = AcquireCredentialsHandle( 142216629Sjilles NULL, sspi_package, SECPKG_CRED_OUTBOUND, 143315722Sbdrewery NULL, NULL, NULL, NULL, 144315722Sbdrewery &ctx->sspi_credentials, NULL); 145315722Sbdrewery 146315722Sbdrewery if (FAILED(sspi_status)) { 147216629Sjilles return map_sspi_status(sspi_status); 148216629Sjilles } 149216629Sjilles 1501556Srgrimes *ctx_p = ctx; 1511556Srgrimes 1521556Srgrimes return APR_SUCCESS; 1531556Srgrimes} 15420416Ssteve 155216629Sjillesstatic apr_status_t 1561556Srgrimesget_canonical_hostname(const char **canonname, 1571556Srgrimes const char *hostname, 158127005Sjmallett apr_pool_t *pool) 159127005Sjmallett{ 16020416Ssteve struct addrinfo hints; 16120416Ssteve struct addrinfo *addrinfo; 16220416Ssteve 163250035Seadler ZeroMemory(&hints, sizeof(hints)); 16420416Ssteve hints.ai_flags = AI_CANONNAME; 165125156Snjl 16620416Ssteve if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) { 16720416Ssteve return apr_get_netos_error(); 16820416Ssteve } 16920416Ssteve 17020416Ssteve if (addrinfo) { 17120416Ssteve *canonname = apr_pstrdup(pool, addrinfo->ai_canonname); 172127005Sjmallett } 173127005Sjmallett else { 1741556Srgrimes *canonname = apr_pstrdup(pool, hostname); 1751556Srgrimes } 1761556Srgrimes 17720416Ssteve freeaddrinfo(addrinfo); 178216629Sjilles return APR_SUCCESS; 179216629Sjilles} 180216629Sjilles 181216629Sjillesapr_status_t 182216629Sjillesserf__spnego_reset_sec_context(serf__spnego_context_t *ctx) 1831556Srgrimes{ 1841556Srgrimes if (SecIsValidHandle(&ctx->sspi_context)) { 185127005Sjmallett DeleteSecurityContext(&ctx->sspi_context); 18690110Simp SecInvalidateHandle(&ctx->sspi_context); 1871556Srgrimes } 18820416Ssteve 1891556Srgrimes ctx->initalized = FALSE; 190125156Snjl 19120416Ssteve return APR_SUCCESS; 192125156Snjl} 1931556Srgrimes 19420416Ssteveapr_status_t 19520416Ssteveserf__spnego_init_sec_context(serf_connection_t *conn, 1961556Srgrimes serf__spnego_context_t *ctx, 1971556Srgrimes const char *service, 1981556Srgrimes const char *hostname, 199127005Sjmallett serf__spnego_buffer_t *input_buf, 20090110Simp serf__spnego_buffer_t *output_buf, 2011556Srgrimes apr_pool_t *result_pool, 2021556Srgrimes apr_pool_t *scratch_pool 20326465Scharnier ) 20426465Scharnier{ 20526465Scharnier SECURITY_STATUS status; 20626465Scharnier ULONG actual_attr; 20726465Scharnier SecBuffer sspi_in_buffer; 208216629Sjilles SecBufferDesc sspi_in_buffer_desc; 209216629Sjilles SecBuffer sspi_out_buffer; 210216629Sjilles SecBufferDesc sspi_out_buffer_desc; 211216629Sjilles apr_status_t apr_status; 212216629Sjilles const char *canonname; 2131556Srgrimes 214 if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) { 215 apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool); 216 if (apr_status) { 217 return apr_status; 218 } 219 220 ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname, 221 NULL); 222 223 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 224 "Using SPN '%s' for '%s'\n", ctx->target_name, hostname); 225 } 226 else if (ctx->authn_type == SERF_AUTHN_NTLM) 227 { 228 /* Target name is not used for NTLM authentication. */ 229 ctx->target_name = NULL; 230 } 231 232 /* Prepare input buffer description. */ 233 sspi_in_buffer.BufferType = SECBUFFER_TOKEN; 234 sspi_in_buffer.pvBuffer = input_buf->value; 235 sspi_in_buffer.cbBuffer = input_buf->length; 236 237 sspi_in_buffer_desc.cBuffers = 1; 238 sspi_in_buffer_desc.pBuffers = &sspi_in_buffer; 239 sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION; 240 241 /* Output buffers. Output buffer will be allocated by system. */ 242 sspi_out_buffer.BufferType = SECBUFFER_TOKEN; 243 sspi_out_buffer.pvBuffer = NULL; 244 sspi_out_buffer.cbBuffer = 0; 245 246 sspi_out_buffer_desc.cBuffers = 1; 247 sspi_out_buffer_desc.pBuffers = &sspi_out_buffer; 248 sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION; 249 250 status = InitializeSecurityContext( 251 &ctx->sspi_credentials, 252 ctx->initalized ? &ctx->sspi_context : NULL, 253 ctx->target_name, 254 ISC_REQ_ALLOCATE_MEMORY 255 | ISC_REQ_MUTUAL_AUTH 256 | ISC_REQ_CONFIDENTIALITY, 257 0, /* Reserved1 */ 258 SECURITY_NETWORK_DREP, 259 &sspi_in_buffer_desc, 260 0, /* Reserved2 */ 261 &ctx->sspi_context, 262 &sspi_out_buffer_desc, 263 &actual_attr, 264 NULL); 265 266 if (sspi_out_buffer.cbBuffer > 0) { 267 apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer, 268 cleanup_sec_buffer, 269 apr_pool_cleanup_null); 270 } 271 272 ctx->initalized = TRUE; 273 274 /* Finish authentication if SSPI requires so. */ 275 if (status == SEC_I_COMPLETE_NEEDED 276 || status == SEC_I_COMPLETE_AND_CONTINUE) 277 { 278 CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc); 279 } 280 281 output_buf->value = sspi_out_buffer.pvBuffer; 282 output_buf->length = sspi_out_buffer.cbBuffer; 283 284 switch(status) { 285 case SEC_I_COMPLETE_AND_CONTINUE: 286 case SEC_I_CONTINUE_NEEDED: 287 return APR_EAGAIN; 288 289 case SEC_I_COMPLETE_NEEDED: 290 case SEC_E_OK: 291 return APR_SUCCESS; 292 293 default: 294 return map_sspi_status(status); 295 } 296} 297 298#endif /* SERF_USE_SSPI */ 299