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 <
> > [email protected]> wrote:
...
> > Sorry, the patch is incorrect; per RFC 3986, the parser is correct to 
> > split the authority on the first '@'.  The correct method to include 
> > an '@' in the userinfo part is to percent-encode it as %40.
> 
> Thanks for pointing me the good RFC for the format of userinfo in URI.
> 
> But, I am not sure to understand *where* is located my problem: if it is 
> a problem in ftp(1), or a problem in specific service I try to used with 
> ftp (dyndns server, the http reply come from ngnix server).
> 
> Currently ftp(1) manage http, https with url_get function. The basic
> auth implemented here do *not* try to decode any urlencoded information.
> 
> So pass "user%40example.com:password" as userinfo in URI will result to
> base64 "user%40example.com:password" (and not
> "[email protected]:password").
...
> 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.

Here's a patch to do that.

Also:
 - eliminates the COOKIE_MAX_LEN constant (if they can fit it on the 
   command line or in their environment, surely we can malloc the base64 
   version)
 - renames the variable with user:pass from "cookie" to "credentials"
 - empty password isn't an error
 - add a boolean ishttpurl so that we don't have to do strcmps on the schema
   that we just set
 - when looping across multiple ftp:// urls on the command line, don't
   leak the username/password memory


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;
 
        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

Reply via email to