Ejemplo n.º 1
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 = 0;
  char *userp;
  CURLcode error;

  error = Curl_base64_encode(conn->data,
                             (const char*)neg_ctx->output_token,
                             neg_ctx->output_token_length,
                             &encoded, &len);
  if(error)
    return error;

  if(len == 0)
    return CURLE_REMOTE_ACCESS_DENIED;

  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;
}
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;
  int len;

#ifdef HAVE_SPNEGO /* Handle SPNEGO */
  if(checkprefix("Negotiate", neg_ctx->protocol)) {
    ASN1_OBJECT *   object            = NULL;
    int             rc                = 1;
    unsigned char * spnegoToken       = NULL;
    size_t          spnegoTokenLength = 0;
    unsigned char * responseToken       = NULL;
    size_t          responseTokenLength = 0;

    responseToken = malloc(neg_ctx->output_token.length);
    if( responseToken == NULL)
      return CURLE_OUT_OF_MEMORY;
    memcpy(responseToken, neg_ctx->output_token.value,
           neg_ctx->output_token.length);
    responseTokenLength = neg_ctx->output_token.length;

    object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
    if(!makeSpnegoInitialToken (object,
                                 responseToken,
                                 responseTokenLength,
                                 &spnegoToken,
                                 &spnegoTokenLength)) {
      free(responseToken);
      responseToken = NULL;
      infof(conn->data, "Make SPNEGO Initial Token failed\n");
    }
    else {
      free(neg_ctx->output_token.value);
      responseToken = NULL;
      neg_ctx->output_token.value = malloc(spnegoTokenLength);
      memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength);
      neg_ctx->output_token.length = spnegoTokenLength;
      free(spnegoToken);
      spnegoToken = NULL;
      infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
    }
  }
