aboutsummaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorGarrett Wollman <wollman@FreeBSD.org>1997-02-05 19:59:18 +0000
committerGarrett Wollman <wollman@FreeBSD.org>1997-02-05 19:59:18 +0000
commit76dafb8954ea032dfedfe51a5bb873fc2012758d (patch)
tree3eb28344fec90f861cb268c3467fefc2a8aee72f /usr.bin
parentfb425165411d2dfee76d17e38496fd543b121507 (diff)
downloadsrc-76dafb8954ea032dfedfe51a5bb873fc2012758d.tar.gz
src-76dafb8954ea032dfedfe51a5bb873fc2012758d.zip
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.
Notes
Notes: svn path=/head/; revision=22307
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/fetch/fetch.1118
-rw-r--r--usr.bin/fetch/fetch.h3
-rw-r--r--usr.bin/fetch/ftp.c19
-rw-r--r--usr.bin/fetch/http.c386
-rw-r--r--usr.bin/fetch/main.c2
-rw-r--r--usr.bin/fetch/util.c38
6 files changed, 503 insertions, 63 deletions
diff --git a/usr.bin/fetch/fetch.1 b/usr.bin/fetch/fetch.1
index 4fa89d3461b6..8223cbd7deda 100644
--- a/usr.bin/fetch/fetch.1
+++ b/usr.bin/fetch/fetch.1
@@ -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.
diff --git a/usr.bin/fetch/fetch.h b/usr.bin/fetch/fetch.h
index 89ddbea6c8c9..321af11f671b 100644
--- a/usr.bin/fetch/fetch.h
+++ b/usr.bin/fetch/fetch.h
@@ -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);
diff --git a/usr.bin/fetch/ftp.c b/usr.bin/fetch/ftp.c
index 34caeb0f9b41..0960b8158419 100644
--- a/usr.bin/fetch/ftp.c
+++ b/usr.bin/fetch/ftp.c
@@ -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, "@");
diff --git a/usr.bin/fetch/http.c b/usr.bin/fetch/http.c
index d92d82c8a2fc..5d86599c991f 100644
--- a/usr.bin/fetch/http.c
+++ b/usr.bin/fetch/http.c
@@ -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;
+}
diff --git a/usr.bin/fetch/main.c b/usr.bin/fetch/main.c
index 09d0ae3cdf33..1b899a2b52d0 100644
--- a/usr.bin/fetch/main.c
+++ b/usr.bin/fetch/main.c
@@ -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,
diff --git a/usr.bin/fetch/util.c b/usr.bin/fetch/util.c
index aab9278e28fd..b7d4b2167a17 100644
--- a/usr.bin/fetch/util.c
+++ b/usr.bin/fetch/util.c
@@ -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]
@@ -183,6 +181,20 @@ parse_host_port(const char *s, char **hostname, int *port)
}
/*
+ * 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.
*/
char *
@@ -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;
}