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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
18 * ====================================================================
22 #include "serf_private.h"
23 #include "auth_spnego.h"
25 #ifdef SERF_USE_GSSAPI
26 #include <apr_strings.h>
27 #include <gssapi/gssapi.h>
30 /* This module can support all authentication mechanisms as provided by
31 the GSS-API implementation, but for now it only supports SPNEGO for
33 SPNEGO can delegate authentication to Kerberos if supported by the
36 #ifndef GSS_SPNEGO_MECHANISM
37 static gss_OID_desc spnego_mech_oid = { 6, "\x2b\x06\x01\x05\x05\x02" };
38 #define GSS_SPNEGO_MECHANISM &spnego_mech_oid
41 struct serf__spnego_context_t
46 /* Mechanism used to authenticate. */
51 log_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,
57 OM_uint32 maj_stat, min_stat;
58 gss_buffer_desc stat_buff;
59 OM_uint32 msg_ctx = 0;
62 maj_stat = gss_display_status(&min_stat,
68 if (maj_stat == GSS_S_COMPLETE ||
69 maj_stat == GSS_S_FAILURE) {
70 maj_stat = gss_display_status(&min_stat,
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);
84 /* Cleans the GSS context object, when the pool used to create it gets
85 cleared or destroyed. */
87 cleanup_ctx(void *data)
89 serf__spnego_context_t *ctx = data;
91 if (ctx->gss_ctx != GSS_C_NO_CONTEXT) {
92 OM_uint32 gss_min_stat, gss_maj_stat;
94 gss_maj_stat = gss_delete_sec_context(&gss_min_stat, &ctx->gss_ctx,
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;
108 cleanup_sec_buffer(void *data)
111 gss_buffer_desc *gss_buf = data;
113 gss_release_buffer(&min_stat, gss_buf);
119 serf__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)
124 serf__spnego_context_t *ctx;
126 ctx = apr_pcalloc(result_pool, sizeof(*ctx));
128 ctx->gss_ctx = GSS_C_NO_CONTEXT;
129 ctx->gss_mech = GSS_SPNEGO_MECHANISM;
131 apr_pool_cleanup_register(result_pool, ctx,
133 apr_pool_cleanup_null);
141 serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
143 OM_uint32 dummy_stat;
146 (void)gss_delete_sec_context(&dummy_stat, &ctx->gss_ctx,
148 ctx->gss_ctx = GSS_C_NO_CONTEXT;
154 serf__spnego_init_sec_context(serf_connection_t *conn,
155 serf__spnego_context_t *ctx,
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
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 */
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,
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;
187 /* If the server sent us a token, pass it to gss_init_sec_token for
189 gss_input_buf.value = input_buf->value;
190 gss_input_buf.length = input_buf->length;
192 gss_output_buf_p = apr_pcalloc(result_pool, sizeof(*gss_output_buf_p));
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 */
211 apr_pool_cleanup_register(result_pool, gss_output_buf_p,
213 apr_pool_cleanup_null);
215 output_buf->value = gss_output_buf_p->value;
216 output_buf->length = gss_output_buf_p->length;
218 switch(gss_maj_stat) {
221 case GSS_S_CONTINUE_NEEDED:
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;
231 #endif /* SERF_USE_GSSAPI */