~ubuntu-branches/ubuntu/utopic/curl/utopic-updates

« back to all changes in this revision

Viewing changes to .pc/curl-gssapi-delegation/lib/http_negotiate.c

  • Committer: Bazaar Package Importer
  • Author(s): Ramakrishnan Muthukrishnan
  • Date: 2011-06-29 08:26:56 UTC
  • mfrom: (3.4.19 sid)
  • Revision ID: james.westby@ubuntu.com-20110629082656-s43em5p3orzfg6fg
Apply the Multiarch patch from Steve Langasek.
(closes: #631946)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *                                  _   _ ____  _
 
3
 *  Project                     ___| | | |  _ \| |
 
4
 *                             / __| | | | |_) | |
 
5
 *                            | (__| |_| |  _ <| |___
 
6
 *                             \___|\___/|_| \_\_____|
 
7
 *
 
8
 * Copyright (C) 1998 - 2010, 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 HAVE_GSSAPI
 
25
#ifdef HAVE_OLD_GSSMIT
 
26
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
 
27
#endif
 
28
 
 
29
#ifndef CURL_DISABLE_HTTP
 
30
 /* -- WIN32 approved -- */
 
31
#include <stdio.h>
 
32
#include <string.h>
 
33
#include <stdarg.h>
 
34
#include <stdlib.h>
 
35
#include <ctype.h>
 
36
 
 
37
#include "urldata.h"
 
38
#include "sendf.h"
 
39
#include "rawstr.h"
 
40
#include "curl_base64.h"
 
41
#include "http_negotiate.h"
 
42
#include "curl_memory.h"
 
43
 
 
44
#ifdef HAVE_SPNEGO
 
45
#  include <spnegohelp.h>
 
46
#  ifdef USE_SSLEAY
 
47
#    ifdef USE_OPENSSL
 
48
#      include <openssl/objects.h>
 
49
#    else
 
50
#      include <objects.h>
 
51
#    endif
 
52
#  else
 
53
#    error "Can't compile SPNEGO support without OpenSSL."
 
54
#  endif
 
55
#endif
 
56
 
 
57
#define _MPRINTF_REPLACE /* use our functions only */
 
58
#include <curl/mprintf.h>
 
59
 
 
60
/* The last #include file should be: */
 
61
#include "memdebug.h"
 
62
 
 
63
static int
 
64
get_gss_name(struct connectdata *conn, bool proxy, gss_name_t *server)
 
65
{
 
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;
 
70
  char name[2048];
 
71
  const char* service;
 
72
 
 
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 */
 
77
 
 
78
  /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */
 
79
 
 
80
  if(neg_ctx->gss)
 
81
    service = "KHTTP";
 
82
  else
 
83
    service = "HTTP";
 
84
 
 
85
  token.length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
 
86
                                              conn->host.name) + 1;
 
87
  if(token.length + 1 > sizeof(name))
 
88
    return EMSGSIZE;
 
89
 
 
90
  snprintf(name, sizeof(name), "%s@%s", service, proxy ? conn->proxy.name :
 
91
           conn->host.name);
 
92
 
 
93
  token.value = (void *) name;
 
94
  major_status = gss_import_name(&minor_status,
 
95
                                 &token,
 
96
                                 GSS_C_NT_HOSTBASED_SERVICE,
 
97
                                 server);
 
98
 
 
99
  return GSS_ERROR(major_status) ? -1 : 0;
 
100
}
 
101
 
 
102
static void
 
103
log_gss_error(struct connectdata *conn, OM_uint32 error_status, const char *prefix)
 
104
{
 
105
  OM_uint32 maj_stat, min_stat;
 
106
  OM_uint32 msg_ctx = 0;
 
107
  gss_buffer_desc status_string;
 
108
  char buf[1024];
 
109
  size_t len;
 
110
 
 
111
  snprintf(buf, sizeof(buf), "%s", prefix);
 
112
  len = strlen(buf);
 
113
  do {
 
114
    maj_stat = gss_display_status(&min_stat,
 
115
                                  error_status,
 
116
                                  GSS_C_MECH_CODE,
 
117
                                  GSS_C_NO_OID,
 
118
                                  &msg_ctx,
 
119
                                  &status_string);
 
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;
 
124
    }
 
125
    gss_release_buffer(&min_stat, &status_string);
 
126
  } while(!GSS_ERROR(maj_stat) && msg_ctx != 0);
 
