diff --git a/src/sbbs3/file.cpp b/src/sbbs3/file.cpp index 29c719603d175b46c1734eb60d47fe75fda5406f..efb1b0d0abe17b1cddfd9044b3491991218a3d93 100644 --- a/src/sbbs3/file.cpp +++ b/src/sbbs3/file.cpp @@ -145,7 +145,7 @@ bool sbbs_t::checkfname(const char *fname) hacklog("Filename", fname); return false; } - return allowed_filename(fname); + return allowed_filename(&cfg, fname); } long sbbs_t::delfiles(const char *inpath, const char *spec, size_t keep) diff --git a/src/sbbs3/filedat.c b/src/sbbs3/filedat.c index aa1732e55216b562801f2a37d32385f6f37c0fab..e9427fec7dac8774769886c5ea9c7d0eaab44349 100644 --- a/src/sbbs3/filedat.c +++ b/src/sbbs3/filedat.c @@ -1250,31 +1250,49 @@ bool illegal_filename(const char *fname) { size_t len = strlen(fname); - if(*fname == '-') + if(len < 1) return true; if(strcspn(fname, ILLEGAL_FILENAME_CHARS) != len) return true; if(strstr(fname, "..") != NULL) return true; + if(*fname == '-') // leading dash is a problem for argument parsing + return true; + if(*fname == '.') // leading dot hides files on *nix + return true; + if(*fname == ' ') // leading space is a problem for argument parsing (shells) + return true; + if(fname[len - 1] == '.') // a trailing dot is a problem for Windows + return true; + if(fname[len - 1] == ' ') // a trailing space is a problem for argument parsing (shells) + return true; + for(size_t i = 0; i < len; i++) { + if(IS_CONTROL(fname[i])) // control characters in filenames are evil + return true; + } + return false; } -/*****************************************************************************/ -/* Checks the filename 'fname' for invalid symbol or character sequences */ -/*****************************************************************************/ -bool allowed_filename(const char *fname) +/****************************************************************************/ +/* Checks if the filename chars meet the system requirements for upload */ +/* Assumes the filename has already been checked with illegal_filename() */ +/****************************************************************************/ +bool allowed_filename(scfg_t* cfg, const char *fname) { size_t len = strlen(fname); - if(len < 1) return false; + if(cfg->file_misc & FM_SAFEST) + return safest_filename(fname); + + uchar min = (cfg->file_misc & FM_SPACES) ? ' ' : '!'; + uchar max = (cfg->file_misc & FM_EXASCII) ? 0xff : 0x7f; + for(size_t i = 0; i < len; i++) { - if(fname[i] <= ' ') + if((uchar)fname[i] < min || (uchar)fname[i] > max) return false; } - if(*fname == '.' // leading dot hides files on *nix - || fname[len - 1] == '.') // a trailing dot is a problem for Win32 - return false; return true; } diff --git a/src/sbbs3/filedat.h b/src/sbbs3/filedat.h index 6b2ec95816d90b9d9e9b2f5830265928d153897e..f3ac6278c00498ffa6e413f55ad751d439b69eaa 100644 --- a/src/sbbs3/filedat.h +++ b/src/sbbs3/filedat.h @@ -59,7 +59,7 @@ DLLEXPORT bool removefile(scfg_t*, uint dirnum, const char* filename); DLLEXPORT char* format_filename(const char* fname, char* buf, size_t, bool pad); DLLEXPORT bool safest_filename(const char* fname); DLLEXPORT bool illegal_filename(const char* fname); -DLLEXPORT bool allowed_filename(const char* fname); +DLLEXPORT bool allowed_filename(scfg_t*, const char* fname); DLLEXPORT bool extract_diz(scfg_t*, file_t*, str_list_t diz_fname, char* path, size_t); DLLEXPORT char* read_diz(const char* path, struct sauce_charinfo*); DLLEXPORT char* format_diz(const char* src, char* dest, size_t maxlen, int width, bool ice_color); diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c index 62c228ced1e0d68251107d2ceec6687f2305234b..c7b64237402b9ba544ac5247b122aea0c3f9c97c 100644 --- a/src/sbbs3/ftpsrvr.c +++ b/src/sbbs3/ftpsrvr.c @@ -4573,7 +4573,7 @@ static void ctrl_thread(void* arg) ftp_hacklog("FTP FILENAME", user.alias, cmd, host_name, &ftp.client_addr); continue; } - if(!allowed_filename(p)) { + if(!allowed_filename(&scfg, p)) { lprintf(LOG_WARNING,"%04d <%s> !UNALLOWED FILENAME ATTEMPT by %s [%s]: %s" ,sock, user.alias, host_name, host_ip, p); sockprintf(sock,sess,"553 Unallowed filename attempt"); diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h index 07e027f804fa490b2e87cf8f51c835c4badabe41..881862bb70c9c0c8d3669ecdc433a99de613dfc5 100644 --- a/src/sbbs3/sbbsdefs.h +++ b/src/sbbs3/sbbsdefs.h @@ -261,6 +261,10 @@ #define DIR_NOHASH (1<<22) /* Don't auto calculate/store file content hashes */ #define DIR_FILETAGS (1<<23) /* Allow files to have user-specified tags */ +#define FM_SAFEST (1<<1) /* Allow safest filenames to be uploaded only */ +#define FM_SPACES (1<<2) /* Allow spaces in uploaded filenames */ +#define FM_EXASCII (1<<3) /* Allow extended-ASCII (or UTF-8) in uploaded filenames */ + /* Bit values for cfg.msg_misc (upper 16-bits default to on) */ #define MM_REALNAME (1<<16) /* Allow receipt of e-mail using real names */ #define MM_EMAILSIG (1<<17) /* Include user signatures in e-mail msgs */ diff --git a/src/sbbs3/scfg/scfgxfr1.c b/src/sbbs3/scfg/scfgxfr1.c index f77f311af60ca0a5b0fe1d5d86b81e38e4dfeb37..2c562ef791ad1c9086f47934badbe21d459e4402 100644 --- a/src/sbbs3/scfg/scfgxfr1.c +++ b/src/sbbs3/scfg/scfgxfr1.c @@ -69,6 +69,13 @@ void xfer_opts() else strcpy(str,"Disabled"); sprintf(opt[i++],"%-33.33s%s","Leech Protocol Detection",str); + if(cfg.file_misc & FM_SAFEST) + SAFECOPY(str, "Safest Subset"); + else + SAFEPRINTF2(str, "Most %s, %scluding Spaces" + ,cfg.file_misc & FM_EXASCII ? "CP437" : "ASCII" + ,cfg.file_misc & FM_SPACES ? "In" : "Ex"); + sprintf(opt[i++], "%-33.33s%s", "Allow Filename Characters", str); strcpy(opt[i++],"Viewable Files..."); strcpy(opt[i++],"Testable Files..."); strcpy(opt[i++],"Download Events..."); @@ -186,7 +193,7 @@ void xfer_opts() "\n" "This option allows you to adjust the sensitivity of the leech protocol\n" "detection feature. This value is the minimum length of transfer time\n" - "(in seconds) that must elapse before an aborted tranfser will be\n" + "(in seconds) that must elapse before an aborted transfer will be\n" "considered a possible leech attempt.\n" ; uifc.input(WIN_MID,0,0 @@ -194,6 +201,82 @@ void xfer_opts() ,ultoa(cfg.leech_sec,tmp,10),3,K_EDIT|K_NUMBER); cfg.leech_sec=atoi(tmp); break; + case __COUNTER__: /* Uploaded Filename characters allowed */ + i = 0; + strcpy(opt[i++], "Safest Subset Only (A-Z, a-z, 0-9, -, _, and .)"); + strcpy(opt[i++], "Most ASCII Characters, Excluding Spaces"); + strcpy(opt[i++], "Most ASCII Characters, Including Spaces"); + strcpy(opt[i++], "Most CP437 Characters, Excluding Spaces"); + strcpy(opt[i++], "Most CP437 Characters, Including Spaces"); + opt[i][0] = '\0'; + if(cfg.file_misc & FM_SAFEST) + j = 0; + else { + j = 1; + if(cfg.file_misc & FM_EXASCII) + j = 3; + if(cfg.file_misc & FM_SPACES) + j++; + } + uifc.helpbuf= + "`Allowed Characters in Uploaded Filenames:`\n" + "\n" + "Here you can control which characters will be allowed in the names of\n" + "files uploaded by users (assuming you allow file uploads at all).\n" + "\n" + "The `Safest` (most compatible) filename characters to allow are:\n" + "`" SAFEST_FILENAME_CHARS "`\n" + "\n" + "`Spaces` may be allowed in filenames, but may cause issues with some file\n" + "transfer protocol drivers or clients (older/MS-DOS software).\n" + "\n" + "Filenames that are most often troublesome (including those in your\n" + "`text/file.can` file) are `always disallowed`:\n" + " Filenames beginning with dash (`-`)\n" + " Filenames beginning or ending in space\n" + " Filenames beginning or ending in period (`.`)\n" + " Filenames containing consecutive periods (`..`)\n" + " Filenames containing illegal characters (`" ILLEGAL_FILENAME_CHARS "`)\n" + " Filenames containing control characters (ASCII 0-31 and 127)\n" + ; + i = uifc.list(0, 0, 0, 0, &j, NULL, "Allowed Characters in Uploaded Filenames", opt); + switch(i) { + case 0: + if(cfg.file_misc != FM_SAFEST) { + cfg.file_misc |= FM_SAFEST; + cfg.file_misc &= ~(FM_EXASCII | FM_SPACES); + uifc.changes = TRUE; + } + break; + case 1: + if(cfg.file_misc) { + cfg.file_misc &= ~(FM_SAFEST | FM_SPACES | FM_EXASCII); + uifc.changes = TRUE; + } + break; + case 2: + if(cfg.file_misc != FM_SPACES) { + cfg.file_misc &= ~(FM_SAFEST | FM_EXASCII); + cfg.file_misc |= FM_SPACES; + uifc.changes = TRUE; + } + break; + case 3: + if(cfg.file_misc != FM_EXASCII) { + cfg.file_misc &= ~(FM_SAFEST | FM_SPACES); + cfg.file_misc |= FM_EXASCII; + uifc.changes = TRUE; + } + break; + case 4: + if(cfg.file_misc != (FM_EXASCII | FM_SPACES)) { + cfg.file_misc &= ~(FM_SAFEST); + cfg.file_misc |= FM_EXASCII | FM_SPACES; + uifc.changes = TRUE; + } + break; + } + break; case __COUNTER__: /* Viewable file types */ while(1) { for(i=0;i<cfg.total_fviews && i<MAX_OPTS;i++)