aboutsummaryrefslogtreecommitdiffstats
path: root/FICS
diff options
context:
space:
mode:
Diffstat (limited to 'FICS')
-rw-r--r--FICS/ISC_LICENSE2
-rw-r--r--FICS/adminproc.c111
-rw-r--r--FICS/algcheck.c15
-rw-r--r--FICS/board.c15
-rw-r--r--FICS/build.mk40
-rw-r--r--FICS/command.c50
-rw-r--r--FICS/comproc.c94
-rw-r--r--FICS/fics_addplayer.c8
-rw-r--r--FICS/ficsmain.c8
-rw-r--r--FICS/ficsmain.h13
-rw-r--r--FICS/formula.c2
-rw-r--r--FICS/gamedb.c114
-rw-r--r--FICS/gamedb.h7
-rw-r--r--FICS/gameproc.c6
-rw-r--r--FICS/legal2.c2
-rw-r--r--FICS/lists.c16
-rw-r--r--FICS/makerank.c18
-rw-r--r--FICS/matchproc.c5
-rw-r--r--FICS/network.c44
-rw-r--r--FICS/obsproc.c67
-rw-r--r--FICS/playerdb.c221
-rw-r--r--FICS/ratings.c113
-rw-r--r--FICS/ratings.h5
-rw-r--r--FICS/talkproc.c6
-rw-r--r--FICS/utils.c144
-rw-r--r--FICS/utils.h11
-rw-r--r--FICS/vers.c2
27 files changed, 860 insertions, 279 deletions
diff --git a/FICS/ISC_LICENSE b/FICS/ISC_LICENSE
index eba7139..915b183 100644
--- a/FICS/ISC_LICENSE
+++ b/FICS/ISC_LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2023, 2024 Markus Uhlin <maxxe@rpblc.net>
+Copyright (c) 2023 - 2025 Markus Uhlin <maxxe@rpblc.net>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/FICS/adminproc.c b/FICS/adminproc.c
index 94a9448..9f192bc 100644
--- a/FICS/adminproc.c
+++ b/FICS/adminproc.c
@@ -19,11 +19,16 @@
#include <sys/param.h>
#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
#include "adminproc.h"
#include "command.h"
#include "comproc.h"
#include "fics_getsalt.h"
+#include "ficsmain.h"
#include "gamedb.h"
#include "gameproc.h"
#include "maxxes-utils.h"
@@ -171,6 +176,7 @@ create_news_file(int p, param_list param, int admin)
{
FILE *fp;
char filename[MAX_FILENAME_SIZE] = { '\0' };
+ int fd;
ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
@@ -183,10 +189,14 @@ create_news_file(int p, param_list param, int admin)
msnprintf(filename, sizeof filename, "%s/adminnews.%d",
news_dir,
param[0].val.integer);
- if ((fp = fopen(filename, "w")) != NULL) {
+ fd = open(filename, g_open_flags[1], g_open_modes);
+ if (fd < 0)
+ return COM_FAILED;
+ else if ((fp = fdopen(fd, "w")) != NULL) {
fprintf(fp, "%s\n", param[1].val.string);
fclose(fp);
- }
+ } else
+ close(fd);
}
} else {
if (param[0].val.integer > num_news) {
@@ -196,10 +206,14 @@ create_news_file(int p, param_list param, int admin)
msnprintf(filename, sizeof filename, "%s/news.%d",
news_dir,
param[0].val.integer);
- if ((fp = fopen(filename, "w")) != NULL) {
+ fd = open(filename, g_open_flags[1], g_open_modes);
+ if (fd < 0)
+ return COM_FAILED;
+ else if ((fp = fdopen(fd, "w")) != NULL) {
fprintf(fp, "%s\n", param[1].val.string);
fclose(fp);
- }
+ } else
+ close(fd);
}
}
@@ -212,11 +226,19 @@ add_item(char *new_item, char *filename)
FILE *new_fp, *old_fp;
char junk[MAX_LINE_SIZE] = { '\0' };
char tmp_file[MAX_FILENAME_SIZE] = { '\0' };
+ int fd;
msnprintf(tmp_file, sizeof tmp_file, "%s/.tmp.idx", news_dir);
- if ((new_fp = fopen(tmp_file, "w")) == NULL)
+ fd = open(tmp_file, g_open_flags[1], g_open_modes);
+
+ if (fd < 0)
return 0;
+ else if ((new_fp = fdopen(fd, "w")) == NULL) {
+ close(fd);
+ return 0;
+ }
+
fprintf(new_fp, "%s", new_item);
if ((old_fp = fopen(filename, "r")) == NULL)
@@ -367,12 +389,13 @@ PUBLIC int
com_anews(int p, param_list param)
{
FILE *fp = NULL;
- char *junkp = NULL;
char count[10] = { '\0' };
char filename[MAX_FILENAME_SIZE] = { '\0' };
char junk[MAX_LINE_SIZE] = { '\0' };
+ char *junkp = NULL;
+ const char *v_scan_junk = "%" SCNd64 " " "%9s";
int found = 0;
- long int lval = 0;
+ int64_t lval = 0;
time_t crtime = 0;
msnprintf(filename, sizeof filename, "%s/newadminnews.index", news_dir);
@@ -382,7 +405,6 @@ com_anews(int p, param_list param)
return COM_OK;
}
-#define SCAN_JUNK "%ld %9s"
_Static_assert(9 < ARRAY_SIZE(count), "Array too small");
if (param[0].type == 0) {
@@ -400,7 +422,7 @@ com_anews(int p, param_list param)
fclose(fp);
return COM_FAILED;
}
- if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) {
+ if (sscanf(junk, v_scan_junk, &lval, count) != 2) {
warnx("%s: sscanf() error: too few items", __func__);
fclose(fp);
return COM_FAILED;
@@ -429,7 +451,7 @@ com_anews(int p, param_list param)
fclose(fp);
return COM_FAILED;
}
- if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) {
+ if (sscanf(junk, v_scan_junk, &lval, count) != 2) {
warnx("%s: sscanf() error: too few items", __func__);
fclose(fp);
return COM_FAILED;
@@ -453,8 +475,11 @@ com_anews(int p, param_list param)
break;
if (strlen(junk) > 1) {
- if (sscanf(junkp, SCAN_JUNK, &lval, count) != 2)
- warnx("%s: sscanf() error...", __func__);
+ if (sscanf(junkp, v_scan_junk, &lval,
+ count) != 2) {
+ warnx("%s: sscanf() error...",
+ __func__);
+ }
crtime = lval;
@@ -613,45 +638,57 @@ com_checkPLAYER(int p, param_list param)
pprintf(p, "%s is not logged in.\n", v_player);
stolower(v_player);
- pprintf(p, "name = %s\n", parray[p1].name);
- pprintf(p, "login = %s\n", parray[p1].login);
- pprintf(p, "fullName = %s\n", (parray[p1].fullName ?
- parray[p1].fullName : "(none)"));
- pprintf(p, "emailAddress = %s\n", (parray[p1].emailAddress ?
- parray[p1].emailAddress : "(none)"));
- pprintf(p, "adminLevel = %d\n", parray[p1].adminLevel);
+ pprintf(p, "name = %s\n", parray[p1].name);
+ pprintf(p, "login = %s\n", parray[p1].login);
+ pprintf(p, "fullName = %s\n",
+ (parray[p1].fullName
+ ? parray[p1].fullName
+ : "(none)"));
+ pprintf(p, "emailAddress = %s\n",
+ (parray[p1].emailAddress
+ ? parray[p1].emailAddress
+ : "(none)"));
+ pprintf(p, "adminLevel = %d\n", parray[p1].adminLevel);
#if 0
pprintf(p, "network_player = %d\n", parray[p1].network_player);
#endif
- pprintf(p, "lastHost = %s\n", dotQuad(parray[p1].lastHost));
- pprintf(p, "num_comments = %d\n", parray[p1].num_comments);
+ pprintf(p, "lastHost = %s\n", dotQuad(parray[p1].lastHost));
+ pprintf(p, "num_comments = %d\n", parray[p1].num_comments);
player_remove(p1);
return COM_OK;
} else {
+ char tbuf[30] = { '\0' };
+
p1 = p1 - 1;
pprintf(p, "%s is number %d in parray of size %d\n", v_player, p1,
(p_num + 1));
- pprintf(p, "name = %s\n", parray[p1].name);
- pprintf(p, "login = %s\n", parray[p1].login);
- pprintf(p, "fullName = %s\n", (parray[p1].fullName ?
- parray[p1].fullName : "(none)"));
- pprintf(p, "emailAddress = %s\n", (parray[p1].emailAddress ?
- parray[p1].emailAddress : "(none)"));
- pprintf(p, "socket = %d\n", parray[p1].socket);
- pprintf(p, "registered = %d\n", parray[p1].registered);
- pprintf(p, "last_tell = %d\n", parray[p1].last_tell);
- pprintf(p, "last_channel = %d\n", parray[p1].last_channel);
- pprintf(p, "logon_time = %s",
- ctime((time_t *) &parray[p1].logon_time));
- pprintf(p, "adminLevel = %d\n", parray[p1].adminLevel);
+ pprintf(p, "name = %s\n", parray[p1].name);
+ pprintf(p, "login = %s\n", parray[p1].login);
+ pprintf(p, "fullName = %s\n",
+ (parray[p1].fullName
+ ? parray[p1].fullName
+ : "(none)"));
+ pprintf(p, "emailAddress = %s\n",
+ (parray[p1].emailAddress
+ ? parray[p1].emailAddress
+ : "(none)"));
+ pprintf(p, "socket = %d\n", parray[p1].socket);
+ pprintf(p, "registered = %d\n", parray[p1].registered);
+ pprintf(p, "last_tell = %d\n", parray[p1].last_tell);
+ pprintf(p, "last_channel = %d\n", parray[p1].last_channel);
+ pprintf(p, "logon_time = %s",
+ (ctime_r(&parray[p1].logon_time, tbuf) != NULL
+ ? &tbuf[0]
+ : "n/a"));
+ pprintf(p, "adminLevel = %d\n", parray[p1].adminLevel);
#if 0
pprintf(p, "network_player = %d\n", parray[p1].network_player);
#endif
- pprintf(p, "thisHost = %s\n", dotQuad(parray[p1].thisHost));
- pprintf(p, "lastHost = %s\n", dotQuad(parray[p1].lastHost));
- pprintf(p, "num_comments = %d\n", parray[p1].num_comments);
+ pprintf(p, "thisHost = %s\n", dotQuad(parray[p1].thisHost));
+ pprintf(p, "lastHost = %s\n", dotQuad(parray[p1].lastHost));
+ pprintf(p, "num_comments = %d\n", parray[p1].num_comments);
}
return COM_OK;
diff --git a/FICS/algcheck.c b/FICS/algcheck.c
index 250c129..15642fd 100644
--- a/FICS/algcheck.c
+++ b/FICS/algcheck.c
@@ -21,11 +21,16 @@
name email yy/mm/dd Change
Richard Nash 93/10/22 Created
Markus Uhlin 24/05/05 Revised
+ Markus Uhlin 25/04/05 alg_parse_move:
+ return ambiguous move on
+ out-of-bounds array read/write.
*/
#include "stdinclude.h"
#include "common.h"
+#include <err.h>
+
#include "algcheck.h"
#include "board.h"
#include "maxxes-utils.h"
@@ -250,6 +255,11 @@ alg_parse_move(char *mstr, game_state_t *gs, move_t *mt)
NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
if ((ff != ALG_UNKNOWN) && (ff != f))
continue;
+ if (r < 0 || r >= 8) {
+ warnx("%s: out-of-bounds array read/write: "
+ "r=%d", __func__, r);
+ return MOVE_AMBIGUOUS;
+ }
if (piecetype(gs->board[f][r]) != piece)
continue;
if (gs->onMove == WHITE) {
@@ -257,6 +267,11 @@ alg_parse_move(char *mstr, game_state_t *gs, move_t *mt)
} else {
tmpr = r - 1;
}
+ if (tmpr < 0 || tmpr >= 8) {
+ warnx("%s: out-of-bounds array read/write: "
+ "tmpr=%d", __func__, tmpr);
+ return MOVE_AMBIGUOUS;
+ }
if (gs->board[tf][tmpr] == NOPIECE) {
if ((gs->ep_possible[((gs->onMove == WHITE) ?
diff --git a/FICS/board.c b/FICS/board.c
index f687b3e..2423024 100644
--- a/FICS/board.c
+++ b/FICS/board.c
@@ -25,6 +25,9 @@
Markus Uhlin 24/04/13 Added usage of the functions
from 'maxxes-utils.h'.
Markus Uhlin 24/06/01 Added and made use of brand().
+ Markus Uhlin 25/04/06 Fixed Clang Tidy warnings.
+ Markus Uhlin 25/09/02 wild_update: fixed file created
+ without restricting permissions.
*/
#include "stdinclude.h"
@@ -34,6 +37,7 @@
#include <limits.h>
#include "board.h"
+#include "ficsmain.h"
#include "gamedb.h"
#include "maxxes-utils.h"
#include "playerdb.h"
@@ -1133,7 +1137,7 @@ board_read_file(char *category, char *gname, game_state_t *gs)
case 'g':
case 'h':
onFile = (c - 'a');
- onRank = -1;
+ onRank = -1; // NOLINT: dead store
break;
case '1':
case '2':
@@ -1161,7 +1165,7 @@ board_read_file(char *category, char *gname, game_state_t *gs)
onColor = -1;
onPiece = -1;
onFile = -1;
- onRank = -1;
+ onRank = -1; // NOLINT: dead store
break;
default:
break;
@@ -1320,13 +1324,18 @@ wild_update(int style)
{
FILE *fp;
char fname[MAX_FILENAME_SIZE + 1];
+ int fd;
int onPiece;
msnprintf(fname, sizeof fname, "%s/wild/%d", board_dir, style);
- if ((fp = fopen(fname, "w")) == NULL) {
+ if ((fd = open(fname, g_open_flags[1], g_open_modes)) < 0) {
warn("%s: can't write file name: %s", __func__, fname);
return;
+ } else if ((fp = fdopen(fd, "w")) == NULL) {
+ warn("%s: can't write file name: %s", __func__, fname);
+ close(fd);
+ return;
}
fprintf(fp, "W:");
diff --git a/FICS/build.mk b/FICS/build.mk
index f8c7d05..5d3d9b2 100644
--- a/FICS/build.mk
+++ b/FICS/build.mk
@@ -37,6 +37,40 @@ OBJS = $(SRC_DIR)adminproc.o\
$(SRC_DIR)variable.o\
$(SRC_DIR)vers.o
+SRCS = $(SRC_DIR)adminproc.c\
+ $(SRC_DIR)algcheck.c\
+ $(SRC_DIR)assert_error.c\
+ $(SRC_DIR)board.c\
+ $(SRC_DIR)command.c\
+ $(SRC_DIR)comproc.c\
+ $(SRC_DIR)eco.c\
+ $(SRC_DIR)fics_getsalt.cpp\
+ $(SRC_DIR)ficslim.cpp\
+ $(SRC_DIR)ficsmain.c\
+ $(SRC_DIR)formula.c\
+ $(SRC_DIR)gamedb.c\
+ $(SRC_DIR)gameproc.c\
+ $(SRC_DIR)iset.cpp\
+ $(SRC_DIR)legal.c\
+ $(SRC_DIR)legal2.c\
+ $(SRC_DIR)lists.c\
+ $(SRC_DIR)matchproc.c\
+ $(SRC_DIR)maxxes-utils.c\
+ $(SRC_DIR)movecheck.c\
+ $(SRC_DIR)multicol.c\
+ $(SRC_DIR)network.c\
+ $(SRC_DIR)obsproc.c\
+ $(SRC_DIR)playerdb.c\
+ $(SRC_DIR)rating_conv.c\
+ $(SRC_DIR)ratings.c\
+ $(SRC_DIR)rmalloc.c\
+ $(SRC_DIR)shutdown.c\
+ $(SRC_DIR)sought.cpp\
+ $(SRC_DIR)talkproc.c\
+ $(SRC_DIR)utils.c\
+ $(SRC_DIR)variable.c\
+ $(SRC_DIR)vers.c
+
AP_OBJS = $(SRC_DIR)fics_addplayer.o
MR_OBJS = $(SRC_DIR)makerank.o
@@ -53,7 +87,9 @@ fics_addplayer: $(INCLUDE_DIR)ficspaths.h $(OBJS) $(AP_OBJS)
$(E) " LINK " $@
$(Q) $(CXX) $(CXXFLAGS) -o $@ $(OBJS) \
$(AP_OBJS) $(AP_LDFLAGS) $(AP_LDLIBS)
-makerank: $(INCLUDE_DIR)ficspaths.h $(MR_OBJS)
+makerank: $(INCLUDE_DIR)ficspaths.h $(OBJS) $(MR_OBJS)
+ strip --strip-symbol=main $(SRC_DIR)ficsmain.o
$(E) " LINK " $@
- $(Q) $(CXX) $(CXXFLAGS) -o $@ $(MR_OBJS) $(MR_LDFLAGS) $(MR_LDLIBS)
+ $(Q) $(CXX) $(CXXFLAGS) -o $@ $(OBJS) \
+ $(MR_OBJS) $(MR_LDFLAGS) $(MR_LDLIBS)
# EOF
diff --git a/FICS/command.c b/FICS/command.c
index 528bdf9..df5d4c2 100644
--- a/FICS/command.c
+++ b/FICS/command.c
@@ -36,6 +36,9 @@
Markus Uhlin 25/03/09 Fixed double free()
Markus Uhlin 25/03/11 Fixed memleak
Markus Uhlin 25/03/16 Fixed use of 32-bit 'time_t'
+ Markus Uhlin 25/07/28 Usage of 'int64_t'
+ Markus Uhlin 25/08/23 Fixed file created without
+ restricting permissions.
*/
#include "stdinclude.h"
@@ -44,6 +47,8 @@
#include <sys/param.h>
#include <err.h>
+#include <inttypes.h>
+#include <stdint.h>
#include "command.h"
#include "command_list.h"
@@ -132,7 +137,11 @@ parse_command(char *com_string, char **comm, char **parameters)
PUBLIC int
alias_lookup(char *tmp, alias_type *alias_list, int numalias)
{
- for (int i = 0; (alias_list[i].comm_name && i < numalias); i++) {
+ if (numalias >= MAX_ALIASES)
+ return -1;
+ for (int i = 0;
+ (i < numalias && alias_list[i].comm_name != NULL);
+ i++) {
if (!strcmp(tmp, alias_list[i].comm_name))
return i;
}
@@ -712,10 +721,11 @@ boot_out(int p, int p1)
PUBLIC void
rscan_news(FILE *fp, int p, time_t lc)
{
- char *junkp = NULL;
char count[10] = { '\0' };
char junk[MAX_LINE_SIZE] = { '\0' };
- long int lval = 0;
+ char *junkp = NULL;
+ const char *scan_fmt = "%" SCNd64 " " "%9s";
+ int64_t lval = 0;
time_t crtime = 0;
if (fgets(junk, sizeof junk, fp) == NULL ||
@@ -724,7 +734,7 @@ rscan_news(FILE *fp, int p, time_t lc)
_Static_assert(ARRAY_SIZE(count) > 9, "Unexpected array size");
- if (sscanf(junk, "%ld %9s", &lval, count) != 2) {
+ if (sscanf(junk, scan_fmt, &lval, count) != 2) {
warnx("%s: sscanf() error: too few items", __func__);
return;
}
@@ -748,13 +758,13 @@ rscan_news(FILE *fp, int p, time_t lc)
PRIVATE void
check_news(int p, int admin)
{
-#define SCAN_JUNK "%ld %9s"
FILE *fp = NULL;
char count[10] = { '\0' };
char filename[MAX_FILENAME_SIZE] = { '\0' };
char junk[MAX_LINE_SIZE] = { '\0' };
char *junkp = NULL;
- long int lval = 0;
+ const char *v_scan_fmt = "%" SCNd64 " " "%9s";
+ int64_t lval = 0;
time_t crtime = 0;
time_t lc = player_lastconnect(p);
@@ -784,7 +794,7 @@ check_news(int p, int admin)
warnx("%s: fgets() error", __func__);
fclose(fp);
return;
- } else if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) {
+ } else if (sscanf(junk, v_scan_fmt, &lval, count) != 2) {
warnx("%s: sscanf() error", __func__);
fclose(fp);
return;
@@ -834,7 +844,7 @@ check_news(int p, int admin)
warnx("%s: fgets() error", __func__);
fclose(fp);
return;
- } else if (sscanf(junk, SCAN_JUNK, &lval, count) != 2) {
+ } else if (sscanf(junk, v_scan_fmt, &lval, count) != 2) {
warnx("%s: sscanf() error", __func__);
fclose(fp);
return;
@@ -1295,23 +1305,32 @@ commands_init(void)
{
FILE *fp, *afp;
char fname[MAX_FILENAME_SIZE];
+ int fd[2];
int i = 0;
+ fp = afp = NULL;
snprintf(fname, sizeof fname, "%s/commands", comhelp_dir);
- if ((fp = fopen(fname, "w")) == NULL) {
+ if ((fd[0] = open(fname, g_open_flags[1], g_open_modes)) < 0) {
+ warn("%s: open: %s", __func__, fname);
+ return;
+ } else if ((fp = fdopen(fd[0], "w")) == NULL) {
warn("%s: could not write commands help file (%s)", __func__,
fname);
+ close(fd[0]);
return;
}
snprintf(fname, sizeof fname, "%s/admin_commands", adhelp_dir);
- if ((afp = fopen(fname, "w")) == NULL) {
+ if ((fd[1] = open(fname, g_open_flags[1], g_open_modes)) < 0) {
+ warn("%s: open: %s", __func__, fname);
+ goto clean_up;
+ } else if ((afp = fdopen(fd[1], "w")) == NULL) {
warn("%s: could not write admin_commands help file (%s)",
__func__, fname);
- fclose(fp);
- return;
+ close(fd[1]);
+ goto clean_up;
}
while (command_list[i].comm_name) {
@@ -1322,8 +1341,11 @@ commands_init(void)
i++;
}
- fclose(fp);
- fclose(afp);
+ clean_up:
+ if (fp)
+ fclose(fp);
+ if (afp)
+ fclose(afp);
}
/* Need to save rated games */
diff --git a/FICS/comproc.c b/FICS/comproc.c
index 31f6064..b2504fd 100644
--- a/FICS/comproc.c
+++ b/FICS/comproc.c
@@ -43,6 +43,11 @@
Markus Uhlin 25/03/16 Fixed untrusted array index.
Markus Uhlin 25/03/25 com_unalias: fixed overflowed
array index read/write.
+ Markus Uhlin 25/07/21 com_who: fixed multiplication
+ result converted to larger type.
+ Markus Uhlin 25/07/24 Fixed use of potentially
+ dangerous functions.
+ Markus Uhlin 25/07/29 Usage of 'int64_t'.
*/
#include "stdinclude.h"
@@ -51,6 +56,9 @@
#include <sys/resource.h>
#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
#include "board.h"
#include "command.h"
@@ -125,17 +133,18 @@ com_more(int p, param_list param)
PUBLIC void
rscan_news2(FILE *fp, int p, int num)
{
- char *junkp;
char count[10] = { '\0' };
char junk[MAX_LINE_SIZE] = { '\0' };
- long int lval;
+ char *junkp;
+ const char *v_scan_fmt = "%" SCNd64 " " "%9s";
+ int64_t lval;
time_t crtime;
if (num == 0)
return;
if (fgets(junk, sizeof junk, fp) == NULL || feof(fp) ||
- sscanf(junk, "%ld %9s", &lval, count) != 2)
+ sscanf(junk, v_scan_fmt, &lval, count) != 2)
return;
rscan_news2(fp, p, num - 1);
@@ -152,12 +161,13 @@ PUBLIC int
com_news(int p, param_list param)
{
FILE *fp = NULL;
- char *junkp = NULL;
char count[10] = { '\0' };
char filename[MAX_FILENAME_SIZE] = { '\0' };
char junk[MAX_LINE_SIZE] = { '\0' };
+ char *junkp = NULL;
+ const char *v_scan_fmt = "%" SCNd64 " " "%9s";
int found = 0;
- long int lval = 0;
+ int64_t lval = 0;
time_t crtime = 0;
snprintf(filename, sizeof filename, "%s/newnews.index", news_dir);
@@ -167,7 +177,6 @@ com_news(int p, param_list param)
return COM_OK;
}
-#define SCAN_JUNK "%ld %9s"
_Static_assert(9 < ARRAY_SIZE(count), "'count' too small");
if (param[0].type == 0) {
@@ -179,7 +188,7 @@ com_news(int p, param_list param)
pprintf(p, "Index of recent news items:\n");
if (fgets(junk, sizeof junk, fp) == NULL ||
- sscanf(junk, SCAN_JUNK, &lval, count) != 2) {
+ sscanf(junk, v_scan_fmt, &lval, count) != 2) {
warnx("%s: error: fgets() or sscanf()", __func__);
fclose(fp);
return COM_FAILED;
@@ -204,7 +213,7 @@ com_news(int p, param_list param)
pprintf(p, "Index of all news items:\n");
if (fgets(junk, sizeof junk, fp) == NULL ||
- sscanf(junk, SCAN_JUNK, &lval, count) != 2) {
+ sscanf(junk, v_scan_fmt, &lval, count) != 2) {
warnx("%s: error: fgets() or sscanf()", __func__);
fclose(fp);
return COM_FAILED;
@@ -230,7 +239,7 @@ com_news(int p, param_list param)
if (fgets(junk, sizeof junk, fp) == NULL || feof(fp))
break;
- if (sscanf(junkp, SCAN_JUNK, &lval, count) != 2)
+ if (sscanf(junkp, v_scan_fmt, &lval, count) != 2)
warnx("%s: sscanf() error...", __func__);
crtime = lval;
@@ -387,11 +396,18 @@ com_stats_rating(char *hdr, statistics *stats, char *dest, const size_t dsize)
stats->num);
if (stats->whenbest) {
+ struct tm res = {0};
+
snprintf(tmp, sizeof tmp, " %d", stats->best);
strlcat(dest, tmp, dsize);
- strftime(tmp, sizeof tmp, " (%d-%b-%y)",
- localtime((time_t *) &stats->whenbest));
- strlcat(dest, tmp, dsize);
+
+ errno = 0;
+
+ if (localtime_r(&stats->whenbest, &res) != NULL) {
+ if (strftime(tmp, sizeof tmp, " (%d-%b-%y)", &res) != 0)
+ strlcat(dest, tmp, dsize);
+ } else
+ warn("%s: localtime_r", __func__);
}
if (strlcat(dest, "\n", dsize) >= dsize) {
@@ -581,9 +597,14 @@ com_stats(int p, param_list param)
if (connected &&
parray[p1].registered &&
(p == p1 || parray[p].adminLevel > 0)) {
- char *timeToStr = ctime((time_t *) &parray[p1].timeOfReg);
+ char timeToStr[30] = { '\0' };
+
+ errno = 0;
+
+ if (ctime_r(&parray[p1].timeOfReg, timeToStr) == NULL)
+ warn("%s: ctime_r", __func__);
+ timeToStr[strcspn(timeToStr, "\n")] = '\0';
- timeToStr[strlen(timeToStr) - 1] = '\0';
pprintf(p, "\n");
onTime = ((time(NULL) - parray[p1].logon_time) +
@@ -745,8 +766,10 @@ plogins(int p, char *fname)
FILE *fp = NULL;
char ipstr[20] = { '\0' };
char loginName[MAX_LOGIN_NAME + 1] = { '\0' };
+ const char *v_scan_fmt = "%" SCNu16 " %19s " "%" SCNd64 " "
+ "%d %19s\n";
int registered = 0;
- long int lval = 0;
+ int64_t lval = 0;
time_t tval = 0;
uint16_t inout = 0;
@@ -759,8 +782,8 @@ plogins(int p, char *fname)
_Static_assert(19 < ARRAY_SIZE(loginName), "'loginName' too small");
while (!feof(fp)) {
- if (fscanf(fp, "%hu %19s %ld %d %19s\n", &inout, loginName,
- &lval, &registered, ipstr) != 5) {
+ if (fscanf(fp, v_scan_fmt, &inout, loginName, &lval,
+ &registered, ipstr) != 5) {
fprintf(stderr, "FICS: Error in login info format. "
"%s\n", fname);
fclose(fp);
@@ -1320,10 +1343,7 @@ com_who(int p, param_list param)
sel_bits |= WHO_REGISTERED;
break;
case 'l': // Sort order
- cmp_func = alpha_cmp;
- sort_type = none;
- break;
- case 'A': // Sort order
+ case 'A':
cmp_func = alpha_cmp;
sort_type = none;
break;
@@ -1380,8 +1400,8 @@ com_who(int p, param_list param)
count++;
}
- startpoint = floor((float) count * start_perc);
- stoppoint = ceil((float) count * stop_perc) - 1;
+ startpoint = floorf((float) count * start_perc);
+ stoppoint = ceilf((float) count * stop_perc) - 1;
num_who = 0;
count = 0;
@@ -1810,10 +1830,9 @@ FindAndShowFile(int p, param_list param, char *dir)
{
char *iwant, *filenames[1000];
int i;
- static char nullify = '\0';
if (param[0].type == TYPE_NULL) {
- iwant = &nullify;
+ iwant = NULL;
} else {
iwant = param[0].val.word;
@@ -1827,8 +1846,9 @@ FindAndShowFile(int p, param_list param, char *dir)
i = search_directory(dir, iwant, filenames, ARRAY_SIZE(filenames));
if (i == 0) {
- pprintf(p, "No information available on \"%s\".\n", iwant);
- } else if (i == 1 || !strcmp(*filenames, iwant)) {
+ pprintf(p, "No information available on \"%s\".\n",
+ (iwant ? iwant : ""));
+ } else if (i == 1 || !strcmp(*filenames, iwant ? iwant : "")) {
if (psend_file(p, dir, *filenames)) {
/*
* We should never reach this unless the file
@@ -1839,7 +1859,7 @@ FindAndShowFile(int p, param_list param, char *dir)
"Thank you.\n");
}
} else {
- if (*iwant)
+ if (iwant && *iwant)
pprintf(p, "Matches:\n");
display_directory(p, filenames, i);
}
@@ -1867,7 +1887,6 @@ com_mailsource(int p, param_list param)
char fname[MAX_FILENAME_SIZE];
char subj[120];
int count;
- static char nullify = '\0';
if (!parray[p].registered) {
pprintf(p, "Only registered people can use the mailsource "
@@ -1876,14 +1895,15 @@ com_mailsource(int p, param_list param)
}
if (param[0].type == TYPE_NULL)
- iwant = &nullify;
+ iwant = NULL;
else
iwant = param[0].val.word;
if ((count = search_directory(source_dir, iwant, buffer,
ARRAY_SIZE(buffer))) == 0) {
- pprintf(p, "Found no source file matching \"%s\".\n", iwant);
- } else if ((count == 1) || !strcmp(iwant, *buffer)) {
+ pprintf(p, "Found no source file matching \"%s\".\n",
+ (iwant ? iwant : ""));
+ } else if ((count == 1) || !strcmp(iwant ? iwant : "", *buffer)) {
snprintf(subj, sizeof subj, "FICS source file from server "
"%s: %s",
fics_hostname,
@@ -1899,7 +1919,7 @@ com_mailsource(int p, param_list param)
} else {
pprintf(p, "Found %d source files matching that:\n", count);
- if (*iwant) {
+ if (iwant && *iwant) {
display_directory(p, buffer, count);
} else { // this junk is to get *.c *.h
char *s;
@@ -1930,7 +1950,6 @@ com_mailhelp(int p, param_list param)
char subj[120];
int count;
int lang = parray[p].language;
- static char nullify = '\0';
if (!parray[p].registered) {
pprintf(p, "Only registered people can use the mailhelp "
@@ -1939,7 +1958,7 @@ com_mailhelp(int p, param_list param)
}
if (param[0].type == TYPE_NULL)
- iwant = &nullify;
+ iwant = NULL;
else
iwant = param[0].val.word;
@@ -1960,8 +1979,9 @@ com_mailhelp(int p, param_list param)
}
if (count == 0) {
- pprintf(p, "Found no help file matching \"%s\".\n", iwant);
- } else if (count == 1 || !strcmp(*buffer, iwant)) {
+ pprintf(p, "Found no help file matching \"%s\".\n",
+ (iwant ? iwant : ""));
+ } else if (count == 1 || !strcmp(*buffer, iwant ? iwant : "")) {
snprintf(subj, sizeof subj, "FICS help file from server %s: %s",
fics_hostname,
*buffer);
diff --git a/FICS/fics_addplayer.c b/FICS/fics_addplayer.c
index ad6c43f..160b28d 100644
--- a/FICS/fics_addplayer.c
+++ b/FICS/fics_addplayer.c
@@ -40,6 +40,7 @@
#include "command.h"
#include "config.h"
#include "fics_getsalt.h"
+#include "ficsmain.h"
#include "playerdb.h"
#include "utils.h"
@@ -60,12 +61,17 @@ add_handle_to_list(const char *handle)
{
FILE *fp;
char path[1024];
+ int fd;
snprintf(path, sizeof path, "%s/admin", DEFAULT_LISTS);
- if ((fp = fopen(path, "a")) == NULL) {
+ if ((fd = open(path, g_open_flags[0], g_open_modes)) < 0) {
warn("%s: unable to open %s", __func__, path);
return;
+ } else if ((fp = fdopen(fd, "a")) == NULL) {
+ warn("%s: unable to open %s", __func__, path);
+ close(fd);
+ return;
}
fprintf(fp, "%s\n", handle);
diff --git a/FICS/ficsmain.c b/FICS/ficsmain.c
index 4110635..664874f 100644
--- a/FICS/ficsmain.c
+++ b/FICS/ficsmain.c
@@ -36,9 +36,7 @@
#include <err.h>
#include <errno.h>
-#include <fcntl.h>
#include <stdint.h>
-#include <unistd.h>
#include "board.h"
#include "command.h"
@@ -61,6 +59,12 @@
#include <bsd/string.h>
#endif
+PUBLIC const int g_open_flags[2] = {
+ (O_WRONLY|O_CREAT|O_APPEND),
+ (O_WRONLY|O_CREAT|O_TRUNC),
+};
+PUBLIC const mode_t g_open_modes = (S_IWUSR | S_IRUSR);
+
/* Arguments */
PUBLIC int port;
PUBLIC int withConsole;
diff --git a/FICS/ficsmain.h b/FICS/ficsmain.h
index d47bbcc..08ea466 100644
--- a/FICS/ficsmain.h
+++ b/FICS/ficsmain.h
@@ -26,6 +26,14 @@
#ifndef _FICSMAIN_H
#define _FICSMAIN_H
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "common.h"
+
/*
* Heartbead functions occur approx in this time, including checking
* for new connections and decrementing timeleft counters.
@@ -57,8 +65,13 @@
#define STATS_GAMES "games"
#define STATS_JOURNAL "journal"
+__FICS_BEGIN_DECLS
+extern const int g_open_flags[2];
+extern const mode_t g_open_modes;
+
/* Arguments */
extern int port;
extern int withConsole;
+__FICS_END_DECLS
#endif /* _FICSMAIN_H */
diff --git a/FICS/formula.c b/FICS/formula.c
index 5f9fdb7..dd5ff23 100644
--- a/FICS/formula.c
+++ b/FICS/formula.c
@@ -574,7 +574,7 @@ ChooseClauses(player *who, char *formula)
return ret;
for (i = 0; formula[i] != '\0' && formula[i] != '#'; i++) {
- if (formula[i] != 'f' || (i > 0 && isalnum(formula[i - 1])) ||
+ if ((i > 0 && isalnum(formula[i - 1])) || formula[i] != 'f' ||
!isdigit(formula[i + 1]) ||
sscanf(&formula[i], "f%d", &which) != 1)
continue;
diff --git a/FICS/gamedb.c b/FICS/gamedb.c
index ce0842c..f3a351b 100644
--- a/FICS/gamedb.c
+++ b/FICS/gamedb.c
@@ -40,6 +40,14 @@
Markus Uhlin 25/03/25 ReadGameState: fixed truncated
stdio return value.
Markus Uhlin 25/04/01 Fixed call of risky function
+ Markus Uhlin 25/04/01 ReadV1GameFmt: guard num half
+ moves.
+ Markus Uhlin 25/04/06 Fixed Clang Tidy warnings.
+ Markus Uhlin 25/07/28 Fixed use of potentially
+ dangerous functions.
+ Markus Uhlin 25/07/29 Usage of 'int64_t'.
+ Markus Uhlin 25/08/16 Fixed uncontrolled data used in
+ path expressions.
*/
#include "stdinclude.h"
@@ -47,7 +55,9 @@
#include <err.h>
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
+#include <stdint.h>
#include "command.h"
#include "config.h"
@@ -560,12 +570,12 @@ EndSym(int g)
PUBLIC char *
movesToString(int g, int pgn)
{
+ char tmp[160] = { '\0' };
char *serv_loc = SERVER_LOCATION;
char *serv_name = SERVER_NAME;
- char tmp[160] = { '\0' };
int i, col;
int wr, br;
- struct tm *tm_ptr = NULL;
+ struct tm v_tm = {0};
time_t curTime;
wr = garray[g].white_rating;
@@ -583,14 +593,16 @@ movesToString(int g, int pgn)
serv_name,
serv_loc);
- if ((tm_ptr = localtime(&curTime)) != NULL) {
+ errno = 0;
+
+ if (localtime_r(&curTime, &v_tm) != NULL) {
strftime(tmp, sizeof(tmp),
"[Date \"%Y.%m.%d\"]\n"
"[Time \"%H:%M:%S\"]\n",
- tm_ptr);
+ &v_tm);
mstrlcat(gameString, tmp, sizeof gameString);
} else
- warn("%s: localtime", __func__);
+ warn("%s: localtime_r()", __func__);
msnprintf(tmp, sizeof tmp,
"[Round \"-\"]\n"
@@ -665,11 +677,13 @@ movesToString(int g, int pgn)
mstrlcat(gameString, tmp, sizeof gameString);
mstrlcat(gameString, "--- ", sizeof gameString);
- if ((tm_ptr = localtime(&curTime)) != NULL) {
- strftime(tmp, sizeof tmp, "%Y.%m.%d %H:%M:%S", tm_ptr);
+ errno = 0;
+
+ if (localtime_r(&curTime, &v_tm) != NULL) {
+ strftime(tmp, sizeof tmp, "%Y.%m.%d %H:%M:%S", &v_tm);
mstrlcat(gameString, tmp, sizeof gameString);
} else
- warn("%s: localtime", __func__);
+ warn("%s: localtime_r()", __func__);
if (garray[g].rated) {
mstrlcat(gameString, "\nRated ", sizeof gameString);
@@ -1065,10 +1079,16 @@ got_attr_value(int g, char *attr, char *value, FILE *fp, char *file)
} else if (!strcmp(attr, "type:")) {
garray[g].type = atoi(value);
} else if (!strcmp(attr, "halfmoves:")) {
- garray[g].numHalfMoves = atoi(value);
-
- if (garray[g].numHalfMoves == 0)
+ if ((garray[g].numHalfMoves = atoi(value)) == 0)
return 0;
+ else if (garray[g].numHalfMoves < 0 ||
+ (size_t)garray[g].numHalfMoves > INT_MAX / sizeof(move_t)) {
+ warnx("%s: num half moves out-of-bounds (%d)", __func__,
+ garray[g].numHalfMoves);
+ return -1;
+ } else {
+ /* null */;
+ }
garray[g].moveListSize = garray[g].numHalfMoves;
garray[g].moveList = reallocarray(NULL, sizeof(move_t),
@@ -1267,14 +1287,16 @@ PRIVATE int
ReadV1GameFmt(game *g, FILE *fp, const char *file, int version)
{
int ret[3];
- long int lval;
+ int64_t lval;
_Static_assert(17 < ARRAY_SIZE(g->white_name), "Unexpected array size");
_Static_assert(17 < ARRAY_SIZE(g->black_name), "Unexpected array size");
ret[0] = fscanf(fp, "%17s %17s", g->white_name, g->black_name);
- ret[1] = fscanf(fp, "%d %d", &g->white_rating, &g->black_rating);
- ret[2] = fscanf(fp, "%d %d %d %d",
+ ret[1] = fscanf(fp, "%d %d", // NOLINT
+ &g->white_rating,
+ &g->black_rating);
+ ret[2] = fscanf(fp, "%d %d %d %d", // NOLINT
&g->wInitTime,
&g->wIncrement,
&g->bInitTime,
@@ -1289,7 +1311,7 @@ ReadV1GameFmt(game *g, FILE *fp, const char *file, int version)
if (version < 3 && !g->bInitTime)
g->bInitTime = g->wInitTime;
- if (fscanf(fp, "%ld", &lval) != 1) {
+ if (fscanf(fp, "%" SCNd64, &lval) != 1) {
warnx("%s: %s: failed to get time of start", __func__, file);
return -1;
} else
@@ -1317,7 +1339,7 @@ ReadV1GameFmt(game *g, FILE *fp, const char *file, int version)
ret[0] = fscanf(fp, "%d %d %d %d", &g->private, &g->type, &g->rated,
&g->clockStopped);
- ret[1] = fscanf(fp, "%d", &g->numHalfMoves);
+ ret[1] = fscanf(fp, "%d", &g->numHalfMoves); // NOLINT
if (ret[0] != 4 || ret[1] != 1) {
warnx("%s: fscanf error: %s", __func__, file);
return -1;
@@ -1503,7 +1525,7 @@ PRIVATE void
WriteGameFile(FILE *fp, int g)
{
game *gg = &garray[g];
- long int lval;
+ int64_t lval;
player *bp = &parray[gg->black];
player *wp = &parray[gg->white];
@@ -1514,7 +1536,7 @@ WriteGameFile(FILE *fp, int g)
gg->bInitTime, gg->bIncrement);
lval = gg->timeOfStart;
- fprintf(fp, "%ld\n", lval);
+ fprintf(fp, "%" PRId64 "\n", lval);
#ifdef TIMESEAL
fprintf(fp, "%d %d\n",
@@ -1540,6 +1562,7 @@ game_save(int g)
char fname[MAX_FILENAME_SIZE];
char lname[MAX_FILENAME_SIZE];
game *gg = &garray[g];
+ int fd;
player *wp, *bp;
wp = &parray[gg->white];
@@ -1550,11 +1573,13 @@ game_save(int g)
msnprintf(lname, sizeof lname, "%s/%c/%s-%s", adj_dir, bp->login[0],
wp->login, bp->login);
- fp = fopen(fname, "w");
-
- if (!fp) {
+ if ((fd = open(fname, g_open_flags[1], g_open_modes)) < 0) {
+ warn("%s: open: %s", __func__, fname);
+ return -1;
+ } else if ((fp = fdopen(fd, "w")) == NULL) {
fprintf(stderr, "FICS: Problem opening file %s for write\n",
fname);
+ close(fd);
return -1;
}
@@ -1604,12 +1629,16 @@ OldestHistGame(char *login)
char pFile[MAX_FILENAME_SIZE] = { '\0' };
long int when;
+ /* Centralized validation of login */
+ if (!is_valid_login_name(login)) {
+ warnx("%s: invalid login value: '%s'", __func__, login);
+ return 0L;
+ }
+
msnprintf(pFile, sizeof pFile, "%s/player_data/%c/%s.%s", stats_dir,
login[0], login, STATS_GAMES);
- fp = fopen(pFile, "r");
-
- if (fp == NULL) {
+ if ((fp = fopen(pFile, "r")) == NULL) {
msnprintf(pFile, sizeof pFile, "%s/player_data/%c/.rem.%s.%s",
stats_dir, login[0], login, STATS_GAMES);
fp = fopen(pFile, "r");
@@ -1637,10 +1666,9 @@ RemoveHistGame(char *file, int maxlines)
char Opponent[MAX_LOGIN_NAME + 1] = { '\0' };
char line[MAX_LINE_SIZE] = { '\0' };
int count = 0;
- long int When, oppWhen;
+ long int When = 0, oppWhen = 0;
_Static_assert(20 < ARRAY_SIZE(Opponent), "Not within bounds");
- When = oppWhen = 0;
if ((fp = fopen(file, "r")) == NULL) {
return;
@@ -1695,11 +1723,19 @@ RemHist(char *who)
if (ret != 2) {
warnx("%s: fscanf() error (%s:%ld)", __func__,
fName, iter_no);
-// iter_no++;
break;
}
stolower(Opp);
+
+ // Centralized validation: only allow safe login names
+ if (!is_valid_login_name(Opp)) {
+ warnx("%s: invalid value: "
+ "Opp = '%s' (skipping)", __func__, Opp);
+ iter_no++;
+ continue;
+ }
+
oppWhen = OldestHistGame(Opp);
if (oppWhen > When || oppWhen <= 0L) {
@@ -1725,12 +1761,13 @@ write_g_out(int g, char *file, int maxlines, int isDraw, char *EndSymbol,
char *name, time_t *now)
{
FILE *fp;
- char *goteco;
char cResult;
char tmp[2048] = { '\0' };
- char *ptmp = tmp;
char type[4];
+ char *goteco;
+ char *ptmp = tmp;
int count = -1;
+ int fd;
int wp, bp;
int wr, br;
@@ -1797,8 +1834,14 @@ write_g_out(int g, char *file, int maxlines, int isDraw, char *EndSymbol,
count = (count + 1) % 100;
- if ((fp = fopen(file, "a")) == NULL)
+ if ((fd = open(file, g_open_flags[0], g_open_modes)) < 0) {
+ warn("%s: open: %s", __func__, file);
+ return;
+ } else if ((fp = fdopen(fd, "a")) == NULL) {
+ close(fd);
return;
+ }
+
goteco = getECO(g);
/*
@@ -1917,17 +1960,22 @@ addjournalitem(int p, char count2, char *WhiteName2, int WhiteRating2,
char result[100] = { '\0' };
char type[100] = { '\0' };
int WhiteRating, BlackRating;
+ int fd;
int have_output = 0;
int t, i;
mstrlcpy(fname2, fname, sizeof fname2);
mstrlcat(fname2, ".w", sizeof fname2);
- if ((fp2 = fopen(fname2, "w")) == NULL) {
+ if ((fd = open(fname2, g_open_flags[1], g_open_modes)) < 0) {
+ warn("%s: open", __func__);
+ return;
+ } else if ((fp2 = fdopen(fd, "w")) == NULL) {
fprintf(stderr, "FICS: Problem opening file %s for write\n",
fname);
pprintf(p, "Couldn't update journal! Report this to an admin."
"\n");
+ close(fd);
return;
}
@@ -2113,6 +2161,8 @@ pgames(int p, int p1, char *fname)
_Static_assert(ARRAY_SIZE(ending) > 99, "'ending' too small");
while (!feof(fp)) {
+ char tbuf[30] = { '\0' };
+
if (fscanf(fp, "%d %1s %d %1s %d %19s %99s %d %d %d %d %99s "
"%99s %ld\n",
&count, result, &MyRating, MyColor,
@@ -2135,7 +2185,7 @@ pgames(int p, int p1, char *fname)
count, result, MyRating, MyColor,
OppRating, OppName,
type, (wt / 600), (wi / 10), eco, ending,
- ctime(&t));
+ ctime_r(&t, tbuf) != NULL ? &tbuf[0] : "");
}
fclose(fp);
diff --git a/FICS/gamedb.h b/FICS/gamedb.h
index 2d0edc8..8dc84d3 100644
--- a/FICS/gamedb.h
+++ b/FICS/gamedb.h
@@ -27,6 +27,7 @@
#ifndef _GAMEDB_H
#define _GAMEDB_H
+#include <stdint.h>
#include <time.h>
#include "board.h"
@@ -140,9 +141,9 @@ typedef struct _game {
move_t *examMoveList; // Extra movelist for examine
int examMoveListSize;
- unsigned int startTime; // The relative time the game started
- unsigned int lastMoveTime; // Last time a move was made
- unsigned int lastDecTime; // Last time a players clock was
+ uint64_t startTime; // The relative time the game started
+ uint64_t lastMoveTime; // Last time a move was made
+ uint64_t lastDecTime; // Last time a players clock was
// decremented
int result;
diff --git a/FICS/gameproc.c b/FICS/gameproc.c
index eea5205..cd6398e 100644
--- a/FICS/gameproc.c
+++ b/FICS/gameproc.c
@@ -1834,7 +1834,11 @@ com_goboard(int p, param_list param)
}
on = parray[p].simul_info.onBoard;
- g = parray[p].simul_info.boards[on];
+
+ if ((g = parray[p].simul_info.boards[on]) < 0) {
+ pprintf(p, "Internal error! Unexpected negative value!\n");
+ return COM_OK;
+ }
if (p1 == garray[g].black) {
pprintf(p, "You are already at that board!\n");
diff --git a/FICS/legal2.c b/FICS/legal2.c
index 528814a..15c0c16 100644
--- a/FICS/legal2.c
+++ b/FICS/legal2.c
@@ -1,7 +1,7 @@
#include "legal2.h"
const char legalNotice2[] =
- "Copyright (c) 2023, 2024 Markus Uhlin <maxxe@rpblc.net>\n"
+ "Copyright (c) 2023 - 2025 Markus Uhlin <maxxe@rpblc.net>\n"
"\n"
"Permission to use, copy, modify, and distribute this software for any\n"
"purpose with or without fee is hereby granted, provided that the above\n"
diff --git a/FICS/lists.c b/FICS/lists.c
index d9879d4..c11d75b 100644
--- a/FICS/lists.c
+++ b/FICS/lists.c
@@ -13,6 +13,7 @@
#include "command.h"
#include "comproc.h"
+#include "ficsmain.h"
#include "gamedb.h"
#include "lists.h"
#include "maxxes-utils.h"
@@ -60,7 +61,14 @@ list_find(int p, enum ListWhich l)
int personal;
personal = ListArray[l].rights == P_PERSONAL;
- starter = (personal ? &parray[p].lists : &firstGlobalList);
+
+ if (personal) {
+ if (p < 0)
+ return NULL;
+ starter = &parray[p].lists;
+ } else {
+ starter = &firstGlobalList;
+ }
for (tempList = *starter; tempList != NULL; tempList = tempList->next) {
if (l == tempList->which) {
@@ -363,6 +371,7 @@ list_addsub(int p, char *list, char *who, int addsub)
if (!personal) {
FILE *fp;
char filename[MAX_FILENAME_SIZE] = { '\0' };
+ int fd;
switch (gl->which) {
case L_MUZZLE:
@@ -426,8 +435,11 @@ list_addsub(int p, char *list, char *who, int addsub)
msnprintf(filename, sizeof filename, "%s/%s", lists_dir,
listname);
- if ((fp = fopen(filename, "w")) == NULL) {
+ if ((fd = open(filename, g_open_flags[1], g_open_modes)) < 0) {
+ fprintf(stderr, "Couldn't save %s list.\n", listname);
+ } else if ((fp = fdopen(fd, "w")) == NULL) {
fprintf(stderr, "Couldn't save %s list.\n", listname);
+ close(fd);
} else {
for (int i = 0; i < gl->numMembers; i++)
fprintf(fp, "%s\n", gl->member[i]);
diff --git a/FICS/makerank.c b/FICS/makerank.c
index 4458f31..076e16e 100644
--- a/FICS/makerank.c
+++ b/FICS/makerank.c
@@ -13,7 +13,9 @@
#endif
#include "common.h"
+#include "ficsmain.h"
#include "makerank.h"
+#include "utils.h"
static ENTRY **list;
static ENTRY **sortme;
@@ -184,6 +186,16 @@ LoadEntries(void)
e.name[strcspn(e.name, "\n")] = '\0';
+ /*
+ * Validate that e.name does not contain path
+ * traversal or separators
+ */
+ if (!is_valid_filename(e.name, false)) {
+ printf("Skipping invalid filename: %s\n",
+ e.name);
+ continue;
+ }
+
if (e.name[0] != letter1) {
printf("File %c/%s: wrong directory.\n",
letter1, e.name);
@@ -263,6 +275,7 @@ makerank(void)
{
FILE *fp;
char fName[200];
+ int fd;
int sortnum, sortmesize, i, n;
printf("Loading players\n");
@@ -302,8 +315,9 @@ makerank(void)
snprintf(fName, sizeof fName, "%s/rank.%s", DEFAULT_STATS,
rnames[rtype]);
- if ((fp = fopen(fName, "w")) == NULL)
- err(1, "%s: fopen", __func__);
+ if ((fd = open(fName, g_open_flags[1], g_open_modes)) < 0 ||
+ (fp = fdopen(fd, "w")) == NULL)
+ err(1, "%s: rank file open error", __func__);
for (i = 0; i < sortnum; i++) {
fprintf(fp, "%s %d %d %d\n",
diff --git a/FICS/matchproc.c b/FICS/matchproc.c
index fb13439..1b1eab8 100644
--- a/FICS/matchproc.c
+++ b/FICS/matchproc.c
@@ -1079,7 +1079,10 @@ com_match(int p, param_list param)
.wt = wt,
};
- print_bughouse(p, p1, &ctx, colorstr);
+ if (ctx.white >= 0)
+ print_bughouse(p, p1, &ctx, colorstr);
+ else
+ warnx("%s: cannot print bughouse", __func__);
}
if (in_list(p, L_COMPUTER, parray[p].name)) {
diff --git a/FICS/network.c b/FICS/network.c
index b795e99..19c6f22 100644
--- a/FICS/network.c
+++ b/FICS/network.c
@@ -12,6 +12,7 @@
#include <err.h>
#include <errno.h>
+#include <limits.h>
#include "common.h"
#include "config.h"
@@ -267,7 +268,7 @@ net_send_string(int fd, char *str, int format)
if ((which = findConnection(fd)) < 0)
return -1;
while (*str) {
- const int upbound = strlen(str);
+ const int upbound = (int)strlen(str);
for (i = 0; i < upbound && str[i] >= ' '; i++) {
/* null */;
@@ -314,6 +315,7 @@ net_send_string(int fd, char *str, int format)
break;
case '\033':
con[which].outPos -= 3;
+ // XXX: fallthrough here?
default:
sendme(which, str, 1);
}
@@ -334,7 +336,8 @@ net_send_string(int fd, char *str, int format)
PUBLIC int
readline2(comstr_t *cs, int who)
{
- int howmany, state, fd, v_pending;
+ int bytes_received, state, fd, v_pending;
+ ssize_t ret;
static const uint8_t ayt[] = "[Responding to AYT: Yes, I'm here.]\n";
static const uint8_t will_sga[] = { IAC, WILL, TELOPT_SGA, '\0' };
static const uint8_t will_tm[] = { IAC, WILL, TELOPT_TM, '\0' };
@@ -352,27 +355,34 @@ readline2(comstr_t *cs, int who)
v_pending = con[who].numPending;
fd = con[who].fd;
- if ((howmany = recv(fd, start + v_pending, MAX_STRING_LENGTH - 1 -
- v_pending, 0)) == 0) { // error: they've disconnected
+ ret = recv(fd, start + v_pending, MAX_STRING_LENGTH - 1 - v_pending, 0);
+ if (ret < INT_MIN || ret > INT_MAX)
+ errx(1, "%s: return out of bounds", __func__);
+ bytes_received = (int)ret;
+
+ if (bytes_received == 0) { // error: they've disconnected
return -1;
- } else if (howmany == -1) {
+ } else if (bytes_received == -1) {
if (errno != EWOULDBLOCK) { // some other error
return -1;
} else if (con[who].processed) { // nothing new and nothing old
return 0;
} else { // nothing new
// but some unprocessed old
- howmany = 0;
+ bytes_received = 0;
}
}
if (con[who].processed)
s += v_pending;
- else
- howmany += v_pending;
+ else {
+ if (bytes_received > INT_MAX - v_pending)
+ errx(1, "%s: integer overflow", __func__);
+ bytes_received += v_pending;
+ }
d = s;
- for (; howmany-- > 0; s++) {
+ while (bytes_received-- > 0) {
switch (state) {
case 0: // haven't skipped over any control chars or
// telnet commands
@@ -383,10 +393,10 @@ readline2(comstr_t *cs, int who)
*s = '\0';
msnprintf(cs->com, ARRAY_SIZE(cs->com), "%s",
start);
- if (howmany)
- memmove(start, s + 1, howmany);
+ if (bytes_received > 0)
+ memmove(start, s + 1, bytes_received);
con[who].state = 0;
- con[who].numPending = howmany;
+ con[who].numPending = bytes_received;
con[who].processed = 0;
con[who].outPos = 0;
return 1;
@@ -422,10 +432,10 @@ readline2(comstr_t *cs, int who)
*d = '\0';
msnprintf(cs->com, ARRAY_SIZE(cs->com), "%s",
start);
- if (howmany)
- memmove(start, s + 1, howmany);
+ if (bytes_received > 0)
+ memmove(start, s + 1, bytes_received);
con[who].state = 0;
- con[who].numPending = howmany;
+ con[who].numPending = bytes_received;
con[who].processed = 0;
con[who].outPos = 0;
return 1;
@@ -448,7 +458,9 @@ readline2(comstr_t *cs, int who)
state = 2;
break;
}
- }
+
+ s++;
+ } // while
if (state == 0)
d = s;
diff --git a/FICS/obsproc.c b/FICS/obsproc.c
index 966e30e..8c7194c 100644
--- a/FICS/obsproc.c
+++ b/FICS/obsproc.c
@@ -32,12 +32,15 @@
Markus Uhlin 25/01/18 Fixed -Wshadow
Markus Uhlin 25/03/15 Fixed possible buffer overflow
in FindHistory2().
+ Markus Uhlin 25/04/06 Fixed Clang Tidy warnings.
*/
#include "stdinclude.h"
#include "common.h"
#include <err.h>
+#include <limits.h>
+#include <stdlib.h>
#include "command.h"
#include "comproc.h"
@@ -152,6 +155,9 @@ com_games(int p, param_list param)
wp = garray[i].white;
bp = garray[i].black;
+ UNUSED_VAR(wp);
+ UNUSED_VAR(bp);
+
if ((!selected) &&
s &&
strncasecmp(s, garray[i].white_name, slen) &&
@@ -380,9 +386,8 @@ com_allobservers(int p, param_list param)
start = 0;
end = g_num;
} else if ((obgame >= g_num) ||
- ((obgame < g_num) &&
- ((garray[obgame].status != GAME_ACTIVE) &&
- (garray[obgame].status != GAME_EXAMINE)))) {
+ (garray[obgame].status != GAME_ACTIVE &&
+ garray[obgame].status != GAME_EXAMINE)) {
pprintf(p, "There is no such game.\n");
return COM_OK;
} else {
@@ -980,11 +985,16 @@ FindHistory(int p, int p1, int p_game)
ret = fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d "
"%*d %*s %*s %ld", &index, &when);
- if (ret != 2)
- warn("%s: %s: corrupt", __func__, &fileName[0]);
- } while (!feof(fpHist) && index != p_game);
+ if (ret != 2) {
+ warnx("%s: %s: corrupt", __func__, fileName);
+ fclose(fpHist);
+ return NULL;
+ }
+ } while (!feof(fpHist) &&
+ !ferror(fpHist) &&
+ index != p_game);
- if (feof(fpHist)) {
+ if (feof(fpHist) || ferror(fpHist)) {
pprintf(p, "There is no history game %d for %s.\n", p_game,
parray[p1].name);
fclose(fpHist);
@@ -993,6 +1003,12 @@ FindHistory(int p, int p1, int p_game)
fclose(fpHist);
+ if (when < 0 || when >= LONG_MAX) {
+ pprintf(p, "Corrupt history data for %s (invalid timestamp).\n",
+ parray[p1].name);
+ return NULL;
+ }
+
msnprintf(fileName, sizeof fileName, "%s/%ld/%ld", hist_dir,
(when % 100), when);
return (&fileName[0]);
@@ -1003,6 +1019,7 @@ FindHistory2(int p, int p1, int p_game, char *End, const size_t End_size)
{
FILE *fpHist;
char fmt[80] = { '\0' };
+ char *resolvedPath;
int index = 0;
long int when = 0;
static char fileName[MAX_FILENAME_SIZE];
@@ -1019,11 +1036,16 @@ FindHistory2(int p, int p1, int p_game, char *End, const size_t End_size)
"%%*d %%*d %%*d %%*s %%%zus %%ld\n", (End_size - 1));
do {
- if (fscanf(fpHist, fmt, &index, End, &when) != 3)
- warn("%s: %s: corrupt", __func__, &fileName[0]);
- } while (!feof(fpHist) && index != p_game);
+ if (fscanf(fpHist, fmt, &index, End, &when) != 3) {
+ warnx("%s: %s: corrupt", __func__, fileName);
+ fclose(fpHist);
+ return NULL;
+ }
+ } while (!feof(fpHist) &&
+ !ferror(fpHist) &&
+ index != p_game);
- if (feof(fpHist)) {
+ if (feof(fpHist) || ferror(fpHist)) {
pprintf(p, "There is no history game %d for %s.\n", p_game,
parray[p1].name);
fclose(fpHist);
@@ -1032,8 +1054,31 @@ FindHistory2(int p, int p1, int p_game, char *End, const size_t End_size)
fclose(fpHist);
+ if (when < 0 || when >= LONG_MAX) {
+ pprintf(p, "Invalid history timestamp for %s.\n",
+ parray[p1].name);
+ return NULL;
+ }
+
msnprintf(fileName, sizeof fileName, "%s/%ld/%ld", hist_dir,
(when % 100), when);
+
+ // Validate that the resolved path is within hist_dir
+ if ((resolvedPath = realpath(fileName, NULL)) == NULL) {
+ warn("%s: realpath", __func__);
+ return NULL;
+ }
+
+ if (strncmp(resolvedPath, hist_dir, strlen(hist_dir)) != 0) {
+ warnx("%s: path traversal detected", __func__);
+ free(resolvedPath);
+ return NULL;
+ }
+
+ // Copy 'resolvedPath' back to 'fileName' for return
+ mstrlcpy(fileName, resolvedPath, sizeof fileName);
+ free(resolvedPath);
+
return (&fileName[0]);
}
diff --git a/FICS/playerdb.c b/FICS/playerdb.c
index 309a8fc..9919bfd 100644
--- a/FICS/playerdb.c
+++ b/FICS/playerdb.c
@@ -43,6 +43,12 @@
Markus Uhlin 25/03/29 player_remove_request:
fixed overflowed array index
read/write.
+ Markus Uhlin 25/04/02 add_to_list: added an upper
+ limit for the list size.
+ Markus Uhlin 25/04/06 Fixed Clang Tidy warnings.
+ Markus Uhlin 25/07/28 Restricted file permissions upon
+ creation.
+ Markus Uhlin 25/07/30 Usage of 'int64_t'.
*/
#include "stdinclude.h"
@@ -50,6 +56,8 @@
#include <err.h>
#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <stdint.h>
#include "command.h"
@@ -59,6 +67,7 @@
#include "ficsmain.h"
#include "gamedb.h"
#include "lists.h"
+#include "maxxes-utils.h"
#include "network.h"
#include "playerdb.h"
#include "ratings.h"
@@ -409,10 +418,12 @@ add_to_list(FILE *fp, enum ListWhich lw, int *size, int p)
#define SCAN_STR "%1023s"
- if (*size <= 0)
+ if (*size <= 0 || *size > MAX_GLOBAL_LIST_SIZE)
return -2;
+
while ((*size)-- > 0 && fscanf(fp, SCAN_STR, buf) == 1)
list_add(p, lw, buf);
+
return (*size <= 0 ? 0 : -1);
}
@@ -435,7 +446,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version)
/*
* Name
*/
- if (fgets(tmp2, sizeof tmp2, fp) != NULL &&
+ if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT
strcmp(tmp2, "NONE\n") != 0) {
tmp2[strcspn(tmp2, "\n")] = '\0';
pp->name = xstrdup(tmp2);
@@ -446,7 +457,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version)
/*
* Full name
*/
- if (fgets(tmp2, sizeof tmp2, fp) != NULL &&
+ if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT
strcmp(tmp2, "NONE\n") != 0) {
tmp2[strcspn(tmp2, "\n")] = '\0';
pp->fullName = xstrdup(tmp2);
@@ -457,7 +468,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version)
/*
* Password
*/
- if (fgets(tmp2, sizeof tmp2, fp) != NULL &&
+ if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT
strcmp(tmp2, "NONE\n") != 0) {
tmp2[strcspn(tmp2, "\n")] = '\0';
pp->passwd = xstrdup(tmp2);
@@ -468,7 +479,7 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version)
/*
* Email
*/
- if (fgets(tmp2, sizeof tmp2, fp) != NULL &&
+ if (fgets(tmp2, sizeof tmp2, fp) != NULL && // NOLINT
strcmp(tmp2, "NONE\n") != 0) {
tmp2[strcspn(tmp2, "\n")] = '\0';
pp->emailAddress = xstrdup(tmp2);
@@ -476,7 +487,9 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version)
pp->emailAddress = NULL;
}
- if (fscanf(fp, "%d %d %d %d %d %d %jd %d %jd %d %d %d %d %d %d %jd %d %jd "
+ if (feof(fp) ||
+ ferror(fp) ||
+ fscanf(fp, "%d %d %d %d %d %d %jd %d %jd %d %d %d %d %d %d %jd %d %jd "
"%d %d %d %d %d %d %jd %d %jd %d %d %d %d %d %d %jd %d %jd %d %d %d %d "
"%d %d %jd %d %jd %u\n",
&pp->s_stats.num, &pp->s_stats.win, &pp->s_stats.los,
@@ -578,6 +591,23 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version)
pp->timeOfReg = array[0];
pp->totalTime = array[1];
+ if (pp->num_plan >= MAX_PLAN) {
+ warnx("Player %s is corrupt\nToo many plans (%d)",
+ parray[p].name,
+ pp->num_plan);
+ return;
+ } else if (pp->num_formula >= MAX_FORMULA) {
+ warnx("Player %s is corrupt\nToo many formulas (%d)",
+ parray[p].name,
+ pp->num_formula);
+ return;
+ } else if (pp->numAlias >= MAX_ALIASES) {
+ warnx("Player %s is corrupt\nToo many aliases (%d)",
+ parray[p].name,
+ pp->numAlias);
+ return;
+ }
+
if (pp->num_plan > 0) {
for (i = 0; i < pp->num_plan; i++) {
if (fgets(tmp2, sizeof tmp2, fp) == NULL) {
@@ -642,14 +672,13 @@ ReadV1PlayerFmt(int p, player *pp, FILE *fp, char *file, int version)
return;
}
- if (!(len = strlen(tmp2))) {
+ if (!strlen(tmp2)) { // XXX
fprintf(stderr, "FICS: Error bad alias in "
"file %s\n", file);
i--;
pp->numAlias--;
} else {
tmp2[strcspn(tmp2, "\n")] = '\0';
- tmp = tmp2;
tmp = eatword(tmp2);
*tmp = '\0';
tmp++;
@@ -834,9 +863,11 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
* num_plan
*/
- parray[p].num_plan = atoi(value);
-
- if (parray[p].num_plan > 0) {
+ if ((parray[p].num_plan = atoi(value)) >= MAX_PLAN) {
+ warnx("%s: %s: too many plans (%d)", __func__, file,
+ parray[p].num_plan);
+ return -1;
+ } else if (parray[p].num_plan > 0) {
for (i = 0; i < parray[p].num_plan; i++) {
if (fgets(tmp, sizeof tmp, fp) == NULL) {
@@ -858,15 +889,19 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
xstrdup(tmp) : NULL);
}
}
+ } else {
+ /* null */;
}
} else if (!strcmp(attr, "num_formula:")) {
/*
* num_formula
*/
- parray[p].num_formula = atoi(value);
-
- if (parray[p].num_formula > 0) {
+ if ((parray[p].num_formula = atoi(value)) >= MAX_FORMULA) {
+ warnx("%s: %s: too many formulas (%d)", __func__, file,
+ parray[p].num_formula);
+ return -1;
+ } else if (parray[p].num_formula > 0) {
for (i = 0; i < parray[p].num_formula; i++) {
if (fgets(tmp, sizeof tmp, fp) == NULL) {
warnx("%s: bad formula: feof %s",
@@ -887,6 +922,8 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
xstrdup(tmp) : NULL);
}
}
+ } else {
+ /* null */;
}
} else if (!strcmp(attr, "formula:")) {
/*
@@ -899,9 +936,11 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
* num_alias
*/
- parray[p].numAlias = atoi(value);
-
- if (parray[p].numAlias > 0) {
+ if ((parray[p].numAlias = atoi(value)) >= MAX_ALIASES) {
+ warnx("%s: %s: too many aliases (%d)", __func__, file,
+ parray[p].numAlias);
+ return -1;
+ } else if (parray[p].numAlias > 0) {
for (i = 0; i < parray[p].numAlias; i++) {
if (fgets(tmp, sizeof tmp, fp) == NULL) {
warnx("%s: bad alias: feof %s",
@@ -909,7 +948,7 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
return -1;
}
- if (!(len = strlen(tmp))) {
+ if (!strlen(tmp)) { // XXX
fprintf(stderr, "FICS: Error bad alias "
"in file %s\n", file);
i--;
@@ -928,13 +967,21 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
xstrdup(tmp1);
}
}
+ } else {
+ /* null */;
}
} else if (!strcmp(attr, "num_censor:")) {
/*
* num_censor
*/
- i = atoi(value);
+ if ((i = atoi(value)) < 0) {
+ warnx("%s: num censor negative", __func__);
+ return -1;
+ } else if (i > MAX_CENSOR) {
+ warnx("%s: num censor too large", __func__);
+ return -1;
+ }
while (i--) {
if (fgets(tmp, sizeof tmp, fp) == NULL) {
@@ -953,7 +1000,13 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
}
}
} else if (!strcmp(attr, "num_notify:")) {
- i = atoi(value);
+ if ((i = atoi(value)) < 0) {
+ warnx("%s: num notify negative", __func__);
+ return -1;
+ } else if (i > MAX_NOTIFY) {
+ warnx("%s: num notify too large", __func__);
+ return -1;
+ }
while (i--) {
if (fgets(tmp, sizeof tmp, fp) == NULL) {
@@ -972,7 +1025,10 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
}
}
} else if (!strcmp(attr, "num_noplay:")) {
- i = atoi(value);
+ if ((i = atoi(value)) < 0) {
+ warnx("%s: num noplay negative", __func__);
+ return -1;
+ }
while (i--) {
if (fgets(tmp, sizeof tmp, fp) == NULL) {
@@ -991,7 +1047,10 @@ got_attr_value_player(int p, char *attr, char *value, FILE *fp, char *file)
}
}
} else if (!strcmp(attr, "num_gnotify:")) {
- i = atoi(value);
+ if ((i = atoi(value)) < 0) {
+ warnx("%s: num gnotify negative", __func__);
+ return -1;
+ }
while (i--) {
if (fgets(tmp, sizeof tmp, fp) == NULL) {
@@ -1021,17 +1080,35 @@ PUBLIC int
player_read(int p, char *name)
{
FILE *fp = NULL;
- char *attr, *value;
char fname[MAX_FILENAME_SIZE] = { '\0' };
char line[MAX_LINE_SIZE] = { '\0' };
+ char *attr, *value;
+ char *resolvedPath = NULL;
int len = 0;
int version = 0;
- parray[p].login = stolower(xstrdup(name));
+ parray[p].login = stolower(xstrdup(name)); // free on error?
+
+ if (!is_valid_login_name(parray[p].login)) {
+ warnx("%s: invalid login name: %s", __func__, parray[p].login);
+ return -1;
+ }
snprintf(fname, sizeof fname, "%s/%c/%s", player_dir,
parray[p].login[0], parray[p].login);
+ if ((resolvedPath = realpath(fname, NULL)) != NULL) {
+ if (strncmp(resolvedPath, player_dir,
+ strlen(player_dir)) != 0) {
+ warnx("%s: path traversal detected", __func__);
+ free(resolvedPath);
+ return -1;
+ }
+ mstrlcpy(fname, resolvedPath, sizeof fname);
+ free(resolvedPath);
+ resolvedPath = NULL;
+ }
+
if ((fp = fopen(fname, "r")) == NULL) { // Unregistered player
parray[p].name = xstrdup(name);
parray[p].registered = 0;
@@ -1119,6 +1196,7 @@ player_markdeleted(int p)
FILE *fp;
char fname[MAX_FILENAME_SIZE];
char fname2[MAX_FILENAME_SIZE];
+ int fd;
if (!parray[p].registered) // Player must not be registered
return -1;
@@ -1129,9 +1207,17 @@ player_markdeleted(int p)
parray[p].login[0], parray[p].login);
xrename(__func__, fname, fname2);
- if ((fp = fopen(fname2, "a")) != NULL) { // Touch the file
+ errno = 0;
+ fd = open(fname2, g_open_flags[0], g_open_modes);
+
+ if (fd < 0) {
+ warn("%s: open", __func__);
+ return -1;
+ } else if ((fp = fdopen(fd, "a")) != NULL) { // Touch the file
fprintf(fp, "\n");
fclose(fp);
+ } else {
+ close(fd);
}
return 0;
@@ -1235,6 +1321,7 @@ player_save(int p)
{
FILE *fp;
char fname[MAX_FILENAME_SIZE];
+ int fd;
if (!player_num_ok_chk(p)) {
warnx("%s: invalid player number %d", __func__, p);
@@ -1259,9 +1346,16 @@ player_save(int p)
snprintf(fname, sizeof fname, "%s/%c/%s", player_dir,
parray[p].login[0], parray[p].login);
- if ((fp = fopen(fname, "w")) == NULL) {
+ errno = 0;
+ fd = open(fname, g_open_flags[1], g_open_modes);
+
+ if (fd < 0) {
warn("%s: Problem opening file %s for write", __func__, fname);
return -1;
+ } else if ((fp = fdopen(fd, "w")) == NULL) {
+ warn("%s: Problem opening file %s for write", __func__, fname);
+ close(fd);
+ return -1;
}
WritePlayerFile(fp, p);
@@ -1546,10 +1640,20 @@ player_ontime(int p)
PRIVATE void
write_p_inout(int inout, int p, char *file, int maxlines)
{
- FILE *fp;
+ FILE *fp;
+ int fd;
- if ((fp = fopen(file, "a")) == NULL)
+ errno = 0;
+ fd = open(file, g_open_flags[0], g_open_modes);
+
+ if (fd < 0) {
+ warn("%s: open", __func__);
+ return;
+ } else if ((fp = fdopen(fd, "a")) == NULL) {
+ warn("%s: fdopen", __func__);
+ close(fd);
return;
+ }
fprintf(fp, "%d %s %ld %d %s\n", inout, parray[p].name,
(long int)time(NULL), parray[p].registered,
@@ -1608,7 +1712,7 @@ player_lastconnect(int p)
char loginName[MAX_LOGIN_NAME];
int inout, registered;
int ret, too_long;
- long int lval;
+ int64_t lval = 0;
time_t last = 0;
ret = snprintf(fname, sizeof fname, "%s/player_data/%c/%s.%s",
@@ -1634,8 +1738,8 @@ player_lastconnect(int p)
_Static_assert(19 < ARRAY_SIZE(ipstr),
"'ipstr' too small");
- if (fscanf(fp, "%d %19s %ld %d %19s\n", &inout, loginName,
- &lval, &registered, ipstr) != 5) {
+ if (fscanf(fp, ("%d %19s " "%" SCNd64 " %d %19s\n"), &inout,
+ loginName, &lval, &registered, ipstr) != 5) {
fprintf(stderr, "FICS: Error in login info format. %s"
"\n", fname);
fclose(fp);
@@ -1656,7 +1760,7 @@ player_lastdisconnect(int p)
char loginName[MAX_LOGIN_NAME];
int inout, registered;
int ret, too_long;
- long int lval;
+ int64_t lval;
time_t last = 0;
ret = snprintf(fname, sizeof fname, "%s/player_data/%c/%s.%s",
@@ -1677,8 +1781,8 @@ player_lastdisconnect(int p)
_Static_assert(19 < ARRAY_SIZE(ipstr),
"'ipstr' too small");
- if (fscanf(fp, "%d %19s %ld %d %19s\n", &inout, loginName,
- &lval, &registered, ipstr) != 5) {
+ if (fscanf(fp, ("%d %19s " "%" SCNd64 " %d %19s\n"), &inout,
+ loginName, &lval, &registered, ipstr) != 5) {
fprintf(stderr, "FICS: Error in login info format. %s"
"\n", fname);
fclose(fp);
@@ -2315,7 +2419,6 @@ player_goto_next_board(int p)
on = parray[p].simul_info.onBoard;
start = on;
- g = -1;
do {
on++;
@@ -2344,7 +2447,6 @@ player_goto_prev_board(int p)
on = parray[p].simul_info.onBoard;
start = on;
- g = -1;
do {
--on;
@@ -2520,9 +2622,10 @@ PUBLIC int
player_add_message(int top, int fromp, char *message)
{
FILE *fp;
- char fname[MAX_FILENAME_SIZE];
- char messbody[1024];
- char subj[256];
+ char fname[MAX_FILENAME_SIZE] = { '\0' };
+ char messbody[1024] = { '\0' };
+ char subj[256] = { '\0' };
+ int fd;
time_t t = time(NULL);
if (!parray[top].registered)
@@ -2535,8 +2638,16 @@ player_add_message(int top, int fromp, char *message)
if (lines_file(fname) >= MAX_MESSAGES && parray[top].adminLevel == 0)
return -1;
- if ((fp = fopen(fname, "a")) == NULL)
+ errno = 0;
+ fd = open(fname, g_open_flags[0], g_open_modes);
+
+ if (fd < 0)
return -1;
+ else if ((fp = fdopen(fd, "a")) == NULL) {
+ close(fd);
+ return -1;
+ }
+
fprintf(fp, "%s at %s: %s\n", parray[fromp].name, strltime(&t),
message);
fclose(fp);
@@ -2699,13 +2810,21 @@ PRIVATE int
WriteMsgFile(int p, textlist *Head)
{
FILE *fp;
- char fName[MAX_FILENAME_SIZE];
+ char fName[MAX_FILENAME_SIZE] = { '\0' };
+ int fd;
textlist *Cur;
GetMsgFile(p, fName, sizeof fName, __func__);
- if ((fp = fopen(fName, "w")) == NULL)
+ errno = 0;
+ fd = open(fName, g_open_flags[1], g_open_modes);
+
+ if (fd < 0)
return 0;
+ else if ((fp = fdopen(fd, "w")) == NULL) {
+ close(fd);
+ return 0;
+ }
for (Cur = Head; Cur != NULL; Cur = Cur->next)
fprintf(fp, "%s", Cur->text);
fclose(fp);
@@ -2783,7 +2902,7 @@ player_show_messages(int p)
PUBLIC int
ShowMsgsBySender(int p, param_list param)
{
- int nFrom, nTo;
+ int nFrom = -1, nTo = -1;
int p1, connected;
textlist *Head;
@@ -2796,8 +2915,6 @@ ShowMsgsBySender(int p, param_list param)
return -1; /* no need to disconnect */
}
- nFrom = nTo = -1;
-
if (p != p1) {
if ((nTo = LoadMsgs(p1, p + 1, &Head)) <= 0) {
pprintf(p, "%s has no messages from you.\n",
@@ -3110,7 +3227,8 @@ PUBLIC int
player_add_comment(int p_by, int p_to, char *comment)
{
FILE *fp;
- char fname[MAX_FILENAME_SIZE];
+ char fname[MAX_FILENAME_SIZE] = { '\0' };
+ int fd;
time_t t = time(NULL);
if (!parray[p_to].registered)
@@ -3119,8 +3237,17 @@ player_add_comment(int p_by, int p_to, char *comment)
snprintf(fname, sizeof fname, "%s/player_data/%c/%s.%s", stats_dir,
parray[p_to].login[0], parray[p_to].login, "comments");
- if ((fp = fopen(fname, "a")) == NULL)
+ errno = 0;
+ fd = open(fname, g_open_flags[0], g_open_modes);
+
+ if (fd < 0) {
+ warn("%s: open", __func__);
+ return -1;
+ } else if ((fp = fdopen(fd, "a")) == NULL) {
+ warn("%s: fdopen", __func__);
+ close(fd);
return -1;
+ }
fprintf(fp, "%s at %s: %s\n", parray[p_by].name, strltime(&t), comment);
fclose(fp);
diff --git a/FICS/ratings.c b/FICS/ratings.c
index 3a7cdd0..a85776d 100644
--- a/FICS/ratings.c
+++ b/FICS/ratings.c
@@ -32,6 +32,11 @@
fixed ignored retvals.
Markus Uhlin 24/11/28 Added null checks
Markus Uhlin 25/03/16 Fixed use of 32-bit 'time_t'.
+ Markus Uhlin 25/04/06 Fixed Clang Tidy warnings.
+ Markus Uhlin 25/07/28 Fixed missing return-value check
+ for a 'scanf'-like function.
+ Markus Uhlin 25/07/28 Restricted file permissions upon
+ creation.
*/
#include "stdinclude.h"
@@ -39,11 +44,14 @@
#include <err.h>
#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
#include <stdint.h>
#include "command.h"
#include "comproc.h"
#include "config.h"
+#include "ficsmain.h"
#include "gamedb.h"
#include "lists.h"
#include "playerdb.h"
@@ -347,49 +355,52 @@ load_ratings(void)
return;
}
- for (int i = 0; i < MAXHIST; i++) {
- int ret, errno_save;
+ for (int i = 0; i < MAXHIST && !feof(fp) && !ferror(fp); i++) {
+ int ret;
sHist[i] = bHist[i] = wHist[i] = lHist[i] = 0;
- errno = 0;
ret = fscanf(fp, "%d %d %d %d", &sHist[i], &bHist[i], &wHist[i],
&lHist[i]);
- errno_save = errno;
if (ret != 4) {
- if (feof(fp) || ferror(fp))
- break;
- errno = errno_save;
- warn("%s: too few items assigned (iteration: %d)",
- __func__, i);
+ warnx("%s: %s: too few items assigned (iteration: %d)",
+ __func__, fname, i);
+ fclose(fp);
+ return;
}
}
+ if (ferror(fp)) {
+ warnx("%s: %s: the error indicator is set", __func__, fname);
+ fclose(fp);
+ return;
+ }
+
fclose(fp);
- if (Rs_count) {
- Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ if (Rs_count != 0) {
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count); // NOLINT
Ratings_S_Average = (Rs_total / (double)Rs_count);
} else {
Ratings_S_StdDev = 0;
Ratings_S_Average = 0;
}
- if (Rb_count) {
- Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ if (Rb_count != 0) {
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count); // NOLINT
Ratings_B_Average = (Rb_total / (double)Rb_count);
} else {
Ratings_B_StdDev = 0;
Ratings_B_Average = 0;
}
- if (Rw_count) {
- Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ if (Rw_count != 0) {
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count); // NOLINT
Ratings_W_Average = (Rw_total / (double)Rw_count);
} else {
Ratings_W_StdDev = 0;
Ratings_W_Average = 0;
}
- if (Rl_count) {
- Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
+ if (Rl_count != 0) {
+ Ratings_L_StdDev = sqrt(Rl_S / Rl_count); // NOLINT
Ratings_L_Average = (Rl_total / (double)Rl_count);
} else {
Ratings_L_StdDev = 0;
@@ -402,13 +413,21 @@ save_ratings(void)
{
FILE *fp;
char fname[MAX_FILENAME_SIZE] = { '\0' };
+ int fd;
snprintf(fname, sizeof fname, "%s/newratingsV%d_data", stats_dir,
STATS_VERSION);
- if ((fp = fopen(fname, "w")) == NULL) {
+ errno = 0;
+ fd = open(fname, g_open_flags[1], g_open_modes);
+
+ if (fd < 0) {
warn("%s: can't write ratings data", __func__);
return;
+ } else if ((fp = fdopen(fd, "w")) == NULL) {
+ warn("%s: can't write ratings data", __func__);
+ close(fd);
+ return;
}
fprintf(fp, "%10f %10f %10f %d\n", Rb_M, Rb_S, Rb_total, Rb_count);
@@ -1303,6 +1322,18 @@ com_statistics(int p, param_list param)
return COM_OK;
}
+/*
+ * Return the difference of 'a - b'
+ */
+PUBLIC int
+int_diff(const char *fn, const int a, const int b)
+{
+ if ((b > 0 && a < INT_MIN + b) ||
+ (b < 0 && a > INT_MAX + b))
+ errx(1, "%s: integer overflow (%d - %d)", fn, a, b);
+ return (a - b);
+}
+
PUBLIC int
com_fixrank(int p, param_list param)
{
@@ -1351,10 +1382,8 @@ DisplayRank(int p, param_list param, int showComputers)
end = -1;
ret = sscanf(param[0].val.word, "%d-%d", &start, &end);
- if (ret != 2) {
-// warnx("%s: sscanf() == %d", __func__, ret);
+ if (ret != 2)
return COM_FAILED;
- }
if (end > 0 && (param[1].type != TYPE_NULL))
show = ShowFromString(param[1].val.word);
@@ -1458,7 +1487,8 @@ UpdateRank(int type, char *addName, statistics *sNew, char *delName)
char command[MAX_STRING_LENGTH];
char line[MAX_RANK_LINE] = { '\0' };
char login[MAX_LOGIN_NAME] = { '\0' };
- int comp;
+ int comp = 0;
+ int fd = -1;
statistics sCur;
if (GetRankFileName(RankFile, sizeof RankFile, type) < 0)
@@ -1471,9 +1501,17 @@ UpdateRank(int type, char *addName, statistics *sNew, char *delName)
snprintf(TmpRankFile, sizeof TmpRankFile, "%s/tmpRank", sdir);
- if ((fptemp = fopen(TmpRankFile, "w")) == NULL) {
+ errno = 0;
+ fd = open(TmpRankFile, g_open_flags[1], g_open_modes);
+
+ if (fd < 0) {
+ warn("%s: open", __func__);
+ fclose(fp);
+ return;
+ } else if ((fptemp = fdopen(fd, "w")) == NULL) {
warn("%s: unable to open rank file for updating", __func__);
fclose(fp);
+ close(fd);
return;
}
@@ -1550,7 +1588,8 @@ GetRank(FILE *fp, char *target, int countComp)
char line[MAX_RANK_LINE] = { '\0' };
char login[MAX_LOGIN_NAME] = { '\0' };
int count = 0;
- int nGames, is_computer;
+ int is_computer = 0;
+ int nGames = 0;
int playerFound = 0;
while (fgets(line, sizeof line, fp) != NULL &&
@@ -1558,8 +1597,7 @@ GetRank(FILE *fp, char *target, int countComp)
_Static_assert(ARRAY_SIZE(login) > 19, "'login' too small");
if (sscanf(line, "%19s %*d %d %d", login, &nGames, &is_computer)
- != 1) {
-// warnx("%s: sscanf() error", __func__);
+ < 3) {
continue;
}
@@ -1582,16 +1620,20 @@ PositionFilePtr(FILE *fp, int count, int *last, int *nTied, int showComp)
return;
rating = nGames = is_computer = 0;
+ errno = 0;
rewind(fp);
+ if (errno) {
+ warn("%s: rewind", __func__);
+ return;
+ }
for (int i = 1; i < count; i++) {
do {
_Static_assert(ARRAY_SIZE(login) > 19,
"'login' too small");
- if (fgets(line, sizeof line, fp) == NULL ||
- feof(fp) ||
- ferror(fp))
+ if (feof(fp) || ferror(fp) ||
+ fgets(line, sizeof line, fp) == NULL)
break;
else if (sscanf(line, "%19s %d %d %d", login, &rating,
&nGames, &is_computer) != 4) {
@@ -1600,6 +1642,11 @@ PositionFilePtr(FILE *fp, int count, int *last, int *nTied, int showComp)
}
} while (!CountRankLine(showComp, login, nGames, is_computer));
+ if (ferror(fp)) {
+ warnx("%s: the error indicator is set", __func__);
+ return;
+ }
+
if (rating != *last) {
*nTied = 1;
*last = rating;
@@ -1618,7 +1665,7 @@ ShowRankEntry(int p, FILE *fp, int count, int comp, char *target,
// XXX
rating = 0;
- findable = (count > 0 && !feof(fp));
+ findable = (count > 0 && !feof(fp) && !ferror(fp));
nGames = 0;
is_comp = 0;
@@ -1815,9 +1862,9 @@ DisplayTargetRank(int p, char *target, int show, int showComp)
numAbove = CountAbove(numToShow, blitzRank, stdRank, wildRank, show);
- blitzCount = (blitzRank - numAbove);
- stdCount = (stdRank - numAbove);
- wildCount = (wildRank - numAbove);
+ blitzCount = int_diff(__func__, blitzRank, numAbove);
+ stdCount = int_diff(__func__, stdRank, numAbove);
+ wildCount = int_diff(__func__, wildRank, numAbove);
ShowRankLines(p, fb, fs, fw, blitzCount, stdCount, wildCount, numToShow,
showComp, show, target);
diff --git a/FICS/ratings.h b/FICS/ratings.h
index 4cf47a8..7f2a521 100644
--- a/FICS/ratings.h
+++ b/FICS/ratings.h
@@ -26,6 +26,8 @@
#ifndef _RATINGS_H
#define _RATINGS_H
+#include "common.h"
+
#define STATS_VERSION 2
#define RESULT_WIN 0
@@ -49,6 +51,7 @@ typedef struct _rateStruct {
int rating;
} rateStruct;
+__FICS_BEGIN_DECLS
extern int Best(int, param_list, int);
extern int DisplayRank(int, param_list, int);
extern int DisplayRankedPlayers(int, int, int, int, int);
@@ -61,6 +64,7 @@ extern int com_hbest(int, param_list);
extern int com_hrank(int, param_list);
extern int com_rank(int, param_list);
extern int com_statistics(int, param_list);
+extern int int_diff(const char *, const int, const int);
extern int is_active(int);
extern int rating_delta(int, int, int, int, int);
extern int rating_update(int);
@@ -72,5 +76,6 @@ extern void rating_recalc(void);
extern void rating_remove(int, int);
extern void rating_sterr_delta(int, int, int, time_t, int, int *, double *);
extern void save_ratings(void);
+__FICS_END_DECLS
#endif /* _RATINGS_H */
diff --git a/FICS/talkproc.c b/FICS/talkproc.c
index 3d16be1..601f43b 100644
--- a/FICS/talkproc.c
+++ b/FICS/talkproc.c
@@ -34,6 +34,8 @@
Markus Uhlin 24/04/14 Refactored and reformatted ALL
functions.
Markus Uhlin 25/01/18 Fixed -Wshadow
+ Markus Uhlin 25/04/04 tell: fixed constant expression
+ result
*/
#include "stdinclude.h"
@@ -296,8 +298,8 @@ tell(int p, int p1, char *msg, int why, int ch)
}
if (player_censored(p1, p) && parray[p].adminLevel == 0) {
- if (why != TELL_KIBITZ ||
- why != TELL_WHISPER ||
+ if (why != TELL_KIBITZ &&
+ why != TELL_WHISPER &&
why != TELL_CHANNEL) {
pprintf(p, "Player \"%s\" is censoring you.\n",
parray[p1].name);
diff --git a/FICS/utils.c b/FICS/utils.c
index ce02a5d..4da2411 100644
--- a/FICS/utils.c
+++ b/FICS/utils.c
@@ -38,14 +38,25 @@
in truncate_file().
Markus Uhlin 25/03/09 truncate_file:
fixed null ptr dereference.
+ Markus Uhlin 25/04/06 Fixed Clang Tidy warnings.
+ Markus Uhlin 25/07/21 Replaced non-reentrant functions
+ with their corresponding thread
+ safe version.
+ Markus Uhlin 25/07/28 truncate_file: restricted file
+ permissions upon creation.
*/
#include "stdinclude.h"
#include "common.h"
+#include <ctype.h>
#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
#include "config.h"
+#include "ficsmain.h"
#include "network.h"
#include "playerdb.h"
#include "rmalloc.h"
@@ -70,6 +81,49 @@ struct t_dirs {
PRIVATE char** t_buffer = NULL;
PRIVATE int t_buffersize = 0;
+PUBLIC bool
+is_valid_filename(const char *name, const bool allow_hidden)
+{
+ if (name == NULL || strcmp(name, "") == 0)
+ return false;
+ if (!allow_hidden && name[0] == '.')
+ return false;
+ if (strstr(name, "..") || strchr(name, '/') || strchr(name, '\\') ||
+ name[0] == '/')
+ return false;
+ for (const char *p = name; *p; ++p) {
+ if (isspace((unsigned char)*p) || iscntrl((unsigned char)*p))
+ return false;
+ }
+ for (const char *p = name; *p; ++p) {
+ if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' &&
+ *p != '.')
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Function to validate login names for file path usage
+ */
+PUBLIC bool
+is_valid_login_name(const char *login)
+{
+ if (login == NULL || login[0] == '\0' ||
+ strstr(login, "..") || strchr(login, '/') || strchr(login, '\\'))
+ return false;
+
+ for (const char *p = login; *p; ++p) {
+ if (!((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9') ||
+ *p == '_'))
+ return false;
+ }
+
+ return true;
+}
+
PUBLIC int
count_lines(FILE *fp)
{
@@ -275,10 +329,10 @@ pcommand(int p, char *comstr, ...)
return retval;
}
-PUBLIC int
+PUBLIC void
pprintf(int p, const char *format, ...)
{
- char tmp[10 * MAX_LINE_SIZE];
+ char tmp[10 * MAX_LINE_SIZE] = { '\0' };
int retval;
va_list ap;
@@ -286,8 +340,8 @@ pprintf(int p, const char *format, ...)
retval = vsnprintf(tmp, sizeof tmp, format, ap);
va_end(ap);
+ UNUSED_VAR(retval);
net_send_string(parray[p].socket, tmp, 1);
- return retval;
}
PRIVATE void
@@ -393,7 +447,7 @@ pprintf_noformat(int p, char *format, ...)
}
PUBLIC int
-psend_raw_file(int p, char *dir, char *file)
+psend_raw_file(int p, const char *dir, const char *file)
{
FILE *fp;
char fname[MAX_FILENAME_SIZE] = { '\0' };
@@ -408,9 +462,18 @@ psend_raw_file(int p, char *dir, char *file)
if ((fp = fopen(fname, "r")) == NULL)
return -1;
- while ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp)) > 0) {
- tmp[num] = '\0';
- net_send_string(parray[p].socket, tmp, 1);
+ while (!feof(fp) && !ferror(fp)) {
+ if ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1,
+ fp)) > 0) {
+ tmp[num] = '\0';
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+
+ if (ferror(fp)) {
+ warnx("%s: %s: the error indicator is set", __func__, fname);
+ fclose(fp);
+ return -1;
}
fclose(fp);
@@ -418,7 +481,7 @@ psend_raw_file(int p, char *dir, char *file)
}
PUBLIC int
-psend_file(int p, char *dir, char *file)
+psend_file(int p, const char *dir, const char *file)
{
FILE *fp;
char fname[MAX_FILENAME_SIZE] = { '\0' };
@@ -445,6 +508,12 @@ psend_file(int p, char *dir, char *file)
}
if (!feof(fp)) {
+ if (ferror(fp)) {
+ warnx("%s: %s: the error indicator is set", __func__,
+ fname);
+ fclose(fp);
+ return -1;
+ }
parray[p].last_file = xstrdup(fname);
parray[p].last_file_byte = ftell(fp);
pprintf(p, "Type [next] to see next page.\n");
@@ -513,8 +582,17 @@ pmore_file(int p)
}
if (!feof(fp)) {
- parray[p].last_file_byte = ftell(fp);
- pprintf(p, "Type [next] to see next page.\n");
+ if (ferror(fp)) {
+ warnx("%s: %s: the error indicator is set", __func__,
+ parray[p].last_file);
+ fclose(fp);
+ return -1;
+ } else if ((parray[p].last_file_byte = ftell(fp)) == -1) {
+ warn("%s: %s: ftell", __func__, parray[p].last_file);
+ fclose(fp);
+ return -1;
+ } else
+ pprintf(p, "Type [next] to see next page.\n");
} else {
rfree(parray[p].last_file);
parray[p].last_file = NULL;
@@ -729,19 +807,31 @@ fix_time(char *old_time)
}
PUBLIC char *
-strltime(time_t *clock)
+strltime(time_t *p_clock)
{
- struct tm *stm = localtime(clock);
+ struct tm stm = {0};
+
+ errno = 0;
- return strtime(stm);
+ if (localtime_r(p_clock, &stm) == NULL) {
+ warn("%s: localtime_r", __func__);
+ memset(&stm, 0, sizeof stm);
+ }
+ return strtime(&stm);
}
PUBLIC char *
-strgtime(time_t *clock)
+strgtime(time_t *p_clock)
{
- struct tm *stm = gmtime(clock);
+ struct tm stm = {0};
+
+ errno = 0;
- return strtime(stm);
+ if (gmtime_r(p_clock, &stm) == NULL) {
+ warn("%s: gmtime_r", __func__);
+ memset(&stm, 0, sizeof stm);
+ }
+ return strtime(&stm);
}
/*
@@ -813,8 +903,17 @@ truncate_file(char *file, int lines)
fclose(fp);
if (trunc) {
- if ((fp = fopen(file, "w")) == NULL) {
- warn("%s: fopen", __func__);
+ int fd;
+
+ errno = 0;
+ fd = open(file, g_open_flags[1], g_open_modes);
+
+ if (fd < 0) {
+ warn("%s: open", __func__);
+ return 1;
+ } else if ((fp = fdopen(fd, "w")) == NULL) {
+ warn("%s: fdopen", __func__);
+ close(fd);
return 1;
}
@@ -964,10 +1063,11 @@ ratstrii(int rat, int reg)
* Fill 't_buffer' with anything matching "want*" in file tree
*/
PRIVATE void
-t_sft(char *want, struct t_tree *t)
+t_sft(const char *want, struct t_tree *t)
{
if (t) {
- int cmp = strncmp(want, t->name, strlen(want));
+ const char *v_want = (want ? want : "");
+ int cmp = strncmp(v_want, t->name, strlen(v_want));
if (cmp <= 0) // If 'want' <= this one, look left
t_sft(want, t->left);
@@ -1050,7 +1150,6 @@ PUBLIC int
search_directory(char *dir, char *filter, char **buffer, int buffersize)
{
int cmp;
- static char nullify = '\0';
static struct t_dirs *ramdirs = NULL;
struct stat statbuf;
struct t_dirs** i;
@@ -1059,9 +1158,6 @@ search_directory(char *dir, char *filter, char **buffer, int buffersize)
t_buffersize = buffersize;
if (!stat(dir, &statbuf)) {
- if (filter == NULL) // NULL becomes pointer to null string
- filter = &nullify;
-
i = &ramdirs;
while (*i) { // Find dir in dir tree
diff --git a/FICS/utils.h b/FICS/utils.h
index 5342be2..e376a03 100644
--- a/FICS/utils.h
+++ b/FICS/utils.h
@@ -31,6 +31,7 @@
#ifndef _UTILS_H
#define _UTILS_H
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -58,6 +59,8 @@
#define CheckFlag(VAR, FLAG) (VAR & (FLAG))
__FICS_BEGIN_DECLS
+extern bool is_valid_filename(const char *, const bool);
+extern bool is_valid_login_name(const char *);
extern char *dotQuad(unsigned int);
extern char *eattailwhite(char *);
extern char *eatwhite(char *);
@@ -90,15 +93,15 @@ extern int mail_string_to_address(char *, char *, char *);
extern int mail_string_to_user(int, char *, char *);
extern int pcommand(int, char *, ...) PRINTFLIKE(2);
extern int pmore_file(int);
-extern int pprintf(int, const char *, ...) PRINTFLIKE(2);
+extern void pprintf(int, const char *, ...) PRINTFLIKE(2);
extern int pprintf_highlight(int, char *, ...) PRINTFLIKE(2);
extern int pprintf_noformat(int, char *, ...) PRINTFLIKE(2);
extern int pprintf_prompt(int, char *, ...) PRINTFLIKE(2);
extern int printablestring(char *);
extern int psend_command(int, char *, char *);
-extern int psend_file(int, char *, char *);
+extern int psend_file(int, const char *, const char *);
extern int psend_logoutfile(int, char *, char *);
-extern int psend_raw_file(int, char *, char *);
+extern int psend_raw_file(int, const char *, const char *);
extern int psprintf_highlight(int, char *, size_t, char *, ...)
PRINTFLIKE(4);
extern int safechar(int);
@@ -107,8 +110,6 @@ extern int search_directory(char *, char *, char **, int);
extern int truncate_file(char *, int);
extern time_t untenths(uint64_t);
extern unsigned int tenth_secs(void);
-//extern void pprintf_dohightlight(int);
-//extern void sprintf_dohightlight(int, char *);
__FICS_END_DECLS
#endif /* _UTILS_H */
diff --git a/FICS/vers.c b/FICS/vers.c
index d556d4a..c6dd99c 100644
--- a/FICS/vers.c
+++ b/FICS/vers.c
@@ -1,5 +1,5 @@
#include "vers.h"
const char SGS_VERS[] = "";
-const char VERS_NUM[] = "rpblc-1.4.4";
+const char VERS_NUM[] = "rpblc-1.4.5";
const char COMP_DATE[] = __DATE__;