gsasl  2.2.1
gs2/client.c
Go to the documentation of this file.
1 /* client.c --- SASL mechanism GS2, client side.
2  * Copyright (C) 2002-2024 Simon Josefsson
3  *
4  * This file is part of GNU SASL Library.
5  *
6  * GNU SASL Library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GNU SASL Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GNU SASL Library; if not, write to the Free
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include <config.h>
24 
25 /* Get specification. */
26 #include "gs2.h"
27 
28 /* Get malloc, free. */
29 #include <stdlib.h>
30 
31 /* Get memcpy, strlen. */
32 #include <string.h>
33 
34 #include "gss-extra.h"
35 #include "gs2helper.h"
36 
38 {
39  /* steps: 0 = initial, 1 = first token, 2 = looping, 3 = done */
40  int step;
41  gss_name_t service;
42  gss_ctx_id_t context;
43  gss_OID mech_oid;
44  gss_buffer_desc token;
45  struct gss_channel_bindings_struct cb;
46 };
48 
49 /* Initialize GS2 state into MECH_DATA. Return GSASL_OK if GS2 is
50  ready and initialization succeeded, or an error code. */
51 int
52 _gsasl_gs2_client_start (Gsasl_session *sctx, void **mech_data)
53 {
55  int res;
56 
57  state = (_gsasl_gs2_client_state *) malloc (sizeof (*state));
58  if (state == NULL)
59  return GSASL_MALLOC_ERROR;
60 
61  res = gs2_get_oid (sctx, &state->mech_oid);
62  if (res != GSASL_OK)
63  {
64  free (state);
65  return res;
66  }
67 
68  state->step = 0;
69  state->service = GSS_C_NO_NAME;
70  state->context = GSS_C_NO_CONTEXT;
71  state->token.length = 0;
72  state->token.value = NULL;
73  /* The initiator-address-type and acceptor-address-type fields of
74  the GSS-CHANNEL-BINDINGS structure MUST be set to 0. The
75  initiator-address and acceptor-address fields MUST be the empty
76  string. */
77  state->cb.initiator_addrtype = 0;
78  state->cb.initiator_address.length = 0;
79  state->cb.initiator_address.value = NULL;
80  state->cb.acceptor_addrtype = 0;
81  state->cb.acceptor_address.length = 0;
82  state->cb.acceptor_address.value = NULL;
83  state->cb.application_data.length = 0;
84  state->cb.application_data.value = NULL;
85 
86  *mech_data = state;
87 
88  return GSASL_OK;
89 }
90 
91 /* Return newly allocated copy of STR with all occurrences of ','
92  replaced with =2C and '=' with '=3D', or return NULL on memory
93  allocation errors. */
94 static char *
95 escape_authzid (const char *str)
96 {
97  char *out = malloc (strlen (str) * 3 + 1);
98  char *p = out;
99 
100  if (!out)
101  return NULL;
102 
103  while (*str)
104  {
105  if (*str == ',')
106  {
107  memcpy (p, "=2C", 3);
108  p += 3;
109  }
110  else if (*str == '=')
111  {
112  memcpy (p, "=3D", 3);
113  p += 3;
114  }
115  else
116  {
117  *p = *str;
118  p++;
119  }
120  str++;
121  }
122  *p = '\0';
123 
124  return out;
125 }
126 
127 /* Get service, hostname and authorization identity from application,
128  import the GSS-API name, and initialize the channel binding data.
129  Return GSASL_OK on success or an error code. */
130 static int
131 prepare (Gsasl_session *sctx, _gsasl_gs2_client_state *state)
132 {
133  const char *service = gsasl_property_get (sctx, GSASL_SERVICE);
134  const char *hostname = gsasl_property_get (sctx, GSASL_HOSTNAME);
135  const char *authzid = gsasl_property_get (sctx, GSASL_AUTHZID);
136  gss_buffer_desc bufdesc;
137  OM_uint32 maj_stat, min_stat;
138 
139  if (!service)
140  return GSASL_NO_SERVICE;
141  if (!hostname)
142  return GSASL_NO_HOSTNAME;
143 
144  bufdesc.length = asprintf ((char **) &bufdesc.value, "%s@%s",
145  service, hostname);
146  if (bufdesc.length <= 0 || bufdesc.value == NULL)
147  return GSASL_MALLOC_ERROR;
148 
149  maj_stat = gss_import_name (&min_stat, &bufdesc,
150  GSS_C_NT_HOSTBASED_SERVICE, &state->service);
151  free (bufdesc.value);
152  if (GSS_ERROR (maj_stat))
154 
155  if (authzid)
156  {
157  char *escaped_authzid = escape_authzid (authzid);
158 
159  if (!escaped_authzid)
160  return GSASL_MALLOC_ERROR;
161 
162  state->cb.application_data.length
163  = asprintf ((char **) &state->cb.application_data.value,
164  "n,a=%s,", escaped_authzid);
165 
166  free (escaped_authzid);
167  }
168  else
169  {
170  state->cb.application_data.value = strdup ("n,,");
171  state->cb.application_data.length = 3;
172  }
173 
174  if (state->cb.application_data.length <= 0
175  || state->cb.application_data.value == NULL)
176  return GSASL_MALLOC_ERROR;
177 
178  return GSASL_OK;
179 }
180 
181 /* Copy token to output buffer. On first round trip, strip context
182  token header and add channel binding data. For later round trips,
183  just copy the buffer. Return GSASL_OK on success or an error
184  code. */
185 static int
186 token2output (_gsasl_gs2_client_state *state,
187  const gss_buffer_t token, char **output, size_t *output_len)
188 {
189  OM_uint32 maj_stat, min_stat;
190  gss_buffer_desc bufdesc;
191 
192  if (state->step == 1)
193  {
194  state->step++;
195 
196  maj_stat = gss_decapsulate_token (token, state->mech_oid, &bufdesc);
197  if (GSS_ERROR (maj_stat))
199 
200  *output_len = state->cb.application_data.length + bufdesc.length;
201  *output = malloc (*output_len);
202  if (!*output)
203  {
204  gss_release_buffer (&min_stat, &bufdesc);
205  return GSASL_MALLOC_ERROR;
206  }
207 
208  memcpy (*output, state->cb.application_data.value,
209  state->cb.application_data.length);
210  memcpy (*output + state->cb.application_data.length,
211  bufdesc.value, bufdesc.length);
212 
213  maj_stat = gss_release_buffer (&min_stat, &bufdesc);
214  if (GSS_ERROR (maj_stat))
216  }
217  else
218  {
219  *output_len = token->length;
220  *output = malloc (*output_len);
221  if (!*output)
222  return GSASL_MALLOC_ERROR;
223  if (token->value)
224  memcpy (*output, token->value, token->length);
225  }
226 
227  return GSASL_OK;
228 }
229 
230 /* Perform one GS2 step. GS2 state is in MECH_DATA. Any data from
231  server is provided in INPUT/INPUT_LEN and output from client is
232  expected to be put in newly allocated OUTPUT/OUTPUT_LEN. Return
233  GSASL_NEEDS_MORE or GSASL_OK on success, or an error code. */
234 int
236  void *mech_data,
237  const char *input, size_t input_len,
238  char **output, size_t *output_len)
239 {
240  _gsasl_gs2_client_state *state = mech_data;
241  gss_buffer_desc bufdesc;
242  gss_buffer_t buf = GSS_C_NO_BUFFER;
243  OM_uint32 maj_stat, min_stat, ret_flags;
244  gss_OID actual_mech_type;
245  int res;
246 
247  if (state->step > 2)
249 
250  if (state->step == 0)
251  {
252  res = prepare (sctx, state);
253  if (res != GSASL_OK)
254  return res;
255  state->step++;
256  }
257 
258  if (state->step == 2)
259  {
260  bufdesc.length = input_len;
261  bufdesc.value = (void *) input;
262  buf = &bufdesc;
263  }
264 
265  /* First release memory for token from last round-trip, if any. */
266  if (state->token.value != NULL)
267  {
268  maj_stat = gss_release_buffer (&min_stat, &state->token);
269  if (GSS_ERROR (maj_stat))
271 
272  state->token.value = NULL;
273  state->token.length = 0;
274  }
275 
276  maj_stat = gss_init_sec_context (&min_stat,
277  GSS_C_NO_CREDENTIAL,
278  &state->context,
279  state->service,
280  state->mech_oid,
281  GSS_C_MUTUAL_FLAG,
282  0,
283  &state->cb,
284  buf,
285  &actual_mech_type,
286  &state->token, &ret_flags, NULL);
287  if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
289 
290  if (state->token.length > 0 && state->token.value == NULL)
292 
293  res = token2output (state, &state->token, output, output_len);
294  if (res != GSASL_OK)
295  return res;
296 
297  if (maj_stat == GSS_S_CONTINUE_NEEDED)
298  return GSASL_NEEDS_MORE;
299 
300  /* The GSS-API layer is done here, check that we established a valid
301  security context for GS2 purposes. */
302 
303  if (!(ret_flags & GSS_C_MUTUAL_FLAG))
305 
306  if (!gss_oid_equal (state->mech_oid, actual_mech_type))
308 
309  state->step++;
310  return GSASL_OK;
311 }
312 
313 /* Cleanup GS2 state context, i.e., release memory associated with
314  buffers in MECH_DATA state. */
315 void
316 _gsasl_gs2_client_finish (Gsasl_session *sctx, void *mech_data)
317 {
318  _gsasl_gs2_client_state *state = mech_data;
319  OM_uint32 min_stat;
320  (void) sctx;
321 
322  if (!state)
323  return;
324 
325  if (state->token.value != NULL)
326  gss_release_buffer (&min_stat, &state->token);
327  if (state->service != GSS_C_NO_NAME)
328  gss_release_name (&min_stat, &state->service);
329  if (state->context != GSS_C_NO_CONTEXT)
330  gss_delete_sec_context (&min_stat, &state->context, GSS_C_NO_BUFFER);
331 
332  free (state->cb.application_data.value);
333  free (state);
334 }
void _gsasl_gs2_client_finish(Gsasl_session *sctx, void *mech_data)
Definition: gs2/client.c:316
int _gsasl_gs2_client_step(Gsasl_session *sctx, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
Definition: gs2/client.c:235
int _gsasl_gs2_client_start(Gsasl_session *sctx, void **mech_data)
Definition: gs2/client.c:52
int gs2_get_oid(Gsasl_session *sctx, gss_OID *mech_oid)
Definition: gs2helper.c:38
@ GSASL_GSSAPI_IMPORT_NAME_ERROR
Definition: gsasl.h:158
@ GSASL_OK
Definition: gsasl.h:129
@ GSASL_GSSAPI_RELEASE_BUFFER_ERROR
Definition: gsasl.h:157
@ GSASL_AUTHENTICATION_ERROR
Definition: gsasl.h:138
@ GSASL_NEEDS_MORE
Definition: gsasl.h:130
@ GSASL_MALLOC_ERROR
Definition: gsasl.h:133
@ GSASL_NO_SERVICE
Definition: gsasl.h:149
@ GSASL_GSSAPI_ENCAPSULATE_TOKEN_ERROR
Definition: gsasl.h:168
@ GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
Definition: gsasl.h:132
@ GSASL_NO_HOSTNAME
Definition: gsasl.h:150
@ GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR
Definition: gsasl.h:159
_GSASL_API const char * gsasl_property_get(Gsasl_session *sctx, Gsasl_property prop)
Definition: property.c:292
@ GSASL_HOSTNAME
Definition: gsasl.h:229
@ GSASL_AUTHZID
Definition: gsasl.h:225
@ GSASL_SERVICE
Definition: gsasl.h:228
struct gss_channel_bindings_struct cb
Definition: gs2/client.c:45
gss_buffer_desc token
Definition: gs2/client.c:44
gss_ctx_id_t context
Definition: gs2/client.c:42