diff options
| -rw-r--r-- | lib/http.c | 116 | ||||
| -rw-r--r-- | lib/http_negotiate.c | 109 | ||||
| -rw-r--r-- | lib/http_negotiate.h | 4 | ||||
| -rw-r--r-- | lib/multi.c | 6 | ||||
| -rw-r--r-- | lib/url.c | 4 | ||||
| -rw-r--r-- | lib/urldata.h | 18 | ||||
| -rw-r--r-- | lib/vauth/spnego_gssapi.c | 7 | ||||
| -rw-r--r-- | lib/vauth/spnego_sspi.c | 5 | ||||
| -rw-r--r-- | tests/data/test2056 | 22 | ||||
| -rw-r--r-- | tests/data/test2057 | 24 | 
10 files changed, 199 insertions, 116 deletions
diff --git a/lib/http.c b/lib/http.c index 3f41a451b..a0520b40e 100644 --- a/lib/http.c +++ b/lib/http.c @@ -481,8 +481,36 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)              (curl_off_t)(expectsend - bytessent));      }  #endif +#if defined(USE_SPNEGO) +    /* There is still data left to send */ +    if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || +       (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { +      if(((expectsend - bytessent) < 2000) || +         (conn->negotiate.state != GSS_AUTHNONE) || +         (conn->proxyneg.state != GSS_AUTHNONE)) { +        /* The NEGOTIATE-negotiation has started *OR* +        there is just a little (<2K) data left to send, keep on sending. */ + +        /* rewind data when completely done sending! */ +        if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { +          conn->bits.rewindaftersend = TRUE; +          infof(data, "Rewind stream after send\n"); +        } + +        return CURLE_OK; +      } -    /* This is not NTLM or many bytes left to send: close */ +      if(conn->bits.close) +        /* this is already marked to get closed */ +        return CURLE_OK; + +      infof(data, "NEGOTIATE send, close instead of sending %" +        CURL_FORMAT_CURL_OFF_T " bytes\n", +        (curl_off_t)(expectsend - bytessent)); +    } +#endif + +    /* This is not NEGOTIATE/NTLM or many bytes left to send: close */      streamclose(conn, "Mid-auth HTTP and much data left to send");      data->req.size = 0; /* don't download any more than 0 bytes */ @@ -600,10 +628,6 @@ output_auth_headers(struct connectdata *conn,  #if !defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_SPNEGO)    struct Curl_easy *data = conn->data;  #endif -#ifdef USE_SPNEGO -  struct negotiatedata *negdata = proxy ? -    &data->state.proxyneg : &data->state.negotiate; -#endif  #ifdef CURL_DISABLE_CRYPTO_AUTH    (void)request; @@ -611,15 +635,11 @@ output_auth_headers(struct connectdata *conn,  #endif  #ifdef USE_SPNEGO -  negdata->state = GSS_AUTHNONE; -  if((authstatus->picked == CURLAUTH_NEGOTIATE) && -     negdata->context && !GSS_ERROR(negdata->status)) { +  if((authstatus->picked == CURLAUTH_NEGOTIATE)) {      auth = "Negotiate";      result = Curl_output_negotiate(conn, proxy);      if(result)        return result; -    authstatus->done = TRUE; -    negdata->state = GSS_AUTHSENT;    }    else  #endif @@ -796,7 +816,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,  #ifdef USE_SPNEGO    struct negotiatedata *negdata = proxy? -    &data->state.proxyneg:&data->state.negotiate; +    &conn->proxyneg:&conn->negotiate;  #endif    unsigned long *availp;    struct auth *authp; @@ -835,21 +855,18 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,          authp->avail |= CURLAUTH_NEGOTIATE;          if(authp->picked == CURLAUTH_NEGOTIATE) { -          if(negdata->state == GSS_AUTHSENT || -             negdata->state == GSS_AUTHNONE) { -            CURLcode result = Curl_input_negotiate(conn, proxy, auth); -            if(!result) { -              DEBUGASSERT(!data->req.newurl); -              data->req.newurl = strdup(data->change.url); -              if(!data->req.newurl) -                return CURLE_OUT_OF_MEMORY; -              data->state.authproblem = FALSE; -              /* we received a GSS auth token and we dealt with it fine */ -              negdata->state = GSS_AUTHRECV; -            } -            else -              data->state.authproblem = TRUE; +          CURLcode result = Curl_input_negotiate(conn, proxy, auth); +          if(!result) { +            DEBUGASSERT(!data->req.newurl); +            data->req.newurl = strdup(data->change.url); +            if(!data->req.newurl) +              return CURLE_OUT_OF_MEMORY; +            data->state.authproblem = FALSE; +            /* we received a GSS auth token and we dealt with it fine */ +            negdata->state = GSS_AUTHRECV;            } +          else +            data->state.authproblem = TRUE;          }        }      } @@ -1555,20 +1572,6 @@ CURLcode Curl_http_done(struct connectdata *conn,    Curl_unencode_cleanup(conn); -#ifdef USE_SPNEGO -  if(data->state.proxyneg.state == GSS_AUTHSENT || -     data->state.negotiate.state == GSS_AUTHSENT) { -    /* add forbid re-use if http-code != 401/407 as a WA only needed for -     * 401/407 that signal auth failure (empty) otherwise state will be RECV -     * with current code. -     * Do not close CONNECT_ONLY connections. */ -    if((data->req.httpcode != 401) && (data->req.httpcode != 407) && -       !data->set.connect_only) -      streamclose(conn, "Negotiate transfer completed"); -    Curl_cleanup_negotiate(data); -  } -#endif -    /* set the proper values (possibly modified on POST) */    conn->seek_func = data->set.seek_func; /* restore */    conn->seek_client = data->set.seek_client; /* restore */ @@ -3376,7 +3379,24 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,          data->state.authproblem = TRUE;        }  #endif - +#if defined(USE_SPNEGO) +      if(conn->bits.close && +        (((data->req.httpcode == 401) && +          (conn->negotiate.state == GSS_AUTHRECV)) || +         ((data->req.httpcode == 407) && +          (conn->proxyneg.state == GSS_AUTHRECV)))) { +        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n"); +        data->state.authproblem = TRUE; +      } +      if((conn->negotiate.state == GSS_AUTHDONE) && +         (data->req.httpcode != 401)) { +        conn->negotiate.state = GSS_AUTHSUCC; +      } +      if((conn->proxyneg.state == GSS_AUTHDONE) && +         (data->req.httpcode != 407)) { +        conn->proxyneg.state = GSS_AUTHSUCC; +      } +#endif        /*         * When all the headers have been parsed, see if we should give         * up and return an error. @@ -3953,6 +3973,22 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,        if(result)          return result;      } +  #ifdef USE_SPNEGO +    else if(checkprefix("Persistent-Auth", k->p)) { +      struct negotiatedata *negdata = &conn->negotiate; +      struct auth *authp = &data->state.authhost; +      if(authp->picked == CURLAUTH_NEGOTIATE) { +        char *persistentauth = Curl_copy_header_value(k->p); +        if(!persistentauth) +          return CURLE_OUT_OF_MEMORY; +        negdata->noauthpersist = checkprefix("false", persistentauth); +        negdata->havenoauthpersist = TRUE; +        infof(data, "Negotiate: noauthpersist -> %d, header part: %s", +          negdata->noauthpersist, persistentauth); +        free(persistentauth); +      } +    } +  #endif      else if((k->httpcode >= 300 && k->httpcode < 400) &&              checkprefix("Location:", k->p) &&              !data->req.location) { diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 2a97707eb..9415236fb 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -56,7 +56,7 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,      service = data->set.str[STRING_PROXY_SERVICE_NAME] ?                data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";      host = conn->http_proxy.host.name; -    neg_ctx = &data->state.proxyneg; +    neg_ctx = &conn->proxyneg;    }    else {      userp = conn->user; @@ -64,7 +64,7 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,      service = data->set.str[STRING_SERVICE_NAME] ?                data->set.str[STRING_SERVICE_NAME] : "HTTP";      host = conn->host.name; -    neg_ctx = &data->state.negotiate; +    neg_ctx = &conn->negotiate;    }    /* Not set means empty */ @@ -80,11 +80,16 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,      header++;    len = strlen(header); +  neg_ctx->havenegdata = len != 0;    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 +    if(neg_ctx->state == GSS_AUTHSUCC) { +      infof(conn->data, "Negotiate auth restarted\n"); +      Curl_cleanup_negotiate(conn); +    } +    else if(neg_ctx->state != GSS_AUTHNONE) { +      /* The server rejected our authentication and hasn't supplied any more        negotiation mechanisms */ +      Curl_cleanup_negotiate(conn);        return CURLE_LOGIN_DENIED;      }    } @@ -106,38 +111,96 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,  CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)  { -  struct negotiatedata *neg_ctx = proxy ? &conn->data->state.proxyneg : -    &conn->data->state.negotiate; +  struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg : +    &conn->negotiate; +  struct auth *authp = proxy ? &conn->data->state.authproxy : +    &conn->data->state.authhost;    char *base64 = NULL;    size_t len = 0;    char *userp;    CURLcode result; -  result = Curl_auth_create_spnego_message(conn->data, neg_ctx, &base64, &len); -  if(result) -    return result; +  authp->done = FALSE; + +  if(neg_ctx->state == GSS_AUTHRECV) { +    if(neg_ctx->havenegdata) { +      neg_ctx->havemultiplerequests = TRUE; +    } +  } +  else if(neg_ctx->state == GSS_AUTHSUCC) { +    if(!neg_ctx->havenoauthpersist) { +      neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests; +    } +  } -  userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", -                  base64); +  if(neg_ctx->noauthpersist || +    (neg_ctx->state != GSS_AUTHDONE && neg_ctx->state != GSS_AUTHSUCC)) { -  if(proxy) { -    Curl_safefree(conn->allocptr.proxyuserpwd); -    conn->allocptr.proxyuserpwd = userp; +    if(neg_ctx->noauthpersist && neg_ctx->state == GSS_AUTHSUCC) { +      infof(conn->data, "Curl_output_negotiate, " +       "no persistent authentication: cleanup existing context"); +      Curl_auth_spnego_cleanup(neg_ctx); +    } +    if(!neg_ctx->context) { +      result = Curl_input_negotiate(conn, proxy, "Negotiate"); +      if(result) +        return result; +    } + +    result = Curl_auth_create_spnego_message(conn->data, +      neg_ctx, &base64, &len); +    if(result) +      return result; + +    userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", +      base64); + +    if(proxy) { +      Curl_safefree(conn->allocptr.proxyuserpwd); +      conn->allocptr.proxyuserpwd = userp; +    } +    else { +      Curl_safefree(conn->allocptr.userpwd); +      conn->allocptr.userpwd = userp; +    } + +    free(base64); + +    if(userp == NULL) { +      return CURLE_OUT_OF_MEMORY; +    } + +    neg_ctx->state = GSS_AUTHSENT; +  #ifdef HAVE_GSSAPI +    if(neg_ctx->status == GSS_S_COMPLETE || +       neg_ctx->status == GSS_S_CONTINUE_NEEDED) { +      neg_ctx->state = GSS_AUTHDONE; +    } +  #else +  #ifdef USE_WINDOWS_SSPI +    if(neg_ctx->status == SEC_E_OK || +       neg_ctx->status == SEC_I_CONTINUE_NEEDED) { +      neg_ctx->state = GSS_AUTHDONE; +    } +  #endif +  #endif    } -  else { -    Curl_safefree(conn->allocptr.userpwd); -    conn->allocptr.userpwd = userp; + +  if(neg_ctx->state == GSS_AUTHDONE || neg_ctx->state == GSS_AUTHSUCC) { +    /* connection is already authenticated, +     * don't send a header in future requests */ +    authp->done = TRUE;    } -  free(base64); +  neg_ctx->havenegdata = FALSE; -  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; +  return CURLE_OK;  } -void Curl_cleanup_negotiate(struct Curl_easy *data) +void Curl_cleanup_negotiate(struct connectdata *conn)  { -  Curl_auth_spnego_cleanup(&data->state.negotiate); -  Curl_auth_spnego_cleanup(&data->state.proxyneg); +  Curl_auth_spnego_cleanup(&conn->negotiate); +  Curl_auth_spnego_cleanup(&conn->proxyneg);  }  #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ diff --git a/lib/http_negotiate.h b/lib/http_negotiate.h index c64e54825..d4a7f09e0 100644 --- a/lib/http_negotiate.h +++ b/lib/http_negotiate.h @@ -7,7 +7,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, 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 @@ -31,7 +31,7 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,  /* this is for creating Negotiate header output */  CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy); -void Curl_cleanup_negotiate(struct Curl_easy *data); +void Curl_cleanup_negotiate(struct connectdata *conn);  #endif /* USE_SPNEGO */ diff --git a/lib/multi.c b/lib/multi.c index 1029aea99..cc16924a3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -600,7 +600,7 @@ static CURLcode multi_done(struct Curl_easy *data,    /* if data->set.reuse_forbid is TRUE, it means the libcurl client has       forced us to close this connection. This is ignored for requests taking -     place in a NTLM authentication handshake +     place in a NTLM/NEGOTIATE authentication handshake       if conn->bits.close is TRUE, it means that the connection should be       closed in spite of all our efforts to be nice, due to protocol @@ -618,6 +618,10 @@ static CURLcode multi_done(struct Curl_easy *data,        && !(conn->ntlm.state == NTLMSTATE_TYPE2 ||             conn->proxyntlm.state == NTLMSTATE_TYPE2)  #endif +#if defined(USE_SPNEGO) +      && !(conn->negotiate.state == GSS_AUTHRECV || +           conn->proxyneg.state == GSS_AUTHRECV) +#endif       ) || conn->bits.close         || (premature && !(conn->handler->flags & PROTOPT_STREAM))) {      CURLcode res2 = Curl_disconnect(data, conn, premature); @@ -806,6 +806,10 @@ CURLcode Curl_disconnect(struct Curl_easy *data,    /* Cleanup NTLM connection-related data */    Curl_http_ntlm_cleanup(conn);  #endif +#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) +  /* Cleanup NEGOTIATE connection-related data */ +  Curl_cleanup_negotiate(conn); +#endif    /* the protocol specific disconnect handler and conn_shutdown need a transfer       for the connection! */ diff --git a/lib/urldata.h b/lib/urldata.h index e5596b87f..24187a4c4 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -358,7 +358,9 @@ struct ntlmdata {  struct negotiatedata {    /* When doing Negotiate (SPNEGO) auth, we first need to send a token       and then validate the received one. */ -  enum { GSS_AUTHNONE, GSS_AUTHRECV, GSS_AUTHSENT } state; +  enum { +    GSS_AUTHNONE, GSS_AUTHRECV, GSS_AUTHSENT, GSS_AUTHDONE, GSS_AUTHSUCC +  } state;  #ifdef HAVE_GSSAPI    OM_uint32 status;    gss_ctx_id_t context; @@ -380,6 +382,10 @@ struct negotiatedata {    size_t output_token_length;  #endif  #endif +  bool noauthpersist; +  bool havenoauthpersist; +  bool havenegdata; +  bool havemultiplerequests;  };  #endif @@ -977,6 +983,11 @@ struct connectdata {  #endif  #endif +#ifdef USE_SPNEGO +  struct negotiatedata negotiate; /* state data for host Negotiate auth */ +  struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ +#endif +    /* data used for the asynch name resolve callback */    struct Curl_async async; @@ -1274,11 +1285,6 @@ struct UrlState {    struct digestdata digest;      /* state data for host Digest auth */    struct digestdata proxydigest; /* state data for proxy Digest auth */ -#ifdef USE_SPNEGO -  struct negotiatedata negotiate; /* state data for host Negotiate auth */ -  struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ -#endif -    struct auth authhost;  /* auth details for host */    struct auth authproxy; /* auth details for proxy */ diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 4a48bdd20..7c4bd4b59 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -5,7 +5,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, 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 @@ -273,6 +273,11 @@ void Curl_auth_spnego_cleanup(struct negotiatedata *nego)    /* Reset any variables */    nego->status = 0; +  nego->state = GSS_AUTHNONE; +  nego->noauthpersist = FALSE; +  nego->havenoauthpersist = FALSE; +  nego->havenegdata = FALSE; +  nego->havemultiplerequests = FALSE;  }  #endif /* HAVE_GSSAPI && USE_SPNEGO */ diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 98c471e58..0171ec52b 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -343,6 +343,11 @@ void Curl_auth_spnego_cleanup(struct negotiatedata *nego)    /* Reset any variables */    nego->status = 0;    nego->token_max = 0; +  nego->state = GSS_AUTHNONE; +  nego->noauthpersist = FALSE; +  nego->havenoauthpersist = FALSE; +  nego->havenegdata = FALSE; +  nego->havemultiplerequests = FALSE;  }  #endif /* USE_WINDOWS_SSPI && USE_SPNEGO */ diff --git a/tests/data/test2056 b/tests/data/test2056 index f00e21204..5d2584eec 100644 --- a/tests/data/test2056 +++ b/tests/data/test2056 @@ -8,17 +8,7 @@ HTTP Negotiate auth (stub krb5)  </info>  # Server-side  <reply> -<!-- First request, expect 401 Negotiate --> -<data> -HTTP/1.1 401 Authorization Required -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate -Content-Length: 13 - -Not yet sir! -</data> -<!-- Second request, expect success in one shot --> +<!-- First request, expect success in one shot -->  <data1>  HTTP/1.1 200 Things are fine in server land  Server: Microsoft-IIS/7.0 @@ -29,12 +19,6 @@ Content-Length: 15  Nice auth sir!  </data1>  <datacheck> -HTTP/1.1 401 Authorization Required -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate -Content-Length: 13 -  HTTP/1.1 200 Things are fine in server land  Server: Microsoft-IIS/7.0  Content-Type: text/html; charset=iso-8859-1 @@ -75,10 +59,6 @@ CURL_STUB_GSS_CREDS="KRB5_Alice"  <protocol>  GET /2056 HTTP/1.1
  Host: %HOSTIP:%HTTPPORT
 -Accept: */*
 -
 -GET /2056 HTTP/1.1
 -Host: %HOSTIP:%HTTPPORT
  Authorization: Negotiate IktSQjVfQWxpY2UiOkhUVFBAMTI3LjAuMC4xOjE6QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==
  Accept: */*
 diff --git a/tests/data/test2057 b/tests/data/test2057 index 562505168..92d7824c1 100644 --- a/tests/data/test2057 +++ b/tests/data/test2057 @@ -8,17 +8,7 @@ HTTP Negotiate auth (stub ntlm)  </info>  # Server-side  <reply> -<!-- First request, expect 401 Negotiate --> -<data> -HTTP/1.1 401 Authorization Required -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate -Content-Length: 13 - -Not yet sir! -</data> -<!-- Second request, expect 401 (ntlm challenge) --> +<!-- First request, expect 401 (ntlm challenge) -->  <data1>  HTTP/1.1 401 Authorization Required  Server: Microsoft-IIS/7.0 @@ -28,7 +18,7 @@ Content-Length: 19  Still not yet sir!  </data1> -<!-- Third request, expect success  --> +<!-- Second request, expect success  -->  <data2>  HTTP/1.1 200 Things are fine in server land  Server: Microsoft-IIS/7.0 @@ -42,12 +32,6 @@ Nice auth sir!  HTTP/1.1 401 Authorization Required  Server: Microsoft-IIS/7.0  Content-Type: text/html; charset=iso-8859-1 -WWW-Authenticate: Negotiate -Content-Length: 13 - -HTTP/1.1 401 Authorization Required -Server: Microsoft-IIS/7.0 -Content-Type: text/html; charset=iso-8859-1  WWW-Authenticate: Negotiate Qw==  Content-Length: 19 @@ -91,10 +75,6 @@ CURL_STUB_GSS_CREDS="NTLM_Alice"  <protocol>  GET /2057 HTTP/1.1
  Host: %HOSTIP:%HTTPPORT
 -Accept: */*
 -
 -GET /2057 HTTP/1.1
 -Host: %HOSTIP:%HTTPPORT
  Authorization: Negotiate Ik5UTE1fQWxpY2UiOkhUVFBAMTI3LjAuMC4xOjI6QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==
  Accept: */*
  | 
