1 /* Copyright 2010 Justin Erenkrantz and Greg Stein
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include "auth_spnego.h"
18 #include "serf_private.h"
22 #include <apr_strings.h>
24 #define SECURITY_WIN32
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)
32 struct serf__spnego_context_t
34 CredHandle sspi_credentials;
35 CtxtHandle sspi_context;
39 /* Service Principal Name (SPN) used for authentication. */
40 const char *target_name;
42 /* One of SERF_AUTHN_* authentication types.*/
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
51 map_sspi_status(SECURITY_STATUS sspi_status)
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:
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);
79 return APR_FROM_OS_ERROR(ERROR_TIME_SKEW);
81 return SERF_ERROR_AUTHN_FAILED;
85 /* Cleans the SSPI context object, when the pool used to create it gets
86 cleared or destroyed. */
88 cleanup_ctx(void *data)
90 serf__spnego_context_t *ctx = data;
92 if (SecIsValidHandle(&ctx->sspi_context)) {
93 DeleteSecurityContext(&ctx->sspi_context);
94 SecInvalidateHandle(&ctx->sspi_context);
97 if (SecIsValidHandle(&ctx->sspi_credentials)) {
98 FreeCredentialsHandle(&ctx->sspi_context);
99 SecInvalidateHandle(&ctx->sspi_context);
106 cleanup_sec_buffer(void *data)
108 FreeContextBuffer(data);
114 serf__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)
119 SECURITY_STATUS sspi_status;
120 serf__spnego_context_t *ctx;
121 const char *sspi_package;
123 ctx = apr_pcalloc(result_pool, sizeof(*ctx));
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;
132 apr_pool_cleanup_register(result_pool, ctx,
134 apr_pool_cleanup_null);
136 if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
137 sspi_package = "Negotiate";
139 sspi_package = "NTLM";
141 sspi_status = AcquireCredentialsHandle(
142 NULL, sspi_package, SECPKG_CRED_OUTBOUND,
143 NULL, NULL, NULL, NULL,
144 &ctx->sspi_credentials, NULL);
146 if (FAILED(sspi_status)) {
147 return map_sspi_status(sspi_status);
156 get_canonical_hostname(const char **canonname,
157 const char *hostname,
160 struct addrinfo hints;
161 struct addrinfo *addrinfo;
163 ZeroMemory(&hints, sizeof(hints));
164 hints.ai_flags = AI_CANONNAME;
166 if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) {
167 return apr_get_netos_error();
171 *canonname = apr_pstrdup(pool, addrinfo->ai_canonname);
174 *canonname = apr_pstrdup(pool, hostname);
177 freeaddrinfo(addrinfo);
182 serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
184 if (SecIsValidHandle(&ctx->sspi_context)) {
185 DeleteSecurityContext(&ctx->sspi_context);
186 SecInvalidateHandle(&ctx->sspi_context);
189 ctx->initalized = FALSE;
195 serf__spnego_init_sec_context(serf__spnego_context_t *ctx,
197 const char *hostname,
198 serf__spnego_buffer_t *input_buf,
199 serf__spnego_buffer_t *output_buf,
200 apr_pool_t *result_pool,
201 apr_pool_t *scratch_pool
204 SECURITY_STATUS status;
206 SecBuffer sspi_in_buffer;
207 SecBufferDesc sspi_in_buffer_desc;
208 SecBuffer sspi_out_buffer;
209 SecBufferDesc sspi_out_buffer_desc;
210 apr_status_t apr_status;
211 const char *canonname;
213 if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
214 apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
219 ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
222 serf__log(AUTH_VERBOSE, __FILE__,
223 "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
225 else if (ctx->authn_type == SERF_AUTHN_NTLM)
227 /* Target name is not used for NTLM authentication. */
228 ctx->target_name = NULL;
231 /* Prepare input buffer description. */
232 sspi_in_buffer.BufferType = SECBUFFER_TOKEN;
233 sspi_in_buffer.pvBuffer = input_buf->value;
234 sspi_in_buffer.cbBuffer = input_buf->length;
236 sspi_in_buffer_desc.cBuffers = 1;
237 sspi_in_buffer_desc.pBuffers = &sspi_in_buffer;
238 sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION;
240 /* Output buffers. Output buffer will be allocated by system. */
241 sspi_out_buffer.BufferType = SECBUFFER_TOKEN;
242 sspi_out_buffer.pvBuffer = NULL;
243 sspi_out_buffer.cbBuffer = 0;
245 sspi_out_buffer_desc.cBuffers = 1;
246 sspi_out_buffer_desc.pBuffers = &sspi_out_buffer;
247 sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION;
249 status = InitializeSecurityContext(
250 &ctx->sspi_credentials,
251 ctx->initalized ? &ctx->sspi_context : NULL,
253 ISC_REQ_ALLOCATE_MEMORY
254 | ISC_REQ_MUTUAL_AUTH
255 | ISC_REQ_CONFIDENTIALITY,
257 SECURITY_NETWORK_DREP,
258 &sspi_in_buffer_desc,
261 &sspi_out_buffer_desc,
265 if (sspi_out_buffer.cbBuffer > 0) {
266 apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer,
268 apr_pool_cleanup_null);
271 ctx->initalized = TRUE;
273 /* Finish authentication if SSPI requires so. */
274 if (status == SEC_I_COMPLETE_NEEDED
275 || status == SEC_I_COMPLETE_AND_CONTINUE)
277 CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc);
280 output_buf->value = sspi_out_buffer.pvBuffer;
281 output_buf->length = sspi_out_buffer.cbBuffer;
284 case SEC_I_COMPLETE_AND_CONTINUE:
285 case SEC_I_CONTINUE_NEEDED:
288 case SEC_I_COMPLETE_NEEDED:
293 return map_sspi_status(status);
297 #endif /* SERF_USE_SSPI */