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