Commit 6d6f9ca1 authored by Steve Holme's avatar Steve Holme
Browse files

vauth: Moved the Negotiate authentication code to the new vauth directory

Part 2 of 2 - Moved the GSS-API based Negotiate authentication code.
parent ad5e9bfd
Loading
Loading
Loading
Loading
+1 −2
Original line number Original line Diff line number Diff line
@@ -23,8 +23,7 @@
LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c         \
LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c         \
  vauth/digest.c vauth/digest_sspi.c vauth/krb5_gssapi.c                \
  vauth/digest.c vauth/digest_sspi.c vauth/krb5_gssapi.c                \
  vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c vauth/oauth2.c       \
  vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c vauth/oauth2.c       \
  vauth/spnego_sspi.c
  vauth/spnego_gssapi.c vauth/spnego_sspi.c
  


LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h
LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h


+1 −0
Original line number Original line Diff line number Diff line
@@ -627,6 +627,7 @@ X_OBJS= \
	$(DIROBJ)\ntlm.obj \
	$(DIROBJ)\ntlm.obj \
	$(DIROBJ)\ntlm_sspi.obj \
	$(DIROBJ)\ntlm_sspi.obj \
	$(DIROBJ)\oauth2.obj \
	$(DIROBJ)\oauth2.obj \
	$(DIROBJ)\spnego_gssapi.obj \
	$(DIROBJ)\spnego_sspi.obj \
	$(DIROBJ)\spnego_sspi.obj \
	$(DIROBJ)\vtls.obj \
	$(DIROBJ)\vtls.obj \
	$(DIROBJ)\openssl.obj \
	$(DIROBJ)\openssl.obj \
+27 −130
Original line number Original line Diff line number Diff line
@@ -26,12 +26,9 @@


#include "urldata.h"
#include "urldata.h"
#include "sendf.h"
#include "sendf.h"
#include "curl_gssapi.h"
#include "rawstr.h"
#include "rawstr.h"
#include "curl_base64.h"
#include "http_negotiate.h"
#include "http_negotiate.h"
#include "vauth/vauth.h"
#include "vauth/vauth.h"
#include "url.h"
#include "curl_printf.h"
#include "curl_printf.h"


