gsasl  1.8.0
gs2/client.c
Go to the documentation of this file.
00001 /* client.c --- SASL mechanism GS2, client side.
00002  * Copyright (C) 2002-2012 Simon Josefsson
00003  *
00004  * This file is part of GNU SASL Library.
00005  *
00006  * GNU SASL Library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public License
00008  * as published by the Free Software Foundation; either version 2.1 of
00009  * the License, or (at your option) any later version.
00010  *
00011  * GNU SASL Library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with GNU SASL Library; if not, write to the Free
00018  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026 
00027 /* Get specification. */
00028 #include "gs2.h"
00029 
00030 /* Get malloc, free. */
00031 #include <stdlib.h>
00032 
00033 /* Get memcpy, strlen. */
00034 #include <string.h>
00035 
00036 #include "gss-extra.h"
00037 #include "gs2helper.h"
00038 
00039 struct _gsasl_gs2_client_state
00040 {
00041   /* steps: 0 = initial, 1 = first token, 2 = looping, 3 = done */
00042   int step;
00043   gss_name_t service;
00044   gss_ctx_id_t context;
00045   gss_OID mech_oid;
00046   gss_buffer_desc token;
00047   struct gss_channel_bindings_struct cb;
00048 };
00049 typedef struct _gsasl_gs2_client_state _gsasl_gs2_client_state;
00050 
00051 /* Initialize GS2 state into MECH_DATA.  Return GSASL_OK if GS2 is
00052    ready and initialization succeeded, or an error code. */
00053 int
00054 _gsasl_gs2_client_start (Gsasl_session * sctx, void **mech_data)
00055 {
00056   _gsasl_gs2_client_state *state;
00057   int res;
00058 
00059   state = (_gsasl_gs2_client_state *) malloc (sizeof (*state));
00060   if (state == NULL)
00061     return GSASL_MALLOC_ERROR;
00062 
00063   res = gs2_get_oid (sctx, &state->mech_oid);
00064   if (res != GSASL_OK)
00065     {
00066       free (state);
00067       return res;
00068     }
00069 
00070   state->step = 0;
00071   state->service = GSS_C_NO_NAME;
00072   state->context = GSS_C_NO_CONTEXT;
00073   state->token.length = 0;
00074   state->token.value = NULL;
00075   /* The initiator-address-type and acceptor-address-type fields of
00076      the GSS-CHANNEL-BINDINGS structure MUST be set to 0.  The
00077      initiator-address and acceptor-address fields MUST be the empty
00078      string. */
00079   state->cb.initiator_addrtype = 0;
00080   state->cb.initiator_address.length = 0;
00081   state->cb.initiator_address.value = NULL;
00082   state->cb.acceptor_addrtype = 0;
00083   state->cb.acceptor_address.length = 0;
00084   state->cb.acceptor_address.value = NULL;
00085   state->cb.application_data.length = 0;
00086   state->cb.application_data.value = NULL;
00087 
00088   *mech_data = state;
00089 
00090   return GSASL_OK;
00091 }
00092 
00093 /* Return newly allocated copy of STR with all occurrences of ','
00094    replaced with =2C and '=' with '=3D', or return NULL on memory
00095    allocation errors.  */
00096 static char *
00097 escape_authzid (const char *str)
00098 {
00099   char *out = malloc (strlen (str) * 3 + 1);
00100   char *p = out;
00101 
00102   if (!out)
00103     return NULL;
00104 
00105   while (*str)
00106     {
00107       if (*str == ',')
00108         {
00109           memcpy (p, "=2C", 3);
00110           p += 3;
00111         }
00112       else if (*str == '=')
00113         {
00114           memcpy (p, "=3D", 3);
00115           p += 3;
00116         }
00117       else
00118         {
00119           *p = *str;
00120           p++;
00121         }
00122       str++;
00123     }
00124   *p = '\0';
00125 
00126   return out;
00127 }
00128 
00129 /* Get service, hostname and authorization identity from application,
00130    import the GSS-API name, and initialize the channel binding data.
00131    Return GSASL_OK on success or an error code. */
00132 static int
00133 prepare (Gsasl_session * sctx, _gsasl_gs2_client_state * state)
00134 {
00135   const char *service = gsasl_property_get (sctx, GSASL_SERVICE);
00136   const char *hostname = gsasl_property_get (sctx, GSASL_HOSTNAME);
00137   const char *authzid = gsasl_property_get (sctx, GSASL_AUTHZID);
00138   gss_buffer_desc bufdesc;
00139   OM_uint32 maj_stat, min_stat;
00140 
00141   if (!service)
00142     return GSASL_NO_SERVICE;
00143   if (!hostname)
00144     return GSASL_NO_HOSTNAME;
00145 
00146   bufdesc.length = asprintf ((char **) &bufdesc.value, "%s@%s",
00147                              service, hostname);
00148   if (bufdesc.length <= 0 || bufdesc.value == NULL)
00149     return GSASL_MALLOC_ERROR;
00150 
00151   maj_stat = gss_import_name (&min_stat, &bufdesc,
00152                               GSS_C_NT_HOSTBASED_SERVICE, &state->service);
00153   free (bufdesc.value);
00154   if (GSS_ERROR (maj_stat))
00155     return GSASL_GSSAPI_IMPORT_NAME_ERROR;
00156 
00157   if (authzid)
00158     {
00159       char *escaped_authzid = escape_authzid (authzid);
00160 
00161       if (!escaped_authzid)
00162         return GSASL_MALLOC_ERROR;
00163 
00164       state->cb.application_data.length
00165         = asprintf ((char **) &state->cb.application_data.value,
00166                     "n,a=%s,", escaped_authzid);
00167 
00168       free (escaped_authzid);
00169     }
00170   else
00171     {
00172       state->cb.application_data.value = strdup ("n,,");
00173       state->cb.application_data.length = 3;
00174     }
00175 
00176   if (state->cb.application_data.length <= 0
00177       || state->cb.application_data.value == NULL)
00178     return GSASL_MALLOC_ERROR;
00179 
00180   return GSASL_OK;
00181 }
00182 
00183 /* Copy token to output buffer.  On first round trip, strip context
00184    token header and add channel binding data. For later round trips,
00185    just copy the buffer.  Return GSASL_OK on success or an error
00186    code.  */
00187 static int
00188 token2output (Gsasl_session * sctx,
00189               _gsasl_gs2_client_state * state,
00190               const gss_buffer_t token, char **output, size_t * output_len)
00191 {
00192   OM_uint32 maj_stat, min_stat;
00193   gss_buffer_desc bufdesc;
00194 
00195   if (state->step == 1)
00196     {
00197       state->step++;
00198 
00199       maj_stat = gss_decapsulate_token (token, state->mech_oid, &bufdesc);
00200       if (GSS_ERROR (maj_stat))
00201         return GSASL_GSSAPI_ENCAPSULATE_TOKEN_ERROR;
00202 
00203       *output_len = state->cb.application_data.length + bufdesc.length;
00204       *output = malloc (*output_len);
00205       if (!*output)
00206         {
00207           gss_release_buffer (&min_stat, &bufdesc);
00208           return GSASL_MALLOC_ERROR;
00209         }
00210 
00211       memcpy (*output, state->cb.application_data.value,
00212               state->cb.application_data.length);
00213       memcpy (*output + state->cb.application_data.length,
00214               bufdesc.value, bufdesc.length);
00215 
00216       maj_stat = gss_release_buffer (&min_stat, &bufdesc);
00217       if (GSS_ERROR (maj_stat))
00218         return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
00219     }
00220   else
00221     {
00222       *output_len = token->length;
00223       *output = malloc (*output_len);
00224       if (!*output)
00225         return GSASL_MALLOC_ERROR;
00226       memcpy (*output, token->value, token->length);
00227     }
00228 
00229   return GSASL_OK;
00230 }
00231 
00232 /* Perform one GS2 step.  GS2 state is in MECH_DATA.  Any data from
00233    server is provided in INPUT/INPUT_LEN and output from client is
00234    expected to be put in newly allocated OUTPUT/OUTPUT_LEN.  Return
00235    GSASL_NEEDS_MORE or GSASL_OK on success, or an error code.  */
00236 int
00237 _gsasl_gs2_client_step (Gsasl_session * sctx,
00238                         void *mech_data,
00239                         const char *input, size_t input_len,
00240                         char **output, size_t * output_len)
00241 {
00242   _gsasl_gs2_client_state *state = mech_data;
00243   gss_buffer_desc bufdesc;
00244   gss_buffer_t buf = GSS_C_NO_BUFFER;
00245   OM_uint32 maj_stat, min_stat, ret_flags;
00246   gss_OID actual_mech_type;
00247   int res;
00248 
00249   if (state->step > 2)
00250     return GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
00251 
00252   if (state->step == 0)
00253     {
00254       res = prepare (sctx, state);
00255       if (res != GSASL_OK)
00256         return res;
00257       state->step++;
00258     }
00259 
00260   if (state->step == 2)
00261     {
00262       bufdesc.length = input_len;
00263       bufdesc.value = (void *) input;
00264       buf = &bufdesc;
00265     }
00266 
00267   /* First release memory for token from last round-trip, if any. */
00268   if (state->token.value != NULL)
00269     {
00270       maj_stat = gss_release_buffer (&min_stat, &state->token);
00271       if (GSS_ERROR (maj_stat))
00272         return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
00273 
00274       state->token.value = NULL;
00275       state->token.length = 0;
00276     }
00277 
00278   maj_stat = gss_init_sec_context (&min_stat,
00279                                    GSS_C_NO_CREDENTIAL,
00280                                    &state->context,
00281                                    state->service,
00282                                    state->mech_oid,
00283                                    GSS_C_MUTUAL_FLAG,
00284                                    0,
00285                                    &state->cb,
00286                                    buf,
00287                                    &actual_mech_type,
00288                                    &state->token, &ret_flags, NULL);
00289   if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
00290     return GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR;
00291 
00292   res = token2output (sctx, state, &state->token, output, output_len);
00293   if (res != GSASL_OK)
00294     return res;
00295 
00296   if (maj_stat == GSS_S_CONTINUE_NEEDED)
00297     return GSASL_NEEDS_MORE;
00298 
00299   /* The GSS-API layer is done here, check that we established a valid
00300      security context for GS2 purposes. */
00301 
00302   if (!(ret_flags & GSS_C_MUTUAL_FLAG))
00303     return GSASL_AUTHENTICATION_ERROR;
00304 
00305   if (!gss_oid_equal (state->mech_oid, actual_mech_type))
00306     return GSASL_AUTHENTICATION_ERROR;
00307 
00308   state->step++;
00309   return GSASL_OK;
00310 }
00311 
00312 /* Cleanup GS2 state context, i.e., release memory associated with
00313    buffers in MECH_DATA state. */
00314 void
00315 _gsasl_gs2_client_finish (Gsasl_session * sctx, void *mech_data)
00316 {
00317   _gsasl_gs2_client_state *state = mech_data;
00318   OM_uint32 maj_stat, min_stat;
00319 
00320   if (!state)
00321     return;
00322 
00323   if (state->token.value != NULL)
00324     maj_stat = gss_release_buffer (&min_stat, &state->token);
00325   if (state->service != GSS_C_NO_NAME)
00326     maj_stat = gss_release_name (&min_stat, &state->service);
00327   if (state->context != GSS_C_NO_CONTEXT)
00328     maj_stat = gss_delete_sec_context (&min_stat, &state->context,
00329                                        GSS_C_NO_BUFFER);
00330 
00331   free (state->cb.application_data.value);
00332   free (state);
00333 }