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 int length = APPROX_TOKEN_LEN * 3 / 4;
95 int 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(sizeof(*ctx), 1);
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 = 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