Improved spc_fork to remove all bugs (take that cookbook!). Split out code from sdllink to a seperate file. Added a safe popen() which doesn't return until it knows the application is there or not, and doesn't carry around extra data, can be closed with fclose(). Fixed some warnings. And there was much rejoicing across the land.
This commit is contained in:
@@ -72,7 +72,7 @@ VIDEOBJ=${VIDEODIR}/makev16b.o ${VIDEODIR}/makev16t.o ${VIDEODIR}/makevid.o\
|
||||
${VIDEODIR}/hq4x16.o ${VIDEODIR}/hq4x32.o
|
||||
|
||||
WINOBJ=${WINDIR}/copyvwin.o ${WINDIR}/sdlintrf.o ${WINDIR}/sdllink.o @GL_DRAW@\
|
||||
${WINDIR}/sw_draw.o ${WINDIR}/zfilew.o
|
||||
${WINDIR}/sw_draw.o ${WINDIR}/zfilew.o ${WINDIR}/safelib.o
|
||||
|
||||
WINDOSOBJ=${DOSDIR}/debug.o ${DOSDIR}/joy.o ${DOSDIR}/vesa2.o
|
||||
|
||||
@@ -154,7 +154,7 @@ uic.o: uic.c asm_call.h gblhdr.h
|
||||
vcache.o: vcache.asm macros.mac
|
||||
version.o: version.c
|
||||
zloader.o: zloader.c gblhdr.h
|
||||
zmovie.o: zmovie.c asm_call.h gblhdr.h gblvars.h numconv.h md.o
|
||||
zmovie.o: zmovie.c asm_call.h gblhdr.h gblvars.h numconv.h ${WINDIR}/safelib.h md.o
|
||||
zstate.o: zstate.c asm_call.h gblhdr.h gblvars.h numconv.h
|
||||
|
||||
${CHIPDIR}/c4emu.o: ${CHIPDIR}/c4emu.c gblhdr.h
|
||||
@@ -271,9 +271,10 @@ ${WINDIR}/copyvwin.o: ${WINDIR}/copyvwin.asm macros.mac
|
||||
${WINDIR}/gl_draw.o: ${WINDIR}/gl_draw.c gblhdr.h
|
||||
${WINDIR}/sdlintrf.o: ${WINDIR}/sdlintrf.asm macros.mac
|
||||
${WINDIR}/sdllink.o: ${WINDIR}/sdllink.c asm_call.h gblhdr.h ${WINDIR}/sw_draw.h\
|
||||
${WINDIR}/gl_draw.h
|
||||
${WINDIR}/gl_draw.h ${WINDIR}/safelib.h
|
||||
${WINDIR}/sw_draw.o: ${WINDIR}/sw_draw.c gblhdr.h
|
||||
${WINDIR}/zfilew.o: ${WINDIR}/zfilew.c
|
||||
${WINDIR}/safelib.o: ${WINDIR}/safelib.c ${WINDIR}/safelib.h
|
||||
|
||||
${ZIPDIR}/unzip.o: ${ZIPDIR}/unzip.c gblhdr.h ${ZIPDIR}/zunzip.h
|
||||
${ZIPDIR}/zpng.o: ${ZIPDIR}/zpng.c gblhdr.h ${ZIPDIR}/zpng.h
|
||||
|
||||
406
zsnes/src/linux/safelib.c
Normal file
406
zsnes/src/linux/safelib.c
Normal file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
Copyright (C) 1997-2006 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
|
||||
|
||||
http://www.zsnes.com
|
||||
http://sourceforge.net/projects/zsnes
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "gblhdr.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <paths.h>
|
||||
#include <grp.h>
|
||||
|
||||
#ifndef OPEN_MAX
|
||||
#define OPEN_MAX 256
|
||||
#endif
|
||||
|
||||
#include "safelib.h"
|
||||
|
||||
//C++ style code in C
|
||||
#define bool unsigned char
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
|
||||
//Introducing the secure browser opener for POSIX systems ;) -Nach
|
||||
|
||||
//Taken from the secure programming cookbook, somewhat modified
|
||||
static bool spc_drop_privileges() {
|
||||
gid_t newgid = getgid(), oldgid = getegid();
|
||||
uid_t newuid = getuid(), olduid = geteuid();
|
||||
|
||||
/* If root privileges are to be dropped, be sure to pare down the ancillary
|
||||
* groups for the process before doing anything else because the setgroups()
|
||||
* system call requires root privileges. Drop ancillary groups regardless of
|
||||
* whether privileges are being dropped temporarily or permanently.
|
||||
*/
|
||||
if (!olduid) setgroups(1, &newgid);
|
||||
|
||||
if (newgid != oldgid) {
|
||||
#if !defined(linux)
|
||||
setegid(newgid);
|
||||
if (setgid(newgid) == -1) return(false);
|
||||
#else
|
||||
if (setregid(newgid, newgid) == -1) return(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (newuid != olduid) {
|
||||
#if !defined(linux)
|
||||
seteuid(newuid);
|
||||
if (setuid(newuid) == -1) return(false);
|
||||
#else
|
||||
if (setreuid(newuid, newuid) == -1) return(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* verify that the changes were successful */
|
||||
|
||||
if (newgid != oldgid && (setegid(oldgid) != -1 || getegid() != newgid))
|
||||
return(false);
|
||||
if (newuid != olduid && (seteuid(olduid) != -1 || geteuid() != newuid))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
static int open_devnull(int fd) {
|
||||
FILE *f = 0;
|
||||
|
||||
if (!fd) f = freopen(_PATH_DEVNULL, "rb", stdin);
|
||||
else if (fd == 1) f = freopen(_PATH_DEVNULL, "wb", stdout);
|
||||
else if (fd == 2) f = freopen(_PATH_DEVNULL, "wb", stderr);
|
||||
return (f && fileno(f) == fd);
|
||||
}
|
||||
|
||||
static bool array_contains(int *a, size_t size, int key)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
if (a[i] == key) { return(true); }
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
static bool spc_sanitize_files(int *a, size_t size, int skip)
|
||||
{
|
||||
int fd, fds;
|
||||
struct stat st;
|
||||
|
||||
//Make sure all open descriptors other than the standard ones are closed
|
||||
if ((fds = getdtablesize()) == -1)
|
||||
{
|
||||
fds = OPEN_MAX;
|
||||
}
|
||||
for (fd = 3; fd < fds; fd++)
|
||||
{
|
||||
if ((fd != skip) && !array_contains(a, size, fd)) { close(fd); }
|
||||
}
|
||||
|
||||
//Verify that the standard descriptors are open. If they're not, attempt to
|
||||
//open them using /dev/null. If any are unsuccessful, fail.
|
||||
for (fd = 0; fd < 3; fd++)
|
||||
{
|
||||
if (fstat(fd, &st) == -1 && (errno != EBADF || !open_devnull(fd)))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
static bool child_exited;
|
||||
|
||||
//Pass array of file descriptors to leave open
|
||||
pid_t spc_fork(int *a, size_t size)
|
||||
{
|
||||
int filedes[2];
|
||||
if (!pipe(filedes))
|
||||
{
|
||||
char success = 0;
|
||||
pid_t childpid;
|
||||
if ((childpid = fork()) == -1) //Fork Failed
|
||||
{
|
||||
close(filedes[0]);
|
||||
close(filedes[1]);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (childpid) //Parent Process
|
||||
{
|
||||
close(filedes[1]); //Close writing
|
||||
read(filedes[0], &success, 1);
|
||||
close(filedes[0]);
|
||||
return(success ? childpid : -1);
|
||||
}
|
||||
|
||||
|
||||
//This is the child proccess
|
||||
|
||||
close(filedes[0]); //Close reading
|
||||
|
||||
if (!spc_sanitize_files(a, size, filedes[1]) || !spc_drop_privileges())
|
||||
{
|
||||
write(filedes[1], &success, 1);
|
||||
close(filedes[1]);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
success = 1;
|
||||
write(filedes[1], &success, 1);
|
||||
close(filedes[1]);
|
||||
return(0);
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
||||
|
||||
//Introducing a popen which doesn't return until it knows for sure of program launched or couldn't open -Nach
|
||||
|
||||
static char *decode_string(char *str)
|
||||
{
|
||||
size_t str_len = strlen(str), i = 0;
|
||||
char *dest = str;
|
||||
|
||||
if ((str_len > 1) && ((*str == '\"') || (*str == '\'')) && (str[str_len-1] == *str))
|
||||
{
|
||||
memmove(str, str+1, str_len-2);
|
||||
str[str_len-2] = 0;
|
||||
}
|
||||
|
||||
while (*str)
|
||||
{
|
||||
if (*str == '\\')
|
||||
{
|
||||
str++;
|
||||
}
|
||||
dest[i++] = *str++;
|
||||
}
|
||||
dest[i] = 0;
|
||||
return(dest);
|
||||
}
|
||||
|
||||
static char *find_next_match(char *str, char match_char)
|
||||
{
|
||||
char *pos = 0;
|
||||
|
||||
while (*str)
|
||||
{
|
||||
if (*str == match_char)
|
||||
{
|
||||
pos = str;
|
||||
break;
|
||||
}
|
||||
if (*str == '\\')
|
||||
{
|
||||
if (str[1])
|
||||
{
|
||||
str++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
str++;
|
||||
}
|
||||
return(pos);
|
||||
}
|
||||
|
||||
static char *get_param(char *str)
|
||||
{
|
||||
static char *pos = 0;
|
||||
char *token = 0;
|
||||
|
||||
if (str) //Start a new string?
|
||||
{
|
||||
pos = str;
|
||||
}
|
||||
|
||||
if (pos)
|
||||
{
|
||||
//Skip delimiters
|
||||
while (*pos == ' ') { pos++; }
|
||||
if (*pos)
|
||||
{
|
||||
token = pos;
|
||||
|
||||
//Skip non-delimiters
|
||||
while (*pos && (*pos != ' '))
|
||||
{
|
||||
//Skip quoted characters
|
||||
if ((*pos == '\"') || (*pos == '\''))
|
||||
{
|
||||
char *match_pos = 0;
|
||||
if ((match_pos = find_next_match(pos+1, *pos)))
|
||||
{
|
||||
pos = match_pos;
|
||||
}
|
||||
}
|
||||
//Skip escaped spaces
|
||||
if (*pos == '\\') { pos++; }
|
||||
pos++;
|
||||
}
|
||||
if (*pos) { *pos++ = '\0'; }
|
||||
}
|
||||
}
|
||||
return(token);
|
||||
}
|
||||
|
||||
static size_t count_param(char *str)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
while (*str)
|
||||
{
|
||||
//Skip delimiters
|
||||
while (*str == ' ') { str++; }
|
||||
//Skip non-delimiters
|
||||
while (*str && (*str != ' '))
|
||||
{
|
||||
//Skip quoted characters
|
||||
if ((*str == '\"') || (*str == '\''))
|
||||
{
|
||||
char *match_str = 0;
|
||||
if ((match_str = find_next_match(str+1, *str)))
|
||||
{
|
||||
str = match_str;
|
||||
}
|
||||
}
|
||||
//Skip escaped spaces
|
||||
if (*str == '\\') { str++; }
|
||||
str++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return(i);
|
||||
}
|
||||
|
||||
static char **build_argv(char *str)
|
||||
{
|
||||
size_t argc = count_param(str);
|
||||
char **argv = (char **)malloc(sizeof(char *)*(argc+1));
|
||||
|
||||
if (argv)
|
||||
{
|
||||
char *p, **argp = argv;
|
||||
for (p = get_param(str); p; p = get_param(0), argp++)
|
||||
{
|
||||
*argp = decode_string(p);
|
||||
}
|
||||
*argp = 0;
|
||||
return(argv);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void argv_print(char **argv)
|
||||
{
|
||||
char **argp = argv;
|
||||
while (*argp)
|
||||
{
|
||||
printf("argv[%u]: %s\n", argp-argv, *argp);
|
||||
argp++;
|
||||
}
|
||||
printf("argv[%u]: NULL\n", argp-argv);
|
||||
}
|
||||
|
||||
static bool child_exited;
|
||||
void catch_child(int sig_num)
|
||||
{
|
||||
int child_status;
|
||||
wait(&child_status);
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
child_exited = true;
|
||||
}
|
||||
|
||||
FILE *safe_popen(char *command, const char *mode)
|
||||
{
|
||||
//filedes[0] is for reading
|
||||
//filedes[1] is for writing.
|
||||
int filedes[2];
|
||||
|
||||
if ((*mode == 'r' || *mode == 'w') && !pipe(filedes))
|
||||
{
|
||||
char **argv = build_argv(command);
|
||||
if (argv)
|
||||
{
|
||||
pid_t childpid;
|
||||
|
||||
child_exited = false;
|
||||
signal(SIGCHLD, catch_child);
|
||||
if ((childpid = vfork()) == -1) //Fork Failed
|
||||
{
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
free(argv);
|
||||
close(filedes[0]);
|
||||
close(filedes[1]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (childpid) //Parent
|
||||
{
|
||||
FILE *fp;
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
free(argv);
|
||||
if (!child_exited)
|
||||
{
|
||||
if (*mode == 'r')
|
||||
{
|
||||
close(filedes[1]);
|
||||
fp = fdopen(filedes[0], "r");
|
||||
}
|
||||
else
|
||||
{
|
||||
close(filedes[0]);
|
||||
fp = fdopen(filedes[1], "w");
|
||||
}
|
||||
|
||||
if (fp) { return(fp); }
|
||||
}
|
||||
close(filedes[0]);
|
||||
close(filedes[1]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
//Child
|
||||
|
||||
if (*mode == 'r')
|
||||
{
|
||||
dup2(filedes[1], STDOUT_FILENO);
|
||||
close(filedes[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
dup2(filedes[0], STDIN_FILENO);
|
||||
close(filedes[1]);
|
||||
}
|
||||
|
||||
execvp(argv[0], argv);
|
||||
_exit(0);
|
||||
}
|
||||
close(filedes[0]);
|
||||
close(filedes[1]);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
37
zsnes/src/linux/safelib.h
Normal file
37
zsnes/src/linux/safelib.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright (C) 1997-2006 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
|
||||
|
||||
http://www.zsnes.com
|
||||
http://sourceforge.net/projects/zsnes
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef SAFELIB_H
|
||||
#define SAFELIB_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
pid_t spc_fork(int *, size_t);
|
||||
|
||||
FILE *safe_popen(char *, const char *);
|
||||
void safe_pclose(FILE *);
|
||||
|
||||
#define popen safe_popen
|
||||
#define pclose fclose
|
||||
|
||||
#endif
|
||||
@@ -30,19 +30,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <paths.h>
|
||||
#include <grp.h>
|
||||
|
||||
#ifndef OPEN_MAX
|
||||
#define OPEN_MAX 256
|
||||
#endif
|
||||
|
||||
//C++ style code in C
|
||||
#define bool unsigned char
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#include "safelib.h"
|
||||
#include "../asm_call.h"
|
||||
|
||||
|
||||
@@ -1200,107 +1188,6 @@ float sem_GetTicks()
|
||||
|
||||
|
||||
|
||||
//Introducing the secure browser opener for POSIX systems ;) -Nach
|
||||
|
||||
//Taken from the secure programming cookbook, slightly modified
|
||||
bool spc_drop_privileges() {
|
||||
gid_t newgid = getgid(), oldgid = getegid();
|
||||
uid_t newuid = getuid(), olduid = geteuid();
|
||||
|
||||
/* If root privileges are to be dropped, be sure to pare down the ancillary
|
||||
* groups for the process before doing anything else because the setgroups()
|
||||
* system call requires root privileges. Drop ancillary groups regardless of
|
||||
* whether privileges are being dropped temporarily or permanently.
|
||||
*/
|
||||
if (!olduid) setgroups(1, &newgid);
|
||||
|
||||
if (newgid != oldgid) {
|
||||
#if !defined(linux)
|
||||
setegid(newgid);
|
||||
if (setgid(newgid) == -1) return(false);
|
||||
#else
|
||||
if (setregid(newgid, newgid) == -1) return(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (newuid != olduid) {
|
||||
#if !defined(linux)
|
||||
seteuid(newuid);
|
||||
if (setuid(newuid) == -1) return(false);
|
||||
#else
|
||||
if (setreuid(newuid, newuid) == -1) return(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* verify that the changes were successful */
|
||||
|
||||
if (newgid != oldgid && (setegid(oldgid) != -1 || getegid() != newgid))
|
||||
return(false);
|
||||
if (newuid != olduid && (seteuid(olduid) != -1 || geteuid() != newuid))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
static int open_devnull(int fd) {
|
||||
FILE *f = 0;
|
||||
|
||||
if (!fd) f = freopen(_PATH_DEVNULL, "rb", stdin);
|
||||
else if (fd == 1) f = freopen(_PATH_DEVNULL, "wb", stdout);
|
||||
else if (fd == 2) f = freopen(_PATH_DEVNULL, "wb", stderr);
|
||||
return (f && fileno(f) == fd);
|
||||
}
|
||||
|
||||
void spc_sanitize_files() {
|
||||
int fd, fds;
|
||||
struct stat st;
|
||||
|
||||
/* Make sure all open descriptors other than the standard ones are closed */
|
||||
if ((fds = getdtablesize()) == -1) fds = OPEN_MAX;
|
||||
for (fd = 3; fd < fds; fd++) close(fd);
|
||||
|
||||
/* Verify that the standard descriptors are open. If they're not, attempt to
|
||||
* open them using /dev/null. If any are unsuccessful, abort.
|
||||
*/
|
||||
for (fd = 0; fd < 3; fd++)
|
||||
if (fstat(fd, &st) == -1 && (errno != EBADF || !open_devnull(fd))) abort();
|
||||
}
|
||||
|
||||
pid_t spc_fork() {
|
||||
pid_t childpid;
|
||||
|
||||
if ((childpid = fork()) == -1) return -1;
|
||||
|
||||
//If this us the parent proccess nothing more to do
|
||||
if (childpid != 0) return childpid;
|
||||
|
||||
//This is the child proccess
|
||||
|
||||
spc_sanitize_files();
|
||||
|
||||
/*
|
||||
There actually is a bug here which I submitted to the authors of the book -Nach
|
||||
|
||||
The bug is as follows:
|
||||
The parent returns the child proccess ID in event of success.
|
||||
The child returns 0 on success if and only if it's spc_drop_privileges() call is successful.
|
||||
It is possible that the parent will return with a pid > 0, while the child never returns
|
||||
from spc_fork thus causing a programming error.
|
||||
|
||||
The function should be rewritten that the parent doesn't return till it knows if the
|
||||
child is able to return or not. And then return -1 or the child pid.
|
||||
|
||||
For out purposes in ZSNES to launch a browser, this bug does not effect us. But
|
||||
be careful if you copy this code to use somewhere else.
|
||||
*/
|
||||
if (!spc_drop_privileges()) //Failed to drop special privleges
|
||||
{
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LaunchBrowser(char *browser, char *url)
|
||||
{
|
||||
char *const arglist[] = { browser, url, 0 };
|
||||
@@ -1309,7 +1196,7 @@ void LaunchBrowser(char *browser, char *url)
|
||||
|
||||
void LaunchURL(char *url)
|
||||
{
|
||||
if (spc_fork()) //If fork failed, or we are the parent
|
||||
if (spc_fork(0, 0)) //If fork failed, or we are the parent
|
||||
{
|
||||
MouseX = 0;
|
||||
MouseY = 0;
|
||||
|
||||
@@ -22,6 +22,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
#ifdef __UNIXSDL__
|
||||
#include "gblhdr.h"
|
||||
#include "linux/safelib.h"
|
||||
#include <sys/poll.h>
|
||||
#define DIR_SLASH "/"
|
||||
#define WRITE_BINARY "w"
|
||||
#else
|
||||
@@ -2010,7 +2012,7 @@ static bool raw_video_open()
|
||||
break;
|
||||
|
||||
case 2: case 3:
|
||||
mencoderExists = raw_vid.vp = popen(encode_command(md_command), WRITE_BINARY);
|
||||
mencoderExists = (unsigned char)(int)(raw_vid.vp = popen(encode_command(md_command), WRITE_BINARY));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user