1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25/* Only provides the bare minimum to link with libcurl */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "stub_gssapi.h"
32
33/* !checksrc! disable SNPRINTF all */
34
35#define MAX_CREDS_LENGTH 250
36#define APPROX_TOKEN_LEN 250
37
38enum min_err_code {
39    GSS_OK = 0,
40    GSS_NO_MEMORY,
41    GSS_INVALID_ARGS,
42    GSS_INVALID_CREDS,
43    GSS_INVALID_CTX,
44    GSS_SERVER_ERR,
45    GSS_NO_MECH,
46    GSS_LAST
47};
48
49static const char *min_err_table[] = {
50  "stub-gss: no error",
51  "stub-gss: no memory",
52  "stub-gss: invalid arguments",
53  "stub-gss: invalid credentials",
54  "stub-gss: invalid context",
55  "stub-gss: server returned error",
56  "stub-gss: cannot find a mechanism",
57  NULL
58};
59
60struct gss_ctx_id_t_desc_struct {
61  enum { NONE, KRB5, NTLM1, NTLM3 } sent;
62  int have_krb5;
63  int have_ntlm;
64  OM_uint32 flags;
65  char creds[MAX_CREDS_LENGTH];
66};
67
68/* simple implementation of strndup(), which isn't portable */
69static char *my_strndup(const char *ptr, size_t len)
70{
71  char *copy = malloc(len + 1);
72  if(!copy)
73    return NULL;
74  memcpy(copy, ptr, len);
75  copy[len] = '\0';
76  return copy;
77}
78
79OM_uint32 gss_init_sec_context(OM_uint32 *min,
80            gss_const_cred_id_t initiator_cred_handle,
81            gss_ctx_id_t *context_handle,
82            gss_const_name_t target_name,
83            const gss_OID mech_type,
84            OM_uint32 req_flags,
85            OM_uint32 time_req,
86            const gss_channel_bindings_t input_chan_bindings,
87            const gss_buffer_t input_token,
88            gss_OID *actual_mech_type,
89            gss_buffer_t output_token,
90            OM_uint32 *ret_flags,
91            OM_uint32 *time_rec)
92{
93  /* The token will be encoded in base64 */
94  size_t length = APPROX_TOKEN_LEN * 3 / 4;
95  size_t used = 0;
96  char *token = NULL;
97  const char *creds = NULL;
98  gss_ctx_id_t ctx = NULL;
99
100  (void)initiator_cred_handle;
101  (void)mech_type;
102  (void)time_req;
103  (void)input_chan_bindings;
104  (void)actual_mech_type;
105
106  if(!min)
107    return GSS_S_FAILURE;
108
109  *min = 0;
110
111  if(!context_handle || !target_name || !output_token) {
112    *min = GSS_INVALID_ARGS;
113    return GSS_S_FAILURE;
114  }
115
116  creds = getenv("CURL_STUB_GSS_CREDS");
117  if(!creds || strlen(creds) >= MAX_CREDS_LENGTH) {
118    *min = GSS_INVALID_CREDS;
119    return GSS_S_FAILURE;
120  }
121
122  ctx = *context_handle;
123  if(ctx && strcmp(ctx->creds, creds)) {
124    *min = GSS_INVALID_CREDS;
125    return GSS_S_FAILURE;
126  }
127
128  output_token->length = 0;
129  output_token->value = NULL;
130
131  if(input_token && input_token->length) {
132    if(!ctx) {
133      *min = GSS_INVALID_CTX;
134      return GSS_S_FAILURE;
135    }
136
137    /* Server response, either D (RA==) or C (Qw==) */
138    if(((char *) input_token->value)[0] == 'D') {
139      /* Done */
140      switch(ctx->sent) {
141      case KRB5:
142      case NTLM3:
143        if(ret_flags)
144          *ret_flags = ctx->flags;
145        if(time_rec)
146          *time_rec = GSS_C_INDEFINITE;
147        return GSS_S_COMPLETE;
148      default:
149        *min = GSS_SERVER_ERR;
150        return GSS_S_FAILURE;
151      }
152    }
153
154    if(((char *) input_token->value)[0] != 'C') {
155      /* We only support Done or Continue */
156      *min = GSS_SERVER_ERR;
157      return GSS_S_FAILURE;
158    }
159
160    /* Continue */
161    switch(ctx->sent) {
162    case KRB5:
163      /* We sent KRB5 and it failed, let's try NTLM */
164      if(ctx->have_ntlm) {
165        ctx->sent = NTLM1;
166        break;
167      }
168      else {
169        *min = GSS_SERVER_ERR;
170        return GSS_S_FAILURE;
171      }
172    case NTLM1:
173      ctx->sent = NTLM3;
174      break;
175    default:
176      *min = GSS_SERVER_ERR;
177      return GSS_S_FAILURE;
178    }
179  }
180  else {
181    if(ctx) {
182      *min = GSS_INVALID_CTX;
183      return GSS_S_FAILURE;
184    }
185
186    ctx = (gss_ctx_id_t) calloc(1, sizeof(*ctx));
187    if(!ctx) {
188      *min = GSS_NO_MEMORY;
189      return GSS_S_FAILURE;
190    }
191
192    if(strstr(creds, "KRB5"))
193      ctx->have_krb5 = 1;
194
195    if(strstr(creds, "NTLM"))
196      ctx->have_ntlm = 1;
197
198    if(ctx->have_krb5)
199      ctx->sent = KRB5;
200    else if(ctx->have_ntlm)
201      ctx->sent = NTLM1;
202    else {
203      free(ctx);
204      *min = GSS_NO_MECH;
205      return GSS_S_FAILURE;
206    }
207
208    strcpy(ctx->creds, creds);
209    ctx->flags = req_flags;
210  }
211
212  token = malloc(length);
213  if(!token) {
214    free(ctx);
215    *min = GSS_NO_MEMORY;
216    return GSS_S_FAILURE;
217  }
218
219  /* Token format: creds:target:type:padding */
220  /* Note: this is using the *real* snprintf() and not the curl provided
221     one */
222  used = (size_t) snprintf(token, length, "%s:%s:%d:", creds,
223                           (char *) target_name, ctx->sent);
224
225  if(used >= length) {
226    free(token);
227    free(ctx);
228    *min = GSS_NO_MEMORY;
229    return GSS_S_FAILURE;
230  }
231
232  /* Overwrite null terminator */
233  memset(token + used, 'A', length - used);
234
235  *context_handle = ctx;
236
237  output_token->value = token;
238  output_token->length = length;
239
240  return GSS_S_CONTINUE_NEEDED;
241}
242
243OM_uint32 gss_delete_sec_context(OM_uint32 *min,
244                                 gss_ctx_id_t *context_handle,
245                                 gss_buffer_t output_token)
246{
247  (void)output_token;
248
249  if(!min)
250    return GSS_S_FAILURE;
251
252  if(!context_handle) {
253    *min = GSS_INVALID_CTX;
254    return GSS_S_FAILURE;
255  }
256
257  free(*context_handle);
258  *context_handle = NULL;
259  *min = 0;
260
261  return GSS_S_COMPLETE;
262}
263
264OM_uint32 gss_release_buffer(OM_uint32 *min,
265                             gss_buffer_t buffer)
266{
267  if(min)
268    *min = 0;
269
270  if(buffer && buffer->length) {
271    free(buffer->value);
272    buffer->length = 0;
273  }
274
275  return GSS_S_COMPLETE;
276}
277
278OM_uint32 gss_import_name(OM_uint32 *min,
279                          const gss_buffer_t input_name_buffer,
280                          const gss_OID input_name_type,
281                          gss_name_t *output_name)
282{
283  char *name = NULL;
284  (void)input_name_type;
285
286  if(!min)
287    return GSS_S_FAILURE;
288
289  if(!input_name_buffer || !output_name) {
290    *min = GSS_INVALID_ARGS;
291    return GSS_S_FAILURE;
292  }
293
294  name = my_strndup(input_name_buffer->value, input_name_buffer->length);
295  if(!name) {
296    *min = GSS_NO_MEMORY;
297    return GSS_S_FAILURE;
298  }
299
300  *output_name = (gss_name_t) name;
301  *min = 0;
302
303  return GSS_S_COMPLETE;
304}
305
306OM_uint32 gss_release_name(OM_uint32 *min,
307                           gss_name_t *input_name)
308{
309  if(min)
310    *min = 0;
311
312  if(input_name)
313    free(*input_name);
314
315  return GSS_S_COMPLETE;
316}
317
318OM_uint32 gss_display_status(OM_uint32 *min,
319                             OM_uint32 status_value,
320                             int status_type,
321                             const gss_OID mech_type,
322                             OM_uint32 *message_context,
323                             gss_buffer_t status_string)
324{
325  const char maj_str[] = "Stub GSS error";
326  (void)mech_type;
327  if(min)
328    *min = 0;
329
330  if(message_context)
331    *message_context = 0;
332
333  if(status_string) {
334    status_string->value = NULL;
335    status_string->length = 0;
336
337    if(status_value >= GSS_LAST)
338      return GSS_S_FAILURE;
339
340    switch(status_type) {
341      case GSS_C_GSS_CODE:
342        status_string->value = strdup(maj_str);
343        break;
344      case GSS_C_MECH_CODE:
345        status_string->value = strdup(min_err_table[status_value]);
346        break;
347      default:
348        return GSS_S_FAILURE;
349    }
350
351    if(status_string->value)
352      status_string->length = strlen(status_string->value);
353    else
354      return GSS_S_FAILURE;
355  }
356
357  return GSS_S_COMPLETE;
358}
359
360/* Stubs returning error */
361
362OM_uint32 gss_display_name(OM_uint32 *min,
363                           gss_const_name_t input_name,
364                           gss_buffer_t output_name_buffer,
365                           gss_OID *output_name_type)
366{
367  (void)min;
368  (void)input_name;
369  (void)output_name_buffer;
370  (void)output_name_type;
371  return GSS_S_FAILURE;
372}
373
374OM_uint32 gss_inquire_context(OM_uint32 *min,
375                              gss_const_ctx_id_t context_handle,
376                              gss_name_t *src_name,
377                              gss_name_t *targ_name,
378                              OM_uint32 *lifetime_rec,
379                              gss_OID *mech_type,
380                              OM_uint32 *ctx_flags,
381                              int *locally_initiated,
382                              int *open_context)
383{
384  (void)min;
385  (void)context_handle;
386  (void)src_name;
387  (void)targ_name;
388  (void)lifetime_rec;
389  (void)mech_type;
390  (void)ctx_flags;
391  (void)locally_initiated;
392  (void)open_context;
393  return GSS_S_FAILURE;
394}
395
396OM_uint32 gss_wrap(OM_uint32 *min,
397                   gss_const_ctx_id_t context_handle,
398                   int conf_req_flag,
399                   gss_qop_t qop_req,
400                   const gss_buffer_t input_message_buffer,
401                   int *conf_state,
402                   gss_buffer_t output_message_buffer)
403{
404  (void)min;
405  (void)context_handle;
406  (void)conf_req_flag;
407  (void)qop_req;
408  (void)input_message_buffer;
409  (void)conf_state;
410  (void)output_message_buffer;
411  return GSS_S_FAILURE;
412}
413
414OM_uint32 gss_unwrap(OM_uint32 *min,
415                     gss_const_ctx_id_t context_handle,
416                     const gss_buffer_t input_message_buffer,
417                     gss_buffer_t output_message_buffer,
418                     int *conf_state,
419                     gss_qop_t *qop_state)
420{
421  (void)min;
422  (void)context_handle;
423  (void)input_message_buffer;
424  (void)output_message_buffer;
425  (void)conf_state;
426  (void)qop_state;
427  return GSS_S_FAILURE;
428}
429
430OM_uint32 gss_seal(OM_uint32 *min,
431                   gss_ctx_id_t context_handle,
432                   int conf_req_flag,
433                   int qop_req,
434                   gss_buffer_t input_message_buffer,
435                   int *conf_state,
436                   gss_buffer_t output_message_buffer)
437{
438  (void)min;
439  (void)context_handle;
440  (void)conf_req_flag;
441  (void)qop_req;
442  (void)input_message_buffer;
443  (void)conf_state;
444  (void)output_message_buffer;
445  return GSS_S_FAILURE;
446}
447
448OM_uint32 gss_unseal(OM_uint32 *min,
449                     gss_ctx_id_t context_handle,
450                     gss_buffer_t input_message_buffer,
451                     gss_buffer_t output_message_buffer,
452                     int *conf_state,
453                     int *qop_state)
454{
455  (void)min;
456  (void)context_handle;
457  (void)input_message_buffer;
458  (void)output_message_buffer;
459  (void)conf_state;
460  (void)qop_state;
461  return GSS_S_FAILURE;
462}
463