auth_spnego_sspi.c revision 289166
1/* Copyright 2010 Justin Erenkrantz and Greg Stein 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "auth_spnego.h" 17#include "serf.h" 18#include "serf_private.h" 19 20#ifdef SERF_USE_SSPI 21#include <apr.h> 22#include <apr_strings.h> 23 24#define SECURITY_WIN32 25#include <sspi.h> 26 27/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */ 28#ifndef SEC_E_MUTUAL_AUTH_FAILED 29#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L) 30#endif 31 32struct serf__spnego_context_t 33{ 34 CredHandle sspi_credentials; 35 CtxtHandle sspi_context; 36 BOOL initalized; 37 apr_pool_t *pool; 38 39 /* Service Principal Name (SPN) used for authentication. */ 40 const char *target_name; 41 42 /* One of SERF_AUTHN_* authentication types.*/ 43 int authn_type; 44}; 45 46/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped 47 * to our own codes and some to Win32 error codes: 48 * http://support.microsoft.com/kb/113996 49 */ 50static apr_status_t 51map_sspi_status(SECURITY_STATUS sspi_status) 52{ 53 switch(sspi_status) 54 { 55 case SEC_E_INSUFFICIENT_MEMORY: 56 return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES); 57 case SEC_E_INVALID_HANDLE: 58 return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE); 59 case SEC_E_UNSUPPORTED_FUNCTION: 60 return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION); 61 case SEC_E_TARGET_UNKNOWN: 62 return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH); 63 case SEC_E_INTERNAL_ERROR: 64 return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR); 65 case SEC_E_SECPKG_NOT_FOUND: 66 case SEC_E_BAD_PKGID: 67 return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE); 68 case SEC_E_NO_IMPERSONATION: 69 return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE); 70 case SEC_E_NO_AUTHENTICATING_AUTHORITY: 71 return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS); 72 case SEC_E_UNTRUSTED_ROOT: 73 return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE); 74 case SEC_E_WRONG_PRINCIPAL: 75 return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME); 76 case SEC_E_MUTUAL_AUTH_FAILED: 77 return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED); 78 case SEC_E_TIME_SKEW: 79 return APR_FROM_OS_ERROR(ERROR_TIME_SKEW); 80 default: 81 return SERF_ERROR_AUTHN_FAILED; 82 } 83} 84 85/* Cleans the SSPI context object, when the pool used to create it gets 86 cleared or destroyed. */ 87static apr_status_t 88cleanup_ctx(void *data) 89{ 90 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