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