diff --git a/Recipe b/Recipe index 89609df6..0bc27c7f 100644 --- a/Recipe +++ b/Recipe @@ -125,6 +125,12 @@ # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # +# - XFLAGS=/DFUZZING +# Builds a version of PuTTY with some tweaks to make fuzz testing +# easier: the SSH random number generator is replaced by one that +# always returns the same thing. Note that this makes SSH +# completely insecure -- a FUZZING build should never be used to +# connect to a real server. !end # ------------------------------------------------------------ @@ -310,3 +316,6 @@ pageant : [X] uxpgnt uxagentc pageant sshrsa sshpubk sshdes sshbn sshmd5 PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH + ux_x11 uxpty uxsignal testback putty.icns info.plist + +fuzzterm : [U] UXTERM CHARSET misc uxmisc uxucs fuzzterm time settings + + uxstore be_none diff --git a/fuzzterm.c b/fuzzterm.c new file mode 100644 index 00000000..0da3b68d --- /dev/null +++ b/fuzzterm.c @@ -0,0 +1,185 @@ +#include +#include +#include + +#define PUTTY_DO_GLOBALS +#include "putty.h" +#include "terminal.h" + +int main(int argc, char **argv) +{ + char blk[512]; + size_t len; + Terminal *term; + Conf *conf; + struct unicode_data ucsdata; + + conf = conf_new(); + do_defaults(NULL, conf); + init_ucs(&ucsdata, conf_get_str(conf, CONF_line_codepage), + conf_get_int(conf, CONF_utf8_override), + CS_NONE, conf_get_int(conf, CONF_vtmode)); + + term = term_init(conf, &ucsdata, NULL); + term_size(term, 24, 80, 10000); + term->ldisc = NULL; + /* Tell american fuzzy lop that this is a good place to fork. */ +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + while (!feof(stdin)) { + len = fread(blk, 1, sizeof(blk), stdin); + term_data(term, 0, blk, len); + } + term_update(term); + return 0; +} + +int from_backend(void *frontend, int is_stderr, const char *data, int len) +{ return 0; } + +/* functions required by terminal.c */ + +void request_resize(void *frontend, int x, int y) { } +void do_text(Context ctx, int x, int y, wchar_t * text, int len, + unsigned long attr, int lattr) +{ + int i; + + printf("TEXT[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y); + for (i = 0; i < len; i++) { + printf(" %x", (unsigned)text[i]); + } + printf("\n"); +} +void do_cursor(Context ctx, int x, int y, wchar_t * text, int len, + unsigned long attr, int lattr) +{ + int i; + + printf("CURS[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y); + for (i = 0; i < len; i++) { + printf(" %x", (unsigned)text[i]); + } + printf("\n"); +} +int char_width(Context ctx, int uc) { return 1; } +void set_title(void *frontend, char *t) { } +void set_icon(void *frontend, char *t) { } +void set_sbar(void *frontend, int a, int b, int c) { } + +void ldisc_send(void *handle, const char *buf, int len, int interactive) {} +void ldisc_echoedit_update(void *handle) {} +Context get_ctx(void *frontend) { + static char x; + + return &x; +} +void free_ctx(Context ctx) { } +void palette_set(void *frontend, int a, int b, int c, int d) { } +void palette_reset(void *frontend) { } +void write_clip(void *frontend, wchar_t *a, int *b, int c, int d) { } +void get_clip(void *frontend, wchar_t **w, int *i) { } +void set_raw_mouse_mode(void *frontend, int m) { } +void request_paste(void *frontend) { } +void do_beep(void *frontend, int a) { } +void sys_cursor(void *frontend, int x, int y) { } +void fatalbox(const char *fmt, ...) { exit(0); } +void modalfatalbox(const char *fmt, ...) { exit(0); } +void nonfatal(const char *fmt, ...) { } + +void set_iconic(void *frontend, int iconic) { } +void move_window(void *frontend, int x, int y) { } +void set_zorder(void *frontend, int top) { } +void refresh_window(void *frontend) { } +void set_zoomed(void *frontend, int zoomed) { } +int is_iconic(void *frontend) { return 0; } +void get_window_pos(void *frontend, int *x, int *y) { *x = 0; *y = 0; } +void get_window_pixels(void *frontend, int *x, int *y) { *x = 0; *y = 0; } +char *get_window_title(void *frontend, int icon) { return "moo"; } + +/* needed by timing.c */ +void timer_change_notify(unsigned long next) { } + +/* needed by config.c and sercfg.c */ + +void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton) { } +int dlg_radiobutton_get(union control *ctrl, void *dlg) { return 0; } +void dlg_checkbox_set(union control *ctrl, void *dlg, int checked) { } +int dlg_checkbox_get(union control *ctrl, void *dlg) { return 0; } +void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) { } +char *dlg_editbox_get(union control *ctrl, void *dlg) { return dupstr("moo"); } +void dlg_listbox_clear(union control *ctrl, void *dlg) { } +void dlg_listbox_del(union control *ctrl, void *dlg, int index) { } +void dlg_listbox_add(union control *ctrl, void *dlg, char const *text) { } +void dlg_listbox_addwithid(union control *ctrl, void *dlg, + char const *text, int id) { } +int dlg_listbox_getid(union control *ctrl, void *dlg, int index) { return 0; } +int dlg_listbox_index(union control *ctrl, void *dlg) { return -1; } +int dlg_listbox_issel(union control *ctrl, void *dlg, int index) { return 0; } +void dlg_listbox_select(union control *ctrl, void *dlg, int index) { } +void dlg_text_set(union control *ctrl, void *dlg, char const *text) { } +void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { } +Filename *dlg_filesel_get(union control *ctrl, void *dlg) { return NULL; } +void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn) { } +FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { return NULL; } +void dlg_update_start(union control *ctrl, void *dlg) { } +void dlg_update_done(union control *ctrl, void *dlg) { } +void dlg_set_focus(union control *ctrl, void *dlg) { } +void dlg_label_change(union control *ctrl, void *dlg, char const *text) { } +union control *dlg_last_focused(union control *ctrl, void *dlg) { return NULL; } +void dlg_beep(void *dlg) { } +void dlg_error_msg(void *dlg, const char *msg) { } +void dlg_end(void *dlg, int value) { } +void dlg_coloursel_start(union control *ctrl, void *dlg, + int r, int g, int b) { } +int dlg_coloursel_results(union control *ctrl, void *dlg, + int *r, int *g, int *b) { return 0; } +void dlg_refresh(union control *ctrl, void *dlg) { } + +/* miscellany */ +void logevent(void *frontend, const char *msg) { } +int askappend(void *frontend, Filename *filename, + void (*callback)(void *ctx, int result), void *ctx) { return 0; } + +const char *const appname = "FuZZterm"; +const int ngsslibs = 0; +const char *const gsslibnames[0] = { }; +const struct keyvalwhere gsslibkeywords[0] = { }; + +/* + * Default settings that are specific to Unix plink. + */ +char *platform_default_s(const char *name) +{ + if (!strcmp(name, "TermType")) + return dupstr(getenv("TERM")); + if (!strcmp(name, "SerialLine")) + return dupstr("/dev/ttyS0"); + return NULL; +} + +int platform_default_i(const char *name, int def) +{ + return def; +} + +FontSpec *platform_default_fontspec(const char *name) +{ + return fontspec_new(""); +} + +Filename *platform_default_filename(const char *name) +{ + if (!strcmp(name, "LogFileName")) + return filename_from_str("putty.log"); + else + return filename_from_str(""); +} + +char *x_get_default(const char *key) +{ + return NULL; /* this is a stub */ +} + + diff --git a/misc.c b/misc.c index b1bfb361..6af441c3 100644 --- a/misc.c +++ b/misc.c @@ -1064,7 +1064,7 @@ int match_ssh_id(int stringlen, const void *string, const char *id) void *get_ssh_string(int *datalen, const void **data, int *stringlen) { void *ret; - int len; + unsigned int len; if (*datalen < 4) return NULL; diff --git a/putty.h b/putty.h index c854d944..3ccd96cb 100644 --- a/putty.h +++ b/putty.h @@ -291,7 +291,7 @@ enum { * Proxy types. */ PROXY_NONE, PROXY_SOCKS4, PROXY_SOCKS5, - PROXY_HTTP, PROXY_TELNET, PROXY_CMD + PROXY_HTTP, PROXY_TELNET, PROXY_CMD, PROXY_FUZZ }; enum { diff --git a/ssh.c b/ssh.c index 1f8a214b..de8259ed 100644 --- a/ssh.c +++ b/ssh.c @@ -3017,6 +3017,10 @@ static void ssh_send_verstring(Ssh ssh, const char *protoname, char *svers) } ssh_fix_verstring(verstring + strlen(protoname)); +#ifdef FUZZING + /* FUZZING make PuTTY insecure, so make live use difficult. */ + verstring[0] = 'I'; +#endif if (ssh->version == 2) { size_t len; @@ -4050,6 +4054,9 @@ static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen, "rsa", keystr, fingerprint, ssh_dialog_callback, ssh); sfree(keystr); +#ifdef FUZZING + s->dlgret = 1; +#endif if (s->dlgret < 0) { do { crReturn(0); @@ -6487,6 +6494,11 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, /* List encryption algorithms (client->server then server->client). */ for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) { warn = FALSE; +#ifdef FUZZING + alg = ssh2_kexinit_addalg(s->kexlists[k], "none"); + alg->u.cipher.cipher = NULL; + alg->u.cipher.warn = warn; +#endif /* FUZZING */ for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) warn = TRUE; @@ -6500,6 +6512,11 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, } /* List MAC algorithms (client->server then server->client). */ for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) { +#ifdef FUZZING + alg = ssh2_kexinit_addalg(s->kexlists[j], "none"); + alg->u.mac.mac = NULL; + alg->u.mac.etm = FALSE; +#endif /* FUZZING */ for (i = 0; i < s->nmacs; i++) { alg = ssh2_kexinit_addalg(s->kexlists[j], s->maclist[i]->name); alg->u.mac.mac = s->maclist[i]; @@ -6772,8 +6789,8 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, { int csbits, scbits; - csbits = s->cscipher_tobe->real_keybits; - scbits = s->sccipher_tobe->real_keybits; + csbits = s->cscipher_tobe ? s->cscipher_tobe->real_keybits : 0; + scbits = s->sccipher_tobe ? s->sccipher_tobe->real_keybits : 0; s->nbits = (csbits > scbits ? csbits : scbits); } /* The keys only have hlen-bit entropy, since they're based on @@ -7109,12 +7126,18 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, dmemdump(s->exchange_hash, ssh->kex->hash->hlen); #endif - if (!s->hkey || - !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, + if (!s->hkey) { + bombout(("Server's host key is invalid")); + crStopV; + } + + if (!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, (char *)s->exchange_hash, ssh->kex->hash->hlen)) { +#ifndef FUZZING bombout(("Server's host key did not match the signature supplied")); - crStopV; + crStopV;f +#endif } s->keystr = ssh->hostkey->fmtkey(s->hkey); @@ -7139,6 +7162,9 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, ssh->hostkey->keytype, s->keystr, s->fingerprint, ssh_dialog_callback, ssh); +#ifdef FUZZING + s->dlgret = 1; +#endif if (s->dlgret < 0) { do { crReturnV; @@ -7171,8 +7197,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, * the one we saw before. */ if (strcmp(ssh->hostkey_str, s->keystr)) { +#ifndef FUZZING bombout(("Host key was different in repeat key exchange")); crStopV; +#endif } sfree(s->keystr); } @@ -7206,13 +7234,14 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, if (ssh->cs_cipher_ctx) ssh->cscipher->free_context(ssh->cs_cipher_ctx); ssh->cscipher = s->cscipher_tobe; - ssh->cs_cipher_ctx = ssh->cscipher->make_context(); + if (ssh->cscipher) ssh->cs_cipher_ctx = ssh->cscipher->make_context(); if (ssh->cs_mac_ctx) ssh->csmac->free_context(ssh->cs_mac_ctx); ssh->csmac = s->csmac_tobe; ssh->csmac_etm = s->csmac_etm_tobe; - ssh->cs_mac_ctx = ssh->csmac->make_context(ssh->cs_cipher_ctx); + if (ssh->csmac) + ssh->cs_mac_ctx = ssh->csmac->make_context(ssh->cs_cipher_ctx); if (ssh->cs_comp_ctx) ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx); @@ -7223,7 +7252,7 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, * Set IVs on client-to-server keys. Here we use the exchange * hash from the _first_ key exchange. */ - { + if (ssh->cscipher) { unsigned char *key; key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'C', @@ -7237,6 +7266,9 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, ssh->cscipher->setiv(ssh->cs_cipher_ctx, key); smemclr(key, ssh->cscipher->blksize); sfree(key); + } + if (ssh->csmac) { + unsigned char *key; key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'E', ssh->csmac->keylen); @@ -7245,12 +7277,14 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, sfree(key); } - logeventf(ssh, "Initialised %.200s client->server encryption", - ssh->cscipher->text_name); - logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s%s", - ssh->csmac->text_name, - ssh->csmac_etm ? " (in ETM mode)" : "", - ssh->cscipher->required_mac ? " (required by cipher)" : ""); + if (ssh->cscipher) + logeventf(ssh, "Initialised %.200s client->server encryption", + ssh->cscipher->text_name); + if (ssh->csmac) + logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s%s", + ssh->csmac->text_name, + ssh->csmac_etm ? " (in ETM mode)" : "", + ssh->cscipher->required_mac ? " (required by cipher)" : ""); if (ssh->cscomp->text_name) logeventf(ssh, "Initialised %s compression", ssh->cscomp->text_name); @@ -7278,14 +7312,18 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, */ if (ssh->sc_cipher_ctx) ssh->sccipher->free_context(ssh->sc_cipher_ctx); - ssh->sccipher = s->sccipher_tobe; - ssh->sc_cipher_ctx = ssh->sccipher->make_context(); + if (ssh->sccipher) { + ssh->sccipher = s->sccipher_tobe; + ssh->sc_cipher_ctx = ssh->sccipher->make_context(); + } if (ssh->sc_mac_ctx) ssh->scmac->free_context(ssh->sc_mac_ctx); - ssh->scmac = s->scmac_tobe; - ssh->scmac_etm = s->scmac_etm_tobe; - ssh->sc_mac_ctx = ssh->scmac->make_context(ssh->sc_cipher_ctx); + if (ssh->scmac) { + ssh->scmac = s->scmac_tobe; + ssh->scmac_etm = s->scmac_etm_tobe; + ssh->sc_mac_ctx = ssh->scmac->make_context(ssh->sc_cipher_ctx); + } if (ssh->sc_comp_ctx) ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx); @@ -7296,7 +7334,7 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, * Set IVs on server-to-client keys. Here we use the exchange * hash from the _first_ key exchange. */ - { + if (ssh->sccipher) { unsigned char *key; key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'D', @@ -7310,6 +7348,9 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, ssh->sccipher->setiv(ssh->sc_cipher_ctx, key); smemclr(key, ssh->sccipher->blksize); sfree(key); + } + if (ssh->scmac) { + unsigned char *key; key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'F', ssh->scmac->keylen); @@ -7317,12 +7358,14 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, smemclr(key, ssh->scmac->keylen); sfree(key); } - logeventf(ssh, "Initialised %.200s server->client encryption", - ssh->sccipher->text_name); - logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s%s", - ssh->scmac->text_name, - ssh->scmac_etm ? " (in ETM mode)" : "", - ssh->sccipher->required_mac ? " (required by cipher)" : ""); + if (ssh->sccipher) + logeventf(ssh, "Initialised %.200s server->client encryption", + ssh->sccipher->text_name); + if (ssh->scmac) + logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s%s", + ssh->scmac->text_name, + ssh->scmac_etm ? " (in ETM mode)" : "", + ssh->sccipher->required_mac ? " (required by cipher)" : ""); if (ssh->sccomp->text_name) logeventf(ssh, "Initialised %s decompression", ssh->sccomp->text_name); diff --git a/sshbn.c b/sshbn.c index fd9e5c0a..7aa10697 100644 --- a/sshbn.c +++ b/sshbn.c @@ -1142,8 +1142,7 @@ Bignum bignum_from_bytes(const unsigned char *data, int nbytes) (BignumInt)byte << (8*i % BIGNUM_INT_BITS); } - while (result[0] > 1 && result[result[0]] == 0) - result[0]--; + bn_restore_invariant(result); return result; } @@ -1165,8 +1164,7 @@ Bignum bignum_from_bytes_le(const unsigned char *data, int nbytes) (BignumInt)byte << (8*i % BIGNUM_INT_BITS); } - while (result[0] > 1 && result[result[0]] == 0) - result[0]--; + bn_restore_invariant(result); return result; } @@ -1311,9 +1309,9 @@ int bignum_bit(Bignum bn, int i) */ void bignum_set_bit(Bignum bn, int bitnum, int value) { - if (bitnum < 0 || bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) - abort(); /* beyond the end */ - else { + if (bitnum < 0 || bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) { + if (value) abort(); /* beyond the end */ + } else { int v = bitnum / BIGNUM_INT_BITS + 1; BignumInt mask = (BignumInt)1 << (bitnum % BIGNUM_INT_BITS); if (value) diff --git a/sshecc.c b/sshecc.c index 5f170215..3912c5f1 100644 --- a/sshecc.c +++ b/sshecc.c @@ -1648,6 +1648,7 @@ static int decodepoint_ed(const char *p, int length, struct ec_point *point) /* Read x bit and then reset it */ negative = bignum_bit(point->y, point->curve->fieldBits - 1); bignum_set_bit(point->y, point->curve->fieldBits - 1, 0); + bn_restore_invariant(point->y); /* Get the x from the y */ point->x = ecp_edx(point->curve, point->y); @@ -1770,6 +1771,7 @@ static void *ecdsa_newkey(const struct ssh_signkey *self, /* Curve name is duplicated for Weierstrass form */ if (curve->type == EC_WEIERSTRASS) { getstring(&data, &len, &p, &slen); + if (!p) return NULL; if (!match_ssh_id(slen, p, curve->name)) return NULL; } @@ -1781,11 +1783,11 @@ static void *ecdsa_newkey(const struct ssh_signkey *self, ec->publicKey.x = NULL; ec->publicKey.y = NULL; ec->publicKey.z = NULL; + ec->privateKey = NULL; if (!getmppoint(&data, &len, &ec->publicKey)) { ecdsa_freekey(ec); return NULL; } - ec->privateKey = NULL; if (!ec->publicKey.x || !ec->publicKey.y || bignum_cmp(ec->publicKey.x, curve->p) >= 0 || @@ -2266,6 +2268,7 @@ static int ecdsa_verifysig(void *key, const char *sig, int siglen, } getstring(&sig, &siglen, &p, &slen); + if (!p) return 0; if (ec->publicKey.curve->type == EC_EDWARDS) { struct ec_point *r; Bignum s, h; diff --git a/sshrand.c b/sshrand.c index ead39a9b..0fbefb48 100644 --- a/sshrand.c +++ b/sshrand.c @@ -45,8 +45,23 @@ struct RandPool { int stir_pending; }; -static struct RandPool pool; int random_active = 0; + +#ifdef FUZZING +/* + * Special dummy version of the RNG for use when fuzzing. + */ +void random_add_noise(void *noise, int length) { } +void random_add_heavynoise(void *noise, int length) { } +void random_ref(void) { } +void random_unref(void) { } +int random_byte(void) +{ + return 0x45; /* Chosen by eight fair coin tosses */ +} +void random_get_savedata(void **data, int *len) { } +#else /* !FUZZING */ +static struct RandPool pool; long next_noise_collection; #ifdef RANDOM_DIAGNOSTICS @@ -326,3 +341,4 @@ void random_get_savedata(void **data, int *len) *data = buf; random_stir(); } +#endif diff --git a/terminal.c b/terminal.c index c5fdeeb0..26a3f0c9 100644 --- a/terminal.c +++ b/terminal.c @@ -4725,7 +4725,7 @@ static void term_out(Terminal *term) } term_print_flush(term); - if (term->logflush) + if (term->logflush && term->logctx) logflush(term->logctx); } diff --git a/unix/uxplink.c b/unix/uxplink.c index 8351a6ea..b8c55489 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -25,7 +25,7 @@ #define MAX_STDIN_BACKLOG 4096 -void *logctx; +static void *logctx; static struct termios orig_termios; @@ -690,6 +690,10 @@ int main(int argc, char **argv) } } else if (!strcmp(p, "-shareexists")) { just_test_share_exists = TRUE; + } else if (!strcmp(p, "-fuzznet")) { + conf_set_int(conf, CONF_proxy_type, PROXY_FUZZ); + conf_set_str(conf, CONF_proxy_telnet_command, + "%host"); } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = 1; @@ -988,6 +992,11 @@ int main(int argc, char **argv) /* nodelay is only useful if stdin is a terminal device */ int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && isatty(0); + /* This is a good place for a fuzzer to fork us. */ +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + error = back->init(NULL, &backhandle, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), diff --git a/unix/uxproxy.c b/unix/uxproxy.c index 0399245d..8c916dc9 100644 --- a/unix/uxproxy.c +++ b/unix/uxproxy.c @@ -250,13 +250,12 @@ Socket platform_new_connection(SockAddr addr, const char *hostname, }; Local_Proxy_Socket ret; - int to_cmd_pipe[2], from_cmd_pipe[2], pid; + int to_cmd_pipe[2], from_cmd_pipe[2], pid, proxytype; - if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) + proxytype = conf_get_int(conf, CONF_proxy_type); + if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ) return NULL; - cmd = format_telnet_command(addr, port, conf); - ret = snew(struct Socket_localproxy_tag); ret->fn = &socket_fn_table; ret->plug = plug; @@ -266,46 +265,65 @@ Socket platform_new_connection(SockAddr addr, const char *hostname, bufchain_init(&ret->pending_input_data); bufchain_init(&ret->pending_output_data); - /* - * Create the pipes to the proxy command, and spawn the proxy - * command process. - */ - if (pipe(to_cmd_pipe) < 0 || - pipe(from_cmd_pipe) < 0) { - ret->error = dupprintf("pipe: %s", strerror(errno)); - sfree(cmd); - return (Socket)ret; - } - cloexec(to_cmd_pipe[1]); - cloexec(from_cmd_pipe[0]); + if (proxytype == PROXY_CMD) { + cmd = format_telnet_command(addr, port, conf); - pid = fork(); + /* + * Create the pipes to the proxy command, and spawn the proxy + * command process. + */ + if (pipe(to_cmd_pipe) < 0 || + pipe(from_cmd_pipe) < 0) { + ret->error = dupprintf("pipe: %s", strerror(errno)); + sfree(cmd); + return (Socket)ret; + } + cloexec(to_cmd_pipe[1]); + cloexec(from_cmd_pipe[0]); + + pid = fork(); + + if (pid < 0) { + ret->error = dupprintf("fork: %s", strerror(errno)); + sfree(cmd); + return (Socket)ret; + } else if (pid == 0) { + close(0); + close(1); + dup2(to_cmd_pipe[0], 0); + dup2(from_cmd_pipe[1], 1); + close(to_cmd_pipe[0]); + close(from_cmd_pipe[1]); + noncloexec(0); + noncloexec(1); + execl("/bin/sh", "sh", "-c", cmd, (void *)NULL); + _exit(255); + } + + sfree(cmd); - if (pid < 0) { - ret->error = dupprintf("fork: %s", strerror(errno)); - sfree(cmd); - return (Socket)ret; - } else if (pid == 0) { - close(0); - close(1); - dup2(to_cmd_pipe[0], 0); - dup2(from_cmd_pipe[1], 1); close(to_cmd_pipe[0]); close(from_cmd_pipe[1]); - noncloexec(0); - noncloexec(1); - execl("/bin/sh", "sh", "-c", cmd, (void *)NULL); - _exit(255); + + ret->to_cmd = to_cmd_pipe[1]; + ret->from_cmd = from_cmd_pipe[0]; + } else { + cmd = format_telnet_command(addr, port, conf); + ret->to_cmd = open("/dev/null", O_WRONLY); + if (ret->to_cmd == -1) { + ret->error = dupprintf("/dev/null: %s", strerror(errno)); + sfree(cmd); + return (Socket)ret; + } + ret->from_cmd = open(cmd, O_RDONLY); + if (ret->from_cmd == -1) { + ret->error = dupprintf("%s: %s", cmd, strerror(errno)); + sfree(cmd); + return (Socket)ret; + } + sfree(cmd); } - sfree(cmd); - - close(to_cmd_pipe[0]); - close(from_cmd_pipe[1]); - - ret->to_cmd = to_cmd_pipe[1]; - ret->from_cmd = from_cmd_pipe[0]; - if (!localproxy_by_fromfd) localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp); if (!localproxy_by_tofd)