diff options
| -rw-r--r-- | lib/Makefile.inc | 2 | ||||
| -rw-r--r-- | lib/http.c | 8 | ||||
| -rw-r--r-- | lib/http_negotiate.h | 10 | ||||
| -rw-r--r-- | lib/http_negotiate_sspi.c | 292 | ||||
| -rw-r--r-- | lib/setup.h | 6 | ||||
| -rw-r--r-- | lib/url.c | 8 | ||||
| -rw-r--r-- | lib/urldata.h | 18 | ||||
| -rw-r--r-- | lib/version.c | 4 | 
8 files changed, 330 insertions, 18 deletions
| diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 89e6b71f8..c749168f3 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -21,7 +21,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c	\    socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c		\    curl_memrchr.c imap.c pop3.c smtp.c pingpong.c rtsp.c curl_threads.c	\    warnless.c hmac.c polarssl.c curl_rtmp.c openldap.c curl_gethostname.c\ -  gopher.c axtls.c idn_win32.c +  gopher.c axtls.c idn_win32.c http_negotiate_sspi.c  HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h	\    progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h	\ diff --git a/lib/http.c b/lib/http.c index 2cf4dd2d3..b61426ec5 100644 --- a/lib/http.c +++ b/lib/http.c @@ -5,7 +5,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.   *   * This software is licensed as described in the file COPYING, which   * you should have received as part of this distribution. The terms @@ -520,7 +520,7 @@ output_auth_headers(struct connectdata *conn,    struct SessionHandle *data = conn->data;    const char *auth=NULL;    CURLcode result = CURLE_OK; -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE    struct negotiatedata *negdata = proxy?      &data->state.proxyneg:&data->state.negotiate;  #endif @@ -530,7 +530,7 @@ output_auth_headers(struct connectdata *conn,    (void)path;  #endif -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE    if((authstatus->picked == CURLAUTH_GSSNEGOTIATE) &&       negdata->context && !GSS_ERROR(negdata->status)) {      auth="GSS-Negotiate"; @@ -727,7 +727,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn,     *     */ -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE    if(checkprefix("GSS-Negotiate", start) ||        checkprefix("Negotiate", start)) {      int neg; diff --git a/lib/http_negotiate.h b/lib/http_negotiate.h index 35501f044..e584d76fc 100644 --- a/lib/http_negotiate.h +++ b/lib/http_negotiate.h @@ -7,7 +7,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.   *   * This software is licensed as described in the file COPYING, which   * you should have received as part of this distribution. The terms @@ -22,7 +22,7 @@   *   ***************************************************************************/ -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE  /* this is for Negotiate header input */  int Curl_input_negotiate(struct connectdata *conn, bool proxy, @@ -33,6 +33,10 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy);  void Curl_cleanup_negotiate(struct SessionHandle *data); -#endif /* HAVE_GSSAPI */ +#ifdef USE_WINDOWS_SSPI +#define GSS_ERROR(status) (status & 0x80000000) +#endif + +#endif /* USE_HTTP_NEGOTIATE */  #endif /* HEADER_CURL_HTTP_NEGOTIATE_H */ diff --git a/lib/http_negotiate_sspi.c b/lib/http_negotiate_sspi.c new file mode 100644 index 000000000..39d6aefba --- /dev/null +++ b/lib/http_negotiate_sspi.c @@ -0,0 +1,292 @@ +/*************************************************************************** + *                                  _   _ ____  _ + *  Project                     ___| | | |  _ \| | + *                             / __| | | | |_) | | + *                            | (__| |_| |  _ <| |___ + *                             \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "setup.h" + +#ifdef USE_WINDOWS_SSPI + +#ifndef CURL_DISABLE_HTTP +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> + +#include "urldata.h" +#include "sendf.h" +#include "rawstr.h" +#include "curl_base64.h" +#include "http_negotiate.h" +#include "curl_memory.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#include "memdebug.h" + +static int +get_gss_name(struct connectdata *conn, bool proxy, char *server) +{ +  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: +    &conn->data->state.negotiate; +  const char* service; +  size_t length; + +  /* GSSAPI implementation by Globus (known as GSI) requires the name to be +     of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead +     of at-sign). Also GSI servers are often identified as 'host' not 'khttp'. +     Change following lines if you want to use GSI */ + +  /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name, +     and SSPI then generates an NTLM token. When using <service>/<fqdn> a +     Kerberos token is generated. */ + +  if(neg_ctx->gss) +    service = "KHTTP"; +  else +    service = "HTTP"; + +  length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name : +                                        conn->host.name) + 1; +  if(length + 1 > sizeof(neg_ctx->server_name)) +    return EMSGSIZE; + +  snprintf(server, sizeof(neg_ctx->server_name), "%s/%s", +           service, proxy ? conn->proxy.name : conn->host.name); + +  return 0; +} + +/* returning zero (0) means success, everything else is treated as "failure" +   with no care exactly what the failure was */ +int Curl_input_negotiate(struct connectdata *conn, bool proxy, +                         const char *header) +{ +  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: +    &conn->data->state.negotiate; +  BYTE                          *input_token = 0; +  SecBufferDesc     out_buff_desc; +  SecBuffer         out_sec_buff; +  SecBufferDesc     in_buff_desc; +  SecBuffer         in_sec_buff; +  ULONG             context_attributes; +  TimeStamp         lifetime; + +  int ret; +  size_t len = 0, input_token_len = 0; +  bool gss = FALSE; +  const char* protocol; + +  while(*header && ISSPACE(*header)) +    header++; + +  if(checkprefix("GSS-Negotiate", header)) { +    protocol = "GSS-Negotiate"; +    gss = TRUE; +  } +  else if(checkprefix("Negotiate", header)) { +    protocol = "Negotiate"; +    gss = FALSE; +  } +  else +    return -1; + +  if(neg_ctx->context) { +    if(neg_ctx->gss != gss) { +      return -1; +    } +  } +  else { +    neg_ctx->protocol = protocol; +    neg_ctx->gss = gss; +  } + +  if(neg_ctx->context && neg_ctx->status == SEC_E_OK) { +    /* We finished succesfully our part of authentication, but server +     * rejected it (since we're again here). Exit with an error since we +     * can't invent anything better */ +    Curl_cleanup_negotiate(conn->data); +    return -1; +  } + +  if(strlen(neg_ctx->server_name) == 0 && +     (ret = get_gss_name(conn, proxy, neg_ctx->server_name))) +    return ret; + +  if (!neg_ctx->max_token_length) { +    PSecPkgInfo SecurityPackage; +    ret = s_pSecFn->QuerySecurityPackageInfo("Negotiate", &SecurityPackage); +    if (ret != SEC_E_OK) +      return -1; + +    /* Allocate input and output buffers according to the max token size +       as indicated by the security package */ +    neg_ctx->max_token_length = SecurityPackage->cbMaxToken; +    neg_ctx->output_token = (BYTE *)malloc(neg_ctx->max_token_length); +    s_pSecFn->FreeContextBuffer(SecurityPackage); +  } + +  /* Obtain the input token, if any */ +  header += strlen(neg_ctx->protocol); +  while(*header && ISSPACE(*header)) +    header++; + +  len = strlen(header); +  if(len > 0) { +    input_token = malloc(neg_ctx->max_token_length); +    if(!input_token) +      return -1; + +    input_token_len = Curl_base64_decode(header, +                                         (unsigned char **)&input_token); +    if(input_token_len == 0) +      return -1; +  } + +  if ( !input_token ) { +    /* first call in a new negotation, we have to require credentials, +       and allocate memory for the context */ + +    neg_ctx->credentials = (CredHandle *)malloc(sizeof(CredHandle)); +    neg_ctx->context = (CtxtHandle *)malloc(sizeof(CtxtHandle)); + +    if ( !neg_ctx->credentials || !neg_ctx->context) +      return -1; + +    neg_ctx->status = +      s_pSecFn->AcquireCredentialsHandle(NULL, "Negotiate", +                                         SECPKG_CRED_OUTBOUND, NULL, NULL, +                                         NULL, NULL, neg_ctx->credentials, +                                         &lifetime); +    if ( neg_ctx->status != SEC_E_OK ) +      return -1; +  } + +  /* prepare the output buffers, and input buffers if present */ +  out_buff_desc.ulVersion = 0; +  out_buff_desc.cBuffers  = 1; +  out_buff_desc.pBuffers  = &out_sec_buff; + +  out_sec_buff.cbBuffer   = neg_ctx->max_token_length; +  out_sec_buff.BufferType = SECBUFFER_TOKEN; +  out_sec_buff.pvBuffer   = neg_ctx->output_token; + + +  if (input_token) { +    in_buff_desc.ulVersion = 0; +    in_buff_desc.cBuffers  = 1; +    in_buff_desc.pBuffers  = &out_sec_buff; + +    in_sec_buff.cbBuffer   = input_token_len; +    in_sec_buff.BufferType = SECBUFFER_TOKEN; +    in_sec_buff.pvBuffer   = input_token; +  } + +  neg_ctx->status = s_pSecFn->InitializeSecurityContext( +    neg_ctx->credentials, +    input_token ? neg_ctx->context : 0, +    neg_ctx->server_name, +    ISC_REQ_CONFIDENTIALITY, +    0, +    SECURITY_NATIVE_DREP, +    input_token ? &in_buff_desc : 0, +    0, +    neg_ctx->context, +    &out_buff_desc, +    &context_attributes, +    &lifetime); + +  if ( GSS_ERROR(neg_ctx->status) ) +    return -1; + +  if ( neg_ctx->status == SEC_I_COMPLETE_NEEDED || +       neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE ) { +    neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context, +                                                  &out_buff_desc); +    if ( GSS_ERROR(neg_ctx->status) ) +      return -1; +  } + +  neg_ctx->output_token_length = out_sec_buff.cbBuffer; + +  return 0; +} + + +CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) +{ +  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: +    &conn->data->state.negotiate; +  char *encoded = NULL; +  size_t len; +  char *userp; + +  len = Curl_base64_encode(conn->data, +                           neg_ctx->output_token, +                           neg_ctx->output_token_length, +                           &encoded); + +  if(len == 0) +    return CURLE_OUT_OF_MEMORY; + +  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "", +                  neg_ctx->protocol, encoded); + +  if(proxy) +    conn->allocptr.proxyuserpwd = userp; +  else +    conn->allocptr.userpwd = userp; +  free(encoded); +  Curl_cleanup_negotiate (conn->data); +  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; +} + +static void cleanup(struct negotiatedata *neg_ctx) +{ +  if(neg_ctx->context) { +    s_pSecFn->DeleteSecurityContext(neg_ctx->context); +    free(neg_ctx->context); +    neg_ctx->context = 0; +  } + +  if(neg_ctx->credentials) { +    s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials); +    free(neg_ctx->credentials); +    neg_ctx->credentials = 0; +  } + +  if(neg_ctx->output_token) { +    free(neg_ctx->output_token); +    neg_ctx->output_token = 0; +  } +} + +void Curl_cleanup_negotiate(struct SessionHandle *data) +{ +  cleanup(&data->state.negotiate); +  cleanup(&data->state.proxyneg); +} + + +#endif +#endif diff --git a/lib/setup.h b/lib/setup.h index 0902d2c6f..63f51e4fb 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -7,7 +7,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.   *   * This software is licensed as described in the file COPYING, which   * you should have received as part of this distribution. The terms @@ -541,6 +541,10 @@ int netware_init(void);  #define USE_SSL    /* SSL support has been enabled */  #endif +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) +#define USE_HTTP_NEGOTIATE +#endif +  #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM)  #if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || defined(USE_GNUTLS) || defined(USE_NSS)  #define USE_NTLM @@ -1454,8 +1454,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,  #ifndef USE_NTLM      auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */  #endif -#ifndef HAVE_GSSAPI -    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */ +#ifndef USE_HTTP_NEGOTIATE +    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or WINDOWS_SSPI */  #endif      if(!auth)        return CURLE_FAILED_INIT; /* no supported types left! */ @@ -1514,8 +1514,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,  #ifndef USE_NTLM      auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */  #endif -#ifndef HAVE_GSSAPI -    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */ +#ifndef USE_HTTP_NEGOTIATE +    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or WINDOWS_SSPI */  #endif      if(!auth)        return CURLE_FAILED_INIT; /* no supported types left! */ diff --git a/lib/urldata.h b/lib/urldata.h index 2765c3ebe..55167fbc3 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.   *   * This software is licensed as described in the file COPYING, which   * you should have received as part of this distribution. The terms @@ -358,17 +358,29 @@ struct ntlmdata {  #endif  }; -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE  struct negotiatedata {    /* when doing Negotiate we first need to receive an auth token and then we       need to send our header */    enum { GSS_AUTHNONE, GSS_AUTHRECV, GSS_AUTHSENT } state;    bool gss; /* Whether we're processing GSS-Negotiate or Negotiate */    const char* protocol; /* "GSS-Negotiate" or "Negotiate" */ +#ifdef HAVE_GSSAPI    OM_uint32 status;    gss_ctx_id_t context;    gss_name_t server_name;    gss_buffer_desc output_token; +#else +#ifdef USE_WINDOWS_SSPI +  DWORD status; +  CtxtHandle *context; +  CredHandle *credentials; +  char server_name[1024]; +  size_t max_token_length; +  BYTE *output_token; +  size_t output_token_length; +#endif +#endif  };  #endif @@ -1127,7 +1139,7 @@ struct UrlState {    struct digestdata digest;      /* state data for host Digest auth */    struct digestdata proxydigest; /* state data for proxy Digest auth */ -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE    struct negotiatedata negotiate; /* state data for host Negotiate auth */    struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */  #endif diff --git a/lib/version.c b/lib/version.c index aab5517d0..5931e7180 100644 --- a/lib/version.c +++ b/lib/version.c @@ -5,7 +5,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.   *   * This software is licensed as described in the file COPYING, which   * you should have received as part of this distribution. The terms @@ -243,7 +243,7 @@ static curl_version_info_data version_info = {  #ifdef HAVE_LIBZ    | CURL_VERSION_LIBZ  #endif -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE    | CURL_VERSION_GSSNEGOTIATE  #endif  #ifdef DEBUGBUILD | 
