* Always decode %xx in a url's user & pass components.

* Only remember {WWW,Proxy}-Authenticate "Basic" challenges; no point
  in tracking any others since ftp doesn't support them.
* Improve the parsing of HTTP responses.
This commit is contained in:
lukem 2004-12-10 06:44:15 +00:00
parent 67a256629f
commit a76c6e6173
2 changed files with 92 additions and 81 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: fetch.c,v 1.153 2004/10/30 17:36:31 dsl Exp $ */
/* $NetBSD: fetch.c,v 1.154 2004/12/10 06:44:15 lukem Exp $ */
/*-
* Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
@ -41,7 +41,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: fetch.c,v 1.153 2004/10/30 17:36:31 dsl Exp $");
__RCSID("$NetBSD: fetch.c,v 1.154 2004/12/10 06:44:15 lukem Exp $");
#endif /* not lint */
/*
@ -84,11 +84,12 @@ typedef enum {
void aborthttp(int);
#ifndef NO_AUTH
static int auth_url(const char *, char **, const char *, const char *);
static void base64_encode(const u_char *, size_t, u_char *);
static void base64_encode(const unsigned char *, size_t, unsigned char *);
#endif
static int go_fetch(const char *);
static int fetch_ftp(const char *);
static int fetch_url(const char *, const char *, char *, char *);
static const char *match_token(const char **, const char *);
static int parse_url(const char *, const char *, url_t *, char **,
char **, char **, char **, in_port_t *, char **);
static void url_decode(char *);
@ -107,6 +108,34 @@ static int redirect_loop;
#define HTTP_URL "http://" /* http URL prefix */
/*
* Determine if token is the next word in buf (case insensitive).
* If so, advance buf past the token and any trailing LWS, and
* return a pointer to the token (in buf). Otherwise, return NULL.
* token may be preceeded by LWS.
* token must be followed by LWS or NUL. (I.e, don't partial match).
*/
static const char *
match_token(const char **buf, const char *token)
{
const char *p, *orig;
size_t tlen;
tlen = strlen(token);
p = *buf;
SKIPLWS(p);
orig = p;
if (strncasecmp(p, token, tlen) != 0)
return NULL;
p += tlen;
if (*p != '\0' && !ISLWS(*p))
return NULL;
SKIPLWS(p);
orig = *buf;
*buf = p;
return orig;
}
#ifndef NO_AUTH
/*
* Generate authorization response based on given authentication challenge.
@ -117,51 +146,52 @@ static int
auth_url(const char *challenge, char **response, const char *guser,
const char *gpass)
{
char *cp, *ep, *clear, *line, *realm, *scheme;
const char *cp, *scheme;
char *ep, *clear, *realm;
char user[BUFSIZ], *pass;
int rval;
size_t len, clen, rlen;
*response = NULL;
clear = realm = scheme = NULL;
clear = realm = NULL;
rval = -1;
line = xstrdup(challenge);
cp = line;
cp = challenge;
scheme = "Basic"; /* only support Basic authentication */
if (debug)
fprintf(ttyout, "auth_url: challenge `%s'\n", challenge);
scheme = strsep(&cp, " ");
if (! STRNEQUAL(scheme, "Basic")) {
warnx("Unsupported WWW Authentication challenge - `%s'",
if (! match_token(&cp, scheme)) {
warnx("Unsupported authentication challenge - `%s'",
challenge);
goto cleanup_auth_url;
}
cp += strspn(cp, " ");
#define REALM "realm=\""
if (STRNEQUAL(cp, REALM))
cp += sizeof(REALM) - 1;
else {
warnx("Unsupported WWW Authentication challenge - `%s'",
warnx("Unsupported authentication challenge - `%s'",
challenge);
goto cleanup_auth_url;
}
/* XXX: need to improve quoted-string parsing to support \ quoting, etc. */
if ((ep = strchr(cp, '\"')) != NULL) {
size_t len = ep - cp;
realm = (char *)xmalloc(len + 1);
(void)strlcpy(realm, cp, len + 1);
} else {
warnx("Unsupported WWW Authentication challenge - `%s'",
warnx("Unsupported authentication challenge - `%s'",
challenge);
goto cleanup_auth_url;
}
if (guser != NULL)
fprintf(ttyout, "Username for `%s': ", realm);
if (guser != NULL) {
(void)strlcpy(user, guser, sizeof(user));
else {
fprintf(ttyout, "Username for `%s': ", realm);
fprintf(ttyout, "%s\n", user);
} else {
(void)fflush(ttyout);
if (fgets(user, sizeof(user) - 1, stdin) == NULL) {
clearerr(stdin);
@ -188,13 +218,12 @@ auth_url(const char *challenge, char **response, const char *guser,
(void)strlcpy(*response, scheme, rlen);
len = strlcat(*response, " ", rlen);
/* use `clen - 1' to not encode the trailing NUL */
base64_encode(clear, clen - 1, (u_char *)*response + len);
base64_encode(clear, clen - 1, (unsigned char *)*response + len);
memset(clear, 0, clen);
rval = 0;
cleanup_auth_url:
FREEPTR(clear);
FREEPTR(line);
FREEPTR(realm);
return (rval);
}
@ -204,11 +233,11 @@ auth_url(const char *challenge, char **response, const char *guser,
* which should be at least ((len + 2) * 4 / 3 + 1) in size.
*/
static void
base64_encode(const u_char *clear, size_t len, u_char *encoded)
base64_encode(const unsigned char *clear, size_t len, unsigned char *encoded)
{
static const u_char enc[] =
static const unsigned char enc[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
u_char *cp;
unsigned char *cp;
int i;
cp = encoded;
@ -355,6 +384,9 @@ parse_url(const char *url, const char *desc, url_t *type,
*cp = '\0';
*pass = xstrdup(cp + 1);
}
url_decode(*user);
if (*pass)
url_decode(*pass);
}
#ifdef INET6
@ -443,7 +475,8 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
size_t len;
static size_t bufsize;
static char *xferbuf;
char *cp, *ep, *buf, *savefile;
const char *cp, *token;
char *ep, *buf, *savefile;
char *auth, *location, *message;
char *user, *pass, *host, *port, *path, *decodedpath;
char *puser, *ppass, *useragent;
@ -827,13 +860,13 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
if (debug)
fprintf(ttyout, "received `%s'\n", buf);
/* Look for some headers */
/*
* Look for some headers
*/
cp = buf;
#define CONTENTLEN "Content-Length:"
if (STRNEQUAL(cp, CONTENTLEN)) {
cp += sizeof(CONTENTLEN) - 1;
SKIPLWS(cp);
if (match_token(&cp, "Content-Length:")) {
filesize = STRTOLL(cp, &ep, 10);
if (filesize < 0 || *ep != '\0')
goto improper;
@ -842,17 +875,12 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
"parsed len as: " LLF "\n",
(LLT)filesize);
#define CONTENTRANGE "Content-Range:"
} else if (STRNEQUAL(cp, CONTENTRANGE)) {
cp += sizeof(CONTENTRANGE) - 1;
SKIPLWS(cp);
#define BYTES "bytes "
if (! STRNEQUAL(cp, BYTES))
} else if (match_token(&cp, "Content-Range:")) {
if (! match_token(&cp, "bytes"))
goto improper;
cp += sizeof(BYTES) - 1;
if (*cp == '*') {
ep = cp + 1;
}
if (*cp == '*')
cp++;
else {
rangestart = STRTOLL(cp, &ep, 10);
if (rangestart < 0 || *ep != '-')
@ -861,19 +889,20 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
rangeend = STRTOLL(cp, &ep, 10);
if (rangeend < 0 || rangeend < rangestart)
goto improper;
cp = ep;
}
if (*ep != '/')
if (*cp != '/')
goto improper;
cp = ep + 1;
if (*cp == '*') {
ep = cp + 1;
}
cp++;
if (*cp == '*')
cp++;
else {
entitylen = STRTOLL(cp, &ep, 10);
if (entitylen < 0)
goto improper;
cp = ep;
}
if (*ep != '\0')
if (*cp != '\0')
goto improper;
if (debug) {
@ -892,13 +921,10 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
goto cleanup_fetch_url;
}
#define LASTMOD "Last-Modified:"
} else if (STRNEQUAL(cp, LASTMOD)) {
} else if (match_token(&cp, "Last-Modified:")) {
struct tm parsed;
char *t;
cp += sizeof(LASTMOD) - 1;
SKIPLWS(cp);
/* RFC 1123 */
if ((t = strptime(cp,
"%a, %d %b %Y %H:%M:%S GMT",
@ -921,29 +947,22 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
}
}
#define LOCATION "Location:"
} else if (STRNEQUAL(cp, LOCATION)) {
cp += sizeof(LOCATION) - 1;
SKIPLWS(cp);
} else if (match_token(&cp, "Location:")) {
location = xstrdup(cp);
if (debug)
fprintf(ttyout,
"parsed location as `%s'\n", cp);
#define TRANSENC "Transfer-Encoding:"
} else if (STRNEQUAL(cp, TRANSENC)) {
cp += sizeof(TRANSENC) - 1;
SKIPLWS(cp);
if (strcasecmp(cp, "binary") == 0) {
} else if (match_token(&cp, "Transfer-Encoding:")) {
if (match_token(&cp, "binary")) {
warnx(
"Bogus transfer encoding - `%s' (fetching anyway)",
cp);
"Bogus transfer encoding - `binary' (fetching anyway)");
continue;
}
if (strcasecmp(cp, "chunked") != 0) {
if (! (token = match_token(&cp, "chunked"))) {
warnx(
"Unsupported transfer encoding - `%s'",
cp);
token);
goto cleanup_fetch_url;
}
ischunked++;
@ -951,26 +970,20 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
fprintf(ttyout,
"using chunked encoding\n");
#define PROXYAUTH "Proxy-Authenticate:"
} else if (STRNEQUAL(cp, PROXYAUTH)) {
cp += sizeof(PROXYAUTH) - 1;
SKIPLWS(cp);
} else if (match_token(&cp, "Proxy-Authenticate:")
|| match_token(&cp, "WWW-Authenticate:")) {
if (! (token = match_token(&cp, "Basic"))) {
if (debug)
fprintf(ttyout,
"skipping unknown auth scheme `%s'\n",
token);
continue;
}
FREEPTR(auth);
auth = xstrdup(cp);
auth = xstrdup(token);
if (debug)
fprintf(ttyout,
"parsed proxy-auth as `%s'\n", cp);
#define WWWAUTH "WWW-Authenticate:"
} else if (STRNEQUAL(cp, WWWAUTH)) {
cp += sizeof(WWWAUTH) - 1;
SKIPLWS(cp);
FREEPTR(auth);
auth = xstrdup(cp);
if (debug)
fprintf(ttyout,
"parsed www-auth as `%s'\n", cp);
"parsed auth as `%s'\n", cp);
}
}
@ -1322,8 +1335,6 @@ fetch_ftp(const char *url)
warnx("Invalid URL `%s'", url);
goto cleanup_fetch_ftp;
}
url_decode(user);
url_decode(pass);
/*
* Note: Don't url_decode(path) here. We need to keep the
* distinction between "/" and "%2F" until later.

View File

@ -1,4 +1,4 @@
/* $NetBSD: version.h,v 1.41 2004/08/08 13:52:04 lukem Exp $ */
/* $NetBSD: version.h,v 1.42 2004/12/10 06:44:15 lukem Exp $ */
/*-
* Copyright (c) 1999-2004 The NetBSD Foundation, Inc.
* All rights reserved.
@ -40,5 +40,5 @@
#endif
#ifndef FTP_VERSION
#define FTP_VERSION "20040808"
#define FTP_VERSION "20041210"
#endif