auth_spnego_sspi.c revision 262339
1139969Simp/* Copyright 2010 Justin Erenkrantz and Greg Stein
21556Srgrimes *
31556Srgrimes * Licensed under the Apache License, Version 2.0 (the "License");
41556Srgrimes * you may not use this file except in compliance with the License.
51556Srgrimes * You may obtain a copy of the License at
61556Srgrimes *
71556Srgrimes *     http://www.apache.org/licenses/LICENSE-2.0
81556Srgrimes *
91556Srgrimes * Unless required by applicable law or agreed to in writing, software
101556Srgrimes * distributed under the License is distributed on an "AS IS" BASIS,
111556Srgrimes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121556Srgrimes * See the License for the specific language governing permissions and
131556Srgrimes * limitations under the License.
141556Srgrimes */
151556Srgrimes
161556Srgrimes#include "auth_spnego.h"
171556Srgrimes#include "serf.h"
181556Srgrimes#include "serf_private.h"
191556Srgrimes
201556Srgrimes#ifdef SERF_USE_SSPI
211556Srgrimes#include <apr.h>
221556Srgrimes#include <apr_strings.h>
231556Srgrimes
241556Srgrimes#define SECURITY_WIN32
251556Srgrimes#include <sspi.h>
261556Srgrimes
271556Srgrimes/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */
281556Srgrimes#ifndef SEC_E_MUTUAL_AUTH_FAILED
29219154Sjilles#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L)
30219154Sjilles#endif
31219154Sjilles
32219154Sjillesstruct serf__spnego_context_t
331556Srgrimes{
34114433Sobrien    CredHandle sspi_credentials;
351556Srgrimes    CtxtHandle sspi_context;
3620416Ssteve    BOOL initalized;
371556Srgrimes    apr_pool_t *pool;
381556Srgrimes
391556Srgrimes    /* Service Principal Name (SPN) used for authentication. */
401556Srgrimes    const char *target_name;
411556Srgrimes
4236012Scharnier    /* One of SERF_AUTHN_* authentication types.*/
43114433Sobrien    int authn_type;
4436012Scharnier};
4599109Sobrien
4699109Sobrien/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped
471556Srgrimes * to our own codes and some to Win32 error codes:
481556Srgrimes * http://support.microsoft.com/kb/113996
491556Srgrimes */
501556Srgrimesstatic apr_status_t
511556Srgrimesmap_sspi_status(SECURITY_STATUS sspi_status)
521556Srgrimes{
531556Srgrimes    switch(sspi_status)
541556Srgrimes    {
551556Srgrimes    case SEC_E_INSUFFICIENT_MEMORY:
56216629Sjilles        return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES);
57216629Sjilles    case SEC_E_INVALID_HANDLE:
58216629Sjilles        return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE);
59216629Sjilles    case SEC_E_UNSUPPORTED_FUNCTION:
60216629Sjilles        return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION);
61216629Sjilles    case SEC_E_TARGET_UNKNOWN:
62127005Sjmallett        return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH);
63127005Sjmallett    case SEC_E_INTERNAL_ERROR:
64127005Sjmallett        return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR);
65127005Sjmallett    case SEC_E_SECPKG_NOT_FOUND:
661556Srgrimes    case SEC_E_BAD_PKGID:
671556Srgrimes        return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE);
6890110Simp    case SEC_E_NO_IMPERSONATION:
691556Srgrimes        return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE);
70315722Sbdrewery    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
71315722Sbdrewery        return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS);
72315722Sbdrewery    case SEC_E_UNTRUSTED_ROOT:
731556Srgrimes        return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE);
741556Srgrimes    case SEC_E_WRONG_PRINCIPAL:
751556Srgrimes        return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME);
761556Srgrimes    case SEC_E_MUTUAL_AUTH_FAILED:
771556Srgrimes        return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED);
7820416Ssteve    case SEC_E_TIME_SKEW:
7920416Ssteve        return APR_FROM_OS_ERROR(ERROR_TIME_SKEW);
8020416Ssteve    default:
8120416Ssteve        return SERF_ERROR_AUTHN_FAILED;
8220416Ssteve    }
8320416Ssteve}
8420416Ssteve
8520416Ssteve/* Cleans the SSPI context object, when the pool used to create it gets
8620416Ssteve   cleared or destroyed. */
8720416Sstevestatic apr_status_t
8820416Sstevecleanup_ctx(void *data)
8928554Sjlemon{
90216629Sjilles    serf__spnego_context_t *ctx = data;
9120416Ssteve
9220416Ssteve    if (SecIsValidHandle(&ctx->sspi_context)) {
93125156Snjl        DeleteSecurityContext(&ctx->sspi_context);
9420416Ssteve        SecInvalidateHandle(&ctx->sspi_context);
9520416Ssteve    }
96216629Sjilles
9720416Ssteve    if (SecIsValidHandle(&ctx->sspi_credentials)) {
9820416Ssteve        FreeCredentialsHandle(&ctx->sspi_context);
99216629Sjilles        SecInvalidateHandle(&ctx->sspi_context);
1001556Srgrimes    }
1011556Srgrimes
10220416Ssteve    return APR_SUCCESS;
10320416Ssteve}
10420416Ssteve
10520416Sstevestatic apr_status_t
10620416Sstevecleanup_sec_buffer(void *data)
10720416Ssteve{
10820416Ssteve    FreeContextBuffer(data);
10920416Ssteve
11020416Ssteve    return APR_SUCCESS;
11120416Ssteve}
11220416Ssteve
11320416Ssteveapr_status_t
11498158Stjrserf__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
1151556Srgrimes                                const serf__authn_scheme_t *scheme,
1161556Srgrimes                                apr_pool_t *result_pool,
11720416Ssteve                                apr_pool_t *scratch_pool)
1181556Srgrimes{
1191556Srgrimes    SECURITY_STATUS sspi_status;
1201556Srgrimes    serf__spnego_context_t *ctx;
12128554Sjlemon    const char *sspi_package;
122216629Sjilles
123204308Skib    ctx = apr_pcalloc(result_pool, sizeof(*ctx));
1241556Srgrimes
1251556Srgrimes    SecInvalidateHandle(&ctx->sspi_context);
1261556Srgrimes    SecInvalidateHandle(&ctx->sspi_credentials);
12720416Ssteve    ctx->initalized = FALSE;
1281556Srgrimes    ctx->pool = result_pool;
1291556Srgrimes    ctx->target_name = NULL;
13098158Stjr    ctx->authn_type = scheme->type;
13198158Stjr
13298158Stjr    apr_pool_cleanup_register(result_pool, ctx,
13320416Ssteve                              cleanup_ctx,
1341556Srgrimes                              apr_pool_cleanup_null);
1351556Srgrimes
13620416Ssteve    if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
137216629Sjilles        sspi_package = "Negotiate";
138216629Sjilles    else
139216629Sjilles        sspi_package = "NTLM";
140216629Sjilles
141216629Sjilles    sspi_status = AcquireCredentialsHandle(
142216629Sjilles        NULL, sspi_package, SECPKG_CRED_OUTBOUND,
143315722Sbdrewery        NULL, NULL, NULL, NULL,
144315722Sbdrewery        &ctx->sspi_credentials, NULL);
145315722Sbdrewery
146315722Sbdrewery    if (FAILED(sspi_status)) {
147216629Sjilles        return map_sspi_status(sspi_status);
148216629Sjilles    }
149216629Sjilles
1501556Srgrimes    *ctx_p = ctx;
1511556Srgrimes
1521556Srgrimes    return APR_SUCCESS;
1531556Srgrimes}
15420416Ssteve
155216629Sjillesstatic apr_status_t
1561556Srgrimesget_canonical_hostname(const char **canonname,
1571556Srgrimes                       const char *hostname,
158127005Sjmallett                       apr_pool_t *pool)
159127005Sjmallett{
16020416Ssteve    struct addrinfo hints;
16120416Ssteve    struct addrinfo *addrinfo;
16220416Ssteve
163250035Seadler    ZeroMemory(&hints, sizeof(hints));
16420416Ssteve    hints.ai_flags = AI_CANONNAME;
165125156Snjl
16620416Ssteve    if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) {
16720416Ssteve        return apr_get_netos_error();
16820416Ssteve    }
16920416Ssteve
17020416Ssteve    if (addrinfo) {
17120416Ssteve        *canonname = apr_pstrdup(pool, addrinfo->ai_canonname);
172127005Sjmallett    }
173127005Sjmallett    else {
1741556Srgrimes        *canonname = apr_pstrdup(pool, hostname);
1751556Srgrimes    }
1761556Srgrimes
17720416Ssteve    freeaddrinfo(addrinfo);
178216629Sjilles    return APR_SUCCESS;
179216629Sjilles}
180216629Sjilles
181216629Sjillesapr_status_t
182216629Sjillesserf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
1831556Srgrimes{
1841556Srgrimes    if (SecIsValidHandle(&ctx->sspi_context)) {
185127005Sjmallett        DeleteSecurityContext(&ctx->sspi_context);
18690110Simp        SecInvalidateHandle(&ctx->sspi_context);
1871556Srgrimes    }
18820416Ssteve
1891556Srgrimes    ctx->initalized = FALSE;
190125156Snjl
19120416Ssteve    return APR_SUCCESS;
192125156Snjl}
1931556Srgrimes
19420416Ssteveapr_status_t
19520416Ssteveserf__spnego_init_sec_context(serf_connection_t *conn,
1961556Srgrimes                              serf__spnego_context_t *ctx,
1971556Srgrimes                              const char *service,
1981556Srgrimes                              const char *hostname,
199127005Sjmallett                              serf__spnego_buffer_t *input_buf,
20090110Simp                              serf__spnego_buffer_t *output_buf,
2011556Srgrimes                              apr_pool_t *result_pool,
2021556Srgrimes                              apr_pool_t *scratch_pool
20326465Scharnier                              )
20426465Scharnier{
20526465Scharnier    SECURITY_STATUS status;
20626465Scharnier    ULONG actual_attr;
20726465Scharnier    SecBuffer sspi_in_buffer;
208216629Sjilles    SecBufferDesc sspi_in_buffer_desc;
209216629Sjilles    SecBuffer sspi_out_buffer;
210216629Sjilles    SecBufferDesc sspi_out_buffer_desc;
211216629Sjilles    apr_status_t apr_status;
212216629Sjilles    const char *canonname;
2131556Srgrimes
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