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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
18 * ====================================================================
21 #include "auth_spnego.h"
23 #include "serf_private.h"
27 #include <apr_strings.h>
29 #define SECURITY_WIN32
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)
37 struct serf__spnego_context_t
39 CredHandle sspi_credentials;
40 CtxtHandle sspi_context;
44 /* Service Principal Name (SPN) used for authentication. */
45 const char *target_name;
47 /* One of SERF_AUTHN_* authentication types.*/
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
56 map_sspi_status(SECURITY_STATUS sspi_status)
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:
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);
84 return APR_FROM_OS_ERROR(ERROR_TIME_SKEW);
86 return SERF_ERROR_AUTHN_FAILED;
90 /* Cleans the SSPI context object, when the pool used to create it gets
91 cleared or destroyed. */
93 cleanup_ctx(void *data)
95 serf__spnego_context_t *ctx = data;
97 if (SecIsValidHandle(&ctx->sspi_context)) {
98 DeleteSecurityContext(&ctx->sspi_context);
99 SecInvalidateHandle(&ctx->sspi_context);
102 if (SecIsValidHandle(&ctx->sspi_credentials)) {
103 FreeCredentialsHandle(&ctx->sspi_credentials);
104 SecInvalidateHandle(&ctx->sspi_credentials);
111 cleanup_sec_buffer(void *data)
113 FreeContextBuffer(data);
119 serf__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)
124 SECURITY_STATUS sspi_status;
125 serf__spnego_context_t *ctx;
126 const char *sspi_package;
128 ctx = apr_pcalloc(result_pool, sizeof(*ctx));
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;
137 apr_pool_cleanup_register(result_pool, ctx,
139 apr_pool_cleanup_null);
141 if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
142 sspi_package = "Negotiate";
144 sspi_package = "NTLM";
146 sspi_status = AcquireCredentialsHandleA(
147 NULL, sspi_package, SECPKG_CRED_OUTBOUND,
148 NULL, NULL, NULL, NULL,
149 &ctx->sspi_credentials, NULL);
151 if (FAILED(sspi_status)) {
152 return map_sspi_status(sspi_status);
161 get_canonical_hostname(const char **canonname,
162 const char *hostname,
165 struct addrinfo hints;
166 struct addrinfo *addrinfo;
168 ZeroMemory(&hints, sizeof(hints));
169 hints.ai_flags = AI_CANONNAME;
171 if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) {
172 return apr_get_netos_error();
176 *canonname = apr_pstrdup(pool, addrinfo->ai_canonname);
179 *canonname = apr_pstrdup(pool, hostname);
182 freeaddrinfo(addrinfo);
187 serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
189 if (SecIsValidHandle(&ctx->sspi_context)) {
190 DeleteSecurityContext(&ctx->sspi_context);
191 SecInvalidateHandle(&ctx->sspi_context);
194 ctx->initalized = FALSE;
200 serf__spnego_init_sec_context(serf_connection_t *conn,
201 serf__spnego_context_t *ctx,
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
210 SECURITY_STATUS status;
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;
219 if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
220 apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
225 ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
228 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
229 "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
231 else if (ctx->authn_type == SERF_AUTHN_NTLM)
233 /* Target name is not used for NTLM authentication. */
234 ctx->target_name = NULL;
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;
242 sspi_in_buffer_desc.cBuffers = 1;
243 sspi_in_buffer_desc.pBuffers = &sspi_in_buffer;
244 sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION;
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;
251 sspi_out_buffer_desc.cBuffers = 1;
252 sspi_out_buffer_desc.pBuffers = &sspi_out_buffer;
253 sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION;
255 status = InitializeSecurityContextA(
256 &ctx->sspi_credentials,
257 ctx->initalized ? &ctx->sspi_context : NULL,
259 ISC_REQ_ALLOCATE_MEMORY
260 | ISC_REQ_MUTUAL_AUTH
261 | ISC_REQ_CONFIDENTIALITY,
263 SECURITY_NETWORK_DREP,
264 &sspi_in_buffer_desc,
267 &sspi_out_buffer_desc,
271 if (sspi_out_buffer.cbBuffer > 0) {
272 apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer,
274 apr_pool_cleanup_null);
277 ctx->initalized = TRUE;
279 /* Finish authentication if SSPI requires so. */
280 if (status == SEC_I_COMPLETE_NEEDED
281 || status == SEC_I_COMPLETE_AND_CONTINUE)
283 CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc);
286 output_buf->value = sspi_out_buffer.pvBuffer;
287 output_buf->length = sspi_out_buffer.cbBuffer;
290 case SEC_I_COMPLETE_AND_CONTINUE:
291 case SEC_I_CONTINUE_NEEDED:
294 case SEC_I_COMPLETE_NEEDED:
299 return map_sspi_status(status);
303 #endif /* SERF_USE_SSPI */