]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/serf/auth/auth_spnego_gss.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / serf / auth / auth_spnego_gss.c
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
32 static gss_OID_desc spnego_mech_oid = { 6, "\x2b\x06\x01\x05\x05\x02" };
33 #define GSS_SPNEGO_MECHANISM &spnego_mech_oid
34 #endif
35
36 struct 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
45 static void
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,
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(verbose_flag, filename,
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. */
81 static apr_status_t
82 cleanup_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, __FILE__, 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
102 static apr_status_t
103 cleanup_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
113 apr_status_t
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)
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
135 apr_status_t
136 serf__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
148 apr_status_t
149 serf__spnego_init_sec_context(serf__spnego_context_t *ctx,
150                               const char *service,
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
156                               )
157 {
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 */
164
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,
172                                     &host_gss_name);
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;
178     }
179
180     /* If the server sent us a token, pass it to gss_init_sec_token for
181        validation. */
182     gss_input_buf.value = input_buf->value;
183     gss_input_buf.length = input_buf->length;
184
185     gss_output_buf_p = apr_pcalloc(result_pool, sizeof(*gss_output_buf_p));
186
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 */
202          );
203
204     apr_pool_cleanup_register(result_pool, gss_output_buf_p,
205                               cleanup_sec_buffer,
206                               apr_pool_cleanup_null);
207
208     output_buf->value = gss_output_buf_p->value;
209     output_buf->length = gss_output_buf_p->length;
210
211     switch(gss_maj_stat) {
212     case GSS_S_COMPLETE:
213         return APR_SUCCESS;
214     case GSS_S_CONTINUE_NEEDED:
215         return APR_EAGAIN;
216     default:
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;
221     }
222 }
223
224 #endif /* SERF_USE_GSSAPI */