diff -urN vsftpd-2.0.5-orgin/features.c vsftpd-2.0.5/features.c --- vsftpd-2.0.5-orgin/features.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/features.c 2007-09-29 09:09:01.000000000 +0800 @@ -34,6 +34,10 @@ { vsf_cmdio_write_raw(p_sess, " PASV\r\n"); } + if (tunable_charset_filter_enable) + { + vsf_cmdio_write_raw(p_sess, " UTF8\r\n"); + } if (tunable_ssl_enable) { vsf_cmdio_write_raw(p_sess, " PBSZ\r\n"); diff -urN vsftpd-2.0.5-orgin/ftpcodes.h vsftpd-2.0.5/ftpcodes.h --- vsftpd-2.0.5-orgin/ftpcodes.h 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/ftpcodes.h 2007-09-29 10:20:11.000000000 +0800 @@ -14,6 +14,7 @@ #define FTP_MODEOK 200 #define FTP_PBSZOK 200 #define FTP_PROTOK 200 +#define FTP_OPTSOK 200 #define FTP_ALLOOK 202 #define FTP_FEAT 211 #define FTP_STATOK 211 diff -urN vsftpd-2.0.5-orgin/main.c vsftpd-2.0.5/main.c --- vsftpd-2.0.5-orgin/main.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/main.c 2007-09-28 20:07:22.000000000 +0800 @@ -22,6 +22,7 @@ #include "tcpwrap.h" #include "vsftpver.h" #include "ssl.h" +#include /* * Forward decls of helper functions @@ -34,6 +35,7 @@ int main(int argc, const char* argv[]) { + iconv_t cd; struct vsf_session the_session = { /* Control connection */ @@ -106,6 +108,17 @@ } vsf_sysutil_free(p_statbuf); } + if (tunable_charset_filter_enable == 1) + { + if ((cd = iconv_open(tunable_charset_client, tunable_charset_server))!=(iconv_t)-1) + { + iconv_close(cd); + tunable_charset_filter_enable=1; + } + else + tunable_charset_filter_enable = 0; + } + /* Resolve pasv_address if required */ if (tunable_pasv_address && tunable_pasv_addr_resolve) { diff -urN vsftpd-2.0.5-orgin/parseconf.c vsftpd-2.0.5/parseconf.c --- vsftpd-2.0.5-orgin/parseconf.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/parseconf.c 2007-09-28 20:08:56.000000000 +0800 @@ -99,6 +99,7 @@ { "mdtm_write", &tunable_mdtm_write }, { "lock_upload_files", &tunable_lock_upload_files }, { "pasv_addr_resolve", &tunable_pasv_addr_resolve }, + { "charset_filter_enable", &tunable_charset_filter_enable }, { 0, 0 } }; @@ -168,6 +169,8 @@ { "ssl_ciphers", &tunable_ssl_ciphers }, { "rsa_private_key_file", &tunable_rsa_private_key_file }, { "dsa_private_key_file", &tunable_dsa_private_key_file }, + { "charset_client", &tunable_charset_client }, + { "charset_server", &tunable_charset_server }, { 0, 0 } }; diff -urN vsftpd-2.0.5-orgin/postlogin.c vsftpd-2.0.5/postlogin.c --- vsftpd-2.0.5-orgin/postlogin.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/postlogin.c 2007-09-29 10:20:04.000000000 +0800 @@ -340,7 +340,14 @@ } else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS")) { - vsf_cmdio_write(p_sess, FTP_BADOPTS, "Option not understood."); + if (tunable_charset_filter_enable) + { + handle_opts(p_sess); + } + else + { + vsf_cmdio_write(p_sess, FTP_BADOPTS, "Option not understood."); + } } else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") && str_isempty(&p_sess->ftp_arg_str)) @@ -1702,6 +1709,33 @@ } } +handle_opts(struct vsf_session* p_sess) +{ + struct mystr opts = INIT_MYSTR; + struct mystr prm = INIT_MYSTR; + + str_copy(&opts, &p_sess->ftp_arg_str); + str_upper(&opts); + str_split_char(&opts, &prm, ' '); + + if (str_equal_text(&opts, "UTF8")) + { + if (str_equal_text(&prm, "ON")) + { + tunable_charset_client=vsf_sysutil_strdup("UTF8"); + vsf_cmdio_write(p_sess, FTP_OPTSOK, "UTF8 option is On."); + } + else + if (str_equal_text(&prm, "OFF")) + { + vsf_cmdio_write(p_sess, FTP_OPTSOK, "UTF8 option is Off."); + } + else + { + vsf_cmdio_write(p_sess, FTP_BADOPTS, "Invalid UTF8 option."); + } + } +} static void handle_stat(struct vsf_session* p_sess) { @@ -1764,6 +1798,27 @@ vsf_cmdio_write_raw(p_sess, vsf_sysutil_ulong_to_str(p_sess->num_clients)); vsf_cmdio_write_raw(p_sess, "\r\n"); } + + if (tunable_charset_filter_enable) + { + vsf_cmdio_write_raw(p_sess, " Server charset is "); + vsf_cmdio_write_raw(p_sess, tunable_charset_server); + vsf_cmdio_write_raw(p_sess, "\r\n"); + vsf_cmdio_write_raw(p_sess, " Remote charset is "); + vsf_cmdio_write_raw(p_sess, tunable_charset_client); + vsf_cmdio_write_raw(p_sess, "\r\n"); +/* vsf_cmdio_write_raw(p_sess, " Char convertion is "); + * if (p_sess->enable_convertion) + * { + * vsf_cmdio_write_raw(p_sess, "on"); + * } + * else + * { + * vsf_cmdio_write_raw(p_sess, "off"); + * } + * vsf_cmdio_write_raw(p_sess, "\r\n"); + */ + } vsf_cmdio_write_raw(p_sess, " vsFTPd " VSF_VERSION " - secure, fast, stable\r\n"); vsf_cmdio_write(p_sess, FTP_STATOK, "End of status"); diff -urN vsftpd-2.0.5-orgin/readwrite.c vsftpd-2.0.5/readwrite.c --- vsftpd-2.0.5-orgin/readwrite.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/readwrite.c 2007-09-28 20:12:45.000000000 +0800 @@ -15,11 +15,15 @@ #include "privsock.h" #include "defs.h" #include "sysutil.h" +#include "str.h" +#include "tunables.h" int ftp_write_str(const struct vsf_session* p_sess, const struct mystr* p_str, enum EVSFRWTarget target) { + if(tunable_charset_filter_enable) + str_iconv_write(p_str); if (target == kVSFRWData) { if (p_sess->data_use_ssl) @@ -94,5 +98,7 @@ str_netfd_alloc( p_str, VSFTP_COMMAND_FD, '\n', p_buf, VSFTP_MAX_COMMAND_LINE); } + if(tunable_charset_filter_enable) + str_iconv_read(p_str); } diff -urN vsftpd-2.0.5-orgin/str.c vsftpd-2.0.5/str.c --- vsftpd-2.0.5-orgin/str.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/str.c 2007-09-29 11:08:36.000000000 +0800 @@ -19,6 +19,9 @@ /* Ick. Its for die() */ #include "utility.h" #include "sysutil.h" +#include +#include +#include "tunables.h" /* File local functions */ static void str_split_text_common(struct mystr* p_src, struct mystr* p_rhs, @@ -666,3 +669,152 @@ } } +void +str_iconv_write(struct mystr* p_str) +{ + iconv_t cd; + char * from_buf; + char * dyn_from_buf, * to_buf, * dyn_to_buf; + size_t from_len; + size_t dyn_from_len, to_len, dyn_to_len; + size_t print_buf; + + from_buf = str_getbuf(p_str); + from_len = str_getlen(p_str); + + p_str->p_buf = 0; + str_free(p_str); + + private_str_alloc_memchunk(p_str, from_buf, from_len); + str_reserve(p_str, 2*from_len); + p_str->len=2*from_len; + vsf_sysutil_memclr(p_str->p_buf, p_str->len+1); + dyn_from_buf = from_buf; + dyn_from_len = from_len; + to_buf = p_str->p_buf; + dyn_to_buf = to_buf; + to_len = p_str->len; + dyn_to_len = to_len; + + if((cd = vsf_sysutil_iconv_init_write())==(iconv_t)(-1)) + { + bug("str_iconv_write"); + } + + while(vsf_sysutil_iconv(cd, &dyn_from_buf, &dyn_from_len, &dyn_to_buf, &dyn_to_len)==(size_t)(-1)) + { + switch(errno) + { + case EILSEQ: + if((dyn_to_buf=to_buf)) + { + vsf_sysutil_memcpy(dyn_to_buf, dyn_from_buf, 1); + dyn_to_buf+=1; + dyn_to_len-=1; + dyn_from_buf+=1; + dyn_from_len = from_buf + from_len -dyn_from_buf; + if(dyn_from_len==0) break; + } + else + break; + continue; + + case EINVAL: + break; + + case E2BIG: + str_reserve(p_str, to_len+dyn_from_len); + p_str->len=to_len+dyn_from_len; + dyn_to_len+=dyn_from_len; + dyn_to_buf = p_str->p_buf + (dyn_to_buf-to_buf); + to_buf = p_str->p_buf; + to_len = p_str->len; + continue; + + default: + die("iconv set strange errno. Should not happenned!"); + break; + } + break; + } + + str_trunc(p_str, (p_str->len)-dyn_to_len); + + vsf_sysutil_iconv_close(cd); + vsf_sysutil_free(from_buf); +} + +void +str_iconv_read(struct mystr* p_str) +{ + iconv_t cd; + char *from_buf; + char *dyn_from_buf, *to_buf, *dyn_to_buf; + size_t from_len; + size_t dyn_from_len, to_len, dyn_to_len; + size_t print_buf; + + from_buf = str_getbuf(p_str); + from_len = str_getlen(p_str); + + p_str->p_buf = 0; + str_free(p_str); + + private_str_alloc_memchunk(p_str, from_buf, from_len); + str_reserve(p_str, 2*from_len); + p_str->len=2*from_len; + vsf_sysutil_memclr(p_str->p_buf, p_str->len+1); + dyn_from_buf = from_buf; + dyn_from_len = from_len; + to_buf = p_str->p_buf; + dyn_to_buf = to_buf; + to_len = p_str->len; + dyn_to_len = to_len; + + if((cd = vsf_sysutil_iconv_init_read())==(iconv_t)(-1)) + { + bug("str_iconv_read"); + } + + while(vsf_sysutil_iconv(cd, &dyn_from_buf, &dyn_from_len, &dyn_to_buf, &dyn_to_len)==(size_t)(-1)) + { + switch(errno) + { + case EILSEQ: + if((dyn_to_buf=to_buf)) + { + vsf_sysutil_memcpy(dyn_to_buf, dyn_from_buf, 1); + dyn_to_buf+=1; + dyn_to_len-=1; + dyn_from_buf+=1; + dyn_from_len = from_buf + from_len -dyn_from_buf; + if(dyn_from_len==0) break; + } + else + break; + continue; + + case EINVAL: + break; + + case E2BIG: + str_reserve(p_str, to_len+dyn_from_len); + p_str->len=to_len+dyn_from_len; + dyn_to_len+=dyn_from_len; + dyn_to_buf = p_str->p_buf + (dyn_to_buf-to_buf); + to_buf = p_str->p_buf; + to_len = p_str->len; + continue; + + default: + die("iconv set strange errno. Should not happenned!"); + break; + } + break; + } + + str_trunc(p_str, (p_str->len)-dyn_to_len); + + vsf_sysutil_iconv_close(cd); + vsf_sysutil_free(from_buf); +} diff -urN vsftpd-2.0.5-orgin/str.h vsftpd-2.0.5/str.h --- vsftpd-2.0.5-orgin/str.h 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/str.h 2007-09-29 11:05:12.000000000 +0800 @@ -120,5 +120,9 @@ int str_contains_line(const struct mystr* p_str, const struct mystr* p_line_str); +/* Locale string conversion */ +void str_iconv_read(struct mystr* p_str); +void str_iconv_write(struct mystr* p_str); + #endif /* VSFTP_STR_H */ diff -urN vsftpd-2.0.5-orgin/sysutil.c vsftpd-2.0.5/sysutil.c --- vsftpd-2.0.5-orgin/sysutil.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/sysutil.c 2007-09-28 20:16:43.000000000 +0800 @@ -53,6 +53,7 @@ #include #include #include +#include /* Private variables to this file */ /* Current umask() */ @@ -2644,3 +2645,63 @@ return utime(p_file, &new_times); } +iconv_t +vsf_sysutil_iconv_init_read(void) +{ + iconv_t cd; + + if((cd=iconv_open(tunable_charset_server, tunable_charset_client))==(iconv_t)(-1)) + { + if(errno==EINVAL) + { + vsf_sysutil_free(tunable_charset_server); + tunable_charset_server=vsf_sysutil_strdup("UTF8"); + vsf_sysutil_free(tunable_charset_client); + tunable_charset_client=vsf_sysutil_strdup("UTF8"); + return iconv_open(tunable_charset_server, tunable_charset_client); + } + else + { + bug("iconv_open error!"); + } + } + + return cd; +} + +iconv_t +vsf_sysutil_iconv_init_write(void) +{ + iconv_t cd; + + if((cd=iconv_open(tunable_charset_client, tunable_charset_server))==(iconv_t)(-1)) + { + if(errno==EINVAL) + { + vsf_sysutil_free(tunable_charset_server); + tunable_charset_server=vsf_sysutil_strdup("UTF8"); + vsf_sysutil_free(tunable_charset_client); + tunable_charset_client=vsf_sysutil_strdup("UTF8"); + return iconv_open(tunable_charset_server, tunable_charset_client); + } + else + { + bug("iconv_open error!"); + } + } + return cd; +} + +int +vsf_sysutil_iconv_close(iconv_t cd) +{ + return iconv_close(cd); +} + + +size_t +vsf_sysutil_iconv(iconv_t cd, char **inbuf, size_t *inbytes, char **outbuf, size_t *outbytes) +{ + return iconv(cd, inbuf, inbytes, outbuf, outbytes); +} + diff -urN vsftpd-2.0.5-orgin/sysutil.h vsftpd-2.0.5/sysutil.h --- vsftpd-2.0.5-orgin/sysutil.h 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/sysutil.h 2007-09-28 20:18:16.000000000 +0800 @@ -7,6 +7,14 @@ #include "filesize.h" #endif +#ifndef VSF_SYSUTIL_ICONV_H +#include +#endif + +#ifndef VSF_SYSUTIL_STDDEF_H +#include +#endif + /* Return value queries */ int vsf_sysutil_retval_is_error(int retval); enum EVSFSysUtilError @@ -330,5 +338,12 @@ void vsf_sysutil_sleep(double seconds); int vsf_sysutil_setmodtime(const char* p_file, long the_time, int is_localtime); +/* Locale string conversion */ + +iconv_t vsf_sysutil_iconv_init_read(void); +iconv_t vsf_sysutil_iconv_init_write(void); +int vsf_sysutil_iconv_close(iconv_t cd); +size_t vsf_sysutil_iconv(iconv_t cd, char **inbuf, size_t *inbytes, char **outbuf, size_t *outbytes); + #endif /* VSF_SYSUTIL_H */ diff -urN vsftpd-2.0.5-orgin/tunables.c vsftpd-2.0.5/tunables.c --- vsftpd-2.0.5-orgin/tunables.c 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/tunables.c 2007-09-28 20:21:12.000000000 +0800 @@ -68,6 +68,7 @@ int tunable_tilde_user_enable = 0; int tunable_force_anon_logins_ssl = 0; int tunable_force_anon_data_ssl = 0; +int tunable_charset_filter_enable = 0; int tunable_mdtm_write = 1; int tunable_lock_upload_files = 1; int tunable_pasv_addr_resolve = 0; @@ -125,4 +126,5 @@ const char* tunable_ssl_ciphers = "DES-CBC3-SHA"; const char* tunable_rsa_private_key_file = 0; const char* tunable_dsa_private_key_file = 0; - +const char* tunable_charset_client = "UTF-8"; +const char* tunable_charset_server = "UTF-8"; diff -urN vsftpd-2.0.5-orgin/tunables.h vsftpd-2.0.5/tunables.h --- vsftpd-2.0.5-orgin/tunables.h 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/tunables.h 2007-09-28 20:22:23.000000000 +0800 @@ -64,6 +64,7 @@ extern int tunable_tilde_user_enable; /* Support e.g. ~chris */ extern int tunable_force_anon_logins_ssl; /* Require anon logins use SSL */ extern int tunable_force_anon_data_ssl; /* Require anon data uses SSL */ +extern int tunable_charset_filter_enable; /* Enable charset transfer */ extern int tunable_mdtm_write; /* Allow MDTM to set timestamps */ extern int tunable_lock_upload_files; /* Lock uploading files */ extern int tunable_pasv_addr_resolve; /* DNS resolve pasv_addr */ @@ -120,6 +121,8 @@ extern const char* tunable_ssl_ciphers; extern const char* tunable_rsa_private_key_file; extern const char* tunable_dsa_private_key_file; +extern const char* tunable_charset_client; +extern const char* tunable_charset_server; #endif /* VSF_TUNABLES_H */ diff -urN vsftpd-2.0.5-orgin/vsftpd.conf.5 vsftpd-2.0.5/vsftpd.conf.5 --- vsftpd-2.0.5-orgin/vsftpd.conf.5 2007-09-28 20:05:33.000000000 +0800 +++ vsftpd-2.0.5/vsftpd.conf.5 2007-09-28 20:26:52.000000000 +0800 @@ -112,6 +112,13 @@ Default: NO .TP +.B charset_filter_enable +When enabled, vsftpd will setup a character set filter. It is not recommended because +it depends on the implementation of external glibc library. vsftpd can't ensure +the security. + +Default: NO +.TP .B check_shell Note! This option only has an effect for non-PAM builds of vsftpd. If disabled, vsftpd will not check /etc/shells for a valid user shell for local logins. @@ -702,6 +709,20 @@ Default: (none) .TP +.B charset_client +For this option to take effect, +.BR charset_filter_enable +must be set. This option set the character set for client side. + +Default: UTF-8 +.TP +.B charset_server +For this option to take effect, +.BR charset_filter_enable +must be set. This option set the character set for server side. + +Default: UTF-8 +.TP .B chown_username This is the name of the user who is given ownership of anonymously uploaded files. This option is only relevant if another option, @@ -963,4 +984,4 @@ .SH AUTHOR chris@scary.beasts.org - +wzhou@princeton.edu