]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/serf/auth/auth_spnego_gss.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / contrib / serf / auth / auth_spnego_gss.c
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
37 static gss_OID_desc spnego_mech_oid = { 6, "\x2b\x06\x01\x05\x05\x02" };
38 #define GSS_SPNEGO_MECHANISM &spnego_mech_oid
39 #endif
40
41 struct 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
50 static void
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,
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. */
86 static apr_status_t
87 cleanup_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
107 static apr_status_t
108 cleanup_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
118 apr_status_t
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)
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
140 apr_status_t
141 serf__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
153 apr_status_t
154 serf__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 */