1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2011, 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 http://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#include "setup.h" 23 24#ifdef HAVE_GSSAPI 25#ifdef HAVE_OLD_GSSMIT 26#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name 27#endif 28 29#ifndef CURL_DISABLE_HTTP 30 /* -- WIN32 approved -- */ 31#include <stdio.h> 32#include <string.h> 33#include <stdarg.h> 34#include <stdlib.h> 35#include <ctype.h> 36 37#include "urldata.h" 38#include "sendf.h" 39#include "rawstr.h" 40#include "curl_base64.h" 41#include "http_negotiate.h" 42#include "curl_memory.h" 43 44#ifdef HAVE_SPNEGO 45# include <spnegohelp.h> 46# ifdef USE_SSLEAY 47# ifdef USE_OPENSSL 48# include <openssl/objects.h> 49# else 50# include <objects.h> 51# endif 52# else 53# error "Can't compile SPNEGO support without OpenSSL." 54# endif 55#endif 56 57#define _MPRINTF_REPLACE /* use our functions only */ 58#include <curl/mprintf.h> 59 60/* The last #include file should be: */ 61#include "memdebug.h" 62 63static int 64get_gss_name(struct connectdata *conn, bool proxy, gss_name_t *server) 65{ 66 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 67 &conn->data->state.negotiate; 68 OM_uint32 major_status, minor_status; 69 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 70 char name[2048]; 71 const char* service; 72 73 /* GSSAPI implementation by Globus (known as GSI) requires the name to be 74 of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead 75 of at-sign). Also GSI servers are often identified as 'host' not 'khttp'. 76 Change following lines if you want to use GSI */ 77 78 /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */ 79 80 if(neg_ctx->gss) 81 service = "KHTTP"; 82 else 83 service = "HTTP"; 84 85 token.length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name : 86 conn->host.name) + 1; 87 if(token.length + 1 > sizeof(name)) 88 return EMSGSIZE; 89 90 snprintf(name, sizeof(name), "%s@%s", service, proxy ? conn->proxy.name : 91 conn->host.name); 92 93 token.value = (void *) name; 94 major_status = gss_import_name(&minor_status, 95 &token, 96 GSS_C_NT_HOSTBASED_SERVICE, 97 server); 98 99 return GSS_ERROR(major_status) ? -1 : 0; 100} 101 102static void 103log_gss_error(struct connectdata *conn, OM_uint32 error_status, 104 const char *prefix) 105{ 106 OM_uint32 maj_stat, min_stat; 107 OM_uint32 msg_ctx = 0; 108 gss_buffer_desc status_string; 109 char buf[1024]; 110 size_t len; 111 112 snprintf(buf, sizeof(buf), "%s", prefix); 113 len = strlen(buf); 114 do { 115 maj_stat = gss_display_status(&min_stat, 116 error_status, 117 GSS_C_MECH_CODE, 118 GSS_C_NO_OID, 119 &msg_ctx, 120 &status_string); 121 if(sizeof(buf) > len + status_string.length + 1) { 122 snprintf(buf + len, sizeof(buf) - len, 123 ": %s", (char*) status_string.value); 124 len += status_string.length; 125 } 126 gss_release_buffer(&min_stat, &status_string); 127 } while(!GSS_ERROR(maj_stat) && msg_ctx != 0); 128 129 infof(conn->data, "%s", buf); 130} 131 132/* returning zero (0) means success, everything else is treated as "failure" 133 with no care exactly what the failure was */ 134int Curl_input_negotiate(struct connectdata *conn, bool proxy, 135 const char *header) 136{ 137 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 138 &conn->data->state.negotiate; 139 OM_uint32 major_status, minor_status, minor_status2; 140 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; 141 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 142 int ret; 143 size_t len, rawlen; 144 bool gss; 145 const char* protocol; 146 147 while(*header && ISSPACE(*header)) 148 header++; 149 if(checkprefix("GSS-Negotiate", header)) { 150 protocol = "GSS-Negotiate"; 151 gss = TRUE; 152 } 153 else if(checkprefix("Negotiate", header)) { 154 protocol = "Negotiate"; 155 gss = FALSE; 156 } 157 else 158 return -1; 159 160 if(neg_ctx->context) { 161 if(neg_ctx->gss != gss) { 162 return -1; 163 } 164 } 165 else { 166 neg_ctx->protocol = protocol; 167 neg_ctx->gss = gss; 168 } 169 170 if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { 171 /* We finished successfully our part of authentication, but server 172 * rejected it (since we're again here). Exit with an error since we 173 * can't invent anything better */ 174 Curl_cleanup_negotiate(conn->data); 175 return -1; 176 } 177 178 if(neg_ctx->server_name == NULL && 179 (ret = get_gss_name(conn, proxy, &neg_ctx->server_name))) 180 return ret; 181 182 header += strlen(neg_ctx->protocol); 183 while(*header && ISSPACE(*header)) 184 header++; 185 186 len = strlen(header); 187 if(len > 0) { 188 rawlen = Curl_base64_decode(header, 189 (unsigned char **)&input_token.value); 190 if(rawlen == 0) 191 return -1; 192 input_token.length = rawlen; 193 194#ifdef HAVE_SPNEGO /* Handle SPNEGO */ 195 if(checkprefix("Negotiate", header)) { 196 ASN1_OBJECT * object = NULL; 197 int rc = 1; 198 unsigned char * spnegoToken = NULL; 199 size_t spnegoTokenLength = 0; 200 unsigned char * mechToken = NULL; 201 size_t mechTokenLength = 0; 202 203 if(input_token.value == NULL) 204 return CURLE_OUT_OF_MEMORY; 205 206 spnegoToken = malloc(input_token.length); 207 if(spnegoToken == NULL) 208 return CURLE_OUT_OF_MEMORY; 209 210 spnegoTokenLength = input_token.length; 211 212 object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); 213 if(!parseSpnegoTargetToken(spnegoToken, 214 spnegoTokenLength, 215 NULL, 216 NULL, 217 &mechToken, 218 &mechTokenLength, 219 NULL, 220 NULL)) { 221 free(spnegoToken); 222 spnegoToken = NULL; 223 infof(conn->data, "Parse SPNEGO Target Token failed\n"); 224 } 225 else { 226 free(input_token.value); 227 input_token.value = malloc(mechTokenLength); 228 if(input_token.value == NULL) 229 return CURLE_OUT_OF_MEMORY; 230 231 memcpy(input_token.value, mechToken,mechTokenLength); 232 input_token.length = mechTokenLength; 233 free(mechToken); 234 mechToken = NULL; 235 infof(conn->data, "Parse SPNEGO Target Token succeeded\n"); 236 } 237 } 238#endif 239 } 240 241 major_status = gss_init_sec_context(&minor_status, 242 GSS_C_NO_CREDENTIAL, 243 &neg_ctx->context, 244 neg_ctx->server_name, 245 GSS_C_NO_OID, 246 0, 247 0, 248 GSS_C_NO_CHANNEL_BINDINGS, 249 &input_token, 250 NULL, 251 &output_token, 252 NULL, 253 NULL); 254 if(input_token.length > 0) 255 gss_release_buffer(&minor_status2, &input_token); 256 neg_ctx->status = major_status; 257 if(GSS_ERROR(major_status)) { 258 /* Curl_cleanup_negotiate(conn->data) ??? */ 259 log_gss_error(conn, minor_status, 260 "gss_init_sec_context() failed: "); 261 return -1; 262 } 263 264 if(output_token.length == 0) { 265 return -1; 266 } 267 268 neg_ctx->output_token = output_token; 269 /* conn->bits.close = FALSE; */ 270 271 return 0; 272} 273 274 275CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) 276{ 277 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 278 &conn->data->state.negotiate; 279 char *encoded = NULL; 280 size_t len; 281 char *userp; 282 283#ifdef HAVE_SPNEGO /* Handle SPNEGO */ 284 if(checkprefix("Negotiate", neg_ctx->protocol)) { 285 ASN1_OBJECT * object = NULL; 286 int rc = 1; 287 unsigned char * spnegoToken = NULL; 288 size_t spnegoTokenLength = 0; 289 unsigned char * responseToken = NULL; 290 size_t responseTokenLength = 0; 291 292 responseToken = malloc(neg_ctx->output_token.length); 293 if(responseToken == NULL) 294 return CURLE_OUT_OF_MEMORY; 295 memcpy(responseToken, neg_ctx->output_token.value, 296 neg_ctx->output_token.length); 297 responseTokenLength = neg_ctx->output_token.length; 298 299 object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); 300 if(!makeSpnegoInitialToken (object, 301 responseToken, 302 responseTokenLength, 303 &spnegoToken, 304 &spnegoTokenLength)) { 305 free(responseToken); 306 responseToken = NULL; 307 infof(conn->data, "Make SPNEGO Initial Token failed\n"); 308 } 309 else { 310 free(responseToken); 311 responseToken = NULL; 312 free(neg_ctx->output_token.value); 313 neg_ctx->output_token.value = malloc(spnegoTokenLength); 314 if(neg_ctx->output_token.value == NULL) { 315 free(spnegoToken); 316 spnegoToken = NULL; 317 return CURLE_OUT_OF_MEMORY; 318 } 319 memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength); 320 neg_ctx->output_token.length = spnegoTokenLength; 321 free(spnegoToken); 322 spnegoToken = NULL; 323 infof(conn->data, "Make SPNEGO Initial Token succeeded\n"); 324 } 325 } 326#endif 327 len = Curl_base64_encode(conn->data, 328 neg_ctx->output_token.value, 329 neg_ctx->output_token.length, 330 &encoded); 331 332 if(len == 0) 333 return CURLE_OUT_OF_MEMORY; 334 335 userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "", 336 neg_ctx->protocol, encoded); 337 338 if(proxy) 339 conn->allocptr.proxyuserpwd = userp; 340 else 341 conn->allocptr.userpwd = userp; 342 free(encoded); 343 Curl_cleanup_negotiate (conn->data); 344 return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; 345} 346 347static void cleanup(struct negotiatedata *neg_ctx) 348{ 349 OM_uint32 minor_status; 350 if(neg_ctx->context != GSS_C_NO_CONTEXT) 351 gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER); 352 353 if(neg_ctx->output_token.length != 0) 354 gss_release_buffer(&minor_status, &neg_ctx->output_token); 355 356 if(neg_ctx->server_name != GSS_C_NO_NAME) 357 gss_release_name(&minor_status, &neg_ctx->server_name); 358 359 memset(neg_ctx, 0, sizeof(*neg_ctx)); 360} 361 362void Curl_cleanup_negotiate(struct SessionHandle *data) 363{ 364 cleanup(&data->state.negotiate); 365 cleanup(&data->state.proxyneg); 366} 367 368 369#endif 370#endif 371