pkg://xcdroast-debuginfo-0.98a15-14.fc7.ppc.rpm:614443/
usr/
src/
debug/
xcdroast-0.98alpha15/
src/xcdrwrap.c
info downloads
/*
xcdrwrap.c
hopefully secure wrapper to call cdrtools
12.7.01 tn
remove all references to glib functions to have no external
libraries in use - on some platforms suid mode is forbidden then
23.2.03 tn
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "largefile.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <pwd.h>
#include "xcdroast.h"
#undef DEBUG
/* dont allow any known exploit code to be passed through the cdrtools */
#define ANTI_CDRTOOLS_EXPLOIT
/* copy glib glist stuff we need into this code, so we dont need
to link with glib */
/* code ripped from glib 1.2.10 - glib.h and glist.c */
typedef struct _GList GList;
typedef void* gpointer;
struct _GList
{
gpointer data;
GList *next;
GList *prev;
};
static GList* g_list_last (GList *list)
{
if (list)
{
while (list->next)
list = list->next;
}
return list;
}
static GList* g_list_first (GList *list)
{
if (list)
{
while (list->prev)
list = list->prev;
}
return list;
}
static GList* g_list_append (GList *list, gpointer data)
{
GList *new_list;
GList *last;
new_list = calloc(1, sizeof(GList));
new_list->data = data;
if (list)
{
last = g_list_last (list);
/* g_assert (last != NULL); */
last->next = new_list;
new_list->prev = last;
return list;
}
else
return new_list;
}
/* end of glib code */
static char sharedir[MAXLINE];
static char prefixdir[MAXLINE];
static char rootconfig[MAXLINE];
static char username[MAXLINE];
static char hostname[MAXLINE];
static int root_users_access;
static int root_hosts_access;
static GList *root_users_lists;
static GList *root_hosts_lists;
static int load_rootconfig(char *cnf);
static void check_access(int verbose);
static void get_spawn_path(char *app, char *ret, int debugout);
/* code duplicated from tools.c and cleared from glib symbols */
static int isroot() {
#ifdef DEBUG
printf("I am uid: %d\n", getuid());
#endif
if (getuid() == 0) {
return 1;
} else {
return 0;
}
}
static int is_directory(char *path) {
struct stat buf;
if (stat(path,&buf) != 0) {
return 0;
}
if (S_ISDIR(buf.st_mode) == 1) {
return 1;
} else {
return 0;
}
}
static int is_file(char *path) {
struct stat buf;
if (stat(path,&buf) != 0) {
return 0;
}
return 1;
}
static char *strip_string(char *str) {
int i,j;
int c1;
if ( str == NULL) return (NULL);
/* count how many leading chars to be whitespace */
for(i=0; i<strlen(str); i++) {
if (str[i] != ' ' && str[i] != '\t' && str[i] != '\r')
break;
}
/* count how many trailing chars to be whitespace */
for(j=strlen(str)-1; j >= 0; j--) {
if (str[j] != ' ' && str[j] != '\t' && str[j] != '\n' && str[j] != '\r')
break;
}
/* string contains only whitespace? */
if (j<i) {
str[0] = '\0';
return(str);
}
/* now move the chars to the front */
for(c1=i; c1 <= j; c1++) {
str[c1-i] = str[c1];
}
str[j+1-i] = '\0';
return(str);
}
static char *escape_parse(char *str) {
char tmp[MAXLINE];
char c;
int i,j;
if ( str == NULL) return (NULL);
if (strlen(str) > MAXLINE) return (NULL);
j = 0;
for(i=0; i<strlen(str); i++) {
c = str[i];
if (c == '\\') {
i++;
switch(str[i]) {
case 'n':
c = '\n';
break;
case 't':
c = '\t';
break;
case 'b':
c = '\b';
break;
default:
c = str[i];
}
}
tmp[j]=c;
j++;
}
tmp[j] = '\0';
strcpy(str,tmp);
return(str);
}
static int parse_config_line(char *iline, char *id, char *value) {
char *p,*p2;
char line[1024];
char tmp[1024];
strncpy(line,iline, MAXLINE);
strcpy(id,"");
p = strtok(line,"=");
if (p != NULL) {
/* got id */
strcpy(id,p);
strip_string(id);
} else {
return 1;
}
strcpy(tmp,"");
p = strtok(NULL,"");
if (p != NULL) {
/* string after = */
strcpy(tmp,p);
strip_string(tmp);
} else {
return 1;
}
/* now strip quotes from string */
p = tmp;
if (*p == '\"') {
p2 = p+1;
} else {
p2 = p;
}
if (p[strlen(p)-1] == '\"') {
p[strlen(p)-1] = '\0';
}
strcpy(value,p2);
/* now reconvert escape-chars */
escape_parse(value);
/* all ok */
return 0;
}
/* done code from tools.c */
/* return the defined callpath for a helper-binary */
static char *cmdstring(char *cmd, char *ret) {
char tmp[MAXLINE];
if (strncmp(cmd,"CDRECORD",MAXLINE) == 0) {
strcpy(ret, CDRECORD);
return ret;
}
if (strncmp(cmd,"CDRECORDPRODVD",MAXLINE) == 0) {
strcpy(ret, CDRECORDPRODVD);
return ret;
}
if (strncmp(cmd,"CDDA2WAV",MAXLINE) == 0) {
strcpy(ret, CDDA2WAV);
return ret;
}
if (strncmp(cmd,"READCD",MAXLINE) == 0) {
strcpy(ret, READCD);
return ret;
}
if (strncmp(cmd,"MKISOFS",MAXLINE) == 0) {
strcpy(ret, MKISOFS);
return ret;
}
if (strncmp(cmd,"WRITETEST",MAXLINE) == 0) {
strcpy(ret,"WRITETEST");
return ret;
}
if (strncmp(cmd,"-V",MAXLINE) == 0) {
printf("X-CD-Roast %s\n", XCDROAST_VERSION);
printf("sharedir: %s\n", sharedir);
printf("prefixdir: %s\n", prefixdir);
#if !(defined(__MACH__) && defined(__APPLE__)) && (USE_NONROOTMODE == 1)
if (load_rootconfig(rootconfig)) {
printf("Warning: rootconfig unreadable\n");
} else {
if (!isroot())
check_access(0);
}
#endif
/* show found paths of helper apps */
get_spawn_path(CDRECORD, tmp, 1);
printf("cdrecord found at: %s\n", tmp);
get_spawn_path(CDRECORDPRODVD, tmp, 1);
printf("cdrecord-ProDVD found at: %s\n", tmp);
get_spawn_path(CDDA2WAV, tmp, 1);
printf("cdda2wav found at: %s\n", tmp);
get_spawn_path(READCD, tmp, 1);
printf("readcd found at: %s\n", tmp);
get_spawn_path(MKISOFS, tmp, 1);
printf("mkisofs found at: %s\n", tmp);
exit(0);
}
return NULL;
}
/* drop any root permissions */
static void drop_root() {
#ifdef HAVE_SETREUID
if (setreuid(-1, getuid()) < 0)
#else
#ifdef HAVE_SETEUID
if (seteuid(getuid()) < 0)
#else
if (setuid(getuid()) < 0)
#endif
#endif
{
perror("Panic: Cannot set back effective uid.");
exit(1);
}
}
/* determine path for helper apps */
static void get_spawn_path(char *app, char *ret, int debugout) {
struct stat buf;
/* when path is with a leading slash (absolute), do nothing */
if (app[0] == '/') {
strncpy(ret,app,MAXLINE);
return;
}
/* otherwise its relative - add sharedir first */
snprintf(ret,MAXLINE,"%s/%s", sharedir, app);
/* now check if this file does exist */
if (stat(ret,&buf) != 0) {
/* it does not, so try the fallback */
snprintf(ret,MAXLINE,"%s/%s", prefixdir, app);
}
/* additional check if we run with -V */
if (debugout) {
/* check the fallback too */
if (stat(ret,&buf) != 0) {
strcpy(ret, "- not found -");
}
return;
}
/* paranoid check */
if (ret[0] != '/') {
printf("ERROR: Invalid relative spawnpath %s\nExiting...\n", ret);
exit(1);
}
return;
}
/* print warning when wrong arguments */
static void usagequit() {
printf("This wrapper should only be called by X-CD-Roast %s\n",
XCDROAST_VERSION);
exit(1);
}
/* read root-user relevant stuff from config file */
static int load_rootconfig(char *cnf) {
FILE *fd;
char line[MAXLINE];
char id[MAXLINE];
char value[MAXLINE];
if ((fd = fopen(cnf,"r")) == NULL) {
/* error opening file */
return 1;
}
for (;;) {
if (fgets(line,MAXLINE,fd) == NULL)
break;
/* skip empty or hashed lines */
strip_string(line);
if (*line == '#' || *line == '\0')
continue;
/* parse lines */
if (parse_config_line(line,id,value) || !value) {
fprintf(stderr,"syntax error in config-file\n");
exit(1);
}
if (strcmp("ROOT_USERS_ACCESS",id) == 0) {
root_users_access = atoi(value);
}
if (strcmp("ROOT_USERS_LISTS",id) == 0) {
root_users_lists = g_list_append(root_users_lists, strdup(value));
}
if (strcmp("ROOT_HOSTS_ACCESS",id) == 0) {
root_hosts_access = atoi(value);
}
if (strcmp("ROOT_HOSTS_LISTS",id) == 0) {
root_hosts_lists = g_list_append(root_hosts_lists, strdup(value));
}
}
if (fclose(fd) != 0) {
/* error closing file */
return 1;
}
return 0;
}
/* do check if the current user and host is allowed to start xcdroast */
/* return 1 if so, 0 if denied */
static int checkuserhost(char *username, char *hostname) {
int userok, hostok;
int match;
GList *loop;
match = 0;
/* user first */
if (root_users_access == 0) {
userok = 1;
} else
if (root_users_access == 1) {
userok = 0;
} else {
loop = g_list_first(root_users_lists);
while (loop) {
#ifdef DEBUG
printf("trying to match %s to %s\n", username,(char *)loop->data);
#endif
if (loop->data && strcmp(username,(char *)loop->data) == 0) {
/* found our login on the list */
match = 1;
}
loop = loop->next;
}
if ((root_users_access == 2 && match) ||
(root_users_access == 3 && !match)) {
userok = 1;
} else {
userok = 0;
}
}
match = 0;
/* now check host */
if (root_hosts_access == 0) {
hostok = 1;
} else
if (root_hosts_access == 1) {
hostok = 0;
} else {
loop = g_list_first(root_hosts_lists);
while (loop) {
#ifdef DEBUG
printf("trying to match %s to %s\n", hostname,(char *)loop->data);
#endif
if (loop->data && strcmp(hostname,(char *)loop->data) == 0) {
/* found our login on the list */
match = 1;
}
loop = loop->next;
}
if ((root_hosts_access == 2 && match) ||
(root_hosts_access == 3 && !match)) {
hostok = 1;
} else {
hostok = 0;
}
}
/* only when both the host and the user are allowed, allow access */
if (userok && hostok) {
return 1;
} else {
return 0;
}
}
/* try to write a file to the given directory
return 0 if ok, or 1 when failed */
static int test_write_perms(char *dir) {
int pidseed;
char tmp[MAXLINE];
int count;
FILE *fd;
drop_root();
if (!is_directory(dir))
return 1;
/* generate a truely unused unique filename */
pidseed = (int) getpid();
count = 0;
while (1) {
snprintf(tmp,MAXLINE,"%s/xcdtmp%02d.%d", dir, count, pidseed);
if (!is_file(tmp))
break;
count++;
/* try max 100 times to find a name */
if (count > 99)
return 1;
}
/* tmp does now contain a full filename which is unused for sure */
/* open that file for writing */
fd = fopen(tmp,"w");
if (fd == NULL) {
/* we failed */
return 1;
}
if (fclose(fd) != 0) {
/* error closing file */
return 1;
}
/* test finished - remove file */
unlink(tmp);
return 0;
}
/* access denied */
static void check_access(int verbose) {
if (!username || !hostname)
return;
/* check if the current user on current host may run xcdroast at all */
if (!checkuserhost(username,hostname)) {
printf("X-CD-Roast %s\n", "ACCESS DENIED");
if (verbose) {
fprintf(stderr,"Unable to run wrapper - permission denied by admin.\n");
fprintf(stderr,"Aborting...\n");
}
exit(1);
}
}
/* returns the current username */
static char *get_username() {
struct passwd *ent;
ent = getpwuid(getuid());
return ent->pw_name;
}
int main(int argc, char **argv) {
int i, stat;
char **arglist;
char callpath[MAXLINE];
char tmp[MAXLINE];
char *p, *p1;
int seen_device_spec;
root_users_access = 0;
root_hosts_access = 0;
root_users_lists = NULL;
root_hosts_lists = NULL;
seen_device_spec = 0;
#ifdef PRE_LIBDIR
/* use prefix as sharedir as it came from the makefile-option */
strncpy(sharedir, PRE_LIBDIR, MAXLINE);
#else
/* otherwise install our default prefix */
strncpy(sharedir, LIBDIR, MAXLINE);
#endif
#ifdef CDRTOOLS_PREFIX
/* use prefix as it came from the makefile-option */
strncpy(prefixdir, CDRTOOLS_PREFIX, MAXLINE);
#else
# ifdef PREFIX
/* use prefix as it came from the makefile-option */
strncpy(prefixdir, PRE_PREFIX, MAXLINE);
# else
/* otherwise install our default prefix */
strncpy(prefixdir, PREFIX, MAXLINE);
# endif
#endif
snprintf(rootconfig, MAXLINE, "%s/%s", SYSCONFDIR, ROOTCONFIG);
if (argc < 2)
usagequit();
/* get username and host */
if (gethostname(hostname,MAXLINE) != 0) {
strncpy(hostname,"not_available",MAXLINE);
}
p1 = get_username();
if (p1 != NULL) {
strncpy(username,p1, MAXLINE);
} else {
strncpy(username,"not_available",MAXLINE);
}
#ifdef DEBUG
printf("User/Host: %s@%s\n", username, hostname);
#endif
/* check for a known first argument */
if (cmdstring(argv[1],tmp) == NULL)
usagequit();
/* now read rootconfig file - just the user and host allowlists */
#if !(defined(__MACH__) && defined(__APPLE__)) && (USE_NONROOTMODE == 1)
if (!isroot()) {
if (load_rootconfig(rootconfig)) {
fprintf(stderr,"Error reading %s - Aborting...\n", rootconfig);
exit(1);
}
/* check access and quit if not allowed */
check_access(1);
}
#endif
/* note: Its NOT possible to change the path of the binaries
via the commandline (-l switch), because this would
be a security risk. You have to use the compiled-in
paths */
/* we should check if a directory is writeable? */
if (strcmp(tmp,"WRITETEST") == 0) {
if (argc != 3)
usagequit();
stat = test_write_perms(argv[2]);
if (stat == 0) {
/* ok, dir is writeable */
return 0;
} else {
return 1;
}
}
/* make path absolute */
get_spawn_path(tmp, callpath, 0);
/* build new command line */
arglist = calloc(argc+1, sizeof(char *));
for (i = 1; i < argc; i++) {
arglist[i-1] = strdup(argv[i]);
}
/* now remove path from first argument */
strcpy(tmp,callpath);
p = rindex(tmp,'/');
if (p != NULL) {
if (arglist[0]) {
free(arglist[0]);
}
arglist[0] = strdup(p+1);
}
#ifdef ANTI_CDRTOOLS_EXPLOIT
i = 0;
while(arglist[i]) {
/* this argument is a device-spec */
if (strstr(arglist[i], "dev=") ||
strstr(arglist[i], "-D") ||
strstr(arglist[i], "--dev")) {
seen_device_spec = i;
}
/* this arg or the previous argument a device spec? */
if (i == seen_device_spec || i == seen_device_spec + 1) {
/* ok, here is no %x or %n allowed */
/* these tags show up in exploit code */
if (strstr(arglist[i], "%x") ||
strstr(arglist[i], "%n")) {
printf("Illegal input detected. Go away.\n");
exit(1);
}
}
i++;
}
#endif
#ifdef DEBUG
printf(":%s:\n",callpath);
i = 0;
while(arglist[i]) {
printf(":%s:\n",arglist[i]);
i++;
}
exit(1);
#endif
/* now we recreated the original command line */
if (execv(callpath,arglist) < 0) {
fprintf(stderr,"execv error while calling %s (%s)\n",
callpath, strerror(errno));
}
return 1;
}