mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-17 08:00:48 +01:00
Some bug-fixes, clean-ups, and one new feature:
- Fix the bug with URIs of the form ftp://host/filename. - Fix some more string-termination bugs in util.c. - Use safe_malloc() rather than testing the return value of regular malloc() in 15 places. - Implement HTTP authentication, for both servers and proxies. Currently only ``basic'' authentication is supported; This Is A Bug (but less of one tjhan nmot supporting any authentication). I think there is only one more feature which is required for full HTTP/1.1 support, which is Transfer-Encoding: chunked; this should not be toohard, but it isn't very important, either.
This commit is contained in:
parent
fb42516541
commit
76dafb8954
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=22307
@ -1,7 +1,7 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd July 2, 1996
|
||||
.Dt FETCH 1
|
||||
.Os
|
||||
.Os FreeBSD 3.0
|
||||
.Sh NAME
|
||||
.Nm fetch
|
||||
.Nd retrieve a file by Uniform Resource Locator
|
||||
@ -10,6 +10,7 @@
|
||||
.Op Fl MPamnpqr
|
||||
.Op Fl o Ar file
|
||||
.Ar URL
|
||||
.Op Ar ...
|
||||
.Nm fetch
|
||||
.Op Fl MPRmnpqr
|
||||
.Op Fl o Ar file
|
||||
@ -20,9 +21,9 @@
|
||||
.Nm fetch
|
||||
allows a user to transfer files from a remote network site using
|
||||
either the
|
||||
.Em ftp
|
||||
.Tn FTP
|
||||
or the
|
||||
.Em http
|
||||
.Tn HTTP
|
||||
protocol. In the first form of the command, the
|
||||
.Ar URL
|
||||
may be of the form
|
||||
@ -44,7 +45,7 @@ and the
|
||||
flags.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width Fl -compact
|
||||
.Bl -tag -width Fl
|
||||
.It Fl a
|
||||
Automatically retry the transfer upon soft failures.
|
||||
.It Fl c Ar dir
|
||||
@ -144,7 +145,7 @@ proxy client specifies
|
||||
as its user name, and passes the remote user name and host as the
|
||||
.Tn FTP
|
||||
session's password, in the form
|
||||
.Dq Va remoteuser Ns Li \&@ Va remotehost .
|
||||
.Dq Va remoteuser Ns Li \&@ Ns Va remotehost .
|
||||
The
|
||||
.Tn HTTP
|
||||
proxy client simply passes the originally-requested URI to the remote
|
||||
@ -156,34 +157,116 @@ When multiple proxy protcols are configured,
|
||||
.Nm
|
||||
will prefer
|
||||
.Tn HTTP .
|
||||
.Sh HTTP AUTHENTICATION
|
||||
The
|
||||
.Tn HTTP
|
||||
protocol includes support for various methods of authentication.
|
||||
Currently, the
|
||||
.Dq basic
|
||||
method, which provides no security from packet-sniffing or
|
||||
man-in-the-middle attacks, is the only method supported in
|
||||
.Nm fetch .
|
||||
Authentication is enabled by the
|
||||
.Ev HTTP_AUTH
|
||||
and
|
||||
.Ev HTTP_PROXY_AUTH
|
||||
environment variables. Both variables have the same format, which
|
||||
consists of space-separated list of parameter settings, where each
|
||||
setting consists of a colon-separated list of parameters. The first
|
||||
two parameters are always the (case-insensitive) authentication scheme
|
||||
name and the realm in which authentication is to be performed. If the
|
||||
realm is specified as
|
||||
.Sq Li \&* ,
|
||||
then it will match all realms not specified otherwise.
|
||||
.Pp
|
||||
For the
|
||||
.Li basic
|
||||
authentication scheme uses two additional optional parameters; the
|
||||
first is a user name, and the second is the password associated with
|
||||
it. If either the password or both parameters are not specified in
|
||||
the environment, and the standard input of
|
||||
.Nm
|
||||
is connected to a terminal, then
|
||||
.Nm
|
||||
will prompt the user to enter the missing parameters. Thus, if the
|
||||
user is known as
|
||||
.Dq Li jane
|
||||
in the
|
||||
.Dq Li WallyWorld
|
||||
realm, and has a password of
|
||||
.Dq Li QghiLx79
|
||||
there, then she might set her
|
||||
.Ev HTTP_AUTH
|
||||
variable to:
|
||||
.Bl -enum -offset indent
|
||||
.It
|
||||
.Dq Li basic:WallyWorld:jane:QghiLx79
|
||||
.It
|
||||
.Dq Li basic:WallyWorld:jane ,
|
||||
or
|
||||
.It
|
||||
.Dq Li basic:WallyWorld
|
||||
.El
|
||||
.Pp
|
||||
and
|
||||
.Nm
|
||||
will prompt for the missing information if it is required. She might
|
||||
also specify a realm of
|
||||
.Dq Li \&*
|
||||
instead of
|
||||
.Dq Li WallyWorld
|
||||
to indicate that the parameters can be applied to any realm. (This is
|
||||
most commonly used in a construction such as
|
||||
.Dq Li basic:* ,
|
||||
which indicates to
|
||||
.Nm
|
||||
that it may offer to do
|
||||
.Li basic
|
||||
authentication for any realm.
|
||||
.Sh ERRORS
|
||||
The
|
||||
.Nm
|
||||
command returns zero on success, or a non-zero value from
|
||||
.Aq Pa sysexits.h
|
||||
on failure. If multiple URIs are given for retrieval,
|
||||
.Nm
|
||||
will attempt all of them and return zero only if all succeeded
|
||||
(otherwise it will return the error from the last failure).
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width FTP_PASSIVE_MODE -offset indent
|
||||
.It Ev FTP_TIMEOUT
|
||||
maximum time, in seconds, to wait before aborting an
|
||||
.Tn FTP
|
||||
connection.
|
||||
.It Ev HTTP_TIMEOUT
|
||||
maximum time, in seconds, to wait before aborting an
|
||||
.Tn HTTP
|
||||
connection.
|
||||
.It Ev FTP_LOGIN
|
||||
the login name used for
|
||||
.Tn FTP
|
||||
transfers (default
|
||||
.Dq Li anonymous )
|
||||
.It Ev FTP_PASSIVE_MODE
|
||||
force the use of passive mode FTP
|
||||
.It Ev FTP_PASSWORD
|
||||
the password used for
|
||||
.Tn FTP
|
||||
transfers (default
|
||||
.Dq Va yourname Ns Li \&@ Ns Va yourhost )
|
||||
.It Ev FTP_PASSIVE_MODE
|
||||
force the use of passive mode FTP
|
||||
.It Ev HTTP_PROXY
|
||||
the address of a proxy server which understands
|
||||
.Tn HTTP
|
||||
.It Ev FTP_PROXY
|
||||
the address of a proxy server which understands
|
||||
.Tn FTP
|
||||
.It Ev HTTP_AUTH
|
||||
defines authentication parameters for
|
||||
.Tn HTTP
|
||||
.It Ev HTTP_PROXY
|
||||
the address of a proxy server which understands
|
||||
.Tn HTTP
|
||||
.It Ev HTTP_PROXY_AUTH
|
||||
defines authentication parameters for
|
||||
.Tn HTTP
|
||||
proxy servers
|
||||
.It Ev HTTP_TIMEOUT
|
||||
maximum time, in seconds, to wait before aborting an
|
||||
.Tn HTTP
|
||||
connection.
|
||||
.Sh SEE ALSO
|
||||
.Xr ftp 1 ,
|
||||
.Xr tftp 1
|
||||
@ -209,5 +292,8 @@ failures, and no
|
||||
.Tn FTP
|
||||
failures.
|
||||
.Pp
|
||||
.Tn HTTP
|
||||
authentication is not yet implememnted.
|
||||
Only the
|
||||
.Dq basic
|
||||
authentication mode is implemented for
|
||||
.Tn HTTP .
|
||||
This should be replaced by digest authentication.
|
||||
|
@ -26,7 +26,7 @@
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: fetch.h,v 1.1 1997/01/30 21:43:38 wollman Exp $
|
||||
* $Id: fetch.h,v 1.2 1997/01/31 19:55:49 wollman Exp $
|
||||
*/
|
||||
|
||||
#ifndef fetch_h
|
||||
@ -78,6 +78,7 @@ void init_schemes(void);
|
||||
void rm(struct fetch_state *fs);
|
||||
void setup_sigalrm(void);
|
||||
void unsetup_sigalrm(void);
|
||||
void *safe_malloc(size_t len);
|
||||
char *percent_decode(const char *orig);
|
||||
char *safe_strdup(const char *orig);
|
||||
char *safe_strndup(const char *orig, size_t len);
|
||||
|
@ -26,7 +26,7 @@
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
* $Id: ftp.c,v 1.1 1997/01/30 21:43:40 wollman Exp $
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -114,9 +114,7 @@ ftp_parse(struct fetch_state *fs, const char *uri)
|
||||
|
||||
p = slash + 1;
|
||||
|
||||
ftps = malloc(sizeof *ftps);
|
||||
if (ftps == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
ftps = safe_malloc(sizeof *ftps);
|
||||
|
||||
/*
|
||||
* Now, we have a copy of the hostname in hostname, the specified port
|
||||
@ -146,7 +144,7 @@ ftp_parse(struct fetch_state *fs, const char *uri)
|
||||
|
||||
if (fs->fs_outputfile == 0) {
|
||||
slash = strrchr(p, '/');
|
||||
fs->fs_outputfile = slash + 1;
|
||||
fs->fs_outputfile = slash ? slash + 1 : p;
|
||||
}
|
||||
|
||||
ftps->ftp_password = getenv("FTP_PASSWORD");
|
||||
@ -161,9 +159,7 @@ ftp_parse(struct fetch_state *fs, const char *uri)
|
||||
if (logname == 0)
|
||||
logname = "root";
|
||||
gethostname(localhost, sizeof localhost);
|
||||
pw = malloc(strlen(logname) + 1 + strlen(localhost) + 1);
|
||||
if (pw == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
pw = safe_malloc(strlen(logname) + 1 + strlen(localhost) + 1);
|
||||
strcpy(pw, logname);
|
||||
strcat(pw, "@");
|
||||
strcat(pw, localhost);
|
||||
@ -242,10 +238,9 @@ ftp_proxy_parse(struct fetch_state *fs, const char *uri)
|
||||
|
||||
ftps->ftp_port = portno;
|
||||
user = ftps->ftp_user ? ftps->ftp_user : "anonymous";
|
||||
newpass = malloc(strlen(ftps->ftp_user ? ftps->ftp_user : "anonymous")
|
||||
+ 1 + strlen(ftps->ftp_hostname) + 1);
|
||||
if (newpass == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
newpass = safe_malloc(strlen(ftps->ftp_user
|
||||
? ftps->ftp_user : "anonymous")
|
||||
+ 1 + strlen(ftps->ftp_hostname) + 1);
|
||||
|
||||
strcpy(newpass, user);
|
||||
strcat(newpass, "@");
|
||||
|
@ -26,7 +26,7 @@
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: http.c,v 1.1 1997/01/30 21:43:41 wollman Exp $
|
||||
* $Id: http.c,v 1.2 1997/01/31 19:55:50 wollman Exp $
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -45,6 +45,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/param.h> /* for MAXHOSTNAMELEN */
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
@ -55,14 +56,6 @@
|
||||
|
||||
#include "fetch.h"
|
||||
|
||||
static int http_parse(struct fetch_state *fs, const char *uri);
|
||||
static int http_proxy_parse(struct fetch_state *fs, const char *uri);
|
||||
static int http_close(struct fetch_state *fs);
|
||||
static int http_retrieve(struct fetch_state *fs);
|
||||
|
||||
struct uri_scheme http_scheme =
|
||||
{ "http", http_parse, http_proxy_parse, "HTTP_PROXY", "http" };
|
||||
|
||||
struct http_state {
|
||||
char *http_hostname;
|
||||
char *http_remote_request;
|
||||
@ -74,6 +67,34 @@ struct http_state {
|
||||
int http_redirected;
|
||||
};
|
||||
|
||||
struct http_auth {
|
||||
TAILQ_ENTRY(http_auth) ha_link;
|
||||
char *ha_scheme;
|
||||
char *ha_realm;
|
||||
char *ha_params;
|
||||
const struct http_auth_method *ha_ham;
|
||||
};
|
||||
TAILQ_HEAD(http_auth_head, http_auth);
|
||||
|
||||
static int http_parse(struct fetch_state *fs, const char *uri);
|
||||
static int http_proxy_parse(struct fetch_state *fs, const char *uri);
|
||||
static int http_close(struct fetch_state *fs);
|
||||
static int http_retrieve(struct fetch_state *fs);
|
||||
static int basic_doauth(struct fetch_state *fs, struct http_auth *ha, int prx);
|
||||
|
||||
struct uri_scheme http_scheme =
|
||||
{ "http", http_parse, http_proxy_parse, "HTTP_PROXY", "http" };
|
||||
|
||||
struct http_auth_head http_auth, http_proxy_auth;
|
||||
|
||||
struct http_auth_method {
|
||||
const char *ham_scheme;
|
||||
int (*ham_doauth)(struct fetch_state *, struct http_auth *, int);
|
||||
} http_auth_methods[] = {
|
||||
{ "basic", basic_doauth },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
/* We are only concerned with headers we might receive. */
|
||||
enum http_header {
|
||||
ht_accept_ranges, ht_age, ht_allow, ht_cache_control, ht_connection,
|
||||
@ -93,7 +114,11 @@ static enum http_header http_parse_header(char *line, char **valuep);
|
||||
static int check_md5(FILE *fp, char *base64ofmd5);
|
||||
static int http_first_line(const char *line);
|
||||
static int parse_http_content_range(char *orig, off_t *first, off_t *total);
|
||||
static int process_http_auth(struct fetch_state *fs, char *hdr, int autherr);
|
||||
static struct http_auth *find_http_auth(struct http_auth_head *list,
|
||||
const char *scheme, const char *realm);
|
||||
static time_t parse_http_date(char *datestring);
|
||||
static void setup_http_auth(void);
|
||||
|
||||
static int
|
||||
http_parse(struct fetch_state *fs, const char *uri)
|
||||
@ -146,9 +171,7 @@ http_parse(struct fetch_state *fs, const char *uri)
|
||||
|
||||
p = slash + 1;
|
||||
|
||||
https = malloc(sizeof *https);
|
||||
if (https == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
https = safe_malloc(sizeof *https);
|
||||
|
||||
/*
|
||||
* Now, we have a copy of the hostname in hostname, the specified port
|
||||
@ -181,6 +204,7 @@ http_parse(struct fetch_state *fs, const char *uri)
|
||||
fs->fs_outputfile = p;
|
||||
}
|
||||
https->http_redirected = 0;
|
||||
https->http_authentication = https->http_proxy_authentication = 0;
|
||||
|
||||
fs->fs_proto = https;
|
||||
fs->fs_close = http_close;
|
||||
@ -202,7 +226,7 @@ http_proxy_parse(struct fetch_state *fs, const char *uri)
|
||||
char *file;
|
||||
int rv;
|
||||
|
||||
https = malloc(sizeof *https);
|
||||
https = safe_malloc(sizeof *https);
|
||||
https->http_remote_request = safe_strdup(uri);
|
||||
|
||||
env = getenv("HTTP_PROXY");
|
||||
@ -249,6 +273,7 @@ out:
|
||||
}
|
||||
https->http_decoded_file = percent_decode(file);
|
||||
https->http_redirected = 0;
|
||||
https->http_authentication = https->http_proxy_authentication = 0;
|
||||
free(file);
|
||||
if (fs->fs_outputfile == 0) {
|
||||
slash = strrchr(https->http_decoded_file, '/');
|
||||
@ -272,6 +297,10 @@ http_close(struct fetch_state *fs)
|
||||
free(https->http_remote_request);
|
||||
free(https->http_decoded_file);
|
||||
free(https->http_host_header);
|
||||
if (https->http_authentication)
|
||||
free(https->http_authentication);
|
||||
if (https->http_proxy_authentication)
|
||||
free(https->http_proxy_authentication);
|
||||
free(https);
|
||||
fs->fs_outputfile = 0;
|
||||
return 0;
|
||||
@ -339,7 +368,7 @@ http_retrieve(struct fetch_state *fs)
|
||||
int s;
|
||||
struct sockaddr_in sin;
|
||||
struct msghdr msg;
|
||||
#define NIOV 16 /* max is currently 12 */
|
||||
#define NIOV 16 /* max is currently 14 */
|
||||
struct iovec iov[NIOV];
|
||||
int n, status;
|
||||
const char *env;
|
||||
@ -350,14 +379,17 @@ http_retrieve(struct fetch_state *fs)
|
||||
time_t last_modified, when_to_retry;
|
||||
char *base64ofmd5;
|
||||
static char buf[BUFFER_SIZE];
|
||||
int to_stdout, restarting, redirection, retrying;
|
||||
int to_stdout, restarting, redirection, retrying, autherror;
|
||||
char rangebuf[sizeof("Range: bytes=18446744073709551616-\r\n")];
|
||||
|
||||
setup_http_auth();
|
||||
|
||||
https = fs->fs_proto;
|
||||
to_stdout = (strcmp(fs->fs_outputfile, "-") == 0);
|
||||
restarting = fs->fs_restart;
|
||||
redirection = 0;
|
||||
retrying = 0;
|
||||
autherror = 0;
|
||||
|
||||
/*
|
||||
* Figure out the timeout. Prefer the -T command-line value,
|
||||
@ -386,6 +418,7 @@ http_retrieve(struct fetch_state *fs)
|
||||
sin.sin_len = sizeof sin;
|
||||
sin.sin_port = htons(https->http_port);
|
||||
|
||||
fs->fs_status = "looking up hostname";
|
||||
if (inet_aton(https->http_hostname, &sin.sin_addr) == 0) {
|
||||
struct hostent *hp;
|
||||
|
||||
@ -399,6 +432,7 @@ http_retrieve(struct fetch_state *fs)
|
||||
memcpy(&sin.sin_addr, hp->h_addr_list[0], sizeof sin.sin_addr);
|
||||
}
|
||||
|
||||
fs->fs_status = "creating request message";
|
||||
msg.msg_name = (caddr_t)&sin;
|
||||
msg.msg_namelen = sizeof sin;
|
||||
msg.msg_iov = iov;
|
||||
@ -433,6 +467,10 @@ retry:
|
||||
addstr(iov, n, "Accept: */*\r\n");
|
||||
addstr(iov, n, https->http_host_header);
|
||||
addstr(iov, n, "Connection: close\r\n");
|
||||
if (https->http_proxy_authentication)
|
||||
addstr(iov, n, https->http_proxy_authentication);
|
||||
if (https->http_authentication)
|
||||
addstr(iov, n, https->http_authentication);
|
||||
if (fs->fs_mirror) {
|
||||
struct stat stab;
|
||||
|
||||
@ -490,15 +528,17 @@ retry:
|
||||
return EX_OSERR;
|
||||
}
|
||||
|
||||
fs->fs_status = "sending request message";
|
||||
setup_sigalrm();
|
||||
alarm(timo);
|
||||
if (sendmsg(s, &msg, MSG_EOF) < 0) {
|
||||
warn("%s", https->http_hostname);
|
||||
warn("sendmsg: %s", https->http_hostname);
|
||||
fclose(remote);
|
||||
return EX_OSERR;
|
||||
}
|
||||
|
||||
got100reply:
|
||||
fs->fs_status = "reading reply status";
|
||||
alarm(timo);
|
||||
line = fgetln(remote, &linelen);
|
||||
alarm(0);
|
||||
@ -533,6 +573,7 @@ got100reply:
|
||||
unsetup_sigalrm();
|
||||
return EX_OSERR;
|
||||
}
|
||||
fs->fs_status = "retrieving from HTTP/0.9 server";
|
||||
display(fs, -1, 0);
|
||||
|
||||
do {
|
||||
@ -602,9 +643,15 @@ got100reply:
|
||||
}
|
||||
goto spewerror;
|
||||
case 401: /* Unauthorized */
|
||||
if (https->http_authentication)
|
||||
goto spewerror;
|
||||
autherror = 401;
|
||||
break;
|
||||
case 407: /* Proxy Authentication Required */
|
||||
/* XXX implement authentication */
|
||||
|
||||
if (https->http_proxy_authentication)
|
||||
goto spewerror;
|
||||
autherror = 407;
|
||||
break;
|
||||
case 503: /* Service Unavailable */
|
||||
if (!fs->fs_auto_retry)
|
||||
goto spewerror;
|
||||
@ -632,6 +679,7 @@ spewerror:
|
||||
base64ofmd5 = 0;
|
||||
new_location = 0;
|
||||
restart_from = 0;
|
||||
fs->fs_status = "parsing reply headers";
|
||||
|
||||
while((line = fgetln(remote, &linelen)) != 0) {
|
||||
char *value, *ep;
|
||||
@ -722,10 +770,35 @@ doretry:
|
||||
}
|
||||
break;
|
||||
|
||||
case ht_www_authenticate:
|
||||
if (autherror != 401)
|
||||
break;
|
||||
|
||||
status = process_http_auth(fs, value, autherror);
|
||||
if (status != 0)
|
||||
goto cantauth;
|
||||
break;
|
||||
|
||||
case ht_proxy_authenticate:
|
||||
if (autherror != 407)
|
||||
break;
|
||||
status = process_http_auth(fs, value, autherror);
|
||||
if (status != 0)
|
||||
goto cantauth;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (autherror == 401 && https->http_authentication)
|
||||
goto doretry;
|
||||
if (autherror == 407 && https->http_proxy_authentication)
|
||||
goto doretry;
|
||||
if (autherror) {
|
||||
line = (char *)"HTTP/1.1 401 Unauthorized";
|
||||
goto spewerror;
|
||||
}
|
||||
|
||||
if (retrying) {
|
||||
int howlong;
|
||||
@ -741,6 +814,7 @@ doretry:
|
||||
|
||||
warnx("%s: service unavailable; retrying in %d seconds",
|
||||
https->http_hostname, howlong);
|
||||
fs->fs_status = "waiting to retry";
|
||||
sleep(howlong);
|
||||
goto doretry;
|
||||
}
|
||||
@ -749,6 +823,7 @@ doretry:
|
||||
fclose(remote);
|
||||
if (base64ofmd5)
|
||||
free(base64ofmd5);
|
||||
fs->fs_status = "processing redirection";
|
||||
status = http_redirect(fs, new_location, redirection == 301);
|
||||
free(new_location);
|
||||
return status;
|
||||
@ -761,6 +836,8 @@ doretry:
|
||||
return EX_PROTOCOL;
|
||||
}
|
||||
|
||||
fs->fs_status = "retrieving file from HTTP/1.x server";
|
||||
|
||||
/*
|
||||
* OK, if we got here, then we have finished parsing the header
|
||||
* and have read the `\r\n' line which denotes the end of same.
|
||||
@ -818,12 +895,14 @@ doretry:
|
||||
* we are getting, not the whole thing.
|
||||
*/
|
||||
fseek(local, restart_from, SEEK_SET);
|
||||
fs->fs_status = "computing MD5 message digest";
|
||||
status = check_md5(local, base64ofmd5);
|
||||
free(base64ofmd5);
|
||||
}
|
||||
|
||||
unsetup_sigalrm();
|
||||
fclose(local);
|
||||
out:
|
||||
unsetup_sigalrm();
|
||||
fclose(remote);
|
||||
|
||||
if (status != 0)
|
||||
@ -833,6 +912,14 @@ doretry:
|
||||
|
||||
return status;
|
||||
#undef addstr
|
||||
|
||||
cantauth:
|
||||
warnx("%s: cannot authenticate with %s %s",
|
||||
fs->fs_outputfile,
|
||||
(autherror == 401) ? "server" : "proxy",
|
||||
https->http_hostname);
|
||||
status = EX_NOPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1201,3 +1288,264 @@ parse_http_content_range(char *orig, off_t *restart_from, off_t *total_length)
|
||||
*total_length = last;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do HTTP authentication. We only do ``basic'' right now, but
|
||||
* MD5 ought to be fairly easy. The hard part is actually teasing
|
||||
* apart the header, which is fairly badly designed (so what else is
|
||||
* new?).
|
||||
*/
|
||||
static char *
|
||||
getauthparam(char *params, const char *name)
|
||||
{
|
||||
char *rv;
|
||||
enum state { normal, quoted } state;
|
||||
while (*params) {
|
||||
if (strncasecmp(params, name, strlen(name)) == 0
|
||||
&& params[strlen(name)] == '=')
|
||||
break;
|
||||
state = normal;
|
||||
while (*params) {
|
||||
if (state == normal && *params == ',')
|
||||
break;
|
||||
if (*params == '\"')
|
||||
state = (state == quoted) ? normal : quoted;
|
||||
if (*params == '\\' && params[1] != '\0')
|
||||
params++;
|
||||
params++;
|
||||
}
|
||||
}
|
||||
|
||||
if (*params == '\0')
|
||||
return 0;
|
||||
params += strlen(name) + 1;
|
||||
rv = params;
|
||||
state = normal;
|
||||
while (*params) {
|
||||
if (state == normal && *params == ',')
|
||||
break;
|
||||
if (*params == '\"')
|
||||
state = (state == quoted) ? normal : quoted;
|
||||
if (*params == '\\' && params[1] != '\0')
|
||||
params++;
|
||||
params++;
|
||||
}
|
||||
if (params[-1] == '\"')
|
||||
params[-1] = '\0';
|
||||
else
|
||||
params[0] = '\0';
|
||||
|
||||
if (*rv == '\"')
|
||||
rv++;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int
|
||||
process_http_auth(struct fetch_state *fs, char *hdr, int autherr)
|
||||
{
|
||||
enum state { normal, quoted } state;
|
||||
char *scheme, *params, *nscheme, *realm;
|
||||
struct http_auth *ha;
|
||||
|
||||
do {
|
||||
scheme = params = hdr;
|
||||
/* Look for end of scheme name. */
|
||||
while (*params && !isspace(*params))
|
||||
params++;
|
||||
|
||||
if (*params == '\0')
|
||||
return EX_PROTOCOL;
|
||||
|
||||
/* Null-terminate scheme and skip whitespace. */
|
||||
while (*params && isspace(*params))
|
||||
*params++ = '\0';
|
||||
|
||||
/* Semi-parse parameters to find their end. */
|
||||
nscheme = params;
|
||||
state = normal;
|
||||
while (*nscheme) {
|
||||
if (state == normal && isspace(*nscheme))
|
||||
break;
|
||||
if (*nscheme == '\"')
|
||||
state = (state == quoted) ? normal : quoted;
|
||||
if (*nscheme == '\\' && nscheme[1] != '\0')
|
||||
nscheme++;
|
||||
nscheme++;
|
||||
}
|
||||
|
||||
/* Null-terminate parameters and skip whitespace. */
|
||||
while (*nscheme && isspace(*nscheme))
|
||||
*nscheme++ = '\0';
|
||||
|
||||
realm = getauthparam(params, "realm");
|
||||
if (realm == 0) {
|
||||
scheme = nscheme;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (autherr == 401)
|
||||
ha = find_http_auth(&http_auth, scheme, realm);
|
||||
else
|
||||
ha = find_http_auth(&http_proxy_auth, scheme, realm);
|
||||
|
||||
if (ha)
|
||||
return ha->ha_ham->ham_doauth(fs, ha, autherr == 407);
|
||||
} while (*scheme);
|
||||
return EX_NOPERM;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_http_auth_env(const char *env, struct http_auth_head *ha_tqh)
|
||||
{
|
||||
char *nenv, *p, *scheme, *realm, *params;
|
||||
struct http_auth *ha;
|
||||
struct http_auth_method *ham;
|
||||
|
||||
nenv = alloca(strlen(env) + 1);
|
||||
strcpy(nenv, env);
|
||||
|
||||
while ((p = strsep(&nenv, " \t")) != 0) {
|
||||
scheme = strsep(&p, ":");
|
||||
if (scheme == 0 || *scheme == '\0')
|
||||
continue;
|
||||
realm = strsep(&p, ":");
|
||||
if (realm == 0 || *realm == '\0')
|
||||
continue;
|
||||
params = (p && *p) ? p : 0;
|
||||
for (ham = http_auth_methods; ham->ham_scheme; ham++) {
|
||||
if (strcasecmp(scheme, ham->ham_scheme) == 0)
|
||||
break;
|
||||
}
|
||||
if (ham == 0)
|
||||
continue;
|
||||
ha = safe_malloc(sizeof *ha);
|
||||
ha->ha_scheme = safe_strdup(scheme);
|
||||
ha->ha_realm = safe_strdup(realm);
|
||||
ha->ha_params = params ? safe_strdup(params) : 0;
|
||||
ha->ha_ham = ham;
|
||||
TAILQ_INSERT_TAIL(ha_tqh, ha, ha_link);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up an authentication method. Automatically clone wildcards
|
||||
* into fully-specified entries.
|
||||
*/
|
||||
static struct http_auth *
|
||||
find_http_auth(struct http_auth_head *tqh, const char *scm, const char *realm)
|
||||
{
|
||||
struct http_auth *ha;
|
||||
|
||||
for (ha = tqh->tqh_first; ha; ha = ha->ha_link.tqe_next) {
|
||||
if (strcasecmp(ha->ha_scheme, scm) == 0
|
||||
&& strcasecmp(ha->ha_realm, realm) == 0)
|
||||
return ha;
|
||||
}
|
||||
|
||||
for (ha = tqh->tqh_first; ha; ha = ha->ha_link.tqe_next) {
|
||||
if (strcasecmp(ha->ha_scheme, scm) == 0
|
||||
&& strcmp(ha->ha_realm, "*") == 0)
|
||||
break;
|
||||
}
|
||||
if (ha != 0) {
|
||||
struct http_auth *ha2;
|
||||
|
||||
ha2 = safe_malloc(sizeof *ha2);
|
||||
ha2->ha_scheme = safe_strdup(scm);
|
||||
ha2->ha_realm = safe_strdup(realm);
|
||||
ha2->ha_params = ha->ha_params ? safe_strdup(ha->ha_params) :0;
|
||||
ha2->ha_ham = ha->ha_ham;
|
||||
TAILQ_INSERT_TAIL(tqh, ha2, ha_link);
|
||||
ha = ha2;
|
||||
}
|
||||
|
||||
return ha;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_http_auth(void)
|
||||
{
|
||||
const char *envar;
|
||||
static int once;
|
||||
|
||||
if (once)
|
||||
return;
|
||||
once = 1;
|
||||
|
||||
TAILQ_INIT(&http_auth);
|
||||
TAILQ_INIT(&http_proxy_auth);
|
||||
envar = getenv("HTTP_AUTH");
|
||||
if (envar)
|
||||
parse_http_auth_env(envar, &http_auth);
|
||||
|
||||
envar = getenv("HTTP_PROXY_AUTH");
|
||||
if (envar)
|
||||
parse_http_auth_env(envar, &http_proxy_auth);
|
||||
}
|
||||
|
||||
static int
|
||||
basic_doauth(struct fetch_state *fs, struct http_auth *ha, int isproxy)
|
||||
{
|
||||
struct http_state *https = fs->fs_proto;
|
||||
char *user;
|
||||
char *pass;
|
||||
char *enc;
|
||||
char **hdr;
|
||||
size_t userlen;
|
||||
FILE *fp;
|
||||
|
||||
if (!isatty(0) &&
|
||||
(ha->ha_params == 0 || strchr(ha->ha_params, ':') == 0))
|
||||
return EX_NOPERM;
|
||||
|
||||
fp = fopen("/dev/tty", "r+");
|
||||
if (fp == 0) {
|
||||
warn("opening /dev/tty");
|
||||
return EX_OSERR;
|
||||
}
|
||||
if (ha->ha_params == 0) {
|
||||
fprintf(fp, "Enter `basic' user name for realm `%s': ",
|
||||
ha->ha_realm);
|
||||
fflush(fp);
|
||||
user = fgetln(stdin, &userlen);
|
||||
if (user == 0 || userlen < 1) { /* longer name? */
|
||||
fclose(fp);
|
||||
return EX_NOPERM;
|
||||
}
|
||||
if (user[userlen - 1] == '\n')
|
||||
user[userlen - 1] = '\0';
|
||||
else
|
||||
user[userlen] = '\0';
|
||||
user = safe_strdup(user);
|
||||
pass = 0;
|
||||
} else if ((pass = strchr(ha->ha_params, ':')) == 0) {
|
||||
user = safe_strdup(ha->ha_params);
|
||||
free(ha->ha_params);
|
||||
}
|
||||
|
||||
if (pass == 0) {
|
||||
pass = getpass("Password: ");
|
||||
ha->ha_params = safe_malloc(strlen(user) + 2 + strlen(pass));
|
||||
strcpy(ha->ha_params, user);
|
||||
strcat(ha->ha_params, ":");
|
||||
strcat(ha->ha_params, pass);
|
||||
}
|
||||
|
||||
enc = to_base64(ha->ha_params, strlen(ha->ha_params));
|
||||
|
||||
hdr = isproxy ? &https->http_proxy_authentication
|
||||
: &https->http_authentication;
|
||||
if (*hdr)
|
||||
free(*hdr);
|
||||
*hdr = safe_malloc(sizeof("Proxy-Authorization: basic \r\n")
|
||||
+ strlen(enc));
|
||||
if (isproxy)
|
||||
strcpy(*hdr, "Proxy-Authorization");
|
||||
else
|
||||
strcpy(*hdr, "Authorization");
|
||||
strcat(*hdr, ": Basic ");
|
||||
strcat(*hdr, enc);
|
||||
strcat(*hdr, "\r\n");
|
||||
free(enc);
|
||||
return 0;
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ display(struct fetch_state *fs, off_t size, ssize_t n)
|
||||
gettimeofday(&t0, &tz);
|
||||
t_start = t0;
|
||||
bytes = pr = 0;
|
||||
s = malloc(strlen(fs->fs_outputfile) + 50);
|
||||
s = safe_malloc(strlen(fs->fs_outputfile) + 50);
|
||||
if (size > 0)
|
||||
sprintf (s, "Receiving %s (%qd bytes)%s", fs->fs_outputfile,
|
||||
(quad_t)size,
|
||||
|
@ -26,7 +26,7 @@
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: util.c,v 1.1 1997/01/30 21:43:44 wollman Exp $
|
||||
* $Id: util.c,v 1.2 1997/02/02 09:16:37 bde Exp $
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -129,9 +129,7 @@ percent_decode(const char *uri)
|
||||
{
|
||||
char *rv, *s;
|
||||
|
||||
rv = s = malloc(strlen(uri) + 1);
|
||||
if (rv == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
rv = s = safe_malloc(strlen(uri) + 1);
|
||||
|
||||
while (*uri) {
|
||||
if (*uri == '%' && uri[1]
|
||||
@ -182,6 +180,20 @@ parse_host_port(const char *s, char **hostname, int *port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* safe_malloc is like malloc, but aborts on error.
|
||||
*/
|
||||
void *
|
||||
safe_malloc(size_t len)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
rv = malloc(len);
|
||||
if (rv == 0)
|
||||
err(EX_OSERR, "malloc(%qu)", (u_quad_t)len);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* safe_strdup is like strdup, but aborts on error.
|
||||
*/
|
||||
@ -190,9 +202,7 @@ safe_strdup(const char *orig)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = malloc(strlen(orig) + 1);
|
||||
if (s == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
s = safe_malloc(strlen(orig) + 1);
|
||||
strcpy(s, orig);
|
||||
return s;
|
||||
}
|
||||
@ -206,9 +216,7 @@ safe_strndup(const char *orig, size_t len)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = malloc(len + 1);
|
||||
if (s == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
s = safe_malloc(len + 1);
|
||||
s[0] = '\0';
|
||||
strncat(s, orig, len);
|
||||
return s;
|
||||
@ -223,15 +231,14 @@ static const char base64[] =
|
||||
char *
|
||||
to_base64(const unsigned char *buf, size_t len)
|
||||
{
|
||||
char *s = malloc((4 * (len + 1)) / 3 + 1), *rv;
|
||||
char *s, *rv;
|
||||
unsigned tmp;
|
||||
|
||||
if (s == 0)
|
||||
err(EX_OSERR, "malloc");
|
||||
s = safe_malloc((4 * (len + 1)) / 3 + 1);
|
||||
|
||||
rv = s;
|
||||
while (len >= 3) {
|
||||
tmp = buf[0] << 16 | buf[1] << 8 || buf[2];
|
||||
tmp = buf[0] << 16 | buf[1] << 8 | buf[2];
|
||||
s[0] = base64[tmp >> 18];
|
||||
s[1] = base64[(tmp >> 12) & 077];
|
||||
s[2] = base64[(tmp >> 6) & 077];
|
||||
@ -249,14 +256,17 @@ to_base64(const unsigned char *buf, size_t len)
|
||||
s[1] = base64[(tmp >> 12) & 077];
|
||||
s[2] = base64[(tmp >> 6) & 077];
|
||||
s[3] = '=';
|
||||
s[4] = '\0';
|
||||
break;
|
||||
case 1:
|
||||
tmp = buf[0] << 16;
|
||||
s[0] = base64[(tmp >> 18) & 077];
|
||||
s[1] = base64[(tmp >> 12) & 077];
|
||||
s[2] = s[3] = '=';
|
||||
s[4] = '\0';
|
||||
break;
|
||||
case 0:
|
||||
s[0] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user