1/* ==================================================================== 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * ==================================================================== 19 */ 20 21#include "serf.h" 22#include "serf_private.h" 23#include "auth_spnego.h" 24 25#ifdef SERF_USE_GSSAPI 26#include <apr_strings.h> 27#include <gssapi/gssapi.h> 28 29 30/* This module can support all authentication mechanisms as provided by 31 the GSS-API implementation, but for now it only supports SPNEGO for 32 Negotiate. 33 SPNEGO can delegate authentication to Kerberos if supported by the 34 host. */ 35 36#ifndef GSS_SPNEGO_MECHANISM 37static gss_OID_desc spnego_mech_oid = { 6, "\x2b\x06\x01\x05\x05\x02" }; 38#define GSS_SPNEGO_MECHANISM &spnego_mech_oid 39#endif 40 41struct serf__spnego_context_t 42{ 43 /* GSSAPI context */ 44 gss_ctx_id_t gss_ctx; 45 46 /* Mechanism used to authenticate. */ 47 gss_OID gss_mech; 48}; 49 50static void 51log_error(int verbose_flag, apr_socket_t *skt, 52 serf__spnego_context_t *ctx, 53 OM_uint32 err_maj_stat, 54 OM_uint32 err_min_stat, 55 const char *msg) 56{ 57 OM_uint32 maj_stat, min_stat; 58 gss_buffer_desc stat_buff; 59 OM_uint32 msg_ctx = 0; 60 61 if (verbose_flag) { 62 maj_stat = gss_display_status(&min_stat, 63 err_maj_stat, 64 GSS_C_GSS_CODE, 65 ctx->gss_mech, 66 &msg_ctx, 67 &stat_buff); 68 if (maj_stat == GSS_S_COMPLETE || 69 maj_stat == GSS_S_FAILURE) { 70 maj_stat = gss_display_status(&min_stat, 71 err_min_stat, 72 GSS_C_MECH_CODE, 73 ctx->gss_mech, 74 &msg_ctx, 75 &stat_buff); 76 } 77 78 serf__log_skt(verbose_flag, __FILE__, skt, 79 "%s (%x,%d): %s\n", msg, 80 err_maj_stat, err_min_stat, stat_buff.value); 81 } 82} 83 84/* Cleans the GSS context object, when the pool used to create it gets 85 cleared or destroyed. */ 86static apr_status_t 87cleanup_ctx(void *data) 88{ 89 serf__spnego_context_t *ctx = data; 90 91 if (ctx->gss_ctx != GSS_C_NO_CONTEXT) { 92 OM_uint32 gss_min_stat, gss_maj_stat; 93 94 gss_maj_stat = gss_delete_sec_context(&gss_min_stat, &ctx->gss_ctx, 95 GSS_C_NO_BUFFER); 96 if(GSS_ERROR(gss_maj_stat)) { 97 log_error(AUTH_VERBOSE, NULL, ctx, 98 gss_maj_stat, gss_min_stat, 99 "Error cleaning up GSS security context"); 100 return SERF_ERROR_AUTHN_FAILED; 101 } 102 } 103 104 return APR_SUCCESS; 105} 106 107static apr_status_t 108cleanup_sec_buffer(void *data) 109{ 110 OM_uint32 min_stat; 111 gss_buffer_desc *gss_buf = data; 112 113 gss_release_buffer(&min_stat, gss_buf); 114 115 return APR_SUCCESS; 116} 117 118apr_status_t 119serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p, 120 const serf__authn_scheme_t *scheme, 121 apr_pool_t *result_pool, 122 apr_pool_t *scratch_pool) 123{ 124 serf__spnego_context_t *ctx; 125 126 ctx = apr_pcalloc(result_pool, sizeof(*ctx)); 127 128 ctx->gss_ctx = GSS_C_NO_CONTEXT; 129 ctx->gss_mech = GSS_SPNEGO_MECHANISM; 130 131 apr_pool_cleanup_register(result_pool, ctx, 132 cleanup_ctx, 133 apr_pool_cleanup_null); 134 135 *ctx_p = ctx; 136 137 return APR_SUCCESS; 138} 139 140apr_status_t 141serf__spnego_reset_sec_context(serf__spnego_context_t *ctx) 142{ 143 OM_uint32 dummy_stat; 144 145 if (ctx->gss_ctx) 146 (void)gss_delete_sec_context(&dummy_stat, &ctx->gss_ctx, 147 GSS_C_NO_BUFFER); 148 ctx->gss_ctx = GSS_C_NO_CONTEXT; 149 150 return APR_SUCCESS; 151} 152 153apr_status_t 154serf__spnego_init_sec_context(serf_connection_t *conn, 155 serf__spnego_context_t *ctx, 156 const char *service, 157 const char *hostname, 158 serf__spnego_buffer_t *input_buf, 159 serf__spnego_buffer_t *output_buf, 160 apr_pool_t *result_pool, 161 apr_pool_t *scratch_pool 162 ) 163{ 164 gss_buffer_desc gss_input_buf = GSS_C_EMPTY_BUFFER; 165 gss_buffer_desc *gss_output_buf_p; 166 OM_uint32 gss_min_stat, gss_maj_stat; 167 gss_name_t host_gss_name; 168 gss_buffer_desc bufdesc; 169 gss_OID dummy; /* unused */ 170 171 /* Get the name for the HTTP service at the target host. */ 172 /* TODO: should be shared between multiple requests. */ 173 bufdesc.value = apr_pstrcat(scratch_pool, service, "@", hostname, NULL); 174 bufdesc.length = strlen(bufdesc.value); 175 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 176 "Get principal for %s\n", bufdesc.value); 177 gss_maj_stat = gss_import_name (&gss_min_stat, &bufdesc, 178 GSS_C_NT_HOSTBASED_SERVICE, 179 &host_gss_name); 180 if(GSS_ERROR(gss_maj_stat)) { 181 log_error(AUTH_VERBOSE, conn->skt, ctx, 182 gss_maj_stat, gss_min_stat, 183 "Error converting principal name to GSS internal format "); 184 return SERF_ERROR_AUTHN_FAILED; 185 } 186 187 /* If the server sent us a token, pass it to gss_init_sec_token for 188 validation. */ 189 gss_input_buf.value = input_buf->value; 190 gss_input_buf.length = input_buf->length; 191 192 gss_output_buf_p = apr_pcalloc(result_pool, sizeof(*gss_output_buf_p)); 193 194 /* Establish a security context to the server. */ 195 gss_maj_stat = gss_init_sec_context 196 (&gss_min_stat, /* minor_status */ 197 GSS_C_NO_CREDENTIAL, /* XXXXX claimant_cred_handle */ 198 &ctx->gss_ctx, /* gssapi context handle */ 199 host_gss_name, /* HTTP@server name */ 200 ctx->gss_mech, /* mech_type (SPNEGO) */ 201 GSS_C_MUTUAL_FLAG, /* ensure the peer authenticates itself */ 202 0, /* default validity period */ 203 GSS_C_NO_CHANNEL_BINDINGS, /* do not use channel bindings */ 204 &gss_input_buf, /* server token, initially empty */ 205 &dummy, /* actual mech type */ 206 gss_output_buf_p, /* output_token */ 207 NULL, /* ret_flags */ 208 NULL /* not interested in remaining validity */ 209 ); 210 211 apr_pool_cleanup_register(result_pool, gss_output_buf_p, 212 cleanup_sec_buffer, 213 apr_pool_cleanup_null); 214 215 output_buf->value = gss_output_buf_p->value; 216 output_buf->length = gss_output_buf_p->length; 217 218 switch(gss_maj_stat) { 219 case GSS_S_COMPLETE: 220 return APR_SUCCESS; 221 case GSS_S_CONTINUE_NEEDED: 222 return APR_EAGAIN; 223 default: 224 log_error(AUTH_VERBOSE, conn->skt, ctx, 225 gss_maj_stat, gss_min_stat, 226 "Error during Kerberos handshake"); 227 return SERF_ERROR_AUTHN_FAILED; 228 } 229} 230 231#endif /* SERF_USE_GSSAPI */ 232