123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019 |
- /*
- * Part of Very Secure FTPd
- * Licence: GPL v2
- * Author: Chris Evans
- * postlogin.c
- */
- #include "postlogin.h"
- #include "session.h"
- #include "oneprocess.h"
- #include "twoprocess.h"
- #include "ftpcodes.h"
- #include "ftpcmdio.h"
- #include "ftpdataio.h"
- #include "utility.h"
- #include "tunables.h"
- #include "defs.h"
- #include "str.h"
- #include "sysstr.h"
- #include "banner.h"
- #include "sysutil.h"
- #include "logging.h"
- #include "sysdeputil.h"
- #include "ipaddrparse.h"
- #include "access.h"
- #include "features.h"
- #include "ssl.h"
- #include "vsftpver.h"
- #include "opts.h"
- /* Private local functions */
- static void handle_pwd(struct vsf_session* p_sess);
- static void handle_cwd(struct vsf_session* p_sess);
- static void handle_pasv(struct vsf_session* p_sess, int is_epsv);
- static void handle_retr(struct vsf_session* p_sess, int is_http);
- static void handle_cdup(struct vsf_session* p_sess);
- static void handle_list(struct vsf_session* p_sess);
- static void handle_type(struct vsf_session* p_sess);
- static void handle_port(struct vsf_session* p_sess);
- static void handle_stor(struct vsf_session* p_sess);
- static void handle_mkd(struct vsf_session* p_sess);
- static void handle_rmd(struct vsf_session* p_sess);
- static void handle_dele(struct vsf_session* p_sess);
- static void handle_rest(struct vsf_session* p_sess);
- static void handle_rnfr(struct vsf_session* p_sess);
- static void handle_rnto(struct vsf_session* p_sess);
- static void handle_nlst(struct vsf_session* p_sess);
- static void handle_size(struct vsf_session* p_sess);
- static void handle_site(struct vsf_session* p_sess);
- static void handle_appe(struct vsf_session* p_sess);
- static void handle_mdtm(struct vsf_session* p_sess);
- static void handle_site_chmod(struct vsf_session* p_sess,
- struct mystr* p_arg_str);
- static void handle_site_umask(struct vsf_session* p_sess,
- struct mystr* p_arg_str);
- static void handle_eprt(struct vsf_session* p_sess);
- static void handle_help(struct vsf_session* p_sess);
- static void handle_stou(struct vsf_session* p_sess);
- static void handle_stat(struct vsf_session* p_sess);
- static void handle_stat_file(struct vsf_session* p_sess);
- static void handle_logged_in_user(struct vsf_session* p_sess);
- static void handle_logged_in_pass(struct vsf_session* p_sess);
- static void handle_http(struct vsf_session* p_sess);
- static int pasv_active(struct vsf_session* p_sess);
- static int port_active(struct vsf_session* p_sess);
- static void pasv_cleanup(struct vsf_session* p_sess);
- static void port_cleanup(struct vsf_session* p_sess);
- static void handle_dir_common(struct vsf_session* p_sess, int full_details,
- int stat_cmd);
- static void prepend_path_to_filename(struct mystr* p_str);
- static int get_remote_transfer_fd(struct vsf_session* p_sess,
- const char* p_status_msg);
- static void check_abor(struct vsf_session* p_sess);
- static void handle_sigurg(void* p_private);
- static void handle_upload_common(struct vsf_session* p_sess, int is_append,
- int is_unique);
- static void get_unique_filename(struct mystr* p_outstr,
- const struct mystr* p_base);
- static int data_transfer_checks_ok(struct vsf_session* p_sess);
- static void resolve_tilde(struct mystr* p_str, struct vsf_session* p_sess);
- void
- process_post_login(struct vsf_session* p_sess)
- {
- str_getcwd(&p_sess->home_str);
- if (p_sess->is_anonymous)
- {
- vsf_sysutil_set_umask(tunable_anon_umask);
- p_sess->bw_rate_max = tunable_anon_max_rate;
- }
- else
- {
- vsf_sysutil_set_umask(tunable_local_umask);
- p_sess->bw_rate_max = tunable_local_max_rate;
- }
- if (p_sess->is_http)
- {
- handle_http(p_sess);
- bug("should not be reached");
- }
- /* Don't support async ABOR if we have an SSL channel. The spec says SHOULD
- * NOT, and I think there are synchronization issues between command and
- * data reads.
- */
- if (tunable_async_abor_enable && !p_sess->control_use_ssl)
- {
- vsf_sysutil_install_sighandler(kVSFSysUtilSigURG, handle_sigurg, p_sess, 0);
- vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD);
- }
- /* Handle any login message */
- vsf_banner_dir_changed(p_sess, FTP_LOGINOK);
- vsf_cmdio_write(p_sess, FTP_LOGINOK, "Login successful.");
- while(1)
- {
- int cmd_ok = 1;
- if (tunable_setproctitle_enable)
- {
- vsf_sysutil_setproctitle("IDLE");
- }
- /* Blocks */
- vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str,
- &p_sess->ftp_arg_str, 1);
- if (tunable_setproctitle_enable)
- {
- struct mystr proctitle_str = INIT_MYSTR;
- str_copy(&proctitle_str, &p_sess->ftp_cmd_str);
- if (!str_isempty(&p_sess->ftp_arg_str))
- {
- str_append_char(&proctitle_str, ' ');
- str_append_str(&proctitle_str, &p_sess->ftp_arg_str);
- }
- /* Suggestion from Solar */
- str_replace_unprintable(&proctitle_str, '?');
- vsf_sysutil_setproctitle_str(&proctitle_str);
- str_free(&proctitle_str);
- }
- /* Test command against the allowed lists.. */
- if (tunable_cmds_allowed)
- {
- static struct mystr s_src_str;
- static struct mystr s_rhs_str;
- str_alloc_text(&s_src_str, tunable_cmds_allowed);
- while (1)
- {
- str_split_char(&s_src_str, &s_rhs_str, ',');
- if (str_isempty(&s_src_str))
- {
- cmd_ok = 0;
- break;
- }
- else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
- {
- break;
- }
- str_copy(&s_src_str, &s_rhs_str);
- }
- }
- if (tunable_cmds_denied)
- {
- static struct mystr s_src_str;
- static struct mystr s_rhs_str;
- str_alloc_text(&s_src_str, tunable_cmds_denied);
- while (1)
- {
- str_split_char(&s_src_str, &s_rhs_str, ',');
- if (str_isempty(&s_src_str))
- {
- break;
- }
- else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
- {
- cmd_ok = 0;
- break;
- }
- str_copy(&s_src_str, &s_rhs_str);
- }
- }
- if (!cmd_ok)
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT"))
- {
- vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.", 0);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "PWD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "XPWD"))
- {
- handle_pwd(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "CWD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "XCWD"))
- {
- handle_cwd(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "CDUP") ||
- str_equal_text(&p_sess->ftp_cmd_str, "XCUP"))
- {
- handle_cdup(p_sess);
- }
- else if (tunable_pasv_enable &&
- !p_sess->epsv_all &&
- (str_equal_text(&p_sess->ftp_cmd_str, "PASV") ||
- str_equal_text(&p_sess->ftp_cmd_str, "P@SW")))
- {
- handle_pasv(p_sess, 0);
- }
- else if (tunable_pasv_enable &&
- str_equal_text(&p_sess->ftp_cmd_str, "EPSV"))
- {
- handle_pasv(p_sess, 1);
- }
- else if (tunable_download_enable &&
- str_equal_text(&p_sess->ftp_cmd_str, "RETR"))
- {
- handle_retr(p_sess, 0);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "NOOP"))
- {
- vsf_cmdio_write(p_sess, FTP_NOOPOK, "NOOP ok.");
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "SYST"))
- {
- vsf_cmdio_write(p_sess, FTP_SYSTOK, "UNIX Type: L8");
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "HELP"))
- {
- handle_help(p_sess);
- }
- else if (tunable_dirlist_enable &&
- str_equal_text(&p_sess->ftp_cmd_str, "LIST"))
- {
- handle_list(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "TYPE"))
- {
- handle_type(p_sess);
- }
- else if (tunable_port_enable &&
- !p_sess->epsv_all &&
- str_equal_text(&p_sess->ftp_cmd_str, "PORT"))
- {
- handle_port(p_sess);
- }
- else if (tunable_write_enable &&
- (tunable_anon_upload_enable || !p_sess->is_anonymous) &&
- str_equal_text(&p_sess->ftp_cmd_str, "STOR"))
- {
- handle_stor(p_sess);
- }
- else if (tunable_write_enable &&
- (tunable_anon_mkdir_write_enable || !p_sess->is_anonymous) &&
- (str_equal_text(&p_sess->ftp_cmd_str, "MKD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "XMKD")))
- {
- handle_mkd(p_sess);
- }
- else if (tunable_write_enable &&
- (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
- (str_equal_text(&p_sess->ftp_cmd_str, "RMD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "XRMD")))
- {
- handle_rmd(p_sess);
- }
- else if (tunable_write_enable &&
- (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
- str_equal_text(&p_sess->ftp_cmd_str, "DELE"))
- {
- handle_dele(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "REST"))
- {
- handle_rest(p_sess);
- }
- else if (tunable_write_enable &&
- (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
- str_equal_text(&p_sess->ftp_cmd_str, "RNFR"))
- {
- handle_rnfr(p_sess);
- }
- else if (tunable_write_enable &&
- (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
- str_equal_text(&p_sess->ftp_cmd_str, "RNTO"))
- {
- handle_rnto(p_sess);
- }
- else if (tunable_dirlist_enable &&
- str_equal_text(&p_sess->ftp_cmd_str, "NLST"))
- {
- handle_nlst(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "SIZE"))
- {
- handle_size(p_sess);
- }
- else if (!p_sess->is_anonymous &&
- str_equal_text(&p_sess->ftp_cmd_str, "SITE"))
- {
- handle_site(p_sess);
- }
- /* Note - the weird ABOR string is checking for an async ABOR arriving
- * without a SIGURG condition.
- */
- else if (str_equal_text(&p_sess->ftp_cmd_str, "ABOR") ||
- str_equal_text(&p_sess->ftp_cmd_str, "\377\364\377\362ABOR"))
- {
- vsf_cmdio_write(p_sess, FTP_ABOR_NOCONN, "No transfer to ABOR.");
- }
- else if (tunable_write_enable &&
- (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
- str_equal_text(&p_sess->ftp_cmd_str, "APPE"))
- {
- handle_appe(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "MDTM"))
- {
- handle_mdtm(p_sess);
- }
- else if (tunable_port_enable &&
- str_equal_text(&p_sess->ftp_cmd_str, "EPRT"))
- {
- handle_eprt(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "STRU"))
- {
- str_upper(&p_sess->ftp_arg_str);
- if (str_equal_text(&p_sess->ftp_arg_str, "F"))
- {
- vsf_cmdio_write(p_sess, FTP_STRUOK, "Structure set to F.");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_BADSTRU, "Bad STRU command.");
- }
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "MODE"))
- {
- str_upper(&p_sess->ftp_arg_str);
- if (str_equal_text(&p_sess->ftp_arg_str, "S"))
- {
- vsf_cmdio_write(p_sess, FTP_MODEOK, "Mode set to S.");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_BADMODE, "Bad MODE command.");
- }
- }
- else if (tunable_write_enable &&
- (tunable_anon_upload_enable || !p_sess->is_anonymous) &&
- str_equal_text(&p_sess->ftp_cmd_str, "STOU"))
- {
- handle_stou(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "ALLO"))
- {
- vsf_cmdio_write(p_sess, FTP_ALLOOK, "ALLO command ignored.");
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "REIN"))
- {
- vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "REIN not implemented.");
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "ACCT"))
- {
- vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "ACCT not implemented.");
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "SMNT"))
- {
- vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "SMNT not implemented.");
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "FEAT"))
- {
- handle_feat(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS"))
- {
- handle_opts(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") &&
- str_isempty(&p_sess->ftp_arg_str))
- {
- handle_stat(p_sess);
- }
- else if (tunable_dirlist_enable &&
- str_equal_text(&p_sess->ftp_cmd_str, "STAT"))
- {
- handle_stat_file(p_sess);
- }
- else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PBSZ"))
- {
- handle_pbsz(p_sess);
- }
- else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PROT"))
- {
- handle_prot(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "USER"))
- {
- handle_logged_in_user(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "PASS"))
- {
- handle_logged_in_pass(p_sess);
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "PASV") ||
- str_equal_text(&p_sess->ftp_cmd_str, "PORT") ||
- str_equal_text(&p_sess->ftp_cmd_str, "STOR") ||
- str_equal_text(&p_sess->ftp_cmd_str, "MKD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "XMKD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "RMD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "XRMD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "DELE") ||
- str_equal_text(&p_sess->ftp_cmd_str, "RNFR") ||
- str_equal_text(&p_sess->ftp_cmd_str, "RNTO") ||
- str_equal_text(&p_sess->ftp_cmd_str, "SITE") ||
- str_equal_text(&p_sess->ftp_cmd_str, "APPE") ||
- str_equal_text(&p_sess->ftp_cmd_str, "EPSV") ||
- str_equal_text(&p_sess->ftp_cmd_str, "EPRT") ||
- str_equal_text(&p_sess->ftp_cmd_str, "RETR") ||
- str_equal_text(&p_sess->ftp_cmd_str, "LIST") ||
- str_equal_text(&p_sess->ftp_cmd_str, "NLST") ||
- str_equal_text(&p_sess->ftp_cmd_str, "STOU") ||
- str_equal_text(&p_sess->ftp_cmd_str, "ALLO") ||
- str_equal_text(&p_sess->ftp_cmd_str, "REIN") ||
- str_equal_text(&p_sess->ftp_cmd_str, "ACCT") ||
- str_equal_text(&p_sess->ftp_cmd_str, "SMNT") ||
- str_equal_text(&p_sess->ftp_cmd_str, "FEAT") ||
- str_equal_text(&p_sess->ftp_cmd_str, "OPTS") ||
- str_equal_text(&p_sess->ftp_cmd_str, "STAT") ||
- str_equal_text(&p_sess->ftp_cmd_str, "PBSZ") ||
- str_equal_text(&p_sess->ftp_cmd_str, "PROT"))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- }
- else if (str_isempty(&p_sess->ftp_cmd_str) &&
- str_isempty(&p_sess->ftp_arg_str))
- {
- /* Deliberately ignore to avoid NAT device bugs. ProFTPd does the same. */
- }
- else if (str_equal_text(&p_sess->ftp_cmd_str, "GET") ||
- str_equal_text(&p_sess->ftp_cmd_str, "POST") ||
- str_equal_text(&p_sess->ftp_cmd_str, "HEAD") ||
- str_equal_text(&p_sess->ftp_cmd_str, "OPTIONS") ||
- str_equal_text(&p_sess->ftp_cmd_str, "CONNECT"))
- {
- vsf_cmdio_write_exit(p_sess, FTP_BADCMD,
- "HTTP protocol commands not allowed.", 1);
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown command.");
- }
- if (vsf_log_entry_pending(p_sess))
- {
- vsf_log_do_log(p_sess, 0);
- }
- if (p_sess->data_timeout)
- {
- vsf_cmdio_write_exit(p_sess, FTP_DATA_TIMEOUT,
- "Data timeout. Reconnect. Sorry.", 1);
- }
- }
- }
- static void
- handle_pwd(struct vsf_session* p_sess)
- {
- static struct mystr s_cwd_buf_mangle_str;
- static struct mystr s_pwd_res_str;
- str_getcwd(&s_cwd_buf_mangle_str);
- /* Double up any double-quotes in the pathname! */
- str_replace_text(&s_cwd_buf_mangle_str, "\"", "\"\"");
- /* Enclose pathname in quotes */
- str_alloc_text(&s_pwd_res_str, "\"");
- str_append_str(&s_pwd_res_str, &s_cwd_buf_mangle_str);
- str_append_text(&s_pwd_res_str, "\" is the current directory");
- vsf_cmdio_write_str(p_sess, FTP_PWDOK, &s_pwd_res_str);
- }
- static void
- handle_cwd(struct vsf_session* p_sess)
- {
- int retval;
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- retval = str_chdir(&p_sess->ftp_arg_str);
- if (retval == 0)
- {
- /* Handle any messages */
- vsf_banner_dir_changed(p_sess, FTP_CWDOK);
- vsf_cmdio_write(p_sess, FTP_CWDOK, "Directory successfully changed.");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to change directory.");
- }
- }
- static void
- handle_cdup(struct vsf_session* p_sess)
- {
- str_alloc_text(&p_sess->ftp_arg_str, "..");
- handle_cwd(p_sess);
- }
- static int
- port_active(struct vsf_session* p_sess)
- {
- int ret = 0;
- if (p_sess->p_port_sockaddr != 0)
- {
- ret = 1;
- if (pasv_active(p_sess))
- {
- bug("port and pasv both active");
- }
- }
- return ret;
- }
- static int
- pasv_active(struct vsf_session* p_sess)
- {
- int ret = 0;
- if (tunable_one_process_model)
- {
- ret = vsf_one_process_pasv_active(p_sess);
- }
- else
- {
- ret = vsf_two_process_pasv_active(p_sess);
- }
- if (ret)
- {
- if (port_active(p_sess))
- {
- bug("pasv and port both active");
- }
- }
- return ret;
- }
- static void
- port_cleanup(struct vsf_session* p_sess)
- {
- vsf_sysutil_sockaddr_clear(&p_sess->p_port_sockaddr);
- }
- static void
- pasv_cleanup(struct vsf_session* p_sess)
- {
- if (tunable_one_process_model)
- {
- vsf_one_process_pasv_cleanup(p_sess);
- }
- else
- {
- vsf_two_process_pasv_cleanup(p_sess);
- }
- }
- static void
- handle_pasv(struct vsf_session* p_sess, int is_epsv)
- {
- unsigned short the_port;
- static struct mystr s_pasv_res_str;
- static struct vsf_sysutil_sockaddr* s_p_sockaddr;
- int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
- if (is_epsv && !str_isempty(&p_sess->ftp_arg_str))
- {
- int argval;
- str_upper(&p_sess->ftp_arg_str);
- if (str_equal_text(&p_sess->ftp_arg_str, "ALL"))
- {
- p_sess->epsv_all = 1;
- vsf_cmdio_write(p_sess, FTP_EPSVALLOK, "EPSV ALL ok.");
- return;
- }
- argval = vsf_sysutil_atoi(str_getbuf(&p_sess->ftp_arg_str));
- if (argval < 1 || argval > 2 || (!is_ipv6 && argval == 2))
- {
- vsf_cmdio_write(p_sess, FTP_EPSVBAD, "Bad network protocol.");
- return;
- }
- }
- pasv_cleanup(p_sess);
- port_cleanup(p_sess);
- if (tunable_one_process_model)
- {
- the_port = vsf_one_process_listen(p_sess);
- }
- else
- {
- the_port = vsf_two_process_listen(p_sess);
- }
- if (is_epsv)
- {
- str_alloc_text(&s_pasv_res_str, "Entering Extended Passive Mode (|||");
- str_append_ulong(&s_pasv_res_str, (unsigned long) the_port);
- str_append_text(&s_pasv_res_str, "|)");
- vsf_cmdio_write_str(p_sess, FTP_EPSVOK, &s_pasv_res_str);
- return;
- }
- if (tunable_pasv_address != 0)
- {
- vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr);
- /* Report passive address as specified in configuration */
- if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0)
- {
- die("invalid pasv_address");
- }
- }
- else
- {
- vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
- }
- str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
- if (!is_ipv6)
- {
- str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
- }
- else
- {
- const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
- if (p_v4addr)
- {
- str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
- }
- else
- {
- str_append_text(&s_pasv_res_str, "0,0,0,0");
- }
- }
- str_replace_char(&s_pasv_res_str, '.', ',');
- str_append_text(&s_pasv_res_str, ",");
- str_append_ulong(&s_pasv_res_str, the_port >> 8);
- str_append_text(&s_pasv_res_str, ",");
- str_append_ulong(&s_pasv_res_str, the_port & 255);
- str_append_text(&s_pasv_res_str, ").");
- vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str);
- }
- static void
- handle_retr(struct vsf_session* p_sess, int is_http)
- {
- static struct mystr s_mark_str;
- static struct vsf_sysutil_statbuf* s_p_statbuf;
- struct vsf_transfer_ret trans_ret;
- int remote_fd;
- int opened_file;
- int is_ascii = 0;
- filesize_t offset = p_sess->restart_pos;
- p_sess->restart_pos = 0;
- if (!is_http && !data_transfer_checks_ok(p_sess))
- {
- return;
- }
- if (p_sess->is_ascii && offset != 0)
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL,
- "No support for resume of ASCII transfer.");
- return;
- }
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- vsf_log_start_entry(p_sess, kVSFLogEntryDownload);
- str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&p_sess->log_str);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
- if (vsf_sysutil_retval_is_error(opened_file))
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
- return;
- }
- /* Lock file if required */
- if (tunable_lock_upload_files)
- {
- vsf_sysutil_lock_file_read(opened_file);
- }
- vsf_sysutil_fstat(opened_file, &s_p_statbuf);
- /* No games please */
- if (!vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
- {
- /* Note - pretend open failed */
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
- /* Irritating FireFox does RETR on directories, so avoid logging this
- * very common and noisy case.
- */
- if (vsf_sysutil_statbuf_is_dir(s_p_statbuf))
- {
- vsf_log_clear_entry(p_sess);
- }
- goto file_close_out;
- }
- /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
- * such as XFS DMAPI.
- */
- vsf_sysutil_deactivate_noblock(opened_file);
- /* Optionally, we'll be paranoid and only serve publicly readable stuff */
- if (p_sess->is_anonymous && tunable_anon_world_readable_only &&
- !vsf_sysutil_statbuf_is_readable_other(s_p_statbuf))
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
- goto file_close_out;
- }
- /* Set the download offset (from REST) if any */
- if (offset != 0)
- {
- vsf_sysutil_lseek_to(opened_file, offset);
- }
- str_alloc_text(&s_mark_str, "Opening ");
- if (tunable_ascii_download_enable && p_sess->is_ascii)
- {
- str_append_text(&s_mark_str, "ASCII");
- is_ascii = 1;
- }
- else
- {
- str_append_text(&s_mark_str, "BINARY");
- }
- str_append_text(&s_mark_str, " mode data connection for ");
- str_append_str(&s_mark_str, &p_sess->ftp_arg_str);
- str_append_text(&s_mark_str, " (");
- str_append_filesize_t(&s_mark_str,
- vsf_sysutil_statbuf_get_size(s_p_statbuf));
- str_append_text(&s_mark_str, " bytes).");
- if (is_http)
- {
- remote_fd = VSFTP_COMMAND_FD;
- }
- else
- {
- remote_fd = get_remote_transfer_fd(p_sess, str_getbuf(&s_mark_str));
- if (vsf_sysutil_retval_is_error(remote_fd))
- {
- goto port_pasv_cleanup_out;
- }
- }
- trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
- opened_file, 0, is_ascii);
- if (!is_http &&
- vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 &&
- trans_ret.retval == 0)
- {
- trans_ret.retval = -2;
- }
- p_sess->transfer_size = trans_ret.transferred;
- /* Log _after_ the blocking dispose call, so we get transfer times right */
- if (trans_ret.retval == 0)
- {
- vsf_log_do_log(p_sess, 1);
- }
- if (is_http)
- {
- goto file_close_out;
- }
- /* Emit status message _after_ blocking dispose call to avoid buggy FTP
- * clients truncating the transfer.
- */
- if (trans_ret.retval == -1)
- {
- vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure reading local file.");
- }
- else if (trans_ret.retval == -2)
- {
- if (!p_sess->data_timeout)
- {
- vsf_cmdio_write(p_sess, FTP_BADSENDNET,
- "Failure writing network stream.");
- }
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
- }
- check_abor(p_sess);
- port_pasv_cleanup_out:
- port_cleanup(p_sess);
- pasv_cleanup(p_sess);
- file_close_out:
- vsf_sysutil_close(opened_file);
- }
- static void
- handle_list(struct vsf_session* p_sess)
- {
- handle_dir_common(p_sess, 1, 0);
- }
- static void
- handle_dir_common(struct vsf_session* p_sess, int full_details, int stat_cmd)
- {
- static struct mystr s_option_str;
- static struct mystr s_filter_str;
- static struct mystr s_dir_name_str;
- static struct vsf_sysutil_statbuf* s_p_dirstat;
- int dir_allow_read = 1;
- struct vsf_sysutil_dir* p_dir = 0;
- int retval = 0;
- int use_control = 0;
- str_empty(&s_option_str);
- str_empty(&s_filter_str);
- /* By default open the current directory */
- str_alloc_text(&s_dir_name_str, ".");
- if (!stat_cmd && !data_transfer_checks_ok(p_sess))
- {
- return;
- }
- /* Do we have an option? Going to be strict here - the option must come
- * first. e.g. "ls -a .." fine, "ls .. -a" not fine
- */
- if (!str_isempty(&p_sess->ftp_arg_str) &&
- str_get_char_at(&p_sess->ftp_arg_str, 0) == '-')
- {
- /* Chop off the '-' */
- str_mid_to_end(&p_sess->ftp_arg_str, &s_option_str, 1);
- /* A space will separate options from filter (if any) */
- str_split_char(&s_option_str, &s_filter_str, ' ');
- }
- else
- {
- /* The argument, if any, is just a filter */
- str_copy(&s_filter_str, &p_sess->ftp_arg_str);
- }
- if (!str_isempty(&s_filter_str))
- {
- resolve_tilde(&s_filter_str, p_sess);
- if (!vsf_access_check_file(&s_filter_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- /* First check - is it an outright directory, as in "ls /pub" */
- p_dir = str_opendir(&s_filter_str);
- if (p_dir != 0)
- {
- /* Listing a directory! */
- str_copy(&s_dir_name_str, &s_filter_str);
- str_free(&s_filter_str);
- }
- else
- {
- struct str_locate_result locate_result =
- str_locate_char(&s_filter_str, '/');
- if (locate_result.found)
- {
- /* Includes a path! Reverse scan for / in the arg, to get the
- * base directory and filter (if any)
- */
- str_copy(&s_dir_name_str, &s_filter_str);
- str_split_char_reverse(&s_dir_name_str, &s_filter_str, '/');
- /* If we have e.g. "ls /.message", we just ripped off the leading
- * slash because it is the only one!
- */
- if (str_isempty(&s_dir_name_str))
- {
- str_alloc_text(&s_dir_name_str, "/");
- }
- }
- }
- }
- if (p_dir == 0)
- {
- /* NOTE - failure check done below, it's not forgotten */
- p_dir = str_opendir(&s_dir_name_str);
- }
- /* Fine, do it */
- if (stat_cmd)
- {
- use_control = 1;
- str_append_char(&s_option_str, 'a');
- vsf_cmdio_write_hyphen(p_sess, FTP_STATFILE_OK, "Status follows:");
- }
- else
- {
- int remote_fd = get_remote_transfer_fd(
- p_sess, "Here comes the directory listing.");
- if (vsf_sysutil_retval_is_error(remote_fd))
- {
- goto dir_close_out;
- }
- }
- if (p_sess->is_anonymous && p_dir && tunable_anon_world_readable_only)
- {
- vsf_sysutil_dir_stat(p_dir, &s_p_dirstat);
- if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat))
- {
- dir_allow_read = 0;
- }
- }
- if (p_dir != 0 && dir_allow_read)
- {
- retval = vsf_ftpdataio_transfer_dir(p_sess, use_control, p_dir,
- &s_dir_name_str, &s_option_str,
- &s_filter_str, full_details);
- }
- if (!stat_cmd)
- {
- if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && retval == 0)
- {
- retval = -1;
- }
- }
- if (stat_cmd)
- {
- vsf_cmdio_write(p_sess, FTP_STATFILE_OK, "End of status");
- }
- else if (retval != 0)
- {
- if (!p_sess->data_timeout)
- {
- vsf_cmdio_write(p_sess, FTP_BADSENDNET,
- "Failure writing network stream.");
- }
- }
- else if (p_dir == 0 || !dir_allow_read)
- {
- vsf_cmdio_write(p_sess, FTP_TRANSFEROK,
- "Transfer done (but failed to open directory).");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Directory send OK.");
- }
- check_abor(p_sess);
- dir_close_out:
- if (p_dir)
- {
- vsf_sysutil_closedir(p_dir);
- }
- if (!stat_cmd)
- {
- port_cleanup(p_sess);
- pasv_cleanup(p_sess);
- }
- }
- static void
- handle_type(struct vsf_session* p_sess)
- {
- str_upper(&p_sess->ftp_arg_str);
- if (str_equal_text(&p_sess->ftp_arg_str, "I") ||
- str_equal_text(&p_sess->ftp_arg_str, "L8") ||
- str_equal_text(&p_sess->ftp_arg_str, "L 8"))
- {
- p_sess->is_ascii = 0;
- vsf_cmdio_write(p_sess, FTP_TYPEOK, "Switching to Binary mode.");
- }
- else if (str_equal_text(&p_sess->ftp_arg_str, "A") ||
- str_equal_text(&p_sess->ftp_arg_str, "A N"))
- {
- p_sess->is_ascii = 1;
- vsf_cmdio_write(p_sess, FTP_TYPEOK, "Switching to ASCII mode.");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Unrecognised TYPE command.");
- }
- }
- static void
- handle_port(struct vsf_session* p_sess)
- {
- unsigned short the_port;
- unsigned char vals[6];
- const unsigned char* p_raw;
- pasv_cleanup(p_sess);
- port_cleanup(p_sess);
- p_raw = vsf_sysutil_parse_uchar_string_sep(&p_sess->ftp_arg_str, ',', vals,
- sizeof(vals));
- if (p_raw == 0)
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command.");
- return;
- }
- the_port = (unsigned short) ((vals[4] << 8) | vals[5]);
- vsf_sysutil_sockaddr_clone(&p_sess->p_port_sockaddr, p_sess->p_local_addr);
- vsf_sysutil_sockaddr_set_ipv4addr(p_sess->p_port_sockaddr, vals);
- vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, the_port);
- /* SECURITY:
- * 1) Reject requests not connecting to the control socket IP
- * 2) Reject connects to privileged ports
- */
- if (!tunable_port_promiscuous)
- {
- if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr,
- p_sess->p_port_sockaddr) ||
- vsf_sysutil_is_port_reserved(the_port))
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command.");
- port_cleanup(p_sess);
- return;
- }
- }
- vsf_cmdio_write(p_sess, FTP_PORTOK,
- "PORT command successful. Consider using PASV.");
- }
- static void
- handle_stor(struct vsf_session* p_sess)
- {
- handle_upload_common(p_sess, 0, 0);
- }
- static void
- handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
- {
- static struct vsf_sysutil_statbuf* s_p_statbuf;
- static struct mystr s_filename;
- struct mystr* p_filename;
- struct vsf_transfer_ret trans_ret;
- int new_file_fd;
- int remote_fd;
- int success = 0;
- int created = 0;
- int do_truncate = 0;
- filesize_t offset = p_sess->restart_pos;
- p_sess->restart_pos = 0;
- if (!data_transfer_checks_ok(p_sess))
- {
- return;
- }
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- p_filename = &p_sess->ftp_arg_str;
- if (is_unique)
- {
- get_unique_filename(&s_filename, p_filename);
- p_filename = &s_filename;
- }
- vsf_log_start_entry(p_sess, kVSFLogEntryUpload);
- str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&p_sess->log_str);
- if (!vsf_access_check_file(p_filename))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- /* NOTE - actual file permissions will be governed by the tunable umask */
- /* XXX - do we care about race between create and chown() of anonymous
- * upload?
- */
- if (is_unique || (p_sess->is_anonymous && !tunable_anon_other_write_enable))
- {
- new_file_fd = str_create_exclusive(p_filename);
- }
- else
- {
- /* For non-anonymous, allow open() to overwrite or append existing files */
- new_file_fd = str_create(p_filename);
- if (!is_append && offset == 0)
- {
- do_truncate = 1;
- }
- }
- if (vsf_sysutil_retval_is_error(new_file_fd))
- {
- vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
- return;
- }
- created = 1;
- vsf_sysutil_fstat(new_file_fd, &s_p_statbuf);
- if (vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
- {
- /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
- * such as XFS DMAPI.
- */
- vsf_sysutil_deactivate_noblock(new_file_fd);
- }
- /* Are we required to chown() this file for security? */
- if (p_sess->is_anonymous && tunable_chown_uploads)
- {
- vsf_sysutil_fchmod(new_file_fd, tunable_chown_upload_mode);
- if (tunable_one_process_model)
- {
- vsf_one_process_chown_upload(p_sess, new_file_fd);
- }
- else
- {
- vsf_two_process_chown_upload(p_sess, new_file_fd);
- }
- }
- /* Are we required to lock this file? */
- if (tunable_lock_upload_files)
- {
- vsf_sysutil_lock_file_write(new_file_fd);
- }
- /* Must truncate the file AFTER locking it! */
- if (do_truncate)
- {
- vsf_sysutil_ftruncate(new_file_fd);
- vsf_sysutil_lseek_to(new_file_fd, 0);
- }
- if (!is_append && offset != 0)
- {
- /* XXX - warning, allows seek past end of file! Check for seek > size? */
- vsf_sysutil_lseek_to(new_file_fd, offset);
- }
- else if (is_append)
- {
- vsf_sysutil_lseek_end(new_file_fd);
- }
- if (is_unique)
- {
- struct mystr resp_str = INIT_MYSTR;
- str_alloc_text(&resp_str, "FILE: ");
- str_append_str(&resp_str, p_filename);
- remote_fd = get_remote_transfer_fd(p_sess, str_getbuf(&resp_str));
- str_free(&resp_str);
- }
- else
- {
- remote_fd = get_remote_transfer_fd(p_sess, "Ok to send data.");
- }
- if (vsf_sysutil_retval_is_error(remote_fd))
- {
- goto port_pasv_cleanup_out;
- }
- if (tunable_ascii_upload_enable && p_sess->is_ascii)
- {
- trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
- new_file_fd, 1, 1);
- }
- else
- {
- trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
- new_file_fd, 1, 0);
- }
- if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && trans_ret.retval == 0)
- {
- trans_ret.retval = -2;
- }
- p_sess->transfer_size = trans_ret.transferred;
- if (trans_ret.retval == 0)
- {
- success = 1;
- vsf_log_do_log(p_sess, 1);
- }
- if (trans_ret.retval == -1)
- {
- vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure writing to local file.");
- }
- else if (trans_ret.retval == -2)
- {
- if (!p_sess->data_timeout)
- {
- vsf_cmdio_write(p_sess, FTP_BADSENDNET,
- "Failure reading network stream.");
- }
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
- }
- check_abor(p_sess);
- port_pasv_cleanup_out:
- port_cleanup(p_sess);
- pasv_cleanup(p_sess);
- if (tunable_delete_failed_uploads && created && !success)
- {
- str_unlink(p_filename);
- }
- vsf_sysutil_close(new_file_fd);
- }
- static void
- handle_mkd(struct vsf_session* p_sess)
- {
- int retval;
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- vsf_log_start_entry(p_sess, kVSFLogEntryMkdir);
- str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&p_sess->log_str);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- /* NOTE! Actual permissions will be governed by the tunable umask */
- retval = str_mkdir(&p_sess->ftp_arg_str, 0777);
- if (retval != 0)
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL,
- "Create directory operation failed.");
- return;
- }
- vsf_log_do_log(p_sess, 1);
- {
- static struct mystr s_mkd_res;
- static struct mystr s_tmp_str;
- str_copy(&s_tmp_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&s_tmp_str);
- /* Double up double quotes */
- str_replace_text(&s_tmp_str, "\"", "\"\"");
- /* Build result string */
- str_alloc_text(&s_mkd_res, "\"");
- str_append_str(&s_mkd_res, &s_tmp_str);
- str_append_text(&s_mkd_res, "\" created");
- vsf_cmdio_write_str(p_sess, FTP_MKDIROK, &s_mkd_res);
- }
- }
- static void
- handle_rmd(struct vsf_session* p_sess)
- {
- int retval;
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- vsf_log_start_entry(p_sess, kVSFLogEntryRmdir);
- str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&p_sess->log_str);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- retval = str_rmdir(&p_sess->ftp_arg_str);
- if (retval != 0)
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL,
- "Remove directory operation failed.");
- }
- else
- {
- vsf_log_do_log(p_sess, 1);
- vsf_cmdio_write(p_sess, FTP_RMDIROK,
- "Remove directory operation successful.");
- }
- }
- static void
- handle_dele(struct vsf_session* p_sess)
- {
- int retval;
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- vsf_log_start_entry(p_sess, kVSFLogEntryDelete);
- str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&p_sess->log_str);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- retval = str_unlink(&p_sess->ftp_arg_str);
- if (retval != 0)
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Delete operation failed.");
- }
- else
- {
- vsf_log_do_log(p_sess, 1);
- vsf_cmdio_write(p_sess, FTP_DELEOK, "Delete operation successful.");
- }
- }
- static void
- handle_rest(struct vsf_session* p_sess)
- {
- static struct mystr s_rest_str;
- filesize_t val = str_a_to_filesize_t(&p_sess->ftp_arg_str);
- if (val < 0)
- {
- val = 0;
- }
- p_sess->restart_pos = val;
- str_alloc_text(&s_rest_str, "Restart position accepted (");
- str_append_filesize_t(&s_rest_str, val);
- str_append_text(&s_rest_str, ").");
- vsf_cmdio_write_str(p_sess, FTP_RESTOK, &s_rest_str);
- }
- static void
- handle_rnfr(struct vsf_session* p_sess)
- {
- static struct vsf_sysutil_statbuf* p_statbuf;
- int retval;
- /* Clear old value */
- str_free(&p_sess->rnfr_filename_str);
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_log_start_entry(p_sess, kVSFLogEntryRename);
- str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&p_sess->log_str);
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- /* Does it exist? */
- retval = str_stat(&p_sess->ftp_arg_str, &p_statbuf);
- if (retval == 0)
- {
- /* Yes */
- str_copy(&p_sess->rnfr_filename_str, &p_sess->ftp_arg_str);
- vsf_cmdio_write(p_sess, FTP_RNFROK, "Ready for RNTO.");
- }
- else
- {
- vsf_log_start_entry(p_sess, kVSFLogEntryRename);
- str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&p_sess->log_str);
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "RNFR command failed.");
- }
- }
- static void
- handle_rnto(struct vsf_session* p_sess)
- {
- static struct mystr s_tmp_str;
- int retval;
- /* If we didn't get a RNFR, throw a wobbly */
- if (str_isempty(&p_sess->rnfr_filename_str))
- {
- vsf_cmdio_write(p_sess, FTP_NEEDRNFR,
- "RNFR required first.");
- return;
- }
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- vsf_log_start_entry(p_sess, kVSFLogEntryRename);
- str_copy(&p_sess->log_str, &p_sess->rnfr_filename_str);
- prepend_path_to_filename(&p_sess->log_str);
- str_append_char(&p_sess->log_str, ' ');
- str_copy(&s_tmp_str, &p_sess->ftp_arg_str);
- prepend_path_to_filename(&s_tmp_str);
- str_append_str(&p_sess->log_str, &s_tmp_str);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- /* NOTE - might overwrite destination file. Not a concern because the same
- * could be accomplished with DELE.
- */
- retval = str_rename(&p_sess->rnfr_filename_str, &p_sess->ftp_arg_str);
- /* Clear the RNFR filename; start the two stage process again! */
- str_free(&p_sess->rnfr_filename_str);
- if (retval == 0)
- {
- vsf_log_do_log(p_sess, 1);
- vsf_cmdio_write(p_sess, FTP_RENAMEOK, "Rename successful.");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Rename failed.");
- }
- }
- static void
- handle_nlst(struct vsf_session* p_sess)
- {
- handle_dir_common(p_sess, 0, 0);
- }
- static void
- prepend_path_to_filename(struct mystr* p_str)
- {
- static struct mystr s_tmp_str;
- /* Only prepend current working directory if the incoming filename is
- * relative
- */
- str_empty(&s_tmp_str);
- if (str_isempty(p_str) || str_get_char_at(p_str, 0) != '/')
- {
- str_getcwd(&s_tmp_str);
- /* Careful to not emit // if we are in directory / (common with chroot) */
- if (str_isempty(&s_tmp_str) ||
- str_get_char_at(&s_tmp_str, str_getlen(&s_tmp_str) - 1) != '/')
- {
- str_append_char(&s_tmp_str, '/');
- }
- }
- str_append_str(&s_tmp_str, p_str);
- str_copy(p_str, &s_tmp_str);
- }
- static void
- handle_sigurg(void* p_private)
- {
- struct mystr async_cmd_str = INIT_MYSTR;
- struct mystr async_arg_str = INIT_MYSTR;
- struct mystr real_cmd_str = INIT_MYSTR;
- unsigned int len;
- struct vsf_session* p_sess = (struct vsf_session*) p_private;
- /* Did stupid client sent something OOB without a data connection? */
- if (p_sess->data_fd == -1)
- {
- return;
- }
- /* Get the async command - blocks (use data timeout alarm) */
- vsf_cmdio_get_cmd_and_arg(p_sess, &async_cmd_str, &async_arg_str, 0);
- /* Chop off first four characters; they are telnet characters. The client
- * should have sent the first two normally and the second two as urgent
- * data.
- */
- len = str_getlen(&async_cmd_str);
- if (len >= 4)
- {
- str_right(&async_cmd_str, &real_cmd_str, len - 4);
- }
- if (str_equal_text(&real_cmd_str, "ABOR"))
- {
- p_sess->abor_received = 1;
- /* This is failok because of a small race condition; the SIGURG might
- * be raised after the data socket is closed, but before data_fd is
- * set to -1.
- */
- vsf_sysutil_shutdown_failok(p_sess->data_fd);
- }
- else
- {
- /* Sorry! */
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown command.");
- }
- str_free(&async_cmd_str);
- str_free(&async_arg_str);
- str_free(&real_cmd_str);
- }
- static int
- get_remote_transfer_fd(struct vsf_session* p_sess, const char* p_status_msg)
- {
- int remote_fd;
- if (!pasv_active(p_sess) && !port_active(p_sess))
- {
- bug("neither PORT nor PASV active in get_remote_transfer_fd");
- }
- p_sess->abor_received = 0;
- if (pasv_active(p_sess))
- {
- remote_fd = vsf_ftpdataio_get_pasv_fd(p_sess);
- }
- else
- {
- remote_fd = vsf_ftpdataio_get_port_fd(p_sess);
- }
- if (vsf_sysutil_retval_is_error(remote_fd))
- {
- return remote_fd;
- }
- vsf_cmdio_write(p_sess, FTP_DATACONN, p_status_msg);
- if (vsf_ftpdataio_post_mark_connect(p_sess) != 1)
- {
- vsf_ftpdataio_dispose_transfer_fd(p_sess);
- return -1;
- }
- return remote_fd;
- }
- static void
- check_abor(struct vsf_session* p_sess)
- {
- /* If the client sent ABOR, respond to it here */
- if (p_sess->abor_received)
- {
- p_sess->abor_received = 0;
- vsf_cmdio_write(p_sess, FTP_ABOROK, "ABOR successful.");
- }
- }
- static void
- handle_size(struct vsf_session* p_sess)
- {
- /* Note - in ASCII mode, are supposed to return the size after taking into
- * account ASCII linefeed conversions. At least this is what wu-ftpd does in
- * version 2.6.1. Proftpd-1.2.0pre fails to do this.
- * I will not do it because it is a potential I/O DoS.
- */
- static struct vsf_sysutil_statbuf* s_p_statbuf;
- int retval;
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
- if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Could not get file size.");
- }
- else
- {
- static struct mystr s_size_res_str;
- str_alloc_filesize_t(&s_size_res_str,
- vsf_sysutil_statbuf_get_size(s_p_statbuf));
- vsf_cmdio_write_str(p_sess, FTP_SIZEOK, &s_size_res_str);
- }
- }
- static void
- handle_site(struct vsf_session* p_sess)
- {
- static struct mystr s_site_args_str;
- /* What SITE sub-command is it? */
- str_split_char(&p_sess->ftp_arg_str, &s_site_args_str, ' ');
- str_upper(&p_sess->ftp_arg_str);
- if (tunable_write_enable &&
- tunable_chmod_enable &&
- str_equal_text(&p_sess->ftp_arg_str, "CHMOD"))
- {
- handle_site_chmod(p_sess, &s_site_args_str);
- }
- else if (str_equal_text(&p_sess->ftp_arg_str, "UMASK"))
- {
- handle_site_umask(p_sess, &s_site_args_str);
- }
- else if (str_equal_text(&p_sess->ftp_arg_str, "HELP"))
- {
- if (tunable_write_enable &&
- tunable_chmod_enable)
- {
- vsf_cmdio_write(p_sess, FTP_SITEHELP, "CHMOD UMASK HELP");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_SITEHELP, "UMASK HELP");
- }
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown SITE command.");
- }
- }
- static void
- handle_site_chmod(struct vsf_session* p_sess, struct mystr* p_arg_str)
- {
- static struct mystr s_chmod_file_str;
- unsigned int perms;
- int retval;
- if (str_isempty(p_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
- return;
- }
- str_split_char(p_arg_str, &s_chmod_file_str, ' ');
- if (str_isempty(&s_chmod_file_str))
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
- return;
- }
- resolve_tilde(&s_chmod_file_str, p_sess);
- vsf_log_start_entry(p_sess, kVSFLogEntryChmod);
- str_copy(&p_sess->log_str, &s_chmod_file_str);
- prepend_path_to_filename(&p_sess->log_str);
- str_append_char(&p_sess->log_str, ' ');
- str_append_str(&p_sess->log_str, p_arg_str);
- if (!vsf_access_check_file(&s_chmod_file_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- /* Don't worry - our chmod() implementation only allows 0 - 0777 */
- perms = str_octal_to_uint(p_arg_str);
- retval = str_chmod(&s_chmod_file_str, perms);
- if (vsf_sysutil_retval_is_error(retval))
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL, "SITE CHMOD command failed.");
- }
- else
- {
- vsf_log_do_log(p_sess, 1);
- vsf_cmdio_write(p_sess, FTP_CHMODOK, "SITE CHMOD command ok.");
- }
- }
- static void
- handle_site_umask(struct vsf_session* p_sess, struct mystr* p_arg_str)
- {
- static struct mystr s_umask_resp_str;
- if (str_isempty(p_arg_str))
- {
- /* Empty arg => report current umask */
- str_alloc_text(&s_umask_resp_str, "Your current UMASK is ");
- str_append_text(&s_umask_resp_str,
- vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
- }
- else
- {
- /* Set current umask */
- unsigned int new_umask = str_octal_to_uint(p_arg_str);
- vsf_sysutil_set_umask(new_umask);
- str_alloc_text(&s_umask_resp_str, "UMASK set to ");
- str_append_text(&s_umask_resp_str,
- vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
- }
- vsf_cmdio_write_str(p_sess, FTP_UMASKOK, &s_umask_resp_str);
- }
- static void
- handle_appe(struct vsf_session* p_sess)
- {
- handle_upload_common(p_sess, 1, 0);
- }
- static void
- handle_mdtm(struct vsf_session* p_sess)
- {
- static struct mystr s_filename_str;
- static struct vsf_sysutil_statbuf* s_p_statbuf;
- int do_write = 0;
- long modtime = 0;
- struct str_locate_result loc = str_locate_char(&p_sess->ftp_arg_str, ' ');
- int retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
- if (tunable_mdtm_write && retval != 0 && loc.found &&
- vsf_sysutil_isdigit(str_get_char_at(&p_sess->ftp_arg_str, 0)))
- {
- if (loc.index == 8 || loc.index == 14 ||
- (loc.index > 15 && str_get_char_at(&p_sess->ftp_arg_str, 14) == '.'))
- {
- do_write = 1;
- }
- }
- if (do_write != 0)
- {
- str_split_char(&p_sess->ftp_arg_str, &s_filename_str, ' ');
- modtime = vsf_sysutil_parse_time(str_getbuf(&p_sess->ftp_arg_str));
- str_copy(&p_sess->ftp_arg_str, &s_filename_str);
- }
- resolve_tilde(&p_sess->ftp_arg_str, p_sess);
- if (!vsf_access_check_file(&p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
- return;
- }
- if (do_write && tunable_write_enable &&
- (tunable_anon_other_write_enable || !p_sess->is_anonymous))
- {
- retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
- if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL,
- "Could not set file modification time.");
- }
- else
- {
- retval = vsf_sysutil_setmodtime(
- str_getbuf(&p_sess->ftp_arg_str), modtime, tunable_use_localtime);
- if (retval != 0)
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL,
- "Could not set file modification time.");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_MDTMOK,
- "File modification time set.");
- }
- }
- }
- else
- {
- if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
- {
- vsf_cmdio_write(p_sess, FTP_FILEFAIL,
- "Could not get file modification time.");
- }
- else
- {
- static struct mystr s_mdtm_res_str;
- str_alloc_text(&s_mdtm_res_str,
- vsf_sysutil_statbuf_get_numeric_date(
- s_p_statbuf, tunable_use_localtime));
- vsf_cmdio_write_str(p_sess, FTP_MDTMOK, &s_mdtm_res_str);
- }
- }
- }
- static void
- handle_eprt(struct vsf_session* p_sess)
- {
- static struct mystr s_part1_str;
- static struct mystr s_part2_str;
- static struct mystr s_scopeid_str;
- int proto;
- int port;
- const unsigned char* p_raw_addr;
- int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
- port_cleanup(p_sess);
- pasv_cleanup(p_sess);
- str_copy(&s_part1_str, &p_sess->ftp_arg_str);
- str_split_char(&s_part1_str, &s_part2_str, '|');
- if (!str_isempty(&s_part1_str))
- {
- goto bad_eprt;
- }
- /* Split out the protocol and check it */
- str_split_char(&s_part2_str, &s_part1_str, '|');
- proto = str_atoi(&s_part2_str);
- if (proto < 1 || proto > 2 || (!is_ipv6 && proto == 2))
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT protocol.");
- return;
- }
- /* Split out address and parse it */
- str_split_char(&s_part1_str, &s_part2_str, '|');
- if (proto == 2)
- {
- str_split_char(&s_part1_str, &s_scopeid_str, '%');
- p_raw_addr = vsf_sysutil_parse_ipv6(&s_part1_str);
- }
- else
- {
- p_raw_addr = vsf_sysutil_parse_ipv4(&s_part1_str);
- }
- if (!p_raw_addr)
- {
- goto bad_eprt;
- }
- /* Split out port and parse it */
- str_split_char(&s_part2_str, &s_part1_str, '|');
- if (!str_isempty(&s_part1_str) || str_isempty(&s_part2_str))
- {
- goto bad_eprt;
- }
- port = str_atoi(&s_part2_str);
- if (port < 0 || port > 65535)
- {
- goto bad_eprt;
- }
- vsf_sysutil_sockaddr_clone(&p_sess->p_port_sockaddr, p_sess->p_local_addr);
- if (proto == 2)
- {
- vsf_sysutil_sockaddr_set_ipv6addr(p_sess->p_port_sockaddr, p_raw_addr);
- }
- else
- {
- vsf_sysutil_sockaddr_set_ipv4addr(p_sess->p_port_sockaddr, p_raw_addr);
- }
- vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, (unsigned short) port);
- /* SECURITY:
- * 1) Reject requests not connecting to the control socket IP
- * 2) Reject connects to privileged ports
- */
- if (!tunable_port_promiscuous)
- {
- if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr,
- p_sess->p_port_sockaddr) ||
- vsf_sysutil_is_port_reserved((unsigned short) port))
- {
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal EPRT command.");
- port_cleanup(p_sess);
- return;
- }
- }
- vsf_cmdio_write(p_sess, FTP_EPRTOK,
- "EPRT command successful. Consider using EPSV.");
- return;
- bad_eprt:
- vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT command.");
- }
- /* XXX - add AUTH etc. */
- static void
- handle_help(struct vsf_session* p_sess)
- {
- vsf_cmdio_write_hyphen(p_sess, FTP_HELP,
- "The following commands are recognized.");
- vsf_cmdio_write_raw(p_sess,
- " ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n");
- vsf_cmdio_write_raw(p_sess,
- " MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n");
- vsf_cmdio_write_raw(p_sess,
- " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
- vsf_cmdio_write_raw(p_sess,
- " XPWD XRMD\r\n");
- vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
- }
- static void
- handle_stou(struct vsf_session* p_sess)
- {
- handle_upload_common(p_sess, 0, 1);
- }
- static void
- get_unique_filename(struct mystr* p_outstr, const struct mystr* p_base_str)
- {
- /* Use silly wu-ftpd algorithm for compatibility. It has races of course, if
- * two sessions are using the same file prefix at the same time.
- */
- static struct vsf_sysutil_statbuf* s_p_statbuf;
- static struct mystr s_stou_str;
- unsigned int suffix = 1;
- const struct mystr* p_real_base_str = p_base_str;
- int retval;
- if (str_isempty(p_real_base_str))
- {
- str_alloc_text(&s_stou_str, "STOU");
- p_real_base_str = &s_stou_str;
- }
- else
- {
- /* Do not add any suffix at all if the name is not taken. */
- retval = str_stat(p_real_base_str, &s_p_statbuf);
- if (vsf_sysutil_retval_is_error(retval))
- {
- str_copy(p_outstr, p_real_base_str);
- return;
- }
- }
- while (1)
- {
- str_copy(p_outstr, p_real_base_str);
- str_append_char(p_outstr, '.');
- str_append_ulong(p_outstr, suffix);
- retval = str_stat(p_outstr, &s_p_statbuf);
- if (vsf_sysutil_retval_is_error(retval))
- {
- return;
- }
- ++suffix;
- }
- }
- static void
- handle_stat(struct vsf_session* p_sess)
- {
- vsf_cmdio_write_hyphen(p_sess, FTP_STATOK, "FTP server status:");
- vsf_cmdio_write_raw(p_sess, " Connected to ");
- vsf_cmdio_write_raw(p_sess, str_getbuf(&p_sess->remote_ip_str));
- vsf_cmdio_write_raw(p_sess, "\r\n");
- vsf_cmdio_write_raw(p_sess, " Logged in as ");
- vsf_cmdio_write_raw(p_sess, str_getbuf(&p_sess->user_str));
- vsf_cmdio_write_raw(p_sess, "\r\n");
- vsf_cmdio_write_raw(p_sess, " TYPE: ");
- if (p_sess->is_ascii)
- {
- vsf_cmdio_write_raw(p_sess, "ASCII\r\n");
- }
- else
- {
- vsf_cmdio_write_raw(p_sess, "BINARY\r\n");
- }
- if (p_sess->bw_rate_max == 0)
- {
- vsf_cmdio_write_raw(p_sess, " No session bandwidth limit\r\n");
- }
- else
- {
- vsf_cmdio_write_raw(p_sess, " Session bandwidth limit in byte/s is ");
- vsf_cmdio_write_raw(p_sess, vsf_sysutil_ulong_to_str(p_sess->bw_rate_max));
- vsf_cmdio_write_raw(p_sess, "\r\n");
- }
- if (tunable_idle_session_timeout == 0)
- {
- vsf_cmdio_write_raw(p_sess, " No session timeout\r\n");
- }
- else
- {
- vsf_cmdio_write_raw(p_sess, " Session timeout in seconds is ");
- vsf_cmdio_write_raw(p_sess,
- vsf_sysutil_ulong_to_str(tunable_idle_session_timeout));
- vsf_cmdio_write_raw(p_sess, "\r\n");
- }
- if (p_sess->control_use_ssl)
- {
- vsf_cmdio_write_raw(p_sess, " Control connection is encrypted\r\n");
- }
- else
- {
- vsf_cmdio_write_raw(p_sess, " Control connection is plain text\r\n");
- }
- if (p_sess->data_use_ssl)
- {
- vsf_cmdio_write_raw(p_sess, " Data connections will be encrypted\r\n");
- }
- else
- {
- vsf_cmdio_write_raw(p_sess, " Data connections will be plain text\r\n");
- }
- if (p_sess->num_clients > 0)
- {
- vsf_cmdio_write_raw(p_sess, " At session startup, client count was ");
- vsf_cmdio_write_raw(p_sess, vsf_sysutil_ulong_to_str(p_sess->num_clients));
- 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");
- }
- static void
- handle_stat_file(struct vsf_session* p_sess)
- {
- handle_dir_common(p_sess, 1, 1);
- }
- static int
- data_transfer_checks_ok(struct vsf_session* p_sess)
- {
- if (!pasv_active(p_sess) && !port_active(p_sess))
- {
- vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first.");
- return 0;
- }
- if (tunable_ssl_enable && !p_sess->data_use_ssl &&
- ((tunable_force_local_data_ssl && !p_sess->is_anonymous) ||
- (tunable_force_anon_data_ssl && p_sess->is_anonymous)))
- {
- vsf_cmdio_write(
- p_sess, FTP_NEEDENCRYPT, "Data connections must be encrypted.");
- return 0;
- }
- return 1;
- }
- static void
- resolve_tilde(struct mystr* p_str, struct vsf_session* p_sess)
- {
- unsigned int len = str_getlen(p_str);
- if (len > 0 && str_get_char_at(p_str, 0) == '~')
- {
- static struct mystr s_rhs_str;
- if (len == 1 || str_get_char_at(p_str, 1) == '/')
- {
- str_split_char(p_str, &s_rhs_str, '~');
- str_copy(p_str, &p_sess->home_str);
- str_append_str(p_str, &s_rhs_str);
- }
- else if (tunable_tilde_user_enable && len > 1)
- {
- static struct mystr s_user_str;
- struct vsf_sysutil_user* p_user;
- str_copy(&s_rhs_str, p_str);
- str_split_char(&s_rhs_str, &s_user_str, '~');
- str_split_char(&s_user_str, &s_rhs_str, '/');
- p_user = str_getpwnam(&s_user_str);
- if (p_user != 0)
- {
- str_alloc_text(p_str, vsf_sysutil_user_get_homedir(p_user));
- if (!str_isempty(&s_rhs_str))
- {
- str_append_char(p_str, '/');
- str_append_str(p_str, &s_rhs_str);
- }
- }
- }
- }
- }
- static void handle_logged_in_user(struct vsf_session* p_sess)
- {
- if (p_sess->is_anonymous)
- {
- vsf_cmdio_write(p_sess, FTP_LOGINERR, "Can't change from guest user.");
- }
- else if (str_equal(&p_sess->user_str, &p_sess->ftp_arg_str))
- {
- vsf_cmdio_write(p_sess, FTP_GIVEPWORD, "Any password will do.");
- }
- else
- {
- vsf_cmdio_write(p_sess, FTP_LOGINERR, "Can't change to another user.");
- }
- }
- static void handle_logged_in_pass(struct vsf_session* p_sess)
- {
- vsf_cmdio_write(p_sess, FTP_LOGINOK, "Already logged in.");
- }
- static void
- handle_http(struct vsf_session* p_sess)
- {
- /* Warning: Doesn't respect cmds_allowed etc. because there is currently only
- * one command (GET)!
- * HTTP likely doesn't respect other important FTP options. I don't think
- * logging works.
- */
- if (!tunable_download_enable)
- {
- bug("HTTP needs download - fix your config");
- }
- /* Eat the HTTP headers, which we don't care about. */
- do
- {
- vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str,
- &p_sess->ftp_arg_str, 1);
- }
- while (!str_isempty(&p_sess->ftp_cmd_str) ||
- !str_isempty(&p_sess->ftp_arg_str));
- vsf_cmdio_write_raw(p_sess, "HTTP/1.1 200 OK\r\n");
- vsf_cmdio_write_raw(p_sess, "ftpz alpha\r\n");
- vsf_cmdio_write_raw(p_sess, "Connection: close\r\n");
- vsf_cmdio_write_raw(p_sess, "X-Frame-Options: SAMEORIGIN\r\n");
- vsf_cmdio_write_raw(p_sess, "X-Content-Type-Options: nosniff\r\n");
- /* Split the path from the HTTP/1.x */
- str_split_char(&p_sess->http_get_arg, &p_sess->ftp_arg_str, ' ');
- str_copy(&p_sess->ftp_arg_str, &p_sess->http_get_arg);
- str_split_char(&p_sess->http_get_arg, &p_sess->ftp_cmd_str, '.');
- str_upper(&p_sess->ftp_cmd_str);
- if (str_equal_text(&p_sess->ftp_cmd_str, "HTML") ||
- str_equal_text(&p_sess->ftp_cmd_str, "HTM"))
- {
- vsf_cmdio_write_raw(p_sess, "Content-Type: text/html\r\n");
- }
- else
- {
- vsf_cmdio_write_raw(p_sess, "Content-Type: dunno\r\n");
- }
- vsf_cmdio_write_raw(p_sess, "\r\n");
- p_sess->is_ascii = 0;
- p_sess->restart_pos = 0;
- handle_retr(p_sess, 1);
- if (vsf_log_entry_pending(p_sess))
- {
- vsf_log_do_log(p_sess, 0);
- }
- vsf_sysutil_exit(0);
- }
|