1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, 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 * $Id: http_negotiate.c,v 1.2 2007/03/15 19:22:13 andy Exp $
22 ***************************************************************************/
27 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
30 #ifndef CURL_DISABLE_HTTP
31 /* -- WIN32 approved -- */
42 #include "http_negotiate.h"
45 #define _MPRINTF_REPLACE /* use our functions only */
46 #include <curl/mprintf.h>
48 /* The last #include file should be: */
52 get_gss_name(struct connectdata
*conn
, gss_name_t
*server
)
54 struct negotiatedata
*neg_ctx
= &conn
->data
->state
.negotiate
;
55 OM_uint32 major_status
, minor_status
;
56 gss_buffer_desc token
= GSS_C_EMPTY_BUFFER
;
60 /* GSSAPI implementation by Globus (known as GSI) requires the name to be
61 of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
62 of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
63 Change following lines if you want to use GSI */
65 /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */
72 token
.length
= strlen(service
) + 1 + strlen(conn
->host
.name
) + 1;
73 if (token
.length
+ 1 > sizeof(name
))
76 snprintf(name
, sizeof(name
), "%s@%s", service
, conn
->host
.name
);
78 token
.value
= (void *) name
;
79 major_status
= gss_import_name(&minor_status
,
81 GSS_C_NT_HOSTBASED_SERVICE
,
84 return GSS_ERROR(major_status
) ? -1 : 0;
88 log_gss_error(struct connectdata
*conn
, OM_uint32 error_status
, char *prefix
)
90 OM_uint32 maj_stat
, min_stat
;
91 OM_uint32 msg_ctx
= 0;
92 gss_buffer_desc status_string
;
96 snprintf(buf
, sizeof(buf
), "%s", prefix
);
99 maj_stat
= gss_display_status (&min_stat
,
105 if (sizeof(buf
) > len
+ status_string
.length
+ 1) {
106 snprintf(buf
+ len
, sizeof(buf
) - len
,
107 ": %s", (char*) status_string
.value
);
108 len
+= status_string
.length
;
110 gss_release_buffer(&min_stat
, &status_string
);
111 } while (!GSS_ERROR(maj_stat
) && msg_ctx
!= 0);
113 infof(conn
->data
, "%s", buf
);
116 int Curl_input_negotiate(struct connectdata
*conn
, char *header
)
118 struct negotiatedata
*neg_ctx
= &conn
->data
->state
.negotiate
;
119 OM_uint32 major_status
, minor_status
, minor_status2
;
120 gss_buffer_desc input_token
= GSS_C_EMPTY_BUFFER
;
121 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
;
125 const char* protocol
;
127 while(*header
&& ISSPACE(*header
))
129 if(checkprefix("GSS-Negotiate", header
)) {
130 protocol
= "GSS-Negotiate";
133 else if (checkprefix("Negotiate", header
)) {
134 protocol
= "Negotiate";
140 if (neg_ctx
->context
) {
141 if (neg_ctx
->gss
!= gss
) {
146 neg_ctx
->protocol
= protocol
;
150 if (neg_ctx
->context
&& neg_ctx
->status
== GSS_S_COMPLETE
) {
151 /* We finished succesfully our part of authentication, but server
152 * rejected it (since we're again here). Exit with an error since we
153 * can't invent anything better */
154 Curl_cleanup_negotiate(conn
->data
);
158 if (neg_ctx
->server_name
== NULL
&&
159 (ret
= get_gss_name(conn
, &neg_ctx
->server_name
)))
162 header
+= strlen(neg_ctx
->protocol
);
163 while(*header
&& ISSPACE(*header
))
166 len
= strlen(header
);
168 int rawlen
= Curl_base64_decode(header
, (unsigned char **)&input_token
.value
);
171 input_token
.length
= rawlen
;
173 #ifdef HAVE_SPNEGO /* Handle SPNEGO */
174 if (checkprefix("Negotiate", header
)) {
175 ASN1_OBJECT
* object
= NULL
;
177 unsigned char * spnegoToken
= NULL
;
178 size_t spnegoTokenLength
= 0;
179 unsigned char * mechToken
= NULL
;
180 size_t mechTokenLength
= 0;
182 spnegoToken
= malloc(input_token
.length
);
183 if (input_token
.value
== NULL
)
185 spnegoTokenLength
= input_token
.length
;
187 object
= OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
188 if (!parseSpnegoTargetToken(spnegoToken
,
198 infof(conn
->data
, "Parse SPNEGO Target Token failed\n");
201 free(input_token
.value
);
202 input_token
.value
= NULL
;
203 input_token
.value
= malloc(mechTokenLength
);
204 memcpy(input_token
.value
, mechToken
,mechTokenLength
);
205 input_token
.length
= mechTokenLength
;
208 infof(conn
->data
, "Parse SPNEGO Target Token succeeded\n");
214 major_status
= gss_init_sec_context(&minor_status
,
217 neg_ctx
->server_name
,
221 GSS_C_NO_CHANNEL_BINDINGS
,
227 if (input_token
.length
> 0)
228 gss_release_buffer(&minor_status2
, &input_token
);
229 neg_ctx
->status
= major_status
;
230 if (GSS_ERROR(major_status
)) {
231 /* Curl_cleanup_negotiate(conn->data) ??? */
232 log_gss_error(conn
, minor_status
,
233 (char *)"gss_init_sec_context() failed: ");
237 if (output_token
.length
== 0) {
241 neg_ctx
->output_token
= output_token
;
242 /* conn->bits.close = FALSE; */
248 CURLcode
Curl_output_negotiate(struct connectdata
*conn
)
250 struct negotiatedata
*neg_ctx
= &conn
->data
->state
.negotiate
;
251 OM_uint32 minor_status
;
252 char *encoded
= NULL
;
255 #ifdef HAVE_SPNEGO /* Handle SPNEGO */
256 if (checkprefix("Negotiate",neg_ctx
->protocol
)) {
257 ASN1_OBJECT
* object
= NULL
;
259 unsigned char * spnegoToken
= NULL
;
260 size_t spnegoTokenLength
= 0;
261 unsigned char * responseToken
= NULL
;
262 size_t responseTokenLength
= 0;
264 responseToken
= malloc(neg_ctx
->output_token
.length
);
265 if ( responseToken
== NULL
)
266 return CURLE_OUT_OF_MEMORY
;
267 memcpy(responseToken
, neg_ctx
->output_token
.value
,
268 neg_ctx
->output_token
.length
);
269 responseTokenLength
= neg_ctx
->output_token
.length
;
271 object
=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
272 if (!makeSpnegoInitialToken (object
,
276 &spnegoTokenLength
)) {
278 responseToken
= NULL
;
279 infof(conn
->data
, "Make SPNEGO Initial Token failed\n");
282 free(neg_ctx
->output_token
.value
);
283 responseToken
= NULL
;
284 neg_ctx
->output_token
.value
= malloc(spnegoTokenLength
);
285 memcpy(neg_ctx
->output_token
.value
, spnegoToken
,spnegoTokenLength
);
286 neg_ctx
->output_token
.length
= spnegoTokenLength
;
289 infof(conn
->data
, "Make SPNEGO Initial Token succeeded\n");
293 len
= Curl_base64_encode(conn
->data
,
294 neg_ctx
->output_token
.value
,
295 neg_ctx
->output_token
.length
,
299 return CURLE_OUT_OF_MEMORY
;
301 conn
->allocptr
.userpwd
=
302 aprintf("Authorization: %s %s\r\n", neg_ctx
->protocol
, encoded
);
304 gss_release_buffer(&minor_status
, &neg_ctx
->output_token
);
305 return (conn
->allocptr
.userpwd
== NULL
) ? CURLE_OUT_OF_MEMORY
: CURLE_OK
;
308 void Curl_cleanup_negotiate(struct SessionHandle
*data
)
310 OM_uint32 minor_status
;
311 struct negotiatedata
*neg_ctx
= &data
->state
.negotiate
;
313 if (neg_ctx
->context
!= GSS_C_NO_CONTEXT
)
314 gss_delete_sec_context(&minor_status
, &neg_ctx
->context
, GSS_C_NO_BUFFER
);
316 if (neg_ctx
->output_token
.length
!= 0)
317 gss_release_buffer(&minor_status
, &neg_ctx
->output_token
);
319 if (neg_ctx
->server_name
!= GSS_C_NO_NAME
)
320 gss_release_name(&minor_status
, &neg_ctx
->server_name
);
322 memset(neg_ctx
, 0, sizeof(*neg_ctx
));