1/* ==================================================================== 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * ==================================================================== 19 */ 20 21#include "auth_spnego.h" 22#include "serf.h" 23#include "serf_private.h" 24 25#ifdef SERF_USE_SSPI 26#include <apr.h> 27#include <apr_strings.h> 28 29#define SECURITY_WIN32 30#include <sspi.h> 31 32/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */ 33#ifndef SEC_E_MUTUAL_AUTH_FAILED 34#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L) 35#endif 36 37struct serf__spnego_context_t 38{ 39 CredHandle sspi_credentials; 40 CtxtHandle sspi_context; 41 BOOL initalized; 42 apr_pool_t *pool; 43 44 /* Service Principal Name (SPN) used for authentication. */ 45 const char *target_name; 46 47 /* One of SERF_AUTHN_* authentication types.*/ 48 int authn_type; 49}; 50 51/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped 52 * to our own codes and some to Win32 error codes: 53 * http://support.microsoft.com/kb/113996 54 */ 55static apr_status_t 56map_sspi_status(SECURITY_STATUS sspi_status) 57{ 58 switch(sspi_status) 59 { 60 case SEC_E_INSUFFICIENT_MEMORY: 61 return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES); 62 case SEC_E_INVALID_HANDLE: 63 return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE); 64 case SEC_E_UNSUPPORTED_FUNCTION: 65 return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION); 66 case SEC_E_TARGET_UNKNOWN: 67 return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH); 68 case SEC_E_INTERNAL_ERROR: 69 return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR); 70 case SEC_E_SECPKG_NOT_FOUND: 71 case SEC_E_BAD_PKGID: 72 return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE); 73 case SEC_E_NO_IMPERSONATION: 74 return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE); 75 case SEC_E_NO_AUTHENTICATING_AUTHORITY: 76 return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS); 77 case SEC_E_UNTRUSTED_ROOT: 78 return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE); 79 case SEC_E_WRONG_PRINCIPAL: 80 return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME); 81 case SEC_E_MUTUAL_AUTH_FAILED: 82 return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED); 83 case SEC_E_TIME_SKEW: 84 return APR_FROM_OS_ERROR(ERROR_TIME_SKEW); 85 default: 86 return SERF_ERROR_AUTHN_FAILED; 87 } 88} 89 90/* Cleans the SSPI context object, when the pool used to create it gets 91 cleared or destroyed. */ 92static apr_status_t 93cleanup_ctx(void *data) 94{ 95 serf__spnego_context_t *ctx = data; 96 97 if (SecIsValidHandle(&ctx->sspi_context)) { 98 DeleteSecurityContext(&ctx->sspi_context); 99 SecInvalidateHandle(&ctx->sspi_context); 100 } 101 102 if (SecIsValidHandle(&ctx->sspi_credentials)) { 103 FreeCredentialsHandle(&ctx->sspi_credentials); 104 SecInvalidateHandle(&ctx->sspi_credentials); 105 } 106 107 return APR_SUCCESS; 108} 109 110static apr_status_t 111cleanup_sec_buffer(void *data) 112{ 113 FreeContextBuffer(data); 114 115 return APR_SUCCESS; 116} 117 118apr_status_t 119serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p, 120 const serf__authn_scheme_t *scheme, 121 apr_pool_t *result_pool, 122 apr_pool_t *scratch_pool) 123{ 124 SECURITY_STATUS sspi_status; 125 serf__spnego_context_t *ctx; 126 const char *sspi_package; 127 128 ctx = apr_pcalloc(result_pool, sizeof(*ctx)); 129 130 SecInvalidateHandle(&ctx->sspi_context); 131 SecInvalidateHandle(&ctx->sspi_credentials); 132 ctx->initalized = FALSE; 133 ctx->pool = result_pool; 134 ctx->target_name = NULL; 135 ctx->authn_type = scheme->type; 136 137 apr_pool_cleanup_register(result_pool, ctx, 138 cleanup_ctx, 139 apr_pool_cleanup_null); 140 141 if (ctx->authn_type == SERF_AUTHN_NEGOTIATE) 142 sspi_package = "Negotiate"; 143 else 144 sspi_package = "NTLM"; 145 146 sspi_status = AcquireCredentialsHandleA( 147 NULL, sspi_package, SECPKG_CRED_OUTBOUND, 148 NULL, NULL, NULL, NULL, 149 &ctx->sspi_credentials, NULL); 150 151 if (FAILED(sspi_status)) { 152 return map_sspi_status(sspi_status); 153 } 154 155 *ctx_p = ctx; 156 157 return APR_SUCCESS; 158} 159 160static apr_status_t 161get_canonical_hostname(const char **canonname, 162 const char *hostname, 163 apr_pool_t *pool) 164{ 165 struct addrinfo hints; 166 struct addrinfo *addrinfo; 167 168 ZeroMemory(&hints, sizeof(hints)); 169 hints.ai_flags = AI_CANONNAME; 170 171 if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) { 172 return apr_get_netos_error(); 173 } 174 175 if (addrinfo) { 176 *canonname = apr_pstrdup(pool, addrinfo->ai_canonname); 177 } 178 else { 179 *canonname = apr_pstrdup(pool, hostname); 180 } 181 182 freeaddrinfo(addrinfo); 183 return APR_SUCCESS; 184} 185 186apr_status_t 187serf__spnego_reset_sec_context(serf__spnego_context_t *ctx) 188{ 189 if (SecIsValidHandle(&ctx->sspi_context)) { 190 DeleteSecurityContext(&ctx->sspi_context); 191 SecInvalidateHandle(&ctx->sspi_context); 192 } 193 194 ctx->initalized = FALSE; 195 196 return APR_SUCCESS; 197} 198 199apr_status_t 200serf__spnego_init_sec_context(serf_connection_t *conn, 201 serf__spnego_context_t *ctx, 202 const char *service, 203 const char *hostname, 204 serf__spnego_buffer_t *input_buf, 205 serf__spnego_buffer_t *output_buf, 206 apr_pool_t *result_pool, 207 apr_pool_t *scratch_pool 208 ) 209{ 210 SECURITY_STATUS status; 211 ULONG actual_attr; 212 SecBuffer sspi_in_buffer; 213 SecBufferDesc sspi_in_buffer_desc; 214 SecBuffer sspi_out_buffer; 215 SecBufferDesc sspi_out_buffer_desc; 216 apr_status_t apr_status; 217 const char *canonname; 218 219 if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) { 220 apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool); 221 if (apr_status) { 222 return apr_status; 223 } 224 225 ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname, 226 NULL); 227 228 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 229 "Using SPN '%s' for '%s'\n", ctx->target_name, hostname); 230 } 231 else if (ctx->authn_type == SERF_AUTHN_NTLM) 232 { 233 /* Target name is not used for NTLM authentication. */ 234 ctx->target_name = NULL; 235 } 236 237 /* Prepare input buffer description. */ 238 sspi_in_buffer.BufferType = SECBUFFER_TOKEN; 239 sspi_in_buffer.pvBuffer = input_buf->value; 240 sspi_in_buffer.cbBuffer = input_buf->length; 241 242 sspi_in_buffer_desc.cBuffers = 1; 243 sspi_in_buffer_desc.pBuffers = &sspi_in_buffer; 244 sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION; 245 246 /* Output buffers. Output buffer will be allocated by system. */ 247 sspi_out_buffer.BufferType = SECBUFFER_TOKEN; 248 sspi_out_buffer.pvBuffer = NULL; 249 sspi_out_buffer.cbBuffer = 0; 250 251 sspi_out_buffer_desc.cBuffers = 1; 252 sspi_out_buffer_desc.pBuffers = &sspi_out_buffer; 253 sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION; 254 255 status = InitializeSecurityContextA( 256 &ctx->sspi_credentials, 257 ctx->initalized ? &ctx->sspi_context : NULL, 258 ctx->target_name, 259 ISC_REQ_ALLOCATE_MEMORY 260 | ISC_REQ_MUTUAL_AUTH 261 | ISC_REQ_CONFIDENTIALITY, 262 0, /* Reserved1 */ 263 SECURITY_NETWORK_DREP, 264 &sspi_in_buffer_desc, 265 0, /* Reserved2 */ 266 &ctx->sspi_context, 267 &sspi_out_buffer_desc, 268 &actual_attr, 269 NULL); 270 271 if (sspi_out_buffer.cbBuffer > 0) { 272 apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer, 273 cleanup_sec_buffer, 274 apr_pool_cleanup_null); 275 } 276 277 ctx->initalized = TRUE; 278 279 /* Finish authentication if SSPI requires so. */ 280 if (status == SEC_I_COMPLETE_NEEDED 281 || status == SEC_I_COMPLETE_AND_CONTINUE) 282 { 283 CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc); 284 } 285 286 output_buf->value = sspi_out_buffer.pvBuffer; 287 output_buf->length = sspi_out_buffer.cbBuffer; 288 289 switch(status) { 290 case SEC_I_COMPLETE_AND_CONTINUE: 291 case SEC_I_CONTINUE_NEEDED: 292 return APR_EAGAIN; 293 294 case SEC_I_COMPLETE_NEEDED: 295 case SEC_E_OK: 296 return APR_SUCCESS; 297 298 default: 299 return map_sspi_status(status); 300 } 301} 302 303#endif /* SERF_USE_SSPI */ 304