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