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