diff -Naur --exclude '*.swp' bsd-games-2.17/tetris/scores.c bsd-games-2.17.new/tetris/scores.c --- bsd-games-2.17/tetris/scores.c 2006-04-22 20:46:56.000000000 -0700 +++ bsd-games-2.17.new/tetris/scores.c 2006-04-22 22:02:45.000000000 -0700 @@ -41,6 +41,7 @@ * * Major whacks since then. */ +#define _GNU_SOURCE /* this must be done before the first include of unistd.h */ #include #include #include @@ -77,85 +78,88 @@ static int checkscores(struct highscore *, int); static int cmpscores(const void *, const void *); -static void getscores(FILE **); +static void getscores(FILE *); static void printem(int, int, struct highscore *, int, const char *); static char *thisuser(void); /* - * Read the score file. Can be called from savescore (before showscores) - * or showscores (if savescore will not be called). If the given pointer - * is not NULL, sets *fpp to an open file pointer that corresponds to a - * read/write score file that is locked with LOCK_EX. Otherwise, the - * file is locked with LOCK_SH for the read and closed before return. - * - * Note, we assume closing the stdio file releases the lock. + * Read the score file from a previously opened file pointer. If the file + * pointer is null, then read the scoreboard from a known location. Otherwise, + * the FILE pointer is rewound, read, and left open to be read again later. */ static void -getscores(fpp) - FILE **fpp; +getscores(fp) + FILE *fp; { - int sd, mint, lck; - mode_t mask; - const char *mstr, *human; - FILE *sf; - - if (fpp != NULL) { - mint = O_RDWR | O_CREAT; - mstr = "r+"; - human = "read/write"; - lck = LOCK_EX; - } else { - mint = O_RDONLY; - mstr = "r"; - human = "reading"; - lck = LOCK_SH; - } - setegid(egid); - mask = umask(S_IWOTH); - sd = open(_PATH_SCOREFILE, mint, 0666); - (void)umask(mask); - if (sd < 0) { - if (fpp == NULL) { - nscores = 0; - setegid(gid); - return; - } - err(1, "cannot open %s for %s", _PATH_SCOREFILE, human); - } - if ((sf = fdopen(sd, mstr)) == NULL) { - err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human); - } - setegid(gid); + int sd = -1; + FILE *sf = fp; - /* - * Grab a lock. - */ - if (flock(sd, lck)) - warn("warning: score file %s cannot be locked", - _PATH_SCOREFILE); + if (sf == NULL) { + sd = open(_PATH_SCOREFILE, O_RDONLY, 0664); + if (sd < 0) { + nscores = 0; + return; + err(1, "cannot open %s for reading", _PATH_SCOREFILE); + } + if ((sf = fdopen(sd, "r")) == NULL) { + err(1, "cannot fdopen %s for reading", _PATH_SCOREFILE); + } + } else { + rewind(sf); + } nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf); if (ferror(sf)) { err(1, "error reading %s", _PATH_SCOREFILE); } - if (fpp) - *fpp = sf; - else - (void)fclose(sf); + else { + if (fp == NULL) { + (void)fclose(sf); + } + } +} + +/* + * Open the high score file and then drop setgid privileges. If running + * setgid, then calling this function a second time will result in a + * permission denied error since gid privileges will have been lost. + */ +void +open_score(fd, fp) + int *fd; + FILE **fp; +{ + *fd = open(_PATH_SCOREFILE, O_RDWR | O_CREAT, 0664); + if (*fd < 0) { + err(1, "Could not open scoreboard file %s", _PATH_SCOREFILE); + } else { + *fp = fdopen(*fd, "r+"); + if (*fp == NULL) { + err(1, "Could not open scoreboard file %s", _PATH_SCOREFILE); + } + } + if (setresgid(-1, getgid(), getgid()) == -1) { + perror("Could not drop setgid privileges. Aborting."); + exit(1); + } } void -savescore(level) +savescore(level, sf) int level; + FILE *sf; { struct highscore *sp; int i; int change; - FILE *sf; const char *me; - getscores(&sf); + if (sf == NULL) { + return; + } + + getscores(sf); gotscores = 1; (void)time(&now); @@ -215,14 +219,17 @@ static char * thisuser() { - const char *p; + const char *p = (char *)NULL; struct passwd *pw; size_t l; static char u[sizeof(scores[0].hs_name)]; if (u[0]) return (u); + /* Don't use getlogin() as it will return the wrong result + * for someone who has su'd to a different user. p = getlogin(); + */ if (p == NULL || *p == '\0') { pw = getpwuid(getuid()); if (pw != NULL) @@ -359,7 +366,7 @@ int levelfound[NLEVELS]; if (!gotscores) - getscores((FILE **)NULL); + getscores(NULL); (void)printf("\n\t\t\t Tetris High Scores\n"); /* diff -Naur --exclude '*.swp' bsd-games-2.17/tetris/scores.h bsd-games-2.17.new/tetris/scores.h --- bsd-games-2.17/tetris/scores.h 2006-04-22 20:46:56.000000000 -0700 +++ bsd-games-2.17.new/tetris/scores.h 2006-04-22 16:30:46.000000000 -0700 @@ -48,5 +48,6 @@ #define MAXSCORES 9 /* maximum high score entries per person */ #define EXPIRATION (5L * 365 * 24 * 60 * 60) -void savescore(int); +void savescore(int, FILE *); +void open_score(int *, FILE **); void showscores(int); diff -Naur --exclude '*.swp' bsd-games-2.17/tetris/tetris.c bsd-games-2.17.new/tetris/tetris.c --- bsd-games-2.17/tetris/tetris.c 2006-04-22 20:46:56.000000000 -0700 +++ bsd-games-2.17.new/tetris/tetris.c 2006-04-22 21:02:27.000000000 -0700 @@ -135,10 +135,15 @@ char key_write[6][10]; int ch, i, j; int fd; + int sd = 0; + FILE *sf = (FILE *)NULL; - gid = getgid(); - egid = getegid(); - setegid(gid); + /* + * This function has the side effect of dropping setgid privileges. + * sd is not used, but is kept around in case we want to add code + * to lock shared access to the scoreboard later. + */ + open_score(&sd, &sf); fd = open("/dev/null", O_RDONLY); if (fd < 3) @@ -307,7 +312,7 @@ (void)printf("Your score: %d point%s x level %d = %d\n", score, score == 1 ? "" : "s", level, score * level); - savescore(level); + savescore(level, sf); printf("\nHit RETURN to see high scores, ^C to skip.\n");