tHigh score file format changed, such that setgid dopewars should not now be fo… | |
git clone git://src.adamsgaard.dk/vaccinewars | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 0441b92068ad4864c5afbe6171a89a6df261f747 | |
parent 3d4c0be2224aab5c10dc43b3cec167dc06de0784 | |
Author: Ben Webb <[email protected]> | |
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(-) | |
--- | |
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 | |
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. De… | |
-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\… | |
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… | |
-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 [email protected]… | |
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; | |
} | |
diff --git a/src/dopewars.h b/src/dopewars.h | |
t@@ -153,8 +153,9 @@ extern gboolean Network,Client,Server,NotifyMetaServer,AIP… | |
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; | |
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 *… | |
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 *… | |
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++) { | |
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); |