1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2017-2019, 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 /* !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