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