tHigh score file format changed, such that setgid dopewars should not now be fooled into overwriting other games' save files - vaccinewars - be a doctor and try to vaccinate the world
(HTM) git clone git://src.adamsgaard.dk/vaccinewars
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 0441b92068ad4864c5afbe6171a89a6df261f747
(DIR) parent 3d4c0be2224aab5c10dc43b3cec167dc06de0784
(HTM) Author: Ben Webb <ben@salilab.org>
Date: Thu, 13 Sep 2001 16:23:27 +0000
High score file format changed, such that setgid dopewars should not now be
fooled into overwriting other games' save files
Diffstat:
M ChangeLog | 4 ++++
M src/dopewars.c | 39 ++++++++++++++++++-------------
M src/dopewars.h | 5 +++--
M src/serverside.c | 169 ++++++++++++++++++++++++++-----
M src/serverside.h | 4 +++-
5 files changed, 175 insertions(+), 46 deletions(-)
---
(DIR) diff --git a/ChangeLog b/ChangeLog
t@@ -4,6 +4,10 @@ cvs
- Metaserver code is now non-blocking (and should soon support more
HTTP features, such as redirects and authentication)
- Many code cleanups
+ - High score files now have a "proper" header, so that file(1) can
+ identify them, and so the -f option cannot be used to force setgid-games
+ dopewars to overwrite random files writeable by group "games" - use
+ the -C option to convert old high score files to the new format
1.5.1 19-06-2001
- Improved logging in server via. LogLevel and LogTimestamp variables
(DIR) diff --git a/src/dopewars.c b/src/dopewars.c
t@@ -61,8 +61,8 @@ gboolean Network,Client,Server,NotifyMetaServer,AIPlayer;
*/
unsigned Port=7902;
gboolean Sanitized,ConfigVerbose,DrugValue;
-char *HiScoreFile=NULL,*ServerName=NULL,*Pager=NULL;
-gboolean WantHelp,WantVersion,WantAntique,WantColour,WantNetwork;
+char *HiScoreFile=NULL,*ServerName=NULL,*Pager=NULL,*ConvertFile=NULL;
+gboolean WantHelp,WantVersion,WantAntique,WantColour,WantNetwork,WantConvert;
ClientType WantedClient;
int NumLocation=0,NumGun=0,NumCop=0,NumDrug=0,NumSubway=0,
NumPlaying=0,NumStoppedTo=0;
t@@ -1621,6 +1621,7 @@ void SetupParameters() {
/* Initialise variables */
srand((unsigned)time(NULL));
PidFile=NULL;
+ ConvertFile=NULL;
Location=NULL;
Gun=NULL;
Drug=NULL;
t@@ -1630,8 +1631,8 @@ void SetupParameters() {
NumLocation=NumGun=NumDrug=0;
FirstClient=FirstServer=NULL;
Noone.Name=g_strdup("Noone");
- WantColour=WantNetwork=1;
- WantHelp=WantVersion=WantAntique=0;
+ WantColour=WantNetwork=TRUE;
+ WantHelp=WantConvert=WantVersion=WantAntique=FALSE;
WantedClient=CLIENT_AUTO;
Server=AIPlayer=Client=Network=FALSE;
t@@ -1699,15 +1700,15 @@ Drug dealing game based on \"Drug Wars\" by John E. Dell\n\
-n be boring and don't connect to any available dopewars servers\n\
(i.e. single player mode)\n\
-a \"antique\" dopewars - keep as closely to the original version as\n\
- possible (this also disables any networking)\n\
+ possible (no networking)\n\
-f file specify a file to use as the high score table\n\
(by default %s/dopewars.sco is used)\n\
-o addr specify a hostname where the server for multiplayer dopewars\n\
- can be found (in human-readable - e.g. nowhere.com - format)\n\
+ can be found\n\
-s run in server mode (note: for a \"non-interactive\" server, simply\n\
run as dopewars -s < /dev/null >> logfile & )\n\
-S run a \"private\" server (i.e. do not notify the metaserver)\n\
- -p specify the network port to use (default: 7902)\n\
+ -p port specify the network port to use (default: 7902)\n\
-g file specify the pathname of a dopewars configuration file. This file\n\
is read immediately when the -g option is encountered\n\
-r file maintain pid file \"file\" while running the server\n\
t@@ -1715,6 +1716,7 @@ Drug dealing game based on \"Drug Wars\" by John E. Dell\n\
-w force the use of a graphical (windowed) client (GTK+ or Win32)\n\
-t force the use of a text-mode client (curses)\n\
(by default, a windowed client is used when possible)\n\
+ -C file convert an \"old format\" score file to the new format\n\
-h display this help information\n\
-v output version information and exit\n\n\
dopewars is Copyright (C) Ben Webb 1998-2001, and released under the GNU GPL\n\
t@@ -1723,18 +1725,19 @@ Report bugs to the author at ben@bellatrix.pcl.ox.ac.uk\n"),DATADIR);
void HandleCmdLine(int argc,char *argv[]) {
int c;
+
while (1) {
- c=getopt(argc,argv,"anbchvf:o:sSp:g:r:wt");
- if (c==EOF) break;
+ c=getopt(argc,argv,"anbchvf:o:sSp:g:r:wtC:");
+ if (c==-1) break;
switch(c) {
- case 'n': WantNetwork=0; break;
- case 'b': WantColour=0; break;
- case 'c': AIPlayer=1; break;
- case 'a': WantAntique=1; WantNetwork=0; break;
- case 'v': WantVersion=1; break;
+ case 'n': WantNetwork=FALSE; break;
+ case 'b': WantColour=FALSE; break;
+ case 'c': AIPlayer=TRUE; break;
+ case 'a': WantAntique=TRUE; WantNetwork=FALSE; break;
+ case 'v': WantVersion=TRUE; break;
case 'h':
case 0 :
- case '?': WantHelp=1; break;
+ case '?': WantHelp=TRUE; break;
case 'f': AssignName(&HiScoreFile,optarg); break;
case 'o': AssignName(&ServerName,optarg); break;
case 's': Server=TRUE; NotifyMetaServer=TRUE; break;
t@@ -1744,6 +1747,7 @@ void HandleCmdLine(int argc,char *argv[]) {
case 'r': AssignName(&PidFile,optarg); break;
case 'w': WantedClient=CLIENT_WINDOW; break;
case 't': WantedClient=CLIENT_CURSES; break;
+ case 'C': AssignName(&ConvertFile,optarg); WantConvert=TRUE; break;
}
}
}
t@@ -1753,7 +1757,7 @@ int GeneralStartup(int argc,char *argv[]) {
/* score init.) - Returns 0 if OK, -1 if something failed. */
SetupParameters();
HandleCmdLine(argc,argv);
- if (!WantVersion && !WantHelp && !AIPlayer) {
+ if (!WantVersion && !WantHelp && !AIPlayer && !WantConvert) {
return InitHighScoreFile();
}
return 0;
t@@ -1807,6 +1811,8 @@ int main(int argc,char *argv[]) {
if (GeneralStartup(argc,argv)==0) {
if (WantVersion || WantHelp) {
HandleHelpTexts();
+ } else if (WantConvert) {
+ ConvertHighScoreFile();
} else {
#ifdef NETWORKING
StartNetworking();
t@@ -1846,6 +1852,7 @@ int main(int argc,char *argv[]) {
}
CloseHighScoreFile();
g_free(PidFile);
+ g_free(ConvertFile);
return 0;
}
(DIR) diff --git a/src/dopewars.h b/src/dopewars.h
t@@ -153,8 +153,9 @@ extern gboolean Network,Client,Server,NotifyMetaServer,AIPlayer;
extern unsigned Port;
extern gboolean Sanitized,ConfigVerbose,DrugValue;
extern int NumLocation,NumGun,NumCop,NumDrug,NumSubway,NumPlaying,NumStoppedTo;
-extern gchar *HiScoreFile,*ServerName,*Pager;
-extern gboolean WantHelp,WantVersion,WantAntique,WantColour,WantNetwork;
+extern gchar *HiScoreFile,*ServerName,*Pager,*ConvertFile;
+extern gboolean WantHelp,WantVersion,WantAntique,WantColour,
+ WantNetwork,WantConvert;
extern ClientType WantedClient;
extern int LoanSharkLoc,BankLoc,GunShopLoc,RoughPubLoc;
extern int DrugSortMethod,FightTimeout,IdleTimeout,ConnectTimeout;
(DIR) diff --git a/src/serverside.c b/src/serverside.c
t@@ -127,6 +127,8 @@ int SendSingleHighScore(Player *Play,struct HISCORE *Score,
int ind,gboolean Bold);
static int SendCopOffer(Player *To,OfferForce Force);
static int OfferObject(Player *To,gboolean ForceBitch);
+static gboolean HighScoreWrite(FILE *fp,struct HISCORE *MultiScore,
+ struct HISCORE *AntiqueScore);
#ifdef GUI_SERVER
static void GuiHandleMeta(gpointer data,gint socket,
t@@ -183,7 +185,7 @@ void RegisterWithMetaServer(gboolean Up,gboolean SendData,
AddURLEnc(body,MetaServer.Password);
}
- if (SendData && HighScoreRead(MultiScore,AntiqueScore)) {
+ if (SendData && HighScoreRead(ScoreFP,MultiScore,AntiqueScore,TRUE)) {
for (i=0;i<NUMHISCORE;i++) {
if (MultiScore[i].Name && MultiScore[i].Name[0]) {
g_string_sprintfa(body,"&nm[%d]=",i);
t@@ -1172,19 +1174,110 @@ void CloseHighScoreFile() {
if (ScoreFP) fclose(ScoreFP);
}
-int InitHighScoreFile() {
+static void DropPrivileges() {
+/* If we're running setuid/setgid, drop down to the privilege level of the */
+/* user that started the dopewars process */
+#ifndef CYGWIN
+ if (setregid(getgid(),getgid())!=0) {
+ perror("setregid");
+ exit(1);
+ }
+#endif
+}
+
+static const gchar SCOREHEADER[] = "DOPEWARS SCORES V.";
+static const guint SCOREHDRLEN = sizeof(SCOREHEADER)-1; /* Don't include \0 */
+static const guint SCOREVERSION = 1;
+
+static gboolean HighScoreReadHeader(FILE *fp,gint *ScoreVersion) {
+ gchar *header;
+
+ if (read_string(fp,&header)!=EOF) {
+ if (header && strlen(header) > SCOREHDRLEN &&
+ strncmp(header,SCOREHEADER,SCOREHDRLEN)==0) {
+ if (ScoreVersion) *ScoreVersion = atoi(header+SCOREHDRLEN);
+ g_free(header);
+ return TRUE;
+ }
+ }
+ g_free(header);
+ return FALSE;
+}
+
+static void HighScoreWriteHeader(FILE *fp) {
+ gchar *header;
+
+ header = g_strdup_printf("%s%d",SCOREHEADER,SCOREVERSION);
+ fwrite(header,strlen(header)+1,1,fp);
+}
+
+void ConvertHighScoreFile(void) {
+/* Converts an old format high score file to the new format. */
+ FILE *old,*backup;
+ gchar *BackupFile,ch;
+ struct HISCORE MultiScore[NUMHISCORE],AntiqueScore[NUMHISCORE];
+
+/* The user running dopewars must be allowed to mess with the score file */
+ DropPrivileges();
+
+ BackupFile = g_strdup_printf("%s.bak",ConvertFile);
+
+ old=fopen(ConvertFile,"r+");
+ backup=fopen(BackupFile,"w");
+
+ if (old && backup) {
+
+/* First, make a backup of the old file */
+ ftruncate(fileno(backup),0); rewind(backup);
+ rewind(old);
+ while(1) {
+ ch = fgetc(old);
+ if (ch==EOF) break; else fputc(ch,backup);
+ }
+ fclose(backup);
+
+/* Read in the scores without the header, and then write out with the header */
+ if (!HighScoreRead(old,MultiScore,AntiqueScore,FALSE)) {
+ g_log(NULL,G_LOG_LEVEL_CRITICAL,_("Error reading scores from %s."),
+ ConvertFile);
+ } else {
+ ftruncate(fileno(old),0); rewind(old);
+ if (HighScoreWrite(old,MultiScore,AntiqueScore)) {
+ g_message(_("The high score file %s has been converted to the new "
+ "format.\nA backup of the old file has been created "
+ "as %s.\n"),ConvertFile,BackupFile);
+ }
+ }
+ fclose(old);
+ } else {
+ if (!old) {
+ g_log(NULL,G_LOG_LEVEL_CRITICAL,_("Cannot open high score file %s."),
+ ConvertFile);
+ } else if (!backup) {
+ g_log(NULL,G_LOG_LEVEL_CRITICAL,
+ _("Cannot create backup of the high score file (%s)."),
+ BackupFile);
+ }
+ }
+
+ g_free(BackupFile);
+}
+
+int InitHighScoreFile(void) {
/* Opens the high score file for later use, and then drops privileges. */
/* If the high score file cannot be found, returns -1 (0=success) */
+ gboolean NewFile=FALSE;
if (ScoreFP) return 0; /* If already opened, then we're done */
/* Win32 gets upset if we use "a+" so we use this nasty hack instead */
ScoreFP=fopen(HiScoreFile,"r+");
- if (!ScoreFP) ScoreFP=fopen(HiScoreFile,"w+");
+ if (!ScoreFP) {
+ ScoreFP=fopen(HiScoreFile,"w+");
+ NewFile=TRUE;
+ }
-#ifndef CYGWIN
- if (setregid(getgid(),getgid())!=0) perror("setregid");
-#endif
+ DropPrivileges();
if (!ScoreFP) {
g_log(NULL,G_LOG_LEVEL_CRITICAL,_("Cannot open high score file %s.\n"
t@@ -1193,33 +1286,55 @@ int InitHighScoreFile() {
"the -f command line option."),HiScoreFile);
return -1;
}
+
+ if (NewFile) {
+ HighScoreWriteHeader(ScoreFP);
+ fflush(ScoreFP);
+ } else if (!HighScoreReadHeader(ScoreFP,NULL)) {
+ g_log(NULL,G_LOG_LEVEL_CRITICAL,_("%s does not appear to be a valid\n"
+ "high score file - please check it. If it is a high score file\n"
+ "from an older version of dopewars, then first convert it to the\n"
+ "new format by running \"dopewars -C %s\"\n"
+ "from the command line."),HiScoreFile,HiScoreFile);
+ return -1;
+ }
+
return 0;
}
-int HighScoreRead(struct HISCORE *MultiScore,struct HISCORE *AntiqueScore) {
-/* Reads all the high scores into MultiScore and */
-/* AntiqueScore (antique mode scores). Returns 1 on success, 0 on failure. */
+gboolean HighScoreRead(FILE *fp,struct HISCORE *MultiScore,
+ struct HISCORE *AntiqueScore,gboolean ReadHeader) {
+/* Reads all the high scores into MultiScore and AntiqueScore (antique */
+/* mode scores). If ReadHeader is TRUE, read the high score file header */
+/* first. Returns TRUE on success, FALSE on failure. */
+ gint ScoreVersion=0;
memset(MultiScore,0,sizeof(struct HISCORE)*NUMHISCORE);
memset(AntiqueScore,0,sizeof(struct HISCORE)*NUMHISCORE);
- if (ScoreFP && ReadLock(ScoreFP)==0) {
- rewind(ScoreFP);
- HighScoreTypeRead(AntiqueScore,ScoreFP);
- HighScoreTypeRead(MultiScore,ScoreFP);
- ReleaseLock(ScoreFP);
- } else return 0;
- return 1;
+ if (fp && ReadLock(fp)==0) {
+ rewind(fp);
+ if (ReadHeader && !HighScoreReadHeader(fp,&ScoreVersion)) {
+ ReleaseLock(fp);
+ return FALSE;
+ }
+ HighScoreTypeRead(AntiqueScore,fp);
+ HighScoreTypeRead(MultiScore,fp);
+ ReleaseLock(fp);
+ } else return FALSE;
+ return TRUE;
}
-int HighScoreWrite(struct HISCORE *MultiScore,struct HISCORE *AntiqueScore) {
+gboolean HighScoreWrite(FILE *fp,struct HISCORE *MultiScore,
+ struct HISCORE *AntiqueScore) {
/* Writes out all the high scores from MultiScore and AntiqueScore; returns */
-/* 1 on success, 0 on failure. */
- if (ScoreFP && WriteLock(ScoreFP)==0) {
- ftruncate(fileno(ScoreFP),0);
- rewind(ScoreFP);
- HighScoreTypeWrite(AntiqueScore,ScoreFP);
- HighScoreTypeWrite(MultiScore,ScoreFP);
- ReleaseLock(ScoreFP);
- fflush(ScoreFP);
+/* TRUE on success, FALSE on failure. */
+ if (fp && WriteLock(fp)==0) {
+ ftruncate(fileno(fp),0);
+ rewind(fp);
+ HighScoreWriteHeader(fp);
+ HighScoreTypeWrite(AntiqueScore,fp);
+ HighScoreTypeWrite(MultiScore,fp);
+ ReleaseLock(fp);
+ fflush(fp);
} else return 0;
return 1;
}
t@@ -1237,7 +1352,7 @@ void SendHighScores(Player *Play,gboolean EndGame,char *Message) {
GString *text;
int i,j,InList=-1;
text=g_string_new("");
- if (!HighScoreRead(MultiScore,AntiqueScore)) {
+ if (!HighScoreRead(ScoreFP,MultiScore,AntiqueScore,TRUE)) {
g_warning(_("Unable to read high score file %s"),HiScoreFile);
}
if (Message) {
t@@ -1285,7 +1400,7 @@ void SendHighScores(Player *Play,gboolean EndGame,char *Message) {
if (InList==-1 && EndGame) SendSingleHighScore(Play,&Score,j,TRUE);
SendServerMessage(NULL,C_NONE,C_ENDHISCORE,Play,EndGame ? "end" : NULL);
if (!EndGame) SendDrugsHere(Play,FALSE);
- if (EndGame && !HighScoreWrite(MultiScore,AntiqueScore)) {
+ if (EndGame && !HighScoreWrite(ScoreFP,MultiScore,AntiqueScore)) {
g_warning(_("Unable to write high score file %s"),HiScoreFile);
}
for (i=0;i<NUMHISCORE;i++) {
(DIR) diff --git a/src/serverside.h b/src/serverside.h
t@@ -56,9 +56,11 @@ void SetFightTimeout(Player *Play);
void ClearFightTimeout(Player *Play);
int GetMinimumTimeout(GSList *First);
GSList *HandleTimeouts(GSList *First);
+void ConvertHighScoreFile(void);
int InitHighScoreFile(void);
void CloseHighScoreFile(void);
-int HighScoreRead(struct HISCORE *MultiScore,struct HISCORE *AntiqueScore);
+gboolean HighScoreRead(FILE *fp,struct HISCORE *MultiScore,
+ struct HISCORE *AntiqueScore,gboolean ReadHeader);
void CopsAttackPlayer(Player *Play);
void AttackPlayer(Player *Play,Player *Attacked);
gboolean IsOpponent(Player *Play,Player *Other);