From a6341b904c08b1feb51e264ab487d1f125222a10 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Mon, 25 Sep 2017 07:38:01 -0400 Subject: disable DBus activation in firecfg --- src/firecfg/desktop_files.c | 265 ++++++++++++++++++++++++++++++++++ src/firecfg/firecfg.h | 51 +++++++ src/firecfg/main.c | 342 +------------------------------------------- src/firecfg/sound.c | 65 +++++++++ src/firecfg/util.c | 86 +++++++++++ 5 files changed, 469 insertions(+), 340 deletions(-) create mode 100644 src/firecfg/desktop_files.c create mode 100644 src/firecfg/firecfg.h create mode 100644 src/firecfg/sound.c create mode 100644 src/firecfg/util.c (limited to 'src') diff --git a/src/firecfg/desktop_files.c b/src/firecfg/desktop_files.c new file mode 100644 index 000000000..eaedc4b64 --- /dev/null +++ b/src/firecfg/desktop_files.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "firecfg.h" + +// look for a profile file in /etc/firejail diectory and in homedir/.config/firejail directory +static int have_profile(const char *filename, const char *homedir) { + assert(filename); + assert(homedir); + + if (arg_debug) + printf("checking profile for %s\n", filename); + + // remove .desktop extension; if file name starts with org.gnome... remove it + char *f1; + if (strncmp(filename, "org.gnome.", 10) == 0) + f1 = strdup(filename + 10); + else + f1 = strdup(filename); + if (!f1) + errExit("strdup"); + f1[strlen(f1) - 8] = '\0'; + if (arg_debug) + printf("looking for a profile for %s - %s\n", filename, f1); + + // build profile name + char *profname1; + char *profname2; + if (asprintf(&profname1, "%s/%s.profile", SYSCONFDIR, f1) == -1) + errExit("asprintf"); + if (asprintf(&profname2, "%s/.config/firejail/%s.profile", homedir, f1) == -1) + errExit("asprintf"); + + int rv = 0; + if (access(profname1, R_OK) == 0) { + if (arg_debug) + printf("found %s\n", profname1); + rv = 1; + } + else if (access(profname2, R_OK) == 0) { + if (arg_debug) + printf("found %s\n", profname2); + rv = 1; + } + + if (arg_debug) + printf("Profile for %s %s\n", f1, (rv)? "found": "not found"); + free(f1); + free(profname1); + free(profname2); + return rv; +} + +void fix_desktop_files(char *homedir) { + assert(homedir); + struct stat sb; + + // check user + if (getuid() == 0) { + fprintf(stderr, "Error: this option is not supported for root user; please run as a regular user.\n"); + exit(1); + } + + // destination + // create ~/.local/share/applications directory if necessary + char *user_apps_dir; + if (asprintf(&user_apps_dir, "%s/.local/share/applications", homedir) == -1) + errExit("asprintf"); + if (stat(user_apps_dir, &sb) == -1) { + int rv = mkdir(user_apps_dir, 0700); + if (rv) { + fprintf(stderr, "Error: cannot create ~/.local/application directory\n"); + perror("mkdir"); + exit(1); + } + rv = chmod(user_apps_dir, 0700); + (void) rv; + } + + // source + DIR *dir = opendir("/usr/share/applications"); + if (!dir) { + perror("Error: cannot open /usr/share/applications directory"); + exit(1); + } + if (chdir("/usr/share/applications")) { + perror("Error: cannot chdir to /usr/share/applications"); + exit(1); + } + + printf("\nFixing desktop files in %s\n", user_apps_dir); + // copy + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // skip if not regular file or link + // d_type is not available on some file systems + if (entry->d_type != DT_REG && entry->d_type != DT_LNK && entry->d_type != DT_UNKNOWN) + continue; + + // skip if not .desktop file + if (strstr(entry->d_name,".desktop") != (entry->d_name+strlen(entry->d_name)-8)) + continue; + + char *filename = entry->d_name; + + // skip links + if (is_link(filename)) + continue; + if (stat(filename, &sb) == -1) + errExit("stat"); + + // no profile in /etc/firejail, no desktop file fixing + if (!have_profile(filename, homedir)) + continue; + + //**************************************************** + // load the file in memory and do some basic checking + //**************************************************** + /* coverity[toctou] */ + int fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Error: cannot open /usr/share/applications/%s\n", filename); + continue; + } + + char *buf = mmap(NULL, sb.st_size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (buf == MAP_FAILED) + errExit("mmap"); + close(fd); + + // check format + if (strstr(buf, "[Desktop Entry]\n") == NULL) { + if (arg_debug) + printf(" %s - skipped: wrong format?\n", filename); + munmap(buf, sb.st_size + 1); + continue; + } + + // get executable name + char *ptr = strstr(buf,"\nExec="); + if (!ptr || strlen(ptr) < 7) { + if (arg_debug) + printf(" %s - skipped: wrong format?\n", filename); + munmap(buf, sb.st_size + 1); + continue; + } + + char *execname = ptr + 6; + // executable name can be quoted, this is rare and currently unsupported, TODO + if (execname[0] == '"') { + if (arg_debug) + printf(" %s - skipped: path quoting unsupported\n", filename); + munmap(buf, sb.st_size + 1); + continue; + } + + + // try to decide if we need to covert this file + char *change_exec = NULL; + int change_dbus = 0; + + // https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html + // The executable program can either be specified with its full path + // or with the name of the executable only + if (execname[0] == '/') { + char *end_name = strchr(execname, ' '); + if (end_name) { + *end_name = '\0'; + char *start_name = strrchr(execname, '/'); + if (start_name) { + start_name++; + // check if we have the executable on the regular path + if (which(start_name)) { + change_exec = strdup(start_name); + if (!change_exec) + errExit("strdup"); + } + } + } + } + + if (strstr(buf, "\nDBusActivatable=true")) + change_dbus = 1; + + if (change_exec == NULL && change_dbus == 0) { + munmap(buf, sb.st_size + 1); + continue; + } + + munmap(buf, sb.st_size + 1); + + //**************************************************** + // generate output file + //**************************************************** + char *outname; + if (asprintf(&outname ,"%s/%s", user_apps_dir, filename) == -1) + errExit("asprintf"); + + if (stat(outname, &sb) == 0) { + printf(" %s skipped: file exists\n", filename); + continue; + } + + FILE *fpin = fopen(filename, "r"); + if (!fpin) { + fprintf(stderr, "Error: cannot open /usr/share/applications/%s\n", filename); + continue; + } + + FILE *fpout = fopen(outname, "w"); + if (!fpout) { + fprintf(stderr, "Error: cannot open ~/.local/share/applications/%s\n", outname); + fclose(fpin); + continue; + } + fprintf(fpout, "# converted by firecfg\n"); + free(outname); + + char fbuf[MAX_BUF]; + while (fgets(fbuf, MAX_BUF, fpin)) { + if (change_dbus && strcmp(fbuf, "DBusActivatable=true\n") == 0) + fprintf(fpout, "DBusActivatable=false\n"); + else if (change_exec && strncmp(fbuf, "Exec=", 5) == 0) { + char *start_params = strchr(fbuf + 5, ' '); + assert(start_params); + start_params++; + fprintf(fpout, "Exec=%s %s", change_exec, start_params); + } + else + fprintf(fpout, "%s", fbuf); + } + + if (change_exec) + free(change_exec); + fclose(fpin); + fclose(fpout); + printf(" %s created\n", filename); + + } + + closedir(dir); + free(user_apps_dir); +} + + diff --git a/src/firecfg/firecfg.h b/src/firecfg/firecfg.h new file mode 100644 index 000000000..c4640feb8 --- /dev/null +++ b/src/firecfg/firecfg.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../include/common.h" +#define MAX_BUF 4096 + + +// main.c +extern int arg_debug; + +// util.c +int which(const char *program); +int is_link(const char *fname); + +// sound.c +void sound(void); + +// desktop_files.c +void fix_desktop_files(char *homedir); + diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 5928b9ae5..1cdd39c1f 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c @@ -18,24 +18,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../include/common.h" -static int arg_debug = 0; -#define MAX_BUF 1024 +#include "firecfg.h" +int arg_debug = 0; static void usage(void) { printf("firecfg - version %s\n\n", VERSION); @@ -71,113 +55,6 @@ static void usage(void) { printf("Homepage: http://firejail.wordpress.com\n\n"); } -static void sound(void) { - struct passwd *pw = getpwuid(getuid()); - if (!pw) { - goto errexit; - } - char *home = pw->pw_dir; - if (!home) { - goto errexit; - } - - // the input file is /etc/pulse/client.conf - FILE *fpin = fopen("/etc/pulse/client.conf", "r"); - if (!fpin) { - fprintf(stderr, "PulseAudio is not available on this platform, there is nothing to fix...\n"); - return; - } - - // the dest is PulseAudio user config file - char *fname; - if (asprintf(&fname, "%s/.config/pulse/client.conf", home) == -1) - errExit("asprintf"); - FILE *fpout = fopen(fname, "w"); - free(fname); - if (!fpout) - goto errexit; - - // copy default config - char buf[MAX_BUF]; - while (fgets(buf, MAX_BUF, fpin)) - fputs(buf, fpout); - - // disable shm - fprintf(fpout, "\nenable-shm = no\n"); - fclose(fpin); - fclose(fpout); - printf("PulseAudio configured, please logout and login back again\n"); - return; - -errexit: - fprintf(stderr, "Error: cannot configure sound file\n"); - exit(1); -} - -// return 1 if the program is found -static int find(const char *program, const char *directory) { - int retval = 0; - - char *fname; - if (asprintf(&fname, "/%s/%s", directory, program) == -1) - errExit("asprintf"); - - struct stat s; - if (stat(fname, &s) == 0) { - if (arg_debug) - printf("found %s in directory %s\n", program, directory); - retval = 1; - } - - free(fname); - return retval; -} - - -// return 1 if program is installed on the system -static int which(const char *program) { - // check some well-known paths - if (find(program, "/bin") || find(program, "/usr/bin") || - find(program, "/sbin") || find(program, "/usr/sbin") || - find(program, "/usr/games")) - return 1; - - // check environment - char *path1 = getenv("PATH"); - if (path1) { - char *path2 = strdup(path1); - if (!path2) - errExit("strdup"); - - // use path2 to count the entries - char *ptr = strtok(path2, ":"); - while (ptr) { - if (find(program, ptr)) { - free(path2); - return 1; - } - ptr = strtok(NULL, ":"); - } - free(path2); - } - - return 0; -} - -// return 1 if the file is a link -static int is_link(const char *fname) { - assert(fname); - if (*fname == '\0') - return 0; - - struct stat s; - if (lstat(fname, &s) == 0) { - if (S_ISLNK(s.st_mode)) - return 1; - } - - return 0; -} static void list(void) { DIR *dir = opendir("/usr/local/bin"); @@ -388,221 +265,6 @@ static void set_links_homedir(const char *homedir) { free(firejail_exec); } -// look for a profile file in /etc/firejail diectory and in homedir/.config/firejail directory -static int have_profile(const char *filename, const char *homedir) { - assert(filename); - assert(homedir); - - if (arg_debug) - printf("checking profile for %s\n", filename); - - // remove .desktop extension - char *f1 = strdup(filename); - if (!f1) - errExit("strdup"); - f1[strlen(filename) - 8] = '\0'; - - // build profile name - char *profname1; - char *profname2; - if (asprintf(&profname1, "%s/%s.profile", SYSCONFDIR, f1) == -1) - errExit("asprintf"); - if (asprintf(&profname2, "%s/.config/firejail/%s.profile", homedir, f1) == -1) - errExit("asprintf"); - - int rv = 0; - if (access(profname1, R_OK) == 0) { - if (arg_debug) - printf("found %s\n", profname1); - rv = 1; - } - else if (access(profname2, R_OK) == 0) { - if (arg_debug) - printf("found %s\n", profname2); - rv = 1; - } - - free(f1); - free(profname1); - free(profname2); - return rv; -} - -static void fix_desktop_files(char *homedir) { - assert(homedir); - struct stat sb; - - // check user - if (getuid() == 0) { - fprintf(stderr, "Error: this option is not supported for root user; please run as a regular user.\n"); - exit(1); - } - - // destination - // create ~/.local/share/applications directory if necessary - char *user_apps_dir; - if (asprintf(&user_apps_dir, "%s/.local/share/applications", homedir) == -1) - errExit("asprintf"); - if (stat(user_apps_dir, &sb) == -1) { - int rv = mkdir(user_apps_dir, 0700); - if (rv) { - fprintf(stderr, "Error: cannot create ~/.local/application directory\n"); - perror("mkdir"); - exit(1); - } - rv = chmod(user_apps_dir, 0700); - (void) rv; - } - - // source - DIR *dir = opendir("/usr/share/applications"); - if (!dir) { - perror("Error: cannot open /usr/share/applications directory"); - exit(1); - } - if (chdir("/usr/share/applications")) { - perror("Error: cannot chdir to /usr/share/applications"); - exit(1); - } - - printf("\nFixing desktop files in %s\n", user_apps_dir); - // copy - struct dirent *entry; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - - // skip if not regular file or link - // d_type is not available on some file systems - if (entry->d_type != DT_REG && entry->d_type != DT_LNK && entry->d_type != DT_UNKNOWN) - continue; - - // skip if not .desktop file - if (strstr(entry->d_name,".desktop") != (entry->d_name+strlen(entry->d_name)-8)) - continue; - - char *filename = entry->d_name; - - // skip links - if (is_link(filename)) - continue; - if (stat(filename, &sb) == -1) - errExit("stat"); - - // no profile in /etc/firejail, no desktop file fixing - if (!have_profile(filename, homedir)) - continue; - - /* coverity[toctou] */ - int fd = open(filename, O_RDONLY); - if (fd == -1) - errExit("open"); - - char *buf = mmap(NULL, sb.st_size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (buf == MAP_FAILED) - errExit("mmap"); - - close(fd); - - // check format - if (strstr(buf, "[Desktop Entry]\n") == NULL) { - if (arg_debug) - printf(" %s - SKIPPED: wrong format?\n", filename); - munmap(buf, sb.st_size + 1); - continue; - } - - // get executable name - char *ptr1 = strstr(buf,"\nExec="); - if (!ptr1 || strlen(ptr1) < 7) { - if (arg_debug) - printf(" %s - SKIPPED: wrong format?\n", filename); - munmap(buf, sb.st_size + 1); - continue; - } - - char *execname = ptr1 + 6; - // https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html - // The executable program can either be specified with its full path - // or with the name of the executable only - if (execname[0] != '/') { - if (arg_debug) - printf(" %s - already OK\n", filename); - continue; - } - // executable name can be quoted, this is rare and currently unsupported, TODO - if (execname[0] == '"') { - if (arg_debug) - printf(" %s - skipped: path quoting unsupported\n", filename); - continue; - } - - // put '\0' at end of filename - char *tail = NULL; - char endchar = ' '; - if (execname[0] == '/') { - char *ptr2 = index(execname, ' '); - char *ptr3 = index(execname, '\n'); - if (ptr2 && (!ptr3 || (ptr2 < ptr3))) { - endchar = ptr2[0]; - ptr2[0] = '\0'; - tail = ptr2 + 1; - } else if (ptr3 && (!ptr2 || (ptr3 < ptr2))) { - endchar = ptr3[0]; - ptr3[0] = '\0'; - tail = ptr3 + 1; - } - ptr1[5] = '\0'; - } - - char *bname = basename(execname); - assert(bname); - - // check if basename in PATH - if (!which(bname)) { - printf(" %s - skipped, %s not in PATH\n", filename, bname); - continue; - } - - char *outname; - if (asprintf(&outname ,"%s/%s", user_apps_dir, filename) == -1) - errExit("asprintf"); - - int fd1 = open(outname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); - free(outname); - - if (fd1 == -1) { - printf(" %s skipped: %s\n", filename, strerror(errno)); - munmap(buf, sb.st_size + 1); - continue; - } - - FILE *outfile = fdopen(fd1, "w"); - if (!outfile) { - printf(" %s skipped: %s\n", filename, strerror(errno)); - munmap(buf, sb.st_size + 1); - close(fd1); - continue; - } - - if (fprintf(outfile,\ - "# Converted by firecfg --fix from /usr/share/applications/%s\n\n%s=%s%c%s",\ - filename, buf, bname, endchar, tail) < 0) { - fprintf(stderr, "Unable to write %s/%s: %s\n", user_apps_dir, filename, strerror(errno)); - munmap(buf, sb.st_size + 1); - fclose(outfile); - continue; - } - - fclose(outfile); - munmap(buf, sb.st_size + 1); - - printf(" %s created\n", filename); - } - - closedir(dir); - free(user_apps_dir); -} int main(int argc, char **argv) { int i; diff --git a/src/firecfg/sound.c b/src/firecfg/sound.c new file mode 100644 index 000000000..9dfb305cd --- /dev/null +++ b/src/firecfg/sound.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "firecfg.h" + +void sound(void) { + struct passwd *pw = getpwuid(getuid()); + if (!pw) { + goto errexit; + } + char *home = pw->pw_dir; + if (!home) { + goto errexit; + } + + // the input file is /etc/pulse/client.conf + FILE *fpin = fopen("/etc/pulse/client.conf", "r"); + if (!fpin) { + fprintf(stderr, "PulseAudio is not available on this platform, there is nothing to fix...\n"); + return; + } + + // the dest is PulseAudio user config file + char *fname; + if (asprintf(&fname, "%s/.config/pulse/client.conf", home) == -1) + errExit("asprintf"); + FILE *fpout = fopen(fname, "w"); + free(fname); + if (!fpout) + goto errexit; + + // copy default config + char buf[MAX_BUF]; + while (fgets(buf, MAX_BUF, fpin)) + fputs(buf, fpout); + + // disable shm + fprintf(fpout, "\nenable-shm = no\n"); + fclose(fpin); + fclose(fpout); + printf("PulseAudio configured, please logout and login back again\n"); + return; + +errexit: + fprintf(stderr, "Error: cannot configure sound file\n"); + exit(1); +} + diff --git a/src/firecfg/util.c b/src/firecfg/util.c new file mode 100644 index 000000000..4520e75e8 --- /dev/null +++ b/src/firecfg/util.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2014-2017 Firejail Authors + * + * This file is part of firejail project + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "firecfg.h" + +// return 1 if the program is found +static int find(const char *program, const char *directory) { + int retval = 0; + + char *fname; + if (asprintf(&fname, "/%s/%s", directory, program) == -1) + errExit("asprintf"); + + struct stat s; + if (stat(fname, &s) == 0) { + if (arg_debug) + printf("found %s in directory %s\n", program, directory); + retval = 1; + } + + free(fname); + return retval; +} + + +// return 1 if program is installed on the system +int which(const char *program) { + // check some well-known paths + if (find(program, "/bin") || find(program, "/usr/bin") || + find(program, "/sbin") || find(program, "/usr/sbin") || + find(program, "/usr/games")) + return 1; + + // check environment + char *path1 = getenv("PATH"); + if (path1) { + char *path2 = strdup(path1); + if (!path2) + errExit("strdup"); + + // use path2 to count the entries + char *ptr = strtok(path2, ":"); + while (ptr) { + if (find(program, ptr)) { + free(path2); + return 1; + } + ptr = strtok(NULL, ":"); + } + free(path2); + } + + return 0; +} + +// return 1 if the file is a link +int is_link(const char *fname) { + assert(fname); + if (*fname == '\0') + return 0; + + struct stat s; + if (lstat(fname, &s) == 0) { + if (S_ISLNK(s.st_mode)) + return 1; + } + + return 0; +} -- cgit v1.2.3-70-g09d2