11590Srgrimes/* Copyright 2010 Justin Erenkrantz and Greg Stein 21590Srgrimes * 31590Srgrimes * Licensed under the Apache License, Version 2.0 (the "License"); 41590Srgrimes * you may not use this file except in compliance with the License. 51590Srgrimes * You may obtain a copy of the License at 61590Srgrimes * 71590Srgrimes * http://www.apache.org/licenses/LICENSE-2.0 81590Srgrimes * 91590Srgrimes * Unless required by applicable law or agreed to in writing, software 101590Srgrimes * distributed under the License is distributed on an "AS IS" BASIS, 111590Srgrimes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 121590Srgrimes * See the License for the specific language governing permissions and 131590Srgrimes * limitations under the License. 141590Srgrimes */ 151590Srgrimes 161590Srgrimes#include "auth_spnego.h" 171590Srgrimes#include "serf.h" 181590Srgrimes#include "serf_private.h" 191590Srgrimes 201590Srgrimes#ifdef SERF_USE_SSPI 211590Srgrimes#include <apr.h> 221590Srgrimes#include <apr_strings.h> 231590Srgrimes 241590Srgrimes#define SECURITY_WIN32 251590Srgrimes#include <sspi.h> 261590Srgrimes 271590Srgrimes/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */ 281590Srgrimes#ifndef SEC_E_MUTUAL_AUTH_FAILED 291590Srgrimes#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L) 30105243Scharnier#endif 311590Srgrimes 321590Srgrimesstruct serf__spnego_context_t 33105243Scharnier{ 3427313Scharnier CredHandle sspi_credentials; 35105243Scharnier CtxtHandle sspi_context; 3699112Sobrien BOOL initalized; 3799112Sobrien apr_pool_t *pool; 381590Srgrimes 3927313Scharnier /* Service Principal Name (SPN) used for authentication. */ 40129657Sstefanf const char *target_name; 41129657Sstefanf 421590Srgrimes /* One of SERF_AUTHN_* authentication types.*/ 431590Srgrimes int authn_type; 441590Srgrimes}; 451590Srgrimes 461590Srgrimes/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped 471590Srgrimes * to our own codes and some to Win32 error codes: 481590Srgrimes * http://support.microsoft.com/kb/113996 491590Srgrimes */ 501590Srgrimesstatic apr_status_t 511590Srgrimesmap_sspi_status(SECURITY_STATUS sspi_status) 521590Srgrimes{ 531590Srgrimes switch(sspi_status) 541590Srgrimes { 551590Srgrimes case SEC_E_INSUFFICIENT_MEMORY: 561590Srgrimes return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES); 571590Srgrimes case SEC_E_INVALID_HANDLE: 581590Srgrimes return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE); 591590Srgrimes case SEC_E_UNSUPPORTED_FUNCTION: 601590Srgrimes return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION); 611590Srgrimes case SEC_E_TARGET_UNKNOWN: 621590Srgrimes return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH); 63105243Scharnier case SEC_E_INTERNAL_ERROR: 64246783Scharnier return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR); 651590Srgrimes case SEC_E_SECPKG_NOT_FOUND: 661590Srgrimes case SEC_E_BAD_PKGID: 671590Srgrimes return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE); 681590Srgrimes case SEC_E_NO_IMPERSONATION: 69105243Scharnier return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE); 70105243Scharnier case SEC_E_NO_AUTHENTICATING_AUTHORITY: 711590Srgrimes return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS); 721590Srgrimes case SEC_E_UNTRUSTED_ROOT: 731590Srgrimes return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE); 741590Srgrimes case SEC_E_WRONG_PRINCIPAL: 751590Srgrimes return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME); 761590Srgrimes case SEC_E_MUTUAL_AUTH_FAILED: 77246783Scharnier return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED); 781590Srgrimes case SEC_E_TIME_SKEW: 791590Srgrimes return APR_FROM_OS_ERROR(ERROR_TIME_SKEW); 801590Srgrimes default: 811590Srgrimes return SERF_ERROR_AUTHN_FAILED; 821590Srgrimes } 831590Srgrimes} 841590Srgrimes 851590Srgrimes/* Cleans the SSPI context object, when the pool used to create it gets 861590Srgrimes cleared or destroyed. */ 871590Srgrimesstatic apr_status_t 881590Srgrimescleanup_ctx(void *data) 891590Srgrimes{ 901590Srgrimes serf__spnego_context_t *ctx = data; 91 92 if (SecIsValidHandle(&ctx->sspi_context)) { 93 DeleteSecurityContext(&ctx->sspi_context); 94 SecInvalidateHandle(&ctx->sspi_context); 95 } 96 97 if (SecIsValidHandle(&ctx->sspi_credentials)) { 98 FreeCredentialsHandle(&ctx->sspi_credentials); 99 SecInvalidateHandle(&ctx->sspi_credentials); 100 } 101 102 return APR_SUCCESS; 103} 104 105static apr_status_t 106cleanup_sec_buffer(void *data) 107{ 108 FreeContextBuffer(data); 109 110 return APR_SUCCESS; 111} 112 113apr_status_t 114serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p, 115 const serf__authn_scheme_t *scheme, 116 apr_pool_t *result_pool, 117 apr_pool_t *scratch_pool) 118{ 119 SECURITY_STATUS sspi_status; 120 serf__spnego_context_t *ctx; 121 const char *sspi_package; 122 123 ctx = apr_pcalloc(result_pool, sizeof(*ctx)); 124 125 SecInvalidateHandle(&ctx->sspi_context); 126 SecInvalidateHandle(&ctx->sspi_credentials); 127 ctx->initalized = FALSE; 128 ctx->pool = result_pool; 129 ctx->target_name = NULL; 130 ctx->authn_type = scheme->type; 131 132 apr_pool_cleanup_register(result_pool, ctx, 133 cleanup_ctx, 134 apr_pool_cleanup_null); 135 136 if (ctx->authn_type == SERF_AUTHN_NEGOTIATE) 137 sspi_package = "Negotiate"; 138 else 139 sspi_package = "NTLM"; 140 141 sspi_status = AcquireCredentialsHandle( 142 NULL, sspi_package, SECPKG_CRED_OUTBOUND, 143 NULL, NULL, NULL, NULL, 144 &ctx->sspi_credentials, NULL); 145 146 if (FAILED(sspi_status)) { 147 return map_sspi_status(sspi_status); 148 } 149 150 *ctx_p = ctx; 151 152 return APR_SUCCESS; 153} 154 155static apr_status_t 156get_canonical_hostname(const char **canonname, 157 const char *hostname, 158 apr_pool_t *pool) 159{ 160 struct addrinfo hints; 161 struct addrinfo *addrinfo; 162 163 ZeroMemory(&hints, sizeof(hints)); 164 hints.ai_flags = AI_CANONNAME; 165 166 if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) { 167 return apr_get_netos_error(); 168 } 169 170 if (addrinfo) { 171 *canonname = apr_pstrdup(pool, addrinfo->ai_canonname); 172 } 173 else { 174 *canonname = apr_pstrdup(pool, hostname); 175 } 176 177 freeaddrinfo(addrinfo); 178 return APR_SUCCESS; 179} 180 181apr_status_t 182serf__spnego_reset_sec_context(serf__spnego_context_t *ctx) 183{ 184 if (SecIsValidHandle(&ctx->sspi_context)) { 185 DeleteSecurityContext(&ctx->sspi_context); 186 SecInvalidateHandle(&ctx->sspi_context); 187 } 188 189 ctx->initalized = FALSE; 190 191 return APR_SUCCESS; 192} 193 194apr_status_t 195serf__spnego_init_sec_context(serf_connection_t *conn, 196 serf__spnego_context_t *ctx, 197 const char *service, 198 const char *hostname, 199 serf__spnego_buffer_t *input_buf, 200 serf__spnego_buffer_t *output_buf, 201 apr_pool_t *result_pool, 202 apr_pool_t *scratch_pool 203 ) 204{ 205 SECURITY_STATUS status; 206 ULONG actual_attr; 207 SecBuffer sspi_in_buffer; 208 SecBufferDesc sspi_in_buffer_desc; 209 SecBuffer sspi_out_buffer; 210 SecBufferDesc sspi_out_buffer_desc; 211 apr_status_t apr_status; 212 const char *canonname; 213 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