| /* Copyright 2010 Justin Erenkrantz and Greg Stein |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "auth_kerb.h" |
| |
| #ifdef SERF_USE_SSPI |
| #include <apr.h> |
| #include <apr_strings.h> |
| |
| #define SECURITY_WIN32 |
| #include <sspi.h> |
| |
| struct serf__kerb_context_t |
| { |
| CredHandle sspi_credentials; |
| CtxtHandle sspi_context; |
| BOOL initalized; |
| }; |
| |
| /* Cleans the SSPI context object, when the pool used to create it gets |
| cleared or destroyed. */ |
| static apr_status_t |
| cleanup_ctx(void *data) |
| { |
| serf__kerb_context_t *ctx = data; |
| |
| if (SecIsValidHandle(&ctx->sspi_context)) { |
| DeleteSecurityContext(&ctx->sspi_context); |
| SecInvalidateHandle(&ctx->sspi_context); |
| } |
| |
| if (SecIsValidHandle(&ctx->sspi_credentials)) { |
| FreeCredentialsHandle(&ctx->sspi_context); |
| SecInvalidateHandle(&ctx->sspi_context); |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t |
| cleanup_sec_buffer(void *data) |
| { |
| FreeContextBuffer(data); |
| |
| return APR_SUCCESS; |
| } |
| |
| apr_status_t |
| serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p, |
| apr_pool_t *scratch_pool, |
| apr_pool_t *result_pool) |
| { |
| SECURITY_STATUS sspi_status; |
| serf__kerb_context_t *ctx; |
| |
| ctx = apr_pcalloc(result_pool, sizeof(*ctx)); |
| |
| SecInvalidateHandle(&ctx->sspi_context); |
| SecInvalidateHandle(&ctx->sspi_credentials); |
| ctx->initalized = FALSE; |
| |
| apr_pool_cleanup_register(result_pool, ctx, |
| cleanup_ctx, |
| apr_pool_cleanup_null); |
| |
| sspi_status = AcquireCredentialsHandle( |
| NULL, "Negotiate", SECPKG_CRED_OUTBOUND, |
| NULL, NULL, NULL, NULL, |
| &ctx->sspi_credentials, NULL); |
| |
| if (FAILED(sspi_status)) { |
| return APR_EGENERAL; |
| } |
| |
| *ctx_p = ctx; |
| |
| return APR_SUCCESS; |
| } |
| |
| apr_status_t |
| serf__kerb_init_sec_context(serf__kerb_context_t *ctx, |
| const char *service, |
| const char *hostname, |
| serf__kerb_buffer_t *input_buf, |
| serf__kerb_buffer_t *output_buf, |
| apr_pool_t *scratch_pool, |
| apr_pool_t *result_pool |
| ) |
| { |
| SECURITY_STATUS status; |
| ULONG actual_attr; |
| SecBuffer sspi_in_buffer; |
| SecBufferDesc sspi_in_buffer_desc; |
| SecBuffer sspi_out_buffer; |
| SecBufferDesc sspi_out_buffer_desc; |
| char *target_name; |
| |
| target_name = apr_pstrcat(scratch_pool, service, "/", hostname, NULL); |
| |
| /* Prepare input buffer description. */ |
| sspi_in_buffer.BufferType = SECBUFFER_TOKEN; |
| sspi_in_buffer.pvBuffer = input_buf->value; |
| sspi_in_buffer.cbBuffer = input_buf->length; |
| |
| sspi_in_buffer_desc.cBuffers = 1; |
| sspi_in_buffer_desc.pBuffers = &sspi_in_buffer; |
| sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION; |
| |
| /* Output buffers. Output buffer will be allocated by system. */ |
| sspi_out_buffer.BufferType = SECBUFFER_TOKEN; |
| sspi_out_buffer.pvBuffer = NULL; |
| sspi_out_buffer.cbBuffer = 0; |
| |
| sspi_out_buffer_desc.cBuffers = 1; |
| sspi_out_buffer_desc.pBuffers = &sspi_out_buffer; |
| sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION; |
| |
| status = InitializeSecurityContext( |
| &ctx->sspi_credentials, |
| ctx->initalized ? &ctx->sspi_context : NULL, |
| target_name, |
| ISC_REQ_ALLOCATE_MEMORY |
| | ISC_REQ_MUTUAL_AUTH |
| | ISC_REQ_CONFIDENTIALITY, |
| 0, /* Reserved1 */ |
| SECURITY_NETWORK_DREP, |
| &sspi_in_buffer_desc, |
| 0, /* Reserved2 */ |
| &ctx->sspi_context, |
| &sspi_out_buffer_desc, |
| &actual_attr, |
| NULL); |
| |
| if (sspi_out_buffer.cbBuffer > 0) { |
| apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer, |
| cleanup_sec_buffer, |
| apr_pool_cleanup_null); |
| } |
| |
| ctx->initalized = TRUE; |
| |
| /* Finish authentication if SSPI requires so. */ |
| if (status == SEC_I_COMPLETE_NEEDED |
| || status == SEC_I_COMPLETE_AND_CONTINUE) |
| { |
| CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc); |
| } |
| |
| output_buf->value = sspi_out_buffer.pvBuffer; |
| output_buf->length = sspi_out_buffer.cbBuffer; |
| |
| switch(status) { |
| case SEC_I_COMPLETE_AND_CONTINUE: |
| case SEC_I_CONTINUE_NEEDED: |
| return APR_EAGAIN; |
| |
| case SEC_I_COMPLETE_NEEDED: |
| case SEC_E_OK: |
| return APR_SUCCESS; |
| |
| default: |
| return APR_EGENERAL; |
| } |
| } |
| |
| #endif /* SERF_USE_SSPI */ |