|
gsasl
1.8.0
|
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 }
1.7.6.1