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