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);