xref: /curl/tests/libtest/stub_gssapi.c (revision bc6d3bb1)
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 
38 enum 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 
49 static 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 
60 struct 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 */
my_strndup(const char * ptr,size_t len)69 static 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 
gss_init_sec_context(OM_uint32 * min,gss_const_cred_id_t initiator_cred_handle,gss_ctx_id_t * context_handle,gss_const_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)79 OM_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 
gss_delete_sec_context(OM_uint32 * min,gss_ctx_id_t * context_handle,gss_buffer_t output_token)243 OM_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 
gss_release_buffer(OM_uint32 * min,gss_buffer_t buffer)264 OM_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 
gss_import_name(OM_uint32 * min,const gss_buffer_t input_name_buffer,const gss_OID input_name_type,gss_name_t * output_name)278 OM_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 
gss_release_name(OM_uint32 * min,gss_name_t * input_name)306 OM_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 
gss_display_status(OM_uint32 * min,OM_uint32 status_value,int status_type,const gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)318 OM_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 
gss_display_name(OM_uint32 * min,gss_const_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)362 OM_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 
gss_inquire_context(OM_uint32 * min,gss_const_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * open_context)374 OM_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 
gss_wrap(OM_uint32 * min,gss_const_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,const gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)396 OM_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 
gss_unwrap(OM_uint32 * min,gss_const_ctx_id_t context_handle,const gss_buffer_t input_message_buffer,gss_buffer_t output_message_buffer,int * conf_state,gss_qop_t * qop_state)414 OM_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 
gss_seal(OM_uint32 * min,gss_ctx_id_t context_handle,int conf_req_flag,int qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)430 OM_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 
gss_unseal(OM_uint32 * min,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t output_message_buffer,int * conf_state,int * qop_state)448 OM_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