gsasl  1.10.0
About: GNU SASL is an implementation of the Simple Authentication and Security Layer (SASL). Development version.
  Fossies Dox: gsasl-1.10.0.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

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