diff options
author | wollman <wollman@FreeBSD.org> | 1997-02-05 19:59:18 +0000 |
---|---|---|
committer | wollman <wollman@FreeBSD.org> | 1997-02-05 19:59:18 +0000 |
commit | 37ca85c089f28bd04237b7fd363ad07b07994aac (patch) | |
tree | 3eb28344fec90f861cb268c3467fefc2a8aee72f /usr.bin | |
parent | f061fa28f5ac3f93d633c36e904431dd425e25ba (diff) | |
download | FreeBSD-src-37ca85c089f28bd04237b7fd363ad07b07994aac.zip FreeBSD-src-37ca85c089f28bd04237b7fd363ad07b07994aac.tar.gz |
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.
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/fetch/fetch.1 | 118 | ||||
-rw-r--r-- | usr.bin/fetch/fetch.h | 3 | ||||
-rw-r--r-- | usr.bin/fetch/ftp.c | 19 | ||||
-rw-r--r-- | usr.bin/fetch/http.c | 386 | ||||
-rw-r--r-- | usr.bin/fetch/main.c | 2 | ||||
-rw-r--r-- | usr.bin/fetch/util.c | 38 |
6 files changed, 503 insertions, 63 deletions
diff --git a/usr.bin/fetch/fetch.1 b/usr.bin/fetch/fetch.1 index 4fa89d3..8223cbd 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 89ddbea..321af11 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 34caeb0..0960b81 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 d92d82c..5d86599 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 09d0ae3..1b899a2 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 aab9278..b7d4b21 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; } |