127
 
 
128
  infof(conn->data, "%s", buf);
 
129
}
 
130
 
 
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,
 
134
                         const char *header)
 
135
{
 
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;
 
141
  int ret;
 
142
  size_t len, rawlen;
 
143
  bool gss;
 
144
  const char* protocol;
 
145
 
 
146
  while(*header && ISSPACE(*header))
 
147
    header++;
 
148
  if(checkprefix("GSS-Negotiate", header)) {
 
149
    protocol = "GSS-Negotiate";
 
150
    gss = TRUE;
 
151
  }
 
152
  else if(checkprefix("Negotiate", header)) {
 
153
    protocol = "Negotiate";
 
154
    gss = FALSE;
 
155
  }
 
156
  else
 
157
    return -1;
 
158
 
 
159
  if(neg_ctx->context) {
 
160
    if(neg_ctx->gss != gss) {
 
161
      return -1;
 
162
    }
 
163
  }
 
164
  else {
 
165
    neg_ctx->protocol = protocol;
 
166
    neg_ctx->gss = gss;
 
167
  }
 
168
 
 
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);
 
174
    return -1;
 
175
  }
 
176
 
 
177
  if(neg_ctx->server_name == NULL &&
 
178
      (ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
 
179
    return ret;
 
180
 
 
181
  header += strlen(neg_ctx->protocol);
 
182
  while(*header && ISSPACE(*header))
 
183
    header++;
 
184
 
 
185
  len = strlen(header);
 
186
  if(len > 0) {
 
187
    rawlen = Curl_base64_decode(header,
 
188
                                (unsigned char **)&input_token.value);
 
189
    if(rawlen == 0)
 
190
      return -1;
 
191
    input_token.length = rawlen;
 
192
 
 
193
#ifdef HAVE_SPNEGO /* Handle SPNEGO */
 
194
    if(checkprefix("Negotiate", header)) {
 
195
        ASN1_OBJECT *   object            = NULL;
 
196
        int             rc                = 1;
 
197
        unsigned char * spnegoToken       = NULL;
 
198
        size_t          spnegoTokenLength = 0;
 
199
        unsigned char * mechToken         = NULL;
 
200
        size_t          mechTokenLength   = 0;
 
201
 
 
202
        if(input_token.value == NULL)
 
203
          return CURLE_OUT_OF_MEMORY;
 
204
 
 
205
        spnegoToken = malloc(input_token.length);
 
206
        if(spnegoToken == NULL)
 
207
          return CURLE_OUT_OF_MEMORY;
 
208
 
 
209
        spnegoTokenLength = input_token.length;
 
210
 
 
211
        object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
 
212
        if(!parseSpnegoTargetToken(spnegoToken,
 
213
                                    spnegoTokenLength,
 
214
                                    NULL,
 
215
                                    NULL,
 
216
                                    &mechToken,
 
217
                                    &mechTokenLength,
 
218
                                    NULL,
 
219
                                    NULL)) {
 
220
          free(spnegoToken);
 
221
          spnegoToken = NULL;
 
222
          infof(conn->data, "Parse SPNEGO Target Token failed\n");
 
223
        }
 
224
        else {
 
225
          free(input_token.value);
 
226
          input_token.value = malloc(mechTokenLength);
 
227
          if (input_token.value == NULL)
 
228
            return CURLE_OUT_OF_MEMORY;
 
229
 
 
230
          memcpy(input_token.value, mechToken,mechTokenLength);
 
231
          input_token.length = mechTokenLength;
 
232
          free(mechToken);
 
233
          mechToken = NULL;
 
234
          infof(conn->data, "Parse SPNEGO Target Token succeeded\n");
 
235
        }
 
236
    }
 
237
#endif
 
238
  }
 
239
 
 
240
  major_status = gss_init_sec_context(&minor_status,
 
241
                                      GSS_C_NO_CREDENTIAL,
 
242
                                      &neg_ctx->context,
 
243
                                      neg_ctx->server_name,
 
244
                                      GSS_C_NO_OID,
 
245
                                      GSS_C_DELEG_FLAG,
 
246
                                      0,
 
247
                                      GSS_C_NO_CHANNEL_BINDINGS,
 
248
                                      &input_token,
 
249
                                      NULL,
 
250
                                      &output_token,
 
251
                                      NULL,
 
252
                                      NULL);
 
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: ");
 
260
    return -1;
 
261
  }
 
262
 
 
263
  if(output_token.length == 0) {
 
264
    return -1;
 
265
  }
 
266
 
 
267
  neg_ctx->output_token = output_token;
 
268
  /* conn->bits.close = FALSE; */
 
269
 
 
270
  return 0;
 
271
}
 
