martin 97/11/25 13:34:13
Modified: src/modules/proxy proxy_ftp.c Log: Fix for the buggy behavior of proxy_ftp.c which would return incorrect links for ".." directory entries , depending on the fact whether the request contained a trailing slash or not. Also add a small "feature": the full directory path broken down into separate clickable path components which makes traversal of public ftp servers much easier. Future improvements in this area include: 0) add appropriate cache control headers to prevent caches from keeping sensitive documents (user:password@) 1) make clickable header optional ("ProxyOptions +ClickHeader" 2) return an external redirection if proxy_ftp detects a directory but the request did not contain a trailing slash (that would make the current BASE HREF= header field obsolete which in turn would allow the removal of the last occurrence of the "user:password@" prefix from the generated document). 3) return a 401 authenticate reply if the addressed ftp server requires a user name (other than "anonymous") or a password (other than "apache@"), unless the request contained a WWW-Authenticate: header already. 4) Optionally convert requests ftp://user:[EMAIL PROTECTED]/ on-the-fly into requests of the form ftp://host/ plus the appropriate WWW-Authenticate: header when forwarding to an upstream proxy, lessening the danger of publishing passwords thru use of log file analyzers. Reviewed by: Ken Coar, Roy T. Fielding Revision Changes Path 1.42 +96 -67 apachen/src/modules/proxy/proxy_ftp.c Index: proxy_ftp.c =================================================================== RCS file: /home/cvs/apachen/src/modules/proxy/proxy_ftp.c,v retrieving revision 1.41 retrieving revision 1.42 diff -u -u -r1.41 -r1.42 --- proxy_ftp.c 1997/10/22 20:30:07 1.41 +++ proxy_ftp.c 1997/11/25 21:34:12 1.42 @@ -114,11 +114,11 @@ port = DEFAULT_FTP_PORT; err = proxy_canon_netloc(p, &url, &user, &password, &host, &port); if (err) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; if (user != NULL && !ftp_check_string(user)) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; if (password != NULL && !ftp_check_string(password)) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; /* now parse path/parameters args, according to rfc1738 */ /* N.B. if this isn't a true proxy request, then the URL path @@ -131,28 +131,28 @@ *(strp++) = '\0'; parms = proxy_canonenc(p, strp, strlen(strp), enc_parm, r->proxyreq); if (parms == NULL) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; } else parms = ""; path = proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq); if (path == NULL) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; if (!ftp_check_string(path)) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; if (!r->proxyreq && r->args != NULL) { if (strp != NULL) { strp = proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1); if (strp == NULL) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; parms = pstrcat(p, parms, "?", strp, NULL); } else { strp = proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1); if (strp == NULL) - return BAD_REQUEST; + return HTTP_BAD_REQUEST; path = pstrcat(p, path, "?", strp, NULL); } r->args = NULL; @@ -180,7 +180,7 @@ */ static int ftp_getrc(BUFF *f) { - int i, len, status; + int len, status; char linebuff[100], buff[5]; len = bgets(linebuff, 100, f); @@ -194,7 +194,7 @@ status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; if (linebuff[len - 1] != '\n') { - i = bskiplf(f); + (void)bskiplf(f); } /* skip continuation lines */ @@ -206,7 +206,7 @@ if (len == -1) return -1; if (linebuff[len - 1] != '\n') { - i = bskiplf(f); + (void)bskiplf(f); } } while (memcmp(linebuff, buff, 4) != 0); } @@ -240,27 +240,91 @@ char buf2[IOBUFSIZE]; char *filename; char *tempurl; - char *newurlptr; int searchidx = 0; char *searchptr = NULL; int firstfile = 1; char urlptr[HUGE_STRING_LEN]; - long total_bytes_sent; + unsigned long total_bytes_sent = 0; register int n, o, w; + int hostlen; conn_rec *con = r->connection; + char *dir, *path, *reldir, *site, *psite; tempurl = pstrdup(r->pool, url); - if ((n = strcspn(tempurl, "@")) != strlen(tempurl)) { /* hide user/passwd */ + + (void)decodeenc(tempurl); + + /* Determine length of "scheme://site" prefix */ + for (hostlen=0; tempurl[hostlen]!='/'; ++hostlen) + continue; + if (tempurl[hostlen] == '/' && tempurl[hostlen+1] == '/') { + for (hostlen+=2; tempurl[hostlen]!='/' && tempurl[hostlen]!='?'; ++hostlen) + continue; + } else { + hostlen = 0; + } + + /* Save "scheme://site" prefix */ + site = psite = pstrndup(r->pool, tempurl, hostlen); + + if ((n = strcspn(tempurl, "@")) != strlen(tempurl) && n < hostlen) { /* hide user/passwd */ memmove(tempurl + (n - 5), tempurl, 6); tempurl += n - 5; /* leave room for ftp:// */ + hostlen -= (n-5); + + /* Save "scheme://site" prefix without user/password */ + site = pstrndup(r->pool, tempurl, hostlen); } - n = decodeenc(tempurl); - ap_snprintf(buf, sizeof(buf), "<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>Directory %s</H1><HR><PRE>", tempurl, tempurl); - bwrite(con->client, buf, strlen(buf)); + /* Save "scheme://site" prefix */ + site = pstrndup(r->pool, tempurl, hostlen); + + /* Copy path, strip (all except the last) trailing slashes */ + path = dir = pstrcat(r->pool, tempurl+hostlen, "/", NULL); + while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/') + path[n-1] = '\0'; + + /* print "ftp://host/" */ + ap_snprintf(buf, sizeof(buf), "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n" + "<HTML><HEAD><TITLE>%s</TITLE>\n" + "<BASE HREF=\"%s%s\"></HEAD>\n" + "<BODY><H2>Directory of " + "<A HREF=\"/\">%s</A>/", + tempurl, psite, path, site, site); + bputs(buf, con->client); if (f2 != NULL) - bwrite(f2, buf, strlen(buf)); - total_bytes_sent = strlen(buf); + bputs(buf, f2); + total_bytes_sent += strlen(buf); + + while ((dir = strchr(dir+1, '/')) != NULL) + { + *dir = '\0'; + if ((reldir = strrchr(path+1, '/'))==NULL) + reldir = path+1; + else + ++reldir; + /* print "path/" component */ + ap_snprintf(buf, sizeof(buf), "<A HREF=\"/%s/\">%s</A>/", path+1, reldir); + bputs(buf, con->client); + if (f2 != NULL) + bputs(buf, f2); + total_bytes_sent += strlen(buf); + *dir = '/'; + } + ap_snprintf(buf, sizeof(buf), "</H2>\n<HR><PRE>"); + bputs(buf, con->client); + if (f2 != NULL) + bputs(buf, f2); + total_bytes_sent += strlen(buf); + + for (hostlen=0; url[hostlen]!='/'; ++hostlen) + continue; + if (url[hostlen] == '/' && url[hostlen+1] == '/') { + for (hostlen+=2; url[hostlen]!='/' && url[hostlen]!='?'; ++hostlen) + continue; + } else + hostlen = 0; + while (!con->aborted) { n = bgets(buf, IOBUFSIZE, f); if (n == -1) { /* input error */ @@ -270,18 +334,18 @@ } if (n == 0) break; /* EOF */ - if (buf[0] == 'l') { - char *link_ptr; + if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) { + char *link_ptr = filename; - link_ptr = strstr(buf, " -> "); - filename = link_ptr; do filename--; while (filename[0] != ' '); *(filename++) = 0; *(link_ptr++) = 0; - ap_snprintf(urlptr, sizeof(urlptr), "%s%s%s", url, (url[strlen(url) - 1] == '/' ? "" : "/"), filename); - ap_snprintf(buf2, sizeof(urlptr), "%s <A HREF=\"%s\">%s %s</A>\015\012", buf, urlptr, filename, link_ptr); + if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n') + link_ptr[n - 1] = '\0'; + ap_snprintf(urlptr, sizeof(urlptr), "%s%s%s", url+hostlen, (url[strlen(url) - 1] == '/' ? "" : "/"), filename); + ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s %s</A>\n", buf, filename, filename, link_ptr); strncpy(buf, buf2, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; n = strlen(buf); @@ -312,48 +376,12 @@ } /* Special handling for '.' and '..' */ - if (!strcmp(filename, ".")) { - ap_snprintf(urlptr, sizeof(urlptr), "%s", url); - ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename); - } - else if (!strcmp(filename, "..")) { - char temp[200]; - char newpath[200]; - char *method, *host, *path, *newfile; - - strncpy(temp, url, sizeof(temp) - 1); - temp[sizeof(temp) - 1] = '\0'; - method = temp; - - host = strchr(method, ':'); - if (host == NULL) - host = ""; - else - *(host++) = 0; - host++; - host++; - - path = strchr(host, '/'); - if (path == NULL) - path = ""; - else - *(path++) = 0; - - strncpy(newpath, path, sizeof(newpath) - 1); - newpath[sizeof(newpath) - 1] = '\0'; - newfile = strrchr(newpath, '/'); - if (newfile) - *(newfile) = 0; - else - newpath[0] = 0; - - ap_snprintf(urlptr, sizeof(urlptr), "%s://%s/%s", method, host, newpath); - ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename); + if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') { + ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s/\">%s</A>\n", + buf, filename, filename); } else { - ap_snprintf(urlptr, sizeof(urlptr), "%s%s%s", url, (url[strlen(url) - 1] == '/' ? "" : "/"), filename); - newurlptr = encode_space(r, urlptr); - ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, newurlptr, filename); + ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\n", buf, filename, filename); } strncpy(buf, buf2, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; @@ -376,9 +404,10 @@ o += w; } } - bputs("</PRE><HR></BODY></HTML>\015\012", con->client); + strcpy (buf, "</PRE><HR></BODY></HTML>\n"); + bputs(buf, con->client); if (f2 != NULL) - bputs("</PRE><HR></BODY></HTML>\015\012", f2); + bputs(buf, f2); total_bytes_sent += strlen(buf); bflush(con->client);