/* The last #include files should be: */
/* The last #include files should be: */
@@ -42,136 +39,51 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
                              const char *header)
                              const char *header)
{
{
  struct SessionHandle *data = conn->data;
  struct SessionHandle *data = conn->data;
  struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg:
    &data->state.negotiate;
  OM_uint32 major_status, minor_status, discard_st;
  gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  size_t len;
  size_t rawlen = 0;
  CURLcode result;


  if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
  /* Point to the service and host */
    /* We finished successfully our part of authentication, but server
  const char *service;
     * rejected it (since we're again here). Exit with an error since we
  const char *host;
     * can't invent anything better */
    Curl_cleanup_negotiate(data);
    return CURLE_LOGIN_DENIED;
  }


  if(!neg_ctx->server_name) {
  /* Point to the correct struct with this */
    /* Generate our SPN */
  struct negotiatedata *neg_ctx;
    char *spn = Curl_auth_build_gssapi_spn(
      proxy ? data->set.str[STRING_PROXY_SERVICE_NAME] :
      data->set.str[STRING_SERVICE_NAME],
      proxy ? conn->proxy.name : conn->host.name);
    if(!spn)
      return CURLE_OUT_OF_MEMORY;

    /* Populate the SPN structure */
    spn_token.value = spn;
    spn_token.length = strlen(spn);

    /* Import the SPN */
    major_status = gss_import_name(&minor_status, &spn_token,
                                   GSS_C_NT_HOSTBASED_SERVICE,
                                   &neg_ctx->server_name);
    if(GSS_ERROR(major_status)) {
      Curl_gss_log_error(data, minor_status, "gss_import_name() failed: ");

      free(spn);

      return CURLE_OUT_OF_MEMORY;
    }


    free(spn);
  if(proxy) {
    service = data->set.str[STRING_PROXY_SERVICE_NAME];
    host = conn->host.name;
    neg_ctx = &data->state.proxyneg;
  }
  else {
    service = data->set.str[STRING_SERVICE_NAME];
    host = conn->proxy.name;
    neg_ctx = &data->state.negotiate;
  }
  }


  /* Obtain the input token, if any */
  header += strlen("Negotiate");
  header += strlen("Negotiate");
  while(*header && ISSPACE(*header))
  while(*header && ISSPACE(*header))
    header++;
    header++;


  len = strlen(header);
  /* Initilise the security context and decode our challenge */
  if(len > 0) {
  return Curl_auth_decode_spnego_message(data, NULL, NULL, service, host,
    result = Curl_base64_decode(header, (unsigned char **)&input_token.value,
                                         header, neg_ctx);
                                &rawlen);
    if(result)
      return result;

    if(!rawlen) {
      infof(data, "Negotiate handshake failure (empty challenge message)\n");

      return CURLE_BAD_CONTENT_ENCODING;
    }

    input_token.length = rawlen;

    DEBUGASSERT(input_token.value != NULL);
  }

  major_status = Curl_gss_init_sec_context(data,
                                           &minor_status,
                                           &neg_ctx->context,
                                           neg_ctx->server_name,
                                           &Curl_spnego_mech_oid,
                                           GSS_C_NO_CHANNEL_BINDINGS,
                                           &input_token,
                                           &output_token,
                                           TRUE,
                                           NULL);
  Curl_safefree(input_token.value);

  neg_ctx->status = major_status;
  if(GSS_ERROR(major_status)) {
    if(output_token.value)
      gss_release_buffer(&discard_st, &output_token);
    Curl_gss_log_error(conn->data, minor_status,
                       "gss_init_sec_context() failed: ");
    return CURLE_OUT_OF_MEMORY;
  }

  if(!output_token.value || !output_token.length) {
    if(output_token.value)
      gss_release_buffer(&discard_st, &output_token);
    return CURLE_OUT_OF_MEMORY;
  }

  neg_ctx->output_token = output_token;

  return CURLE_OK;
}
}


CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
{
{
  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
    &conn->data->state.negotiate;
    &conn->data->state.negotiate;
  char *encoded = NULL;
  char *base64 = NULL;
  size_t len = 0;
  size_t len = 0;
  char *userp;
  char *userp;
  CURLcode result;
  CURLcode result;
  OM_uint32 discard_st;

  result = Curl_base64_encode(conn->data,
                              neg_ctx->output_token.value,
                              neg_ctx->output_token.length,
                              &encoded, &len);
  if(result) {
    gss_release_buffer(&discard_st, &neg_ctx->output_token);
    neg_ctx->output_token.value = NULL;
    neg_ctx->output_token.length = 0;
    return result;
  }


  if(!encoded || !len) {
  result = Curl_auth_create_spnego_message(conn->data, neg_ctx, &base64, &len);
    gss_release_buffer(&discard_st, &neg_ctx->output_token);
  if(result)
    neg_ctx->output_token.value = NULL;
    return result;
    neg_ctx->output_token.length = 0;
    return CURLE_REMOTE_ACCESS_DENIED;
  }


  userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
  userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
                  encoded);
                  base64);

  if(proxy) {
  if(proxy) {
    Curl_safefree(conn->allocptr.proxyuserpwd);
    Curl_safefree(conn->allocptr.proxyuserpwd);
    conn->allocptr.proxyuserpwd = userp;
    conn->allocptr.proxyuserpwd = userp;
@@ -181,30 +93,15 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
    conn->allocptr.userpwd = userp;
    conn->allocptr.userpwd = userp;
  }
  }


  free(encoded);
  free(base64);


  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
}
}


