1253893Speter/* Copyright 2010 Justin Erenkrantz and Greg Stein
2253893Speter *
3253893Speter * Licensed under the Apache License, Version 2.0 (the "License");
4253893Speter * you may not use this file except in compliance with the License.
5253893Speter * You may obtain a copy of the License at
6253893Speter *
7253893Speter *     http://www.apache.org/licenses/LICENSE-2.0
8253893Speter *
9253893Speter * Unless required by applicable law or agreed to in writing, software
10253893Speter * distributed under the License is distributed on an "AS IS" BASIS,
11253893Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12253893Speter * See the License for the specific language governing permissions and
13253893Speter * limitations under the License.
14253893Speter */
15253893Speter
16253893Speter#include "auth_spnego.h"
17253893Speter#include "serf.h"
18253893Speter#include "serf_private.h"
19253893Speter
20253893Speter#ifdef SERF_USE_SSPI
21253893Speter#include <apr.h>
22253893Speter#include <apr_strings.h>
23253893Speter
24253893Speter#define SECURITY_WIN32
25253893Speter#include <sspi.h>
26253893Speter
27253893Speter/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */
28253893Speter#ifndef SEC_E_MUTUAL_AUTH_FAILED
29253893Speter#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L)
30253893Speter#endif
31253893Speter
32253893Speterstruct serf__spnego_context_t
33253893Speter{
34253893Speter    CredHandle sspi_credentials;
35253893Speter    CtxtHandle sspi_context;
36253893Speter    BOOL initalized;
37253893Speter    apr_pool_t *pool;
38253893Speter
39253893Speter    /* Service Principal Name (SPN) used for authentication. */
40253893Speter    const char *target_name;
41253893Speter
42253893Speter    /* One of SERF_AUTHN_* authentication types.*/
43253893Speter    int authn_type;
44253893Speter};
45253893Speter
46253893Speter/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped
47253893Speter * to our own codes and some to Win32 error codes:
48253893Speter * http://support.microsoft.com/kb/113996
49253893Speter */
50253893Speterstatic apr_status_t
51253893Spetermap_sspi_status(SECURITY_STATUS sspi_status)
52253893Speter{
53253893Speter    switch(sspi_status)
54253893Speter    {
55253893Speter    case SEC_E_INSUFFICIENT_MEMORY:
56253893Speter        return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES);
57253893Speter    case SEC_E_INVALID_HANDLE:
58253893Speter        return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE);
59253893Speter    case SEC_E_UNSUPPORTED_FUNCTION:
60253893Speter        return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION);
61253893Speter    case SEC_E_TARGET_UNKNOWN:
62253893Speter        return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH);
63253893Speter    case SEC_E_INTERNAL_ERROR:
64253893Speter        return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR);
65253893Speter    case SEC_E_SECPKG_NOT_FOUND:
66253893Speter    case SEC_E_BAD_PKGID:
67253893Speter        return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE);
68253893Speter    case SEC_E_NO_IMPERSONATION:
69253893Speter        return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE);
70253893Speter    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
71253893Speter        return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS);
72253893Speter    case SEC_E_UNTRUSTED_ROOT:
73253893Speter        return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE);
74253893Speter    case SEC_E_WRONG_PRINCIPAL:
75253893Speter        return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME);
76253893Speter    case SEC_E_MUTUAL_AUTH_FAILED:
77253893Speter        return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED);
78253893Speter    case SEC_E_TIME_SKEW:
79253893Speter        return APR_FROM_OS_ERROR(ERROR_TIME_SKEW);
80253893Speter    default:
81253893Speter        return SERF_ERROR_AUTHN_FAILED;
82253893Speter    }
83253893Speter}
84253893Speter
85253893Speter/* Cleans the SSPI context object, when the pool used to create it gets
86253893Speter   cleared or destroyed. */
87253893Speterstatic apr_status_t
88253893Spetercleanup_ctx(void *data)
89253893Speter{
90253893Speter    serf__spnego_context_t *ctx = data;
91253893Speter
92253893Speter    if (SecIsValidHandle(&ctx->sspi_context)) {
93253893Speter        DeleteSecurityContext(&ctx->sspi_context);
94253893Speter        SecInvalidateHandle(&ctx->sspi_context);
95253893Speter    }
96253893Speter
97253893Speter    if (SecIsValidHandle(&ctx->sspi_credentials)) {
98289166Speter        FreeCredentialsHandle(&ctx->sspi_credentials);
99289166Speter        SecInvalidateHandle(&ctx->sspi_credentials);
100253893Speter    }
101253893Speter
102253893Speter    return APR_SUCCESS;
103253893Speter}
104253893Speter
105253893Speterstatic apr_status_t
106253893Spetercleanup_sec_buffer(void *data)
107253893Speter{
108253893Speter    FreeContextBuffer(data);
109253893Speter
110253893Speter    return APR_SUCCESS;
111253893Speter}
112253893Speter
113253893Speterapr_status_t
114253893Speterserf__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
115253893Speter                                const serf__authn_scheme_t *scheme,
116253893Speter                                apr_pool_t *result_pool,
117253893Speter                                apr_pool_t *scratch_pool)
118253893Speter{
119253893Speter    SECURITY_STATUS sspi_status;
120253893Speter    serf__spnego_context_t *ctx;
121253893Speter    const char *sspi_package;
122253893Speter
123253893Speter    ctx = apr_pcalloc(result_pool, sizeof(*ctx));
124253893Speter
125253893Speter    SecInvalidateHandle(&ctx->sspi_context);
126253893Speter    SecInvalidateHandle(&ctx->sspi_credentials);
127253893Speter    ctx->initalized = FALSE;
128253893Speter    ctx->pool = result_pool;
129253893Speter    ctx->target_name = NULL;
130253893Speter    ctx->authn_type = scheme->type;
131253893Speter
132253893Speter    apr_pool_cleanup_register(result_pool, ctx,
133253893Speter                              cleanup_ctx,
134253893Speter                              apr_pool_cleanup_null);
135253893Speter
136253893Speter    if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
137253893Speter        sspi_package = "Negotiate";
138253893Speter    else
139253893Speter        sspi_package = "NTLM";
140253893Speter
141253893Speter    sspi_status = AcquireCredentialsHandle(
142253893Speter        NULL, sspi_package, SECPKG_CRED_OUTBOUND,
143253893Speter        NULL, NULL, NULL, NULL,
144253893Speter        &ctx->sspi_credentials, NULL);
145253893Speter
146253893Speter    if (FAILED(sspi_status)) {
147253893Speter        return map_sspi_status(sspi_status);
148253893Speter    }
149253893Speter
150253893Speter    *ctx_p = ctx;
151253893Speter
152253893Speter    return APR_SUCCESS;
153253893Speter}
154253893Speter
155253893Speterstatic apr_status_t
156253893Speterget_canonical_hostname(const char **canonname,
157253893Speter                       const char *hostname,
158253893Speter                       apr_pool_t *pool)
159253893Speter{
160253893Speter    struct addrinfo hints;
161253893Speter    struct addrinfo *addrinfo;
162253893Speter
163253893Speter    ZeroMemory(&hints, sizeof(hints));
164253893Speter    hints.ai_flags = AI_CANONNAME;
165253893Speter
166253893Speter    if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) {
167253893Speter        return apr_get_netos_error();
168253893Speter    }
169253893Speter
170253893Speter    if (addrinfo) {
171253893Speter        *canonname = apr_pstrdup(pool, addrinfo->ai_canonname);
172253893Speter    }
173253893Speter    else {
174253893Speter        *canonname = apr_pstrdup(pool, hostname);
175253893Speter    }
176253893Speter
177253893Speter    freeaddrinfo(addrinfo);
178253893Speter    return APR_SUCCESS;
179253893Speter}
180253893Speter
181253893Speterapr_status_t
182253893Speterserf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
183253893Speter{
184253893Speter    if (SecIsValidHandle(&ctx->sspi_context)) {
185253893Speter        DeleteSecurityContext(&ctx->sspi_context);
186253893Speter        SecInvalidateHandle(&ctx->sspi_context);
187253893Speter    }
188253893Speter
189253893Speter    ctx->initalized = FALSE;
190253893Speter
191253893Speter    return APR_SUCCESS;
192253893Speter}
193253893Speter
194253893Speterapr_status_t
195262339Speterserf__spnego_init_sec_context(serf_connection_t *conn,
196262339Speter                              serf__spnego_context_t *ctx,
197253893Speter                              const char *service,
198253893Speter                              const char *hostname,
199253893Speter                              serf__spnego_buffer_t *input_buf,
200253893Speter                              serf__spnego_buffer_t *output_buf,
201253893Speter                              apr_pool_t *result_pool,
202253893Speter                              apr_pool_t *scratch_pool
203253893Speter                              )
204253893Speter{
205253893Speter    SECURITY_STATUS status;
206253893Speter    ULONG actual_attr;
207253893Speter    SecBuffer sspi_in_buffer;
208253893Speter    SecBufferDesc sspi_in_buffer_desc;
209253893Speter    SecBuffer sspi_out_buffer;
210253893Speter    SecBufferDesc sspi_out_buffer_desc;
211253893Speter    apr_status_t apr_status;
212253893Speter    const char *canonname;
213253893Speter
214253893Speter    if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
215253893Speter        apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
216253893Speter        if (apr_status) {
217253893Speter            return apr_status;
218253893Speter        }
219253893Speter
220253893Speter        ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
221253893Speter                                       NULL);
222253893Speter
223262339Speter        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
224262339Speter                      "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
225253893Speter    }
226253893Speter    else if (ctx->authn_type == SERF_AUTHN_NTLM)
227253893Speter    {
228253893Speter        /* Target name is not used for NTLM authentication. */
229253893Speter        ctx->target_name = NULL;
230253893Speter    }
231253893Speter
232253893Speter    /* Prepare input buffer description. */
233253893Speter    sspi_in_buffer.BufferType = SECBUFFER_TOKEN;
234253893Speter    sspi_in_buffer.pvBuffer = input_buf->value;
235253893Speter    sspi_in_buffer.cbBuffer = input_buf->length;
236253893Speter
237253893Speter    sspi_in_buffer_desc.cBuffers = 1;
238253893Speter    sspi_in_buffer_desc.pBuffers = &sspi_in_buffer;
239253893Speter    sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION;
240253893Speter
241253893Speter    /* Output buffers. Output buffer will be allocated by system. */
242253893Speter    sspi_out_buffer.BufferType = SECBUFFER_TOKEN;
243253893Speter    sspi_out_buffer.pvBuffer = NULL;
244253893Speter    sspi_out_buffer.cbBuffer = 0;
245253893Speter
246253893Speter    sspi_out_buffer_desc.cBuffers = 1;
247253893Speter    sspi_out_buffer_desc.pBuffers = &sspi_out_buffer;
248253893Speter    sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION;
249253893Speter
250253893Speter    status = InitializeSecurityContext(
251253893Speter        &ctx->sspi_credentials,
252253893Speter        ctx->initalized ? &ctx->sspi_context : NULL,
253253893Speter        ctx->target_name,
254253893Speter        ISC_REQ_ALLOCATE_MEMORY
255253893Speter        | ISC_REQ_MUTUAL_AUTH
256253893Speter        | ISC_REQ_CONFIDENTIALITY,
257253893Speter        0,                          /* Reserved1 */
258253893Speter        SECURITY_NETWORK_DREP,
259253893Speter        &sspi_in_buffer_desc,
260253893Speter        0,                          /* Reserved2 */
261253893Speter        &ctx->sspi_context,
262253893Speter        &sspi_out_buffer_desc,
263253893Speter        &actual_attr,
264253893Speter        NULL);
265253893Speter
266253893Speter    if (sspi_out_buffer.cbBuffer > 0) {
267253893Speter        apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer,
268253893Speter                                  cleanup_sec_buffer,
269253893Speter                                  apr_pool_cleanup_null);
270253893Speter    }
271253893Speter
272253893Speter    ctx->initalized = TRUE;
273253893Speter
274253893Speter    /* Finish authentication if SSPI requires so. */
275253893Speter    if (status == SEC_I_COMPLETE_NEEDED
276253893Speter        || status == SEC_I_COMPLETE_AND_CONTINUE)
277253893Speter    {
278253893Speter        CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc);
279253893Speter    }
280253893Speter
281253893Speter    output_buf->value = sspi_out_buffer.pvBuffer;
282253893Speter    output_buf->length = sspi_out_buffer.cbBuffer;
283253893Speter
284253893Speter    switch(status) {
285253893Speter    case SEC_I_COMPLETE_AND_CONTINUE:
286253893Speter    case SEC_I_CONTINUE_NEEDED:
287253893Speter        return APR_EAGAIN;
288253893Speter
289253893Speter    case SEC_I_COMPLETE_NEEDED:
290253893Speter    case SEC_E_OK:
291253893Speter        return APR_SUCCESS;
292253893Speter
293253893Speter    default:
294253893Speter        return map_sspi_status(status);
295253893Speter    }
296253893Speter}
297253893Speter
298253893Speter#endif /* SERF_USE_SSPI */
299