On Wed, Jun 25, 2014 at 07:07:30PM -0700, Philip Guenther wrote: > On Wed, 25 Jun 2014, S?bastien Marie wrote: > > On Tue, Jun 24, 2014 at 10:55:44AM -0700, Philip Guenther wrote: > > > On Tue, Jun 24, 2014 at 9:01 AM, S?bastien Marie < > > > semarie-open...@latrappe.fr> wrote: > ... > > So, I think ftp(1) should urldecode userinfo before base64. > > I agree. > > > I propose another patch that implement urldecoding of userinfo (as it > > come from URI). > > It also needs to be fixed for proxy authentication. I think the code to > urldecode and reencode in base64 should be factored out.
If urldecode is refactored, it may return the length of decoded string (as size_t* parameter: NULL for no result, output variable else). The purpose is to properly managed the case where %00 is include in the userinfo. Else it results truncation (as string are \0 terminate). But it would need more work: if in recode_credentials there is no problem (just need to pass ulen to urldecode, and don't need strlen), urldecode is used also for FTP URL parsing. But note that I don't sure that manage %00 in userinfo make sense. > Here's a patch to do that. Just a comment in code (unused variables in urldecode). Else it seems ok. And my use-case works. > ... > > Index: fetch.c > =================================================================== > RCS file: /cvs/src/usr.bin/ftp/fetch.c,v > retrieving revision 1.122 > diff -u -p -r1.122 fetch.c > --- fetch.c 20 May 2014 01:25:23 -0000 1.122 > +++ fetch.c 26 Jun 2014 01:40:28 -0000 > @@ -75,7 +75,8 @@ static int url_get(const char *, const c > void aborthttp(int); > void abortfile(int); > char hextochar(const char *); > -char *urldecode(const char *); > +void urldecode(char *); > +char *recode_credentials(char *_userinfo); > int ftp_printf(FILE *, SSL *, const char *, ...) > __attribute__((format(printf, 3, 4))); > char *ftp_readline(FILE *, SSL *, size_t *); > size_t ftp_read(FILE *, SSL *, char *, size_t); > @@ -97,8 +98,6 @@ SSL_CTX *ssl_get_ssl_ctx(void); > #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ > #define HTTP_PROXY "http_proxy" /* env var with http proxy location */ > > -#define COOKIE_MAX_LEN 42 > - > #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0')) > > static const char at_encoding_warning[] = > @@ -393,7 +392,7 @@ url_get(const char *origline, const char > struct addrinfo hints, *res0, *res, *ares = NULL; > const char * volatile savefile; > char * volatile proxyurl = NULL; > - char *cookie = NULL; > + char *credentials = NULL; > volatile int s = -1, out; > volatile sig_t oldintr, oldinti; > FILE *fin = NULL; > @@ -402,9 +401,9 @@ url_get(const char *origline, const char > ssize_t len, wlen; > #ifndef SMALL > char *sslpath = NULL, *sslhost = NULL; > - char *locbase, *full_host = NULL, *auth = NULL; > + char *locbase, *full_host = NULL; > const char *scheme; > - int ishttpsurl = 0; > + int ishttpurl = 0, ishttpsurl = 0; > SSL_CTX *ssl_ctx = NULL; > #endif /* !SMALL */ > SSL *ssl = NULL; > @@ -420,6 +419,7 @@ url_get(const char *origline, const char > if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { > host = newline + sizeof(HTTP_URL) - 1; > #ifndef SMALL > + ishttpurl = 1; > scheme = HTTP_URL; > #endif /* !SMALL */ > } else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) { > @@ -472,17 +472,10 @@ noslash: > * contain the path. Basic auth from RFC 2617, valid > * characters for path are in RFC 3986 section 3.3. > */ > - if (proxyenv == NULL && > - (!strcmp(scheme, HTTP_URL) || !strcmp(scheme, HTTPS_URL))) { > + if (proxyenv == NULL && (ishttpurl || ishttpsurl)) { > if ((p = strchr(host, '@')) != NULL) { > - size_t authlen = (strlen(host) + 5) * 4 / 3; > - *p = 0; /* Kill @ */ > - if ((auth = malloc(authlen)) == NULL) > - err(1, "Can't allocate memory for " > - "authorization"); > - if (b64_ntop(host, strlen(host), > - auth, authlen) == -1) > - errx(1, "error in base64 encoding"); > + *p = '\0'; > + credentials = recode_credentials(host); > host = p + 1; > } > } > @@ -544,27 +537,14 @@ noslash: > path = strchr(host, '@'); /* look for credentials in > proxy */ > if (!EMPTYSTRING(path)) { > *path = '\0'; > - cookie = strchr(host, ':'); > - if (EMPTYSTRING(cookie)) { > + if (strchr(host, ':') == NULL) { > warnx("Malformed proxy URL: %s", proxyenv); > goto cleanup_url_get; > } > - cookie = malloc(COOKIE_MAX_LEN); > - if (cookie == NULL) > - errx(1, "out of memory"); > - if (b64_ntop(host, strlen(host), cookie, > COOKIE_MAX_LEN) == -1) > - errx(1, "error in base64 encoding"); > - *path = '@'; /* restore @ in proxyurl */ > - /* > - * This removes the password from proxyurl, > - * filling with stars > - */ > - for (host = 1 + strchr(proxyurl + 5, ':'); *host != > '@'; > - host++) > - *host = '*'; > - > + credentials = recode_credentials(host); > host = path + 1; > } > + > path = newline; > } > > @@ -697,7 +677,7 @@ noslash: > if (debug) > fprintf(ttyout, "host %s, port %s, path %s, " > "save as %s, auth %s.\n", > - host, portnum, path, savefile, auth); > + host, portnum, path, savefile, credentials); > #endif /* !SMALL */ > > memset(&hints, 0, sizeof(hints)); > @@ -793,7 +773,7 @@ again: > > #ifndef SMALL > if (proxyenv && sslhost) > - proxy_connect(s, sslhost, cookie); > + proxy_connect(s, sslhost, credentials); > #endif /* !SMALL */ > break; > } > @@ -890,10 +870,11 @@ again: > * Host: directive must use the destination host address for > * the original URI (path). We do not attach it at this moment. > */ > - if (cookie) > + if (credentials) > ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n" > "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n", > - epath, cookie, buf ? buf : "", HTTP_USER_AGENT); > + epath, credentials, buf ? buf : "", > + HTTP_USER_AGENT); > else > ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n", > epath, buf ? buf : "", HTTP_USER_AGENT); > @@ -908,14 +889,14 @@ again: > else > restart_point = 0; > } > - if (auth) { > + if (credentials) { > ftp_printf(fin, ssl, > "GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ", > epath, restart_point ? > "HTTP/1.1\r\nConnection: close" : "HTTP/1.0", > - auth); > - free(auth); > - auth = NULL; > + credentials); > + free(credentials); > + credentials = NULL; > } else > #endif /* SMALL */ > ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath, > @@ -1231,7 +1212,7 @@ cleanup_url_get: > SSL_free(ssl); > } > free(full_host); > - free(auth); > + free(credentials); > #endif /* !SMALL */ > if (fin != NULL) > fclose(fin); > @@ -1240,7 +1221,7 @@ cleanup_url_get: > free(buf); > free(proxyurl); > free(newline); > - free(cookie); > + free(credentials); > return (rval); > } > > @@ -1384,8 +1365,8 @@ bad_ftp_url: > rval = argpos + 1; > continue; > } > - username = urldecode(username); > - pass = urldecode(pass); > + urldecode(username); > + urldecode(pass); > } > > #ifdef INET6 > @@ -1586,36 +1567,47 @@ bad_ftp_url: > return (rval); > } > > -char * > -urldecode(const char *str) > +void > +urldecode(char *str) > { > char *ret, c; > - int i, reallen; > + int i, j, reallen; ret and reallen are unused now. > if (str == NULL) > - return NULL; > - if ((ret = malloc(strlen(str)+1)) == NULL) > - err(1, "Can't allocate memory for URL decoding"); > - for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) { > - c = str[i]; > - if (c == '+') { > - *ret = ' '; > - continue; > - } > - > - /* Cannot use strtol here because next char > - * after %xx may be a digit. > - */ > - if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { > - *ret = hextochar(&str[i+1]); > - i+=2; > - continue; > + return; > + for (i = j = 0; (c = str[i]) != '\0'; i++, j++) { > + if (c == '+') > + c = ' '; > + else if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { > + /* > + * Cannot use strtol here because next char > + * after %xx may be a digit. > + */ > + c = hextochar(&str[i+1]); > + i += 2; > } > - *ret = c; > + str[j] = c; > } > - *ret = '\0'; > + str[j] = c; > +} > + > +char * > +recode_credentials(char *userinfo) > +{ > + char *creds; > + size_t ulen, credsize; > + > + /* url-decode the user and pass */ > + urldecode(userinfo); > > - return ret-reallen; > + ulen = strlen(userinfo); > + credsize = (ulen + 2) / 3 * 4 + 1; > + creds = malloc(credsize); > + if (creds == NULL) > + errx(1, "out of memory"); > + if (b64_ntop(userinfo, ulen, creds, credsize) == -1) > + errx(1, "error in base64 encoding"); > + return (creds); > } > > char >