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_connection_t *conn,
196 serf__spnego_context_t *ctx,
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
205 SECURITY_STATUS status;
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;
214 if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
215 apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
220 ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
223 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
224 "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
226 else if (ctx->authn_type == SERF_AUTHN_NTLM)
228 /* Target name is not used for NTLM authentication. */
229 ctx->target_name = NULL;
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;
237 sspi_in_buffer_desc.cBuffers = 1;
238 sspi_in_buffer_desc.pBuffers = &sspi_in_buffer;
239 sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION;
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;
246 sspi_out_buffer_desc.cBuffers = 1;
247 sspi_out_buffer_desc.pBuffers = &sspi_out_buffer;
248 sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION;
250 status = InitializeSecurityContext(
251 &ctx->sspi_credentials,
252 ctx->initalized ? &ctx->sspi_context : NULL,
254 ISC_REQ_ALLOCATE_MEMORY
255 | ISC_REQ_MUTUAL_AUTH
256 | ISC_REQ_CONFIDENTIALITY,
258 SECURITY_NETWORK_DREP,
259 &sspi_in_buffer_desc,
262 &sspi_out_buffer_desc,
266 if (sspi_out_buffer.cbBuffer > 0) {
267 apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer,
269 apr_pool_cleanup_null);
272 ctx->initalized = TRUE;
274 /* Finish authentication if SSPI requires so. */
275 if (status == SEC_I_COMPLETE_NEEDED
276 || status == SEC_I_COMPLETE_AND_CONTINUE)
278 CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc);
281 output_buf->value = sspi_out_buffer.pvBuffer;
282 output_buf->length = sspi_out_buffer.cbBuffer;
285 case SEC_I_COMPLETE_AND_CONTINUE:
286 case SEC_I_CONTINUE_NEEDED:
289 case SEC_I_COMPLETE_NEEDED:
294 return map_sspi_status(status);
298 #endif /* SERF_USE_SSPI */