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