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 USE_WINDOWS_SSPI 25 26#ifndef CURL_DISABLE_HTTP 27/* -- WIN32 approved -- */ 28#include <stdio.h> 29#include <string.h> 30#include <stdarg.h> 31#include <stdlib.h> 32#include <ctype.h> 33 34#include "urldata.h" 35#include "sendf.h" 36#include "rawstr.h" 37#include "curl_base64.h" 38#include "http_negotiate.h" 39#include "curl_memory.h" 40 41#define _MPRINTF_REPLACE /* use our functions only */ 42#include <curl/mprintf.h> 43 44/* The last #include file should be: */ 45#include "memdebug.h" 46 47static int 48get_gss_name(struct connectdata *conn, bool proxy, char *server) 49{ 50 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 51 &conn->data->state.negotiate; 52 const char* service; 53 size_t length; 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(server, 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 ULONG context_attributes; 93 TimeStamp lifetime; 94 95 int ret; 96 size_t len = 0, input_token_len = 0; 97 bool gss = FALSE; 98 const char* protocol; 99 100 while(*header && ISSPACE(*header)) 101 header++; 102 103 if(checkprefix("GSS-Negotiate", header)) { 104 protocol = "GSS-Negotiate"; 105 gss = TRUE; 106 } 107 else if(checkprefix("Negotiate", header)) { 108 protocol = "Negotiate"; 109 gss = FALSE; 110 } 111 else 112 return -1; 113 114 if(neg_ctx->context) { 115 if(neg_ctx->gss != gss) { 116 return -1; 117 } 118 } 119 else { 120 neg_ctx->protocol = protocol; 121 neg_ctx->gss = gss; 122 } 123 124 if(neg_ctx->context && neg_ctx->status == SEC_E_OK) { 125 /* We finished successfully our part of authentication, but server 126 * rejected it (since we're again here). Exit with an error since we 127 * can't invent anything better */ 128 Curl_cleanup_negotiate(conn->data); 129 return -1; 130 } 131 132 if(strlen(neg_ctx->server_name) == 0 && 133 (ret = get_gss_name(conn, proxy, neg_ctx->server_name))) 134 return ret; 135 136 if(!neg_ctx->output_token) { 137 PSecPkgInfo SecurityPackage; 138 ret = s_pSecFn->QuerySecurityPackageInfo((SEC_CHAR *)"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, (SEC_CHAR *)"Negotiate", 168 SECPKG_CRED_OUTBOUND, NULL, NULL, 169 NULL, NULL, neg_ctx->credentials, 170 &lifetime); 171 if(neg_ctx->status != SEC_E_OK) 172 return -1; 173 } 174 else { 175 input_token = malloc(neg_ctx->max_token_length); 176 if(!input_token) 177 return -1; 178 179 input_token_len = Curl_base64_decode(header, 180 (unsigned char **)&input_token); 181 if(input_token_len == 0) 182 return -1; 183 } 184 185 /* prepare the output buffers, and input buffers if present */ 186 out_buff_desc.ulVersion = 0; 187 out_buff_desc.cBuffers = 1; 188 out_buff_desc.pBuffers = &out_sec_buff; 189 190 out_sec_buff.cbBuffer = neg_ctx->max_token_length; 191 out_sec_buff.BufferType = SECBUFFER_TOKEN; 192 out_sec_buff.pvBuffer = neg_ctx->output_token; 193 194 195 if(input_token) { 196 in_buff_desc.ulVersion = 0; 197 in_buff_desc.cBuffers = 1; 198 in_buff_desc.pBuffers = &out_sec_buff; 199 200 in_sec_buff.cbBuffer = input_token_len; 201 in_sec_buff.BufferType = SECBUFFER_TOKEN; 202 in_sec_buff.pvBuffer = input_token; 203 } 204 205 neg_ctx->status = s_pSecFn->InitializeSecurityContext( 206 neg_ctx->credentials, 207 input_token ? neg_ctx->context : 0, 208 neg_ctx->server_name, 209 ISC_REQ_CONFIDENTIALITY, 210 0, 211 SECURITY_NATIVE_DREP, 212 input_token ? &in_buff_desc : 0, 213 0, 214 neg_ctx->context, 215 &out_buff_desc, 216 &context_attributes, 217 &lifetime); 218 219 if(GSS_ERROR(neg_ctx->status)) 220 return -1; 221 222 if(neg_ctx->status == SEC_I_COMPLETE_NEEDED || 223 neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) { 224 neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context, 225 &out_buff_desc); 226 if(GSS_ERROR(neg_ctx->status)) 227 return -1; 228 } 229 230 neg_ctx->output_token_length = out_sec_buff.cbBuffer; 231 232 return 0; 233} 234 235 236CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) 237{ 238 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 239 &conn->data->state.negotiate; 240 char *encoded = NULL; 241 size_t len; 242 char *userp; 243 244 len = Curl_base64_encode(conn->data, 245 (const char*)neg_ctx->output_token, 246 neg_ctx->output_token_length, 247 &encoded); 248 249 if(len == 0) 250 return CURLE_OUT_OF_MEMORY; 251 252 userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "", 253 neg_ctx->protocol, encoded); 254 255 if(proxy) 256 conn->allocptr.proxyuserpwd = userp; 257 else 258 conn->allocptr.userpwd = userp; 259 free(encoded); 260 Curl_cleanup_negotiate (conn->data); 261 return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; 262} 263 264static void cleanup(struct negotiatedata *neg_ctx) 265{ 266 if(neg_ctx->context) { 267 s_pSecFn->DeleteSecurityContext(neg_ctx->context); 268 free(neg_ctx->context); 269 neg_ctx->context = 0; 270 } 271 272 if(neg_ctx->credentials) { 273 s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials); 274 free(neg_ctx->credentials); 275 neg_ctx->credentials = 0; 276 } 277 278 if(neg_ctx->output_token) { 279 free(neg_ctx->output_token); 280 neg_ctx->output_token = 0; 281 } 282 283 neg_ctx->max_token_length = 0; 284} 285 286void Curl_cleanup_negotiate(struct SessionHandle *data) 287{ 288 cleanup(&data->state.negotiate); 289 cleanup(&data->state.proxyneg); 290} 291 292 293#endif 294#endif 295