static void cleanup(struct negotiatedata *neg_ctx)
{
  OM_uint32 minor_status;
  if(neg_ctx->context != GSS_C_NO_CONTEXT)
    gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER);

  if(neg_ctx->output_token.value)
    gss_release_buffer(&minor_status, &neg_ctx->output_token);

  if(neg_ctx->server_name != GSS_C_NO_NAME)
    gss_release_name(&minor_status, &neg_ctx->server_name);

  memset(neg_ctx, 0, sizeof(*neg_ctx));
}

void Curl_cleanup_negotiate(struct SessionHandle *data)
void Curl_cleanup_negotiate(struct SessionHandle *data)
{
{
  cleanup(&data->state.negotiate);
  Curl_auth_spnego_cleanup(&data->state.negotiate);
  cleanup(&data->state.proxyneg);
  Curl_auth_spnego_cleanup(&data->state.proxyneg);
}
}


#endif /* HAVE_GSSAPI && !CURL_DISABLE_HTTP && USE_SPNEGO */
#endif /* HAVE_GSSAPI && !CURL_DISABLE_HTTP && USE_SPNEGO */
+257 −0
Original line number Original line Diff line number Diff line
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2016, 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.
 *
 * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
 *
 ***************************************************************************/

#include "curl_setup.h"

#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO)

#include <curl/curl.h>

#include "vauth/vauth.h"
#include "urldata.h"
#include "curl_base64.h"
#include "curl_gssapi.h"
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"

/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"

/*
 * Curl_auth_decode_spnego_message()
 *
 * This is used to decode an already encoded SPNEGO (Negotiate) challenge
 * message.
 *
 * Parameters:
 *
 * data        [in]     - The session handle.
 * userp       [in]     - The user name in the format User or Domain\User.
 * passdwp     [in]     - The user's password.
 * service     [in]     - The service type such as www, smtp, pop or imap.
 * hostname    [in]     - The host name.
 * chlg64      [in]     - The optional base64 encoded challenge message.
 * nego        [in/out] - The Negotiate data struct being used and modified.
 *
 * Returns CURLE_OK on success.
 */
CURLcode Curl_auth_decode_spnego_message(struct SessionHandle *data,
                                         const char *user,
                                         const char *password,
                                         const char *service,
                                         const char *host,
                                         const char *chlg64,
                                         struct negotiatedata *nego)
{
  CURLcode result = CURLE_OK;
  size_t chlglen = 0;
  unsigned char *chlg = NULL;
  OM_uint32 major_status;
  OM_uint32 minor_status;
  OM_uint32 unused_status;
  gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;

  (void) user;
  (void) password;