#endif
  len = Curl_base64_encode(conn->data,
                           neg_ctx->output_token.value,
                           neg_ctx->output_token.length,
                           &encoded);

  if(len == 0)
    return CURLE_OUT_OF_MEMORY;

  conn->allocptr.userpwd =
    aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
            neg_ctx->protocol, encoded);
  free(encoded);
  Curl_cleanup_negotiate (conn->data);
  return (conn->allocptr.userpwd == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
}
Ejemplo n.º 3
0
int Curl_input_negotiate(struct connectdata *conn, char *header)
{
  struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
  OM_uint32 major_status, minor_status, minor_status2;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  int ret;
  size_t len;
  bool gss;
  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 == GSS_S_COMPLETE) {
    /* 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 (neg_ctx->server_name == NULL &&
      (ret = get_gss_name(conn, &neg_ctx->server_name)))
    return ret;

  header += strlen(neg_ctx->protocol);
  while(*header && ISSPACE(*header))
    header++;

  len = strlen(header);
  if (len > 0) {
    int rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value);
    if (rawlen < 0)
      return -1;
    input_token.length = rawlen;

#ifdef HAVE_SPNEGO /* Handle SPNEGO */
    if (checkprefix("Negotiate", header)) {
        ASN1_OBJECT *   object            = NULL;
        int             rc                = 1;
        unsigned char * spnegoToken       = NULL;
        size_t          spnegoTokenLength = 0;
        unsigned char * mechToken         = NULL;
        size_t          mechTokenLength   = 0;

        spnegoToken = malloc(input_token.length);
        if (input_token.value == NULL)
          return ENOMEM;
        spnegoTokenLength = input_token.length;

        object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
        if (!parseSpnegoTargetToken(spnegoToken,
                                    spnegoTokenLength,
                                    NULL,
                                    NULL,
                                    &mechToken,
                                    &mechTokenLength,
                                    NULL,
                                    NULL)) {
          free(spnegoToken);
          spnegoToken = NULL;
          infof(conn->data, "Parse SPNEGO Target Token failed\n");
        }
        else {
          free(input_token.value);
          input_token.value = NULL;
          input_token.value = malloc(mechTokenLength);
          memcpy(input_token.value, mechToken,mechTokenLength);
          input_token.length = mechTokenLength;
          free(mechToken);
          mechToken = NULL;
          infof(conn->data, "Parse SPNEGO Target Token succeeded\n");
        }
    }
#endif
  }

  major_status = gss_init_sec_context(&minor_status,
                                      GSS_C_NO_CREDENTIAL,
                                      &neg_ctx->context,
                                      neg_ctx->server_name,
                                      GSS_C_NO_OID,
                                      GSS_C_DELEG_FLAG,
                                      0,
                                      GSS_C_NO_CHANNEL_BINDINGS,
                                      &input_token,
                                      NULL,
                                      &output_token,
                                      NULL,
                                      NULL);
  if (input_token.length > 0)
    gss_release_buffer(&minor_status2, &input_token);
  neg_ctx->status = major_status;
  if (GSS_ERROR(major_status)) {
    /* Curl_cleanup_negotiate(conn->data) ??? */
    log_gss_error(conn, minor_status,
                  (char *)"gss_init_sec_context() failed: ");
    return -1;
  }

  if (output_token.length == 0) {
    return -1;
  }

  neg_ctx->output_token = output_token;
  /* conn->bits.close = FALSE; */

  return 0;
}
Ejemplo n.º 4
0
CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
                              const char *header)
{
  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) {
    /* 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_cleanup_negotiate(data);
    return CURLE_LOGIN_DENIED;
  }

  if(!neg_ctx->server_name) {
    /* Generate our SPN */
    char *spn = Curl_sasl_build_gssapi_spn("HTTP", 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);
  }

  header += strlen("Negotiate");
  while(*header && ISSPACE(*header))
    header++;

  len = strlen(header);
  if(len > 0) {
    result = Curl_base64_decode(header, (unsigned char **)&input_token.value,
                                &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;
}
Ejemplo n.º 5
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;
  unsigned long     context_attributes;
  TimeStamp         lifetime;
  TCHAR             *sname;
  int ret;
  size_t len = 0, input_token_len = 0;
  bool gss = FALSE;
  const char* protocol;
  CURLcode error;

  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 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_cleanup_negotiate(conn->data);
    return -1;
  }

  if(0 == strlen(neg_ctx->server_name)) {
    ret = get_gss_name(conn, proxy, neg_ctx);
    if(ret)
      return ret;
  }

  if(!neg_ctx->output_token) {
    PSecPkgInfo SecurityPackage;
    ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("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 = 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) {
    /* first call in a new negotation, we have to acquire credentials,
       and allocate memory for the context */

    neg_ctx->credentials = malloc(sizeof(CredHandle));
    neg_ctx->context = malloc(sizeof(CtxtHandle));

    if(!neg_ctx->credentials || !neg_ctx->context)
      return -1;

    neg_ctx->status =
      s_pSecFn->AcquireCredentialsHandle(NULL,
                                         (TCHAR *) TEXT("Negotiate"),
                                         SECPKG_CRED_OUTBOUND, NULL, NULL,
                                         NULL, NULL, neg_ctx->credentials,
                                         &lifetime);
    if(neg_ctx->status != SEC_E_OK)
      return -1;
  }
  else {
    input_token = malloc(neg_ctx->max_token_length);
    if(!input_token)
      return -1;

    error = Curl_base64_decode(header,
                               (unsigned char **)&input_token,
                               &input_token_len);
    if(error || input_token_len == 0)
      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   = curlx_uztoul(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  = &in_sec_buff;

    in_sec_buff.cbBuffer   = curlx_uztoul(input_token_len);
    in_sec_buff.BufferType = SECBUFFER_TOKEN;
    in_sec_buff.pvBuffer   = input_token;
  }

  sname = Curl_convert_UTF8_to_tchar(neg_ctx->server_name);
  if(!sname)
    return CURLE_OUT_OF_MEMORY;

  neg_ctx->status = s_pSecFn->InitializeSecurityContext(
    neg_ctx->credentials,
    input_token ? neg_ctx->context : 0,
    sname,
    ISC_REQ_CONFIDENTIALITY,
    0,
    SECURITY_NATIVE_DREP,
    input_token ? &in_buff_desc : 0,
    0,
    neg_ctx->context,
    &out_buff_desc,
    &context_attributes,
    &lifetime);

  Curl_unicodefree(sname);

  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;
}
Ejemplo n.º 6
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 = 0;
  char *userp;
  CURLcode error;
  OM_uint32 discard_st;

#ifdef HAVE_SPNEGO /* Handle SPNEGO */
  if(checkprefix("Negotiate", neg_ctx->protocol)) {
    ASN1_OBJECT    *object              = NULL;
    unsigned char  *responseToken       = NULL;
    size_t          responseTokenLength = 0;
    gss_buffer_desc spnegoToken         = GSS_C_EMPTY_BUFFER;

    responseToken = malloc(neg_ctx->output_token.length);
    if(responseToken == NULL)
      return CURLE_OUT_OF_MEMORY;
    memcpy(responseToken, neg_ctx->output_token.value,
           neg_ctx->output_token.length);
    responseTokenLength = neg_ctx->output_token.length;

    object = OBJ_txt2obj("1.2.840.113554.1.2.2", 1);
    if(!object) {
      Curl_safefree(responseToken);
      return CURLE_OUT_OF_MEMORY;
    }

    if(!makeSpnegoInitialToken(object,
                               responseToken,
                               responseTokenLength,
                               (unsigned char**)&spnegoToken.value,
                               &spnegoToken.length)) {
      Curl_safefree(responseToken);
      ASN1_OBJECT_free(object);
      infof(conn->data, "Make SPNEGO Initial Token failed\n");
    }
    else if(!spnegoToken.value || !spnegoToken.length) {
      Curl_safefree(responseToken);
      ASN1_OBJECT_free(object);
      if(spnegoToken.value)
        Curl_safefree(spnegoToken.value);
      infof(conn->data, "Make SPNEGO Initial Token succeeded (NULL token)\n");
    }
    else {
      Curl_safefree(responseToken);
      ASN1_OBJECT_free(object);
      gss_release_buffer(&discard_st, &neg_ctx->output_token);
      neg_ctx->output_token.value = spnegoToken.value;
      neg_ctx->output_token.length = spnegoToken.length;
      infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
    }
  }
#endif
  error = Curl_base64_encode(conn->data,
                             neg_ctx->output_token.value,
                             neg_ctx->output_token.length,
                             &encoded, &len);
  if(error) {
	#ifdef HAVE_SPNEGO
	  Curl_safefree(neg_ctx->output_token.value);	
	#else 
	  gss_release_buffer(&discard_st, &neg_ctx->output_token);
	#endif
    neg_ctx->output_token.value = NULL;
    neg_ctx->output_token.length = 0;
    return error;
  }

  if(!encoded || !len) {
    #ifdef HAVE_SPNEGO
	  Curl_safefree(neg_ctx->output_token.value);	
	#else 
	  gss_release_buffer(&discard_st, &neg_ctx->output_token);
	#endif
    neg_ctx->output_token.value = NULL;
    neg_ctx->output_token.length = 0;
    return CURLE_REMOTE_ACCESS_DENIED;
  }

  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
                  neg_ctx->protocol, encoded);
  if(proxy) {
    Curl_safefree(conn->allocptr.proxyuserpwd);
    conn->allocptr.proxyuserpwd = userp;
  }
  else {
    Curl_safefree(conn->allocptr.userpwd);
    conn->allocptr.userpwd = userp;
  }

  Curl_safefree(encoded);
  Curl_cleanup_negotiate(conn->data);

  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
}
Ejemplo n.º 7
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 SessionHandle *data = conn->data;
  struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg:
    &data->state.negotiate;
  OM_uint32 major_status, minor_status, discard_st, min_stat;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  int ret;
  size_t len;
  size_t rawlen = 0;
  bool gss;
  const char* protocol;
  CURLcode error;

  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 == 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_cleanup_negotiate(data);
    return -1;
  }

	if(neg_ctx->server_name == NULL &&
	  (ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
	return ret;
  	
  header += strlen(neg_ctx->protocol);
  while(*header && ISSPACE(*header))
    header++;

  len = strlen(header);
  if(len > 0) {
    error = Curl_base64_decode(header,
                               (unsigned char **)&input_token.value, &rawlen);
    if(error || rawlen == 0)
      return -1;
    input_token.length = rawlen;

    DEBUGASSERT(input_token.value != NULL);

#ifdef HAVE_SPNEGO /* Handle SPNEGO */
    if(checkprefix("Negotiate", header)) {
      unsigned char  *spnegoToken       = NULL;
      size_t          spnegoTokenLength = 0;
      gss_buffer_desc mechToken         = GSS_C_EMPTY_BUFFER;

      spnegoToken = malloc(input_token.length);
      if(spnegoToken == NULL) {
        Curl_safefree(input_token.value);
        return CURLE_OUT_OF_MEMORY;
      }
      memcpy(spnegoToken, input_token.value, input_token.length);
      spnegoTokenLength = input_token.length;

      if(!parseSpnegoTargetToken(spnegoToken,
                                 spnegoTokenLength,
                                 NULL,
                                 NULL,
                                 (unsigned char**)&mechToken.value,
                                 &mechToken.length,
                                 NULL,
                                 NULL)) {
        Curl_safefree(spnegoToken);
        infof(data, "Parse SPNEGO Target Token failed\n");
      }
      else if(!mechToken.value || !mechToken.length) {
        Curl_safefree(spnegoToken);
        if(mechToken.value)
          gss_release_buffer(&discard_st, &mechToken);
        infof(data, "Parse SPNEGO Target Token succeeded (NULL token)\n");
      }
      else {
        Curl_safefree(spnegoToken);
        Curl_safefree(input_token.value);
        input_token.value = malloc(mechToken.length);
        if(input_token.value == NULL) {
          gss_release_buffer(&discard_st, &mechToken);
          return CURLE_OUT_OF_MEMORY;
        }
        memcpy(input_token.value, mechToken.value, mechToken.length);
        input_token.length = mechToken.length;
        gss_release_buffer(&discard_st, &mechToken);
        infof(data, "Parse SPNEGO Target Token succeeded\n");
      }
    }
#endif
  }

  major_status = Curl_gss_init_sec_context(conn,
                                           &minor_status,
                                           &neg_ctx->context,
                                           neg_ctx->server_name,
                                           GSS_C_NO_CHANNEL_BINDINGS,
                                           &input_token,
                                           &output_token,
                                           NULL);
  gss_release_cred(&min_stat, &conn->data->curl_gss_creds);
  Curl_safefree(input_token.value);
  /*To remove the memory leak issue*/
  if(neg_ctx->server_name != GSS_C_NO_NAME)
    gss_release_name(&min_stat, &neg_ctx->server_name);

  neg_ctx->status = major_status;
  if(GSS_ERROR(major_status)) {
    if(output_token.value)
      gss_release_buffer(&discard_st, &output_token);
    log_gss_error(conn, minor_status, "KRB5_ERROR: gss_init_sec_context() failed: ");
	printf("\n KRB5_ERROR: gss_init_sec_context() failed error code : %d", minor_status);
    return -1;
  }

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

  neg_ctx->output_token = output_token;
  return 0;
}
Ejemplo n.º 8
0
CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
                              const char *header)
{
  BYTE              *input_token = NULL;
  SecBufferDesc     out_buff_desc;
  SecBuffer         out_sec_buff;
  SecBufferDesc     in_buff_desc;
  SecBuffer         in_sec_buff;
  SECURITY_STATUS   status;
  unsigned long     attrs;
  TimeStamp         expiry; /* For Windows 9x compatibility of SSPI calls */
  size_t len = 0, input_token_len = 0;
  CURLcode result;

  /* Point to the username and password */
  const char *userp;
  const char *passwdp;

  /* Point to the correct struct with this */
  struct negotiatedata *neg_ctx;

  if(proxy) {
    userp = conn->proxyuser;
    passwdp = conn->proxypasswd;
    neg_ctx = &conn->data->state.proxyneg;
  }
  else {
    userp = conn->user;
    passwdp = conn->passwd;
    neg_ctx = &conn->data->state.negotiate;
  }

  /* Not set means empty */
  if(!userp)
    userp = "";

  if(!passwdp)
    passwdp = "";

  if(neg_ctx->context && neg_ctx->status == SEC_E_OK) {
    /* 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_cleanup_negotiate(conn->data);
    return CURLE_LOGIN_DENIED;
  }

  if(!neg_ctx->server_name) {
    /* Check proxy auth requested but no given proxy name */
    if(proxy && !conn->proxy.name)
      return CURLE_BAD_FUNCTION_ARGUMENT;

    /* Generate our SPN */
    neg_ctx->server_name = Curl_sasl_build_spn("HTTP",
                                                proxy ? conn->proxy.name :
                                                        conn->host.name);
    if(!neg_ctx->server_name)
      return CURLE_OUT_OF_MEMORY;
  }

  if(!neg_ctx->output_token) {
    PSecPkgInfo SecurityPackage;
    status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
                                                TEXT(SP_NAME_NEGOTIATE),
                                                &SecurityPackage);
    if(status != SEC_E_OK)
      return CURLE_NOT_BUILT_IN;

    /* Allocate input and output buffers according to the max token size
       as indicated by the security package */
    neg_ctx->token_max = SecurityPackage->cbMaxToken;
    neg_ctx->output_token = malloc(neg_ctx->token_max);
    s_pSecFn->FreeContextBuffer(SecurityPackage);
  }

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

  len = strlen(header);
  if(!len) {
    /* Is this the first call in a new negotiation? */
    if(neg_ctx->context) {
      /* The server rejected our authentication and hasn't suppled any more
         negotiation mechanisms */
      return CURLE_LOGIN_DENIED;
    }

    /* We have to acquire credentials and allocate memory for the context */
    neg_ctx->credentials = malloc(sizeof(CredHandle));
    neg_ctx->context = malloc(sizeof(CtxtHandle));

    if(!neg_ctx->credentials || !neg_ctx->context)
      return CURLE_OUT_OF_MEMORY;

    if(userp && *userp) {
      /* Populate our identity structure */
      result = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity);
      if(result)
        return result;

      /* Allow proper cleanup of the identity structure */
      neg_ctx->p_identity = &neg_ctx->identity;
    }
    else
      /* Use the current Windows user */
      neg_ctx->p_identity = NULL;

    /* Acquire our credientials handle */
    neg_ctx->status =
      s_pSecFn->AcquireCredentialsHandle(NULL,
                                         (TCHAR *) TEXT(SP_NAME_NEGOTIATE),
                                         SECPKG_CRED_OUTBOUND, NULL,
                                         neg_ctx->p_identity, NULL, NULL,
                                         neg_ctx->credentials, &expiry);
    if(neg_ctx->status != SEC_E_OK)
      return CURLE_LOGIN_DENIED;
  }
  else {
    result = Curl_base64_decode(header,
                                (unsigned char **)&input_token,
                                &input_token_len);
    if(result)
      return result;

    if(!input_token_len) {
      infof(conn->data,
            "Negotiate handshake failure (empty challenge message)\n");

      return CURLE_BAD_CONTENT_ENCODING;
    }
  }

  /* Setup the "output" security buffer */
  out_buff_desc.ulVersion = SECBUFFER_VERSION;
  out_buff_desc.cBuffers  = 1;
  out_buff_desc.pBuffers  = &out_sec_buff;
  out_sec_buff.BufferType = SECBUFFER_TOKEN;
  out_sec_buff.pvBuffer   = neg_ctx->output_token;
  out_sec_buff.cbBuffer   = curlx_uztoul(neg_ctx->token_max);

  /* Setup the "input" security buffer if present */
  if(input_token) {
    in_buff_desc.ulVersion = SECBUFFER_VERSION;
    in_buff_desc.cBuffers  = 1;
    in_buff_desc.pBuffers  = &in_sec_buff;
    in_sec_buff.BufferType = SECBUFFER_TOKEN;
    in_sec_buff.pvBuffer   = input_token;
    in_sec_buff.cbBuffer   = curlx_uztoul(input_token_len);
  }

  /* Generate our message */
  neg_ctx->status = s_pSecFn->InitializeSecurityContext(
    neg_ctx->credentials,
    input_token ? neg_ctx->context : NULL,
    neg_ctx->server_name,
    ISC_REQ_CONFIDENTIALITY,
    0,
    SECURITY_NATIVE_DREP,
    input_token ? &in_buff_desc : NULL,
    0,
    neg_ctx->context,
    &out_buff_desc,
    &attrs,
    &expiry);

  free(input_token);

  if(GSS_ERROR(neg_ctx->status))
    return CURLE_OUT_OF_MEMORY;

  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 CURLE_RECV_ERROR;
  }

  neg_ctx->output_token_length = out_sec_buff.cbBuffer;

  return CURLE_OK;
}