1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2012, 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 "curl_setup.h" 24 25#ifdef USE_WINDOWS_SSPI 26 27#ifndef CURL_DISABLE_HTTP 28 29#include "urldata.h" 30#include "sendf.h" 31#include "rawstr.h" 32#include "warnless.h" 33#include "curl_base64.h" 34#include "http_negotiate.h" 35#include "curl_memory.h" 36#include "curl_multibyte.h" 37 38#define _MPRINTF_REPLACE /* use our functions only */ 39#include <curl/mprintf.h> 40 41/* The last #include file should be: */ 42#include "memdebug.h" 43 44static int 45get_gss_name(struct connectdata *conn, bool proxy, 46 struct negotiatedata *neg_ctx) 47{ 48 const char* service; 49 size_t length; 50 51 if(proxy && !conn->proxy.name) 52 /* proxy auth requested but no given proxy name, error out! */ 53 return -1; 54 55 /* GSSAPI implementation by Globus (known as GSI) requires the name to be 56 of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead 57 of at-sign). Also GSI servers are often identified as 'host' not 'khttp'. 58 Change following lines if you want to use GSI */ 59 60 /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name, 61 and SSPI then generates an NTLM token. When using <service>/<fqdn> a 62 Kerberos token is generated. */ 63 64 if(neg_ctx->gss) 65 service = "KHTTP"; 66 else 67 service = "HTTP"; 68 69 length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name : 70 conn->host.name) + 1; 71 if(length + 1 > sizeof(neg_ctx->server_name)) 72 return EMSGSIZE; 73 74 snprintf(neg_ctx->server_name, sizeof(neg_ctx->server_name), "%s/%s", 75 service, proxy ? conn->proxy.name : conn->host.name); 76 77 return 0; 78} 79 80/* returning zero (0) means success, everything else is treated as "failure" 81 with no care exactly what the failure was */ 82int Curl_input_negotiate(struct connectdata *conn, bool proxy, 83 const char *header) 84{ 85 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 86 &conn->data->state.negotiate; 87 BYTE *input_token = 0; 88 SecBufferDesc out_buff_desc; 89 SecBuffer out_sec_buff; 90 SecBufferDesc in_buff_desc; 91 SecBuffer in_sec_buff; 92 unsigned long context_attributes; 93 TimeStamp lifetime; 94 TCHAR *sname; 95 int ret; 96 size_t len = 0, input_token_len = 0; 97 bool gss = FALSE; 98 const char* protocol; 99 CURLcode error; 100 101 if(checkprefix("GSS-Negotiate", header)) { 102 protocol = "GSS-Negotiate"; 103 gss = TRUE; 104 } 105 else if(checkprefix("Negotiate", header)) { 106 protocol = "Negotiate"; 107 gss = FALSE; 108 } 109 else 110 return -1; 111 112 if(neg_ctx->context) { 113 if(neg_ctx->gss != gss) { 114 return -1; 115 } 116 } 117 else { 118 neg_ctx->protocol = protocol; 119 neg_ctx->gss = gss; 120 } 121 122 if(neg_ctx->context && neg_ctx->status == SEC_E_OK) { 123 /* We finished successfully our part of authentication, but server 124 * rejected it (since we're again here). Exit with an error since we 125 * can't invent anything better */ 126 Curl_cleanup_negotiate(conn->data); 127 return -1; 128 } 129 130 if(0 == strlen(neg_ctx->server_name)) { 131 ret = get_gss_name(conn, proxy, neg_ctx); 132 if(ret) 133 return ret; 134 } 135 136 if(!neg_ctx->output_token) { 137 PSecPkgInfo SecurityPackage; 138 ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("Negotiate"), 139 &SecurityPackage); 140 if(ret != SEC_E_OK) 141 return -1; 142 143 /* Allocate input and output buffers according to the max token size 144 as indicated by the security package */ 145 neg_ctx->max_token_length = SecurityPackage->cbMaxToken; 146 neg_ctx->output_token = malloc(neg_ctx->max_token_length); 147 s_pSecFn->FreeContextBuffer(SecurityPackage); 148 } 149 150 /* Obtain the input token, if any */ 151 header += strlen(neg_ctx->protocol); 152 while(*header && ISSPACE(*header)) 153 header++; 154 155 len = strlen(header); 156 if(!len) { 157 /* first call in a new negotation, we have to acquire credentials, 158 and allocate memory for the context */ 159 160 neg_ctx->credentials = malloc(sizeof(CredHandle)); 161 neg_ctx->context = malloc(sizeof(CtxtHandle)); 162 163 if(!neg_ctx->credentials || !neg_ctx->context) 164 return -1; 165 166 neg_ctx->status = 167 s_pSecFn->AcquireCredentialsHandle(NULL, 168 (TCHAR *) TEXT("Negotiate"), 169 SECPKG_CRED_OUTBOUND, NULL, NULL, 170 NULL, NULL, neg_ctx->credentials, 171 &lifetime); 172 if(neg_ctx->status != SEC_E_OK) 173 return -1; 174 } 175 else { 176 input_token = malloc(neg_ctx->max_token_length); 177 if(!input_token) 178 return -1; 179 180 error = Curl_base64_decode(header, 181 (unsigned char **)&input_token, 182 &input_token_len); 183 if(error || input_token_len == 0) 184 return -1; 185 } 186 187 /* prepare the output buffers, and input buffers if present */ 188 out_buff_desc.ulVersion = 0; 189 out_buff_desc.cBuffers = 1; 190 out_buff_desc.pBuffers = &out_sec_buff; 191 192 out_sec_buff.cbBuffer = curlx_uztoul(neg_ctx->max_token_length); 193 out_sec_buff.BufferType = SECBUFFER_TOKEN; 194 out_sec_buff.pvBuffer = neg_ctx->output_token; 195 196 197 if(input_token) { 198 in_buff_desc.ulVersion = 0; 199 in_buff_desc.cBuffers = 1; 200 in_buff_desc.pBuffers = &in_sec_buff; 201 202 in_sec_buff.cbBuffer = curlx_uztoul(input_token_len); 203 in_sec_buff.BufferType = SECBUFFER_TOKEN; 204 in_sec_buff.pvBuffer = input_token; 205 } 206 207 sname = Curl_convert_UTF8_to_tchar(neg_ctx->server_name); 208 if(!sname) 209 return CURLE_OUT_OF_MEMORY; 210 211 neg_ctx->status = s_pSecFn->InitializeSecurityContext( 212 neg_ctx->credentials, 213 input_token ? neg_ctx->context : 0, 214 sname, 215 ISC_REQ_CONFIDENTIALITY, 216 0, 217 SECURITY_NATIVE_DREP, 218 input_token ? &in_buff_desc : 0, 219 0, 220 neg_ctx->context, 221 &out_buff_desc, 222 &context_attributes, 223 &lifetime); 224 225 Curl_unicodefree(sname); 226 227 if(GSS_ERROR(neg_ctx->status)) 228 return -1; 229 230 if(neg_ctx->status == SEC_I_COMPLETE_NEEDED || 231 neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) { 232 neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context, 233 &out_buff_desc); 234 if(GSS_ERROR(neg_ctx->status)) 235 return -1; 236 } 237 238 neg_ctx->output_token_length = out_sec_buff.cbBuffer; 239 240 return 0; 241} 242 243 244CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) 245{ 246 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 247 &conn->data->state.negotiate; 248 char *encoded = NULL; 249 size_t len = 0; 250 char *userp; 251 CURLcode error; 252 253 error = Curl_base64_encode(conn->data, 254 (const char*)neg_ctx->output_token, 255 neg_ctx->output_token_length, 256 &encoded, &len); 257 if(error) 258 return error; 259 260 if(len == 0) 261 return CURLE_REMOTE_ACCESS_DENIED; 262 263 userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "", 264 neg_ctx->protocol, encoded); 265 266 if(proxy) 267 conn->allocptr.proxyuserpwd = userp; 268 else 269 conn->allocptr.userpwd = userp; 270 free(encoded); 271 Curl_cleanup_negotiate (conn->data); 272 return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; 273} 274 275static void cleanup(struct negotiatedata *neg_ctx) 276{ 277 if(neg_ctx->context) { 278 s_pSecFn->DeleteSecurityContext(neg_ctx->context); 279 free(neg_ctx->context); 280 neg_ctx->context = 0; 281 } 282 283 if(neg_ctx->credentials) { 284 s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials); 285 free(neg_ctx->credentials); 286 neg_ctx->credentials = 0; 287 } 288 289 if(neg_ctx->output_token) { 290 free(neg_ctx->output_token); 291 neg_ctx->output_token = 0; 292 } 293 294 neg_ctx->max_token_length = 0; 295} 296 297void Curl_cleanup_negotiate(struct SessionHandle *data) 298{ 299 cleanup(&data->state.negotiate); 300 cleanup(&data->state.proxyneg); 301} 302 303 304#endif 305#endif 306