  if(nego->context && nego->status == GSS_S_COMPLETE) {
    /* We finished successfully 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_auth_spnego_cleanup(nego);
    return CURLE_LOGIN_DENIED;
  }

  /* Generate our SPN */
  if(!nego->server_name) {
    char *spn = Curl_auth_build_gssapi_spn(service, host);
    if(!spn)
      return CURLE_OUT_OF_MEMORY;

    /* Populate the SPN structure */
    spn_token.value = spn;
    spn_token.length = strlen(spn);

    /* Import the SPN */
    major_status = gss_import_name(&minor_status, &spn_token,
                                   GSS_C_NT_HOSTBASED_SERVICE,
                                   &nego->server_name);
    if(GSS_ERROR(major_status)) {
      Curl_gss_log_error(data, minor_status, "gss_import_name() failed: ");

      free(spn);

      return CURLE_OUT_OF_MEMORY;
    }

    free(spn);
  }

  if(chlg64 && strlen(chlg64)) {
    /* Decode the base-64 encoded challenge message */
    if(*chlg64 != '=') {
      result = Curl_base64_decode(chlg64, &chlg, &chlglen);
      if(result)
        return result;
    }

    /* Ensure we have a valid challenge message */
    if(!chlg) {
      infof(data, "SPNEGO handshake failure (empty challenge message)\n");

      return CURLE_BAD_CONTENT_ENCODING;
    }

    /* Setup the challenge "input" security buffer */
    input_token.value = chlg;
    input_token.length = chlglen;
  }

  /* Generate our challenge-response message */
  major_status = Curl_gss_init_sec_context(data,
                                           &minor_status,
                                           &nego->context,
                                           nego->server_name,
                                           &Curl_spnego_mech_oid,
                                           GSS_C_NO_CHANNEL_BINDINGS,
                                           &input_token,
                                           &output_token,
                                           TRUE,
                                           NULL);
  Curl_safefree(input_token.value);

  nego->status = major_status;
  if(GSS_ERROR(major_status)) {
    if(output_token.value)
      gss_release_buffer(&unused_status, &output_token);

    Curl_gss_log_error(data, minor_status,
                       "gss_init_sec_context() failed: ");

    return CURLE_OUT_OF_MEMORY;
  }

  if(!output_token.value || !output_token.length) {
    if(output_token.value)
      gss_release_buffer(&unused_status, &output_token);

    return CURLE_OUT_OF_MEMORY;
  }

  nego->output_token = output_token;

  return CURLE_OK;
}

/*
 * Curl_auth_create_spnego_message()
 *
 * This is used to generate an already encoded SPNEGO (Negotiate) response
 * message ready for sending to the recipient.
 *
 * Parameters:
 *
 * data        [in]     - The session handle.
 * nego        [in/out] - The Negotiate data struct being used and modified.
 * outptr      [in/out] - The address where a pointer to newly allocated memory
 *                        holding the result will be stored upon completion.
 * outlen      [out]    - The length of the output message.
 *
 * Returns CURLE_OK on success.
 */
CURLcode Curl_auth_create_spnego_message(struct SessionHandle *data,
                                         struct negotiatedata *nego,
                                         char **outptr, size_t *outlen)
{
  CURLcode result;
  OM_uint32 minor_status;

  /* Base64 encode the already generated response */
  result = Curl_base64_encode(data,
                              nego->output_token.value,
                              nego->output_token.length,
                              outptr, outlen);

  if(result) {
    gss_release_buffer(&minor_status, &nego->output_token);
    nego->output_token.value = NULL;
    nego->output_token.length = 0;

    return result;
  }

  if(!*outptr || !*outlen) {
    gss_release_buffer(&minor_status, &nego->output_token);
    nego->output_token.value = NULL;
    nego->output_token.length = 0;

    return CURLE_REMOTE_ACCESS_DENIED;
  }

  return CURLE_OK;
}

/*
 * Curl_auth_spnego_cleanup()
 *
 * This is used to clean up the SPNEGO (Negotiate) specific data.
 *
 * Parameters:
 *
 * nego     [in/out] - The Negotiate data struct being cleaned up.
 *
 */
void Curl_auth_spnego_cleanup(struct negotiatedata* nego)
{
  OM_uint32 minor_status;

  /* Free our security context */
  if(nego->context != GSS_C_NO_CONTEXT) {
    gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER);
    nego->context = GSS_C_NO_CONTEXT;
  }

  /* Free the output token */
  if(nego->output_token.value) {
    gss_release_buffer(&minor_status, &nego->output_token);
    nego->output_token.value = NULL;
    nego->output_token.length = 0;

  }

  /* Free the SPN */
  if(nego->server_name != GSS_C_NO_NAME) {
    gss_release_name(&minor_status, &nego->server_name);
    nego->server_name = GSS_C_NO_NAME;
  }

  /* Reset any variables */
  nego->status = 0;
}

#endif /* HAVE_GSSAPI && USE_SPNEGO */
+1 −0
Original line number Original line Diff line number Diff line
@@ -291,6 +291,7 @@ void Curl_auth_spnego_cleanup(struct negotiatedata* nego)
  Curl_safefree(nego->output_token);
  Curl_safefree(nego->output_token);


  /* Reset any variables */
  /* Reset any variables */
  nego->status = 0;
  nego->token_max = 0;
  nego->token_max = 0;
}
}


Loading