1
/***************************************************************************
3
* Project ___| | | | _ \| |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
8
* Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
* KIND, either express or implied.
21
***************************************************************************/
25
#ifdef HAVE_OLD_GSSMIT
26
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
29
#ifndef CURL_DISABLE_HTTP
30
/* -- WIN32 approved -- */
40
#include "curl_base64.h"
41
#include "http_negotiate.h"
42
#include "curl_memory.h"
45
# include <spnegohelp.h>
48
# include <openssl/objects.h>
53
# error "Can't compile SPNEGO support without OpenSSL."
57
#define _MPRINTF_REPLACE /* use our functions only */
58
#include <curl/mprintf.h>
60
/* The last #include file should be: */
64
get_gss_name(struct connectdata *conn, bool proxy, gss_name_t *server)
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;
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 */
78
/* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */
85
token.length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
87
if(token.length + 1 > sizeof(name))
90
snprintf(name, sizeof(name), "%s@%s", service, proxy ? conn->proxy.name :
93
token.value = (void *) name;
94
major_status = gss_import_name(&minor_status,
96
GSS_C_NT_HOSTBASED_SERVICE,
99
return GSS_ERROR(major_status) ? -1 : 0;
103
log_gss_error(struct connectdata *conn, OM_uint32 error_status, const char *prefix)
105
OM_uint32 maj_stat, min_stat;
106
OM_uint32 msg_ctx = 0;
107
gss_buffer_desc status_string;
111
snprintf(buf, sizeof(buf), "%s", prefix);
114
maj_stat = gss_display_status(&min_stat,
120
if(sizeof(buf) > len + status_string.length + 1) {
121
snprintf(buf + len, sizeof(buf) - len,
122
": %s", (char*) status_string.value);
123
len += status_string.length;
125
gss_release_buffer(&min_stat, &status_string);
126
} while(!GSS_ERROR(maj_stat) && msg_ctx != 0);
128
infof(conn->data, "%s", buf);
131
/* returning zero (0) means success, everything else is treated as "failure"
132
with no care exactly what the failure was */
133
int Curl_input_negotiate(struct connectdata *conn, bool proxy,
136
struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
137
&conn->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;
144
const char* protocol;
146
while(*header && ISSPACE(*header))
148
if(checkprefix("GSS-Negotiate", header)) {
149
protocol = "GSS-Negotiate";
152
else if(checkprefix("Negotiate", header)) {
153
protocol = "Negotiate";
159
if(neg_ctx->context) {
160
if(neg_ctx->gss != gss) {
165
neg_ctx->protocol = protocol;
169
if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
170
/* We finished successfully our part of authentication, but server
171
* rejected it (since we're again here). Exit with an error since we
172
* can't invent anything better */
173
Curl_cleanup_negotiate(conn->data);
177
if(neg_ctx->server_name == NULL &&
178
(ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
181
header += strlen(neg_ctx->protocol);
182
while(*header && ISSPACE(*header))
185
len = strlen(header);
187
rawlen = Curl_base64_decode(header,
188
(unsigned char **)&input_token.value);
191
input_token.length = rawlen;
193
#ifdef HAVE_SPNEGO /* Handle SPNEGO */
194
if(checkprefix("Negotiate", header)) {
195
ASN1_OBJECT * object = NULL;
197
unsigned char * spnegoToken = NULL;
198
size_t spnegoTokenLength = 0;
199
unsigned char * mechToken = NULL;
200
size_t mechTokenLength = 0;
202
if(input_token.value == NULL)
203
return CURLE_OUT_OF_MEMORY;
205
spnegoToken = malloc(input_token.length);
206
if(spnegoToken == NULL)
207
return CURLE_OUT_OF_MEMORY;
209
spnegoTokenLength = input_token.length;
211
object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
212
if(!parseSpnegoTargetToken(spnegoToken,
222
infof(conn->data, "Parse SPNEGO Target Token failed\n");
225
free(input_token.value);
226
input_token.value = malloc(mechTokenLength);
227
if (input_token.value == NULL)
228
return CURLE_OUT_OF_MEMORY;
230
memcpy(input_token.value, mechToken,mechTokenLength);
231
input_token.length = mechTokenLength;
234
infof(conn->data, "Parse SPNEGO Target Token succeeded\n");
240
major_status = gss_init_sec_context(&minor_status,
243
neg_ctx->server_name,
247
GSS_C_NO_CHANNEL_BINDINGS,
253
if(input_token.length > 0)
254
gss_release_buffer(&minor_status2, &input_token);
255
neg_ctx->status = major_status;
256
if(GSS_ERROR(major_status)) {
257
/* Curl_cleanup_negotiate(conn->data) ??? */
258
log_gss_error(conn, minor_status,
259
"gss_init_sec_context() failed: ");
263
if(output_token.length == 0) {
267
neg_ctx->output_token = output_token;
268
/* conn->bits.close = FALSE; */
274
CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
276
struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
277
&conn->data->state.negotiate;
278
char *encoded = NULL;
282
#ifdef HAVE_SPNEGO /* Handle SPNEGO */
283
if(checkprefix("Negotiate", neg_ctx->protocol)) {
284
ASN1_OBJECT * object = NULL;
286
unsigned char * spnegoToken = NULL;
287
size_t spnegoTokenLength = 0;
288
unsigned char * responseToken = NULL;
289
size_t responseTokenLength = 0;
291
responseToken = malloc(neg_ctx->output_token.length);
292
if( responseToken == NULL)
293
return CURLE_OUT_OF_MEMORY;
294
memcpy(responseToken, neg_ctx->output_token.value,
295
neg_ctx->output_token.length);
296
responseTokenLength = neg_ctx->output_token.length;
298
object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
299
if(!makeSpnegoInitialToken (object,
303
&spnegoTokenLength)) {
305
responseToken = NULL;
306
infof(conn->data, "Make SPNEGO Initial Token failed\n");
310
responseToken = NULL;
311
free(neg_ctx->output_token.value);
312
neg_ctx->output_token.value = malloc(spnegoTokenLength);
313
if(neg_ctx->output_token.value == NULL) {
316
return CURLE_OUT_OF_MEMORY;
318
memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength);
319
neg_ctx->output_token.length = spnegoTokenLength;
322
infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
326
len = Curl_base64_encode(conn->data,
327
neg_ctx->output_token.value,
328
neg_ctx->output_token.length,
332
return CURLE_OUT_OF_MEMORY;
334
userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
335
neg_ctx->protocol, encoded);
338
conn->allocptr.proxyuserpwd = userp;
340
conn->allocptr.userpwd = userp;
342
Curl_cleanup_negotiate (conn->data);
343
return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
346
static void cleanup(struct negotiatedata *neg_ctx)
348
OM_uint32 minor_status;
349
if(neg_ctx->context != GSS_C_NO_CONTEXT)
350
gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER);
352
if(neg_ctx->output_token.length != 0)
353
gss_release_buffer(&minor_status, &neg_ctx->output_token);
355
if(neg_ctx->server_name != GSS_C_NO_NAME)
356
gss_release_name(&minor_status, &neg_ctx->server_name);
358
memset(neg_ctx, 0, sizeof(*neg_ctx));
361
void Curl_cleanup_negotiate(struct SessionHandle *data)
363
cleanup(&data->state.negotiate);
364
cleanup(&data->state.proxyneg);