mirror of
https://github.com/xroche/httrack.git
synced 2026-07-14 12:50:52 +03:00
Compare commits
1 Commits
drop-swf-j
...
phase0-net
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90e804a712 |
48
src/htsftp.c
48
src/htsftp.c
@@ -128,6 +128,31 @@ void launch_ftp(FTPDownloadStruct * params) {
|
|||||||
return 0; \
|
return 0; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bounded split of a hostile-URL "user[:pass]@" prefix (see htsftp.h). */
|
||||||
|
void ftp_split_userpass(const char *src, const char *end, char *user,
|
||||||
|
size_t user_size, char *pass, size_t pass_size) {
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
while (src[n] != '\0' && src[n] != ':') {
|
||||||
|
if (n < user_size - 1)
|
||||||
|
user[n] = src[n];
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
user[n < user_size ? n : user_size - 1] = '\0';
|
||||||
|
pass[0] = '\0';
|
||||||
|
if (src[n] == ':') { // password follows the colon
|
||||||
|
const size_t base = n + 1;
|
||||||
|
size_t k = 0;
|
||||||
|
|
||||||
|
while (&src[base + k + 1] < end && src[base + k] != '\0') {
|
||||||
|
if (k < pass_size - 1)
|
||||||
|
pass[k] = src[base + k];
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
pass[k < pass_size ? k : pass_size - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// la véritable fonction une fois lancées les routines thread/fork
|
// la véritable fonction une fois lancées les routines thread/fork
|
||||||
int run_launch_ftp(FTPDownloadStruct * pStruct) {
|
int run_launch_ftp(FTPDownloadStruct * pStruct) {
|
||||||
lien_back *back = pStruct->pBack;
|
lien_back *back = pStruct->pBack;
|
||||||
@@ -173,24 +198,7 @@ int run_launch_ftp(FTPDownloadStruct * pStruct) {
|
|||||||
while(*real_adr == '/')
|
while(*real_adr == '/')
|
||||||
real_adr++; // sauter /
|
real_adr++; // sauter /
|
||||||
if ((adr = jump_identification(real_adr)) != real_adr) { // user
|
if ((adr = jump_identification(real_adr)) != real_adr) { // user
|
||||||
int i = -1;
|
ftp_split_userpass(real_adr, adr, user, sizeof(user), pass, sizeof(pass));
|
||||||
|
|
||||||
pass[0] = '\0';
|
|
||||||
do {
|
|
||||||
i++;
|
|
||||||
user[i] = real_adr[i];
|
|
||||||
} while((real_adr[i] != ':') && (real_adr[i]));
|
|
||||||
user[i] = '\0';
|
|
||||||
if (real_adr[i] == ':') { // pass
|
|
||||||
int j = -1;
|
|
||||||
|
|
||||||
i++; // oui on saute aussi le :
|
|
||||||
do {
|
|
||||||
j++;
|
|
||||||
pass[j] = real_adr[i + j];
|
|
||||||
} while(((&real_adr[i + j + 1]) < adr) && (real_adr[i + j]));
|
|
||||||
pass[j] = '\0';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Calculer RETR <nom>
|
// Calculer RETR <nom>
|
||||||
{
|
{
|
||||||
@@ -984,8 +992,8 @@ int get_ftp_line(T_SOC soc, char *ptrline, size_t line_size, int timeout) {
|
|||||||
//case 0: break; // pas encore --> erreur (on attend)!
|
//case 0: break; // pas encore --> erreur (on attend)!
|
||||||
case 1:
|
case 1:
|
||||||
HTS_STAT.HTS_TOTAL_RECV += 1; // compter flux entrant
|
HTS_STAT.HTS_TOTAL_RECV += 1; // compter flux entrant
|
||||||
if ((b != 10) && (b != 13))
|
if ((b != 10) && (b != 13) && (i < (int) sizeof(data) - 1))
|
||||||
data[i++] = b;
|
data[i++] = b; // truncate hostile over-long reply lines
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (ptrline)
|
if (ptrline)
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ int back_launch_ftp(FTPDownloadStruct * params);
|
|||||||
int run_launch_ftp(FTPDownloadStruct * params);
|
int run_launch_ftp(FTPDownloadStruct * params);
|
||||||
int send_line(T_SOC soc, const char *data);
|
int send_line(T_SOC soc, const char *data);
|
||||||
int get_ftp_line(T_SOC soc, char *line, size_t line_size, int timeout);
|
int get_ftp_line(T_SOC soc, char *line, size_t line_size, int timeout);
|
||||||
|
/* Split a "user[:pass]@" prefix (end = jump_identification result) into
|
||||||
|
bounded, NUL-terminated user/pass buffers, truncating to fit. */
|
||||||
|
void ftp_split_userpass(const char *src, const char *end, char *user,
|
||||||
|
size_t user_size, char *pass, size_t pass_size);
|
||||||
T_SOC get_datasocket(char *to_send, size_t to_send_size);
|
T_SOC get_datasocket(char *to_send, size_t to_send_size);
|
||||||
int stop_ftp(lien_back * back);
|
int stop_ftp(lien_back * back);
|
||||||
char *linejmp(char *line);
|
char *linejmp(char *line);
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ Please visit our Website: http://www.httrack.com
|
|||||||
/* This file */
|
/* This file */
|
||||||
#include "htsjava.h"
|
#include "htsjava.h"
|
||||||
|
|
||||||
|
/* calloct/freet wrappers */
|
||||||
|
#include "htssafe.h"
|
||||||
|
|
||||||
static int reverse_endian(void) {
|
static int reverse_endian(void) {
|
||||||
int endian = 1;
|
int endian = 1;
|
||||||
|
|
||||||
@@ -204,7 +207,16 @@ static int hts_parse_java(t_hts_callbackarg * carg, httrackp * opt,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tab = (RESP_STRUCT *) calloc(header.count, sizeof(RESP_STRUCT));
|
/* A constant-pool entry is >= 1 byte on disk; reject a count exceeding
|
||||||
|
the file size (hostile .class ~68 MB alloc DoS). */
|
||||||
|
if (!hts_count_fits(header.count, (LLint) fsize(file))) {
|
||||||
|
fclose(fpout);
|
||||||
|
sprintf(str->err_msg,
|
||||||
|
"Invalid constant pool count %u (file len " LLintP ")",
|
||||||
|
(unsigned) header.count, (LLint) fsize(file));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
tab = (RESP_STRUCT *) calloct(header.count, sizeof(RESP_STRUCT));
|
||||||
if (!tab) {
|
if (!tab) {
|
||||||
sprintf(str->err_msg, "Unable to alloc %d bytes",
|
sprintf(str->err_msg, "Unable to alloc %d bytes",
|
||||||
(int) sizeof(RESP_STRUCT));
|
(int) sizeof(RESP_STRUCT));
|
||||||
@@ -230,7 +242,7 @@ static int hts_parse_java(t_hts_callbackarg * carg, httrackp * opt,
|
|||||||
} else { // ++ une erreur est survenue!
|
} else { // ++ une erreur est survenue!
|
||||||
if (strnotempty(str->err_msg) == 0)
|
if (strnotempty(str->err_msg) == 0)
|
||||||
strcpy(str->err_msg, "Internal readtable error");
|
strcpy(str->err_msg, "Internal readtable error");
|
||||||
free(tab);
|
freet(tab);
|
||||||
if (fpout) {
|
if (fpout) {
|
||||||
fclose(fpout);
|
fclose(fpout);
|
||||||
fpout = NULL;
|
fpout = NULL;
|
||||||
@@ -288,7 +300,7 @@ static int hts_parse_java(t_hts_callbackarg * carg, httrackp * opt,
|
|||||||
#if JAVADEBUG
|
#if JAVADEBUG
|
||||||
printf("end\n");
|
printf("end\n");
|
||||||
#endif
|
#endif
|
||||||
free(tab);
|
freet(tab);
|
||||||
if (fpout) {
|
if (fpout) {
|
||||||
fclose(fpout);
|
fclose(fpout);
|
||||||
fpout = NULL;
|
fpout = NULL;
|
||||||
|
|||||||
@@ -456,6 +456,13 @@ static HTS_INLINE HTS_UNUSED const char *htsbuff_str(const htsbuff *b) {
|
|||||||
return b->buf;
|
return b->buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** True if 'count' records of >= 1 byte each fit in 'available' bytes; guards
|
||||||
|
an attacker-controlled count driving a large allocation. */
|
||||||
|
static HTS_INLINE HTS_UNUSED hts_boolean hts_count_fits(size_t count,
|
||||||
|
LLint available) {
|
||||||
|
return (available >= 0 && (LLint) count <= available) ? HTS_TRUE : HTS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Thin aliases over the libc allocator/memcpy (historical "t" suffix); no
|
/* Thin aliases over the libc allocator/memcpy (historical "t" suffix); no
|
||||||
added bounds checking. freet() also NULLs the freed pointer and tolerates
|
added bounds checking. freet() also NULLs the freed pointer and tolerates
|
||||||
NULL. memcpybuff() despite the name is a raw memcpy: the caller owns the
|
NULL. memcpybuff() despite the name is a raw memcpy: the caller owns the
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ Please visit our Website: http://www.httrack.com
|
|||||||
#include "htsdns_selftest.h"
|
#include "htsdns_selftest.h"
|
||||||
#include "htscharset.h"
|
#include "htscharset.h"
|
||||||
#include "htsencoding.h"
|
#include "htsencoding.h"
|
||||||
|
#include "htsftp.h"
|
||||||
#include "htsmd5.h"
|
#include "htsmd5.h"
|
||||||
#if HTS_USEZLIB
|
#if HTS_USEZLIB
|
||||||
#include "htszlib.h"
|
#include "htszlib.h"
|
||||||
@@ -61,6 +62,10 @@ Please visit our Website: http://www.httrack.com
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* very minimalistic internal tests */
|
/* very minimalistic internal tests */
|
||||||
static void basic_selftests(void) {
|
static void basic_selftests(void) {
|
||||||
@@ -1769,6 +1774,75 @@ static int st_robots(httrackp *opt, int argc, char **argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get_ftp_line must bound a hostile, CRLF-less reply into its internal
|
||||||
|
1024-byte buffer; ASan turns the pre-fix overflow into an abort here. */
|
||||||
|
#ifndef _WIN32
|
||||||
|
static int st_ftpline(httrackp *opt, int argc, char **argv) {
|
||||||
|
int sv[2];
|
||||||
|
char line[2048];
|
||||||
|
char flood[4096];
|
||||||
|
|
||||||
|
(void) opt;
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
memset(flood, 'x', sizeof(flood));
|
||||||
|
assertf(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
|
||||||
|
assertf(write(sv[1], "220 ", 4) == 4); // valid 3-digit code
|
||||||
|
assertf(write(sv[1], flood, sizeof(flood)) == (ssize_t) sizeof(flood));
|
||||||
|
assertf(write(sv[1], "\r\n", 2) == 2); // end the line so we return
|
||||||
|
close(sv[1]);
|
||||||
|
line[0] = '\0';
|
||||||
|
get_ftp_line(sv[0], line, sizeof(line), 5);
|
||||||
|
close(sv[0]);
|
||||||
|
printf("ftp-line self-test OK (bounded %d-byte reply)\n",
|
||||||
|
(int) sizeof(flood));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ftp_split_userpass: well-formed split, plus a hostile over-long userinfo
|
||||||
|
that pre-fix overran user[256]/pass[256]. */
|
||||||
|
static int st_ftpuser(httrackp *opt, int argc, char **argv) {
|
||||||
|
char user[256], pass[256];
|
||||||
|
char in[1200];
|
||||||
|
|
||||||
|
(void) opt;
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
{
|
||||||
|
const char ok[] = "bob:secret@host/f"; // '@' at index 10
|
||||||
|
|
||||||
|
ftp_split_userpass(ok, ok + 11, user, sizeof(user), pass, sizeof(pass));
|
||||||
|
assertf(strcmp(user, "bob") == 0);
|
||||||
|
assertf(strcmp(pass, "secret") == 0);
|
||||||
|
}
|
||||||
|
memset(in, 'u', 400);
|
||||||
|
in[400] = ':';
|
||||||
|
memset(in + 401, 'p', 400);
|
||||||
|
in[801] = '@';
|
||||||
|
in[802] = '\0';
|
||||||
|
ftp_split_userpass(in, in + 802, user, sizeof(user), pass, sizeof(pass));
|
||||||
|
assertf(strlen(user) == sizeof(user) - 1);
|
||||||
|
assertf(strlen(pass) == sizeof(pass) - 1);
|
||||||
|
printf("ftp-userpass self-test OK\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hts_count_fits caps the .class constant-pool entry count to the file size,
|
||||||
|
rejecting the ~68 MB-per-file calloc DoS. */
|
||||||
|
static int st_java(httrackp *opt, int argc, char **argv) {
|
||||||
|
(void) opt;
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
assertf(hts_count_fits(10, 1000) == HTS_TRUE);
|
||||||
|
assertf(hts_count_fits(0, 10) == HTS_TRUE);
|
||||||
|
assertf(hts_count_fits(65535, 10) == HTS_FALSE);
|
||||||
|
assertf(hts_count_fits(1, 0) == HTS_FALSE);
|
||||||
|
assertf(hts_count_fits(1, -1) == HTS_FALSE);
|
||||||
|
printf("java constant-pool cap self-test OK\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/* Registry: name -> handler, with a usage hint and a one-line description. */
|
/* Registry: name -> handler, with a usage hint and a one-line description. */
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@@ -1829,6 +1903,12 @@ static const struct selftest_entry {
|
|||||||
"Accept-Encoding advertises gzip+deflate, both decode", st_acceptencoding},
|
"Accept-Encoding advertises gzip+deflate, both decode", st_acceptencoding},
|
||||||
{"robots", "", "robots.txt RFC 9309 Allow/Disallow precedence self-test",
|
{"robots", "", "robots.txt RFC 9309 Allow/Disallow precedence self-test",
|
||||||
st_robots},
|
st_robots},
|
||||||
|
#ifndef _WIN32
|
||||||
|
{"ftp-line", "", "get_ftp_line bounds a hostile FTP reply line",
|
||||||
|
st_ftpline},
|
||||||
|
#endif
|
||||||
|
{"ftp-userpass", "", "ftp_split_userpass bounds URL userinfo", st_ftpuser},
|
||||||
|
{"java", "", "java .class constant-pool count cap self-test", st_java},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void list_selftests(void) {
|
static void list_selftests(void) {
|
||||||
|
|||||||
7
tests/01_engine-ftp-line.test
Executable file
7
tests/01_engine-ftp-line.test
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# get_ftp_line bounds a hostile CRLF-less FTP reply into its 1024-byte buffer.
|
||||||
|
httrack -O /dev/null -#test=ftp-line run | grep -q "ftp-line self-test OK"
|
||||||
7
tests/01_engine-ftp-userpass.test
Executable file
7
tests/01_engine-ftp-userpass.test
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ftp_split_userpass bounds an over-long user:pass@ from a hostile ftp:// URL.
|
||||||
|
httrack -O /dev/null -#test=ftp-userpass run | grep -q "ftp-userpass self-test OK"
|
||||||
7
tests/01_engine-java.test
Executable file
7
tests/01_engine-java.test
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# .class constant-pool count is capped to the file size (calloc DoS).
|
||||||
|
httrack -O /dev/null -#test=java run | grep -q "java constant-pool cap self-test OK"
|
||||||
@@ -35,10 +35,13 @@ TESTS = \
|
|||||||
01_engine-entities.test \
|
01_engine-entities.test \
|
||||||
01_engine-filelist.test \
|
01_engine-filelist.test \
|
||||||
01_engine-filter.test \
|
01_engine-filter.test \
|
||||||
|
01_engine-ftp-line.test \
|
||||||
|
01_engine-ftp-userpass.test \
|
||||||
01_engine-hashtable.test \
|
01_engine-hashtable.test \
|
||||||
01_engine-idna.test \
|
01_engine-idna.test \
|
||||||
01_engine-escape-room.test \
|
01_engine-escape-room.test \
|
||||||
01_engine-inplace-escape.test \
|
01_engine-inplace-escape.test \
|
||||||
|
01_engine-java.test \
|
||||||
01_engine-makeindex.test \
|
01_engine-makeindex.test \
|
||||||
01_engine-mime.test \
|
01_engine-mime.test \
|
||||||
01_engine-parse.test \
|
01_engine-parse.test \
|
||||||
|
|||||||
Reference in New Issue
Block a user