272
 
 
273
 
 
274
CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
 
275
{
 
276
  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
 
277
    &conn->data->state.negotiate;
 
278
  char *encoded = NULL;
 
279
  size_t len;
 
280
  char *userp;
 
281
 
 
282
#ifdef HAVE_SPNEGO /* Handle SPNEGO */
 
283
  if(checkprefix("Negotiate", neg_ctx->protocol)) {
 
284
    ASN1_OBJECT *   object            = NULL;
 
285
    int             rc                = 1;
 
286
    unsigned char * spnegoToken       = NULL;
 
287
    size_t          spnegoTokenLength = 0;
 
288
    unsigned char * responseToken       = NULL;
 
289
    size_t          responseTokenLength = 0;
 
290
 
 
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;
 
297
 
 
298
    object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
 
299
    if(!makeSpnegoInitialToken (object,
 
300
                                 responseToken,
 
301
                                 responseTokenLength,
 
302
                                 &spnegoToken,
 
303
                                 &spnegoTokenLength)) {
 
304
      free(responseToken);
 
305
      responseToken = NULL;
 
306
      infof(conn->data, "Make SPNEGO Initial Token failed\n");
 
307
    }
 
308
    else {
 
309
      free(responseToken);
 
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) {
 
314
        free(spnegoToken);
 
315
        spnegoToken = NULL;
 
316
        return CURLE_OUT_OF_MEMORY;
 
317
      }
 
318
      memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength);
 
319
      neg_ctx->output_token.length = spnegoTokenLength;
 
320
      free(spnegoToken);
 
321
      spnegoToken = NULL;
 
322
      infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
 
323
    }
 
324
  }
 
325
#endif
 
326
  len = Curl_base64_encode(conn->data,
 
327
                           neg_ctx->output_token.value,
 
328
                           neg_ctx->output_token.length,
 
329
                           &encoded);
 
330
 
 
331
  if(len == 0)
 
332
    return CURLE_OUT_OF_MEMORY;
 
333
 
 
334
  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
 
335
                  neg_ctx->protocol, encoded);
 
336
 
 
337
  if(proxy)
 
338
    conn->allocptr.proxyuserpwd = userp;
 
339
  else
 
340
    conn->allocptr.userpwd = userp;
 
341
  free(encoded);
 
342
  Curl_cleanup_negotiate (conn->data);
 
343
  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
 
344
}
 
345
 
 
346
static void cleanup(struct negotiatedata *neg_ctx)
 
347
{
 
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);
 
351
 
 
352
  if(neg_ctx->output_token.length != 0)
 
353
    gss_release_buffer(&minor_status, &neg_ctx->output_token);
 
354
 
 
355
  if(neg_ctx->server_name != GSS_C_NO_NAME)
 
356
    gss_release_name(&minor_status, &neg_ctx->server_name);
 
357
 
 
358
  memset(neg_ctx, 0, sizeof(*neg_ctx));
 
359
}
 
360
 
 
361
void Curl_cleanup_negotiate(struct SessionHandle *data)
 
362
{
 
363
  cleanup(&data->state.negotiate);
 
364
  cleanup(&data->state.proxyneg);
 
365
}
 
366
 
 
367
 
 
368
#endif
 
369
#endif