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