1 /* Copyright 2009 Justin Erenkrantz and Greg Stein
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "serf_private.h"
18 #include "auth_spnego.h"
20 #ifdef SERF_USE_GSSAPI
21 #include <apr_strings.h>
22 #include <gssapi/gssapi.h>
25 /* This module can support all authentication mechanisms as provided by
26 the GSS-API implementation, but for now it only supports SPNEGO for
28 SPNEGO can delegate authentication to Kerberos if supported by the
31 #ifndef GSS_SPNEGO_MECHANISM
32 static gss_OID_desc spnego_mech_oid = { 6, "\x2b\x06\x01\x05\x05\x02" };
33 #define GSS_SPNEGO_MECHANISM &spnego_mech_oid
36 struct serf__spnego_context_t
41 /* Mechanism used to authenticate. */
46 log_error(int verbose_flag, const char *filename,
47 serf__spnego_context_t *ctx,
48 OM_uint32 err_maj_stat,
49 OM_uint32 err_min_stat,
52 OM_uint32 maj_stat, min_stat;
53 gss_buffer_desc stat_buff;
54 OM_uint32 msg_ctx = 0;
57 maj_stat = gss_display_status(&min_stat,
63 if (maj_stat == GSS_S_COMPLETE ||
64 maj_stat == GSS_S_FAILURE) {
65 maj_stat = gss_display_status(&min_stat,
73 serf__log(verbose_flag, filename,
74 "%s (%x,%d): %s\n", msg,
75 err_maj_stat, err_min_stat, stat_buff.value);
79 /* Cleans the GSS context object, when the pool used to create it gets
80 cleared or destroyed. */
82 cleanup_ctx(void *data)
84 serf__spnego_context_t *ctx = data;
86 if (ctx->gss_ctx != GSS_C_NO_CONTEXT) {
87 OM_uint32 gss_min_stat, gss_maj_stat;
89 gss_maj_stat = gss_delete_sec_context(&gss_min_stat, &ctx->gss_ctx,
91 if(GSS_ERROR(gss_maj_stat)) {
92 log_error(AUTH_VERBOSE, __FILE__, ctx,
93 gss_maj_stat, gss_min_stat,
94 "Error cleaning up GSS security context");
95 return SERF_ERROR_AUTHN_FAILED;
103 cleanup_sec_buffer(void *data)
106 gss_buffer_desc *gss_buf = data;
108 gss_release_buffer(&min_stat, gss_buf);
114 serf__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)
119 serf__spnego_context_t *ctx;
121 ctx = apr_pcalloc(result_pool, sizeof(*ctx));
123 ctx->gss_ctx = GSS_C_NO_CONTEXT;
124 ctx->gss_mech = GSS_SPNEGO_MECHANISM;
126 apr_pool_cleanup_register(result_pool, ctx,
128 apr_pool_cleanup_null);
136 serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
138 OM_uint32 dummy_stat;
141 (void)gss_delete_sec_context(&dummy_stat, &ctx->gss_ctx,
143 ctx->gss_ctx = GSS_C_NO_CONTEXT;
149 serf__spnego_init_sec_context(serf__spnego_context_t *ctx,
151 const char *hostname,
152 serf__spnego_buffer_t *input_buf,
153 serf__spnego_buffer_t *output_buf,
154 apr_pool_t *result_pool,
155 apr_pool_t *scratch_pool
158 gss_buffer_desc gss_input_buf = GSS_C_EMPTY_BUFFER;
159 gss_buffer_desc *gss_output_buf_p;
160 OM_uint32 gss_min_stat, gss_maj_stat;
161 gss_name_t host_gss_name;
162 gss_buffer_desc bufdesc;
163 gss_OID dummy; /* unused */
165 /* Get the name for the HTTP service at the target host. */
166 /* TODO: should be shared between multiple requests. */
167 bufdesc.value = apr_pstrcat(scratch_pool, service, "@", hostname, NULL);
168 bufdesc.length = strlen(bufdesc.value);
169 serf__log(AUTH_VERBOSE, __FILE__, "Get principal for %s\n", bufdesc.value);
170 gss_maj_stat = gss_import_name (&gss_min_stat, &bufdesc,
171 GSS_C_NT_HOSTBASED_SERVICE,
173 if(GSS_ERROR(gss_maj_stat)) {
174 log_error(AUTH_VERBOSE, __FILE__, ctx,
175 gss_maj_stat, gss_min_stat,
176 "Error converting principal name to GSS internal format ");
177 return SERF_ERROR_AUTHN_FAILED;
180 /* If the server sent us a token, pass it to gss_init_sec_token for
182 gss_input_buf.value = input_buf->value;
183 gss_input_buf.length = input_buf->length;
185 gss_output_buf_p = apr_pcalloc(result_pool, sizeof(*gss_output_buf_p));
187 /* Establish a security context to the server. */
188 gss_maj_stat = gss_init_sec_context
189 (&gss_min_stat, /* minor_status */
190 GSS_C_NO_CREDENTIAL, /* XXXXX claimant_cred_handle */
191 &ctx->gss_ctx, /* gssapi context handle */
192 host_gss_name, /* HTTP@server name */
193 ctx->gss_mech, /* mech_type (SPNEGO) */
194 GSS_C_MUTUAL_FLAG, /* ensure the peer authenticates itself */
195 0, /* default validity period */
196 GSS_C_NO_CHANNEL_BINDINGS, /* do not use channel bindings */
197 &gss_input_buf, /* server token, initially empty */
198 &dummy, /* actual mech type */
199 gss_output_buf_p, /* output_token */
200 NULL, /* ret_flags */
201 NULL /* not interested in remaining validity */
204 apr_pool_cleanup_register(result_pool, gss_output_buf_p,
206 apr_pool_cleanup_null);
208 output_buf->value = gss_output_buf_p->value;
209 output_buf->length = gss_output_buf_p->length;
211 switch(gss_maj_stat) {
214 case GSS_S_CONTINUE_NEEDED:
217 log_error(AUTH_VERBOSE, __FILE__, ctx,
218 gss_maj_stat, gss_min_stat,
219 "Error during Kerberos handshake");
220 return SERF_ERROR_AUTHN_FAILED;
224 #endif /* SERF_USE_GSSAPI */