aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPhilipp Stephani2020-12-14 21:25:11 +0100
committerPhilipp Stephani2021-04-10 18:47:26 +0200
commitbe8328acf9aa464f848e682e63e417a18529af9e (patch)
treeb7f8191920af5e326b2a2feac9ddbeb8551fadba /src
parent53dfd85a7f971875e716a55f010ee508bce89eed (diff)
downloademacs-be8328acf9aa464f848e682e63e417a18529af9e.tar.gz
emacs-be8328acf9aa464f848e682e63e417a18529af9e.zip
Add support for --seccomp command-line option.scratch/seccomp-emacs-open
When passing this option on GNU/Linux, Emacs installs a Secure Computing kernel system call filter. See Bug#45198. * configure.ac: Check for seccomp header. * src/emacs.c (usage_message): Document --seccomp option. (emacs_seccomp): New wrapper for 'seccomp' syscall. (load_seccomp, maybe_load_seccomp): New helper functions. (main): Potentially load seccomp filters during startup. (standard_args): Add --seccomp option. * lisp/startup.el (command-line): Detect and ignore --seccomp option. * test/src/emacs-tests.el (emacs-tests/seccomp/absent-file) (emacs-tests/seccomp/empty-file) (emacs-tests/seccomp/file-too-large) (emacs-tests/seccomp/invalid-file-size): New unit tests. (emacs-tests--with-temp-file): New helper macro. * etc/NEWS: Document new --seccomp option.
Diffstat (limited to 'src')
-rw-r--r--src/emacs.c167
1 files changed, 166 insertions, 1 deletions
diff --git a/src/emacs.c b/src/emacs.c
index fd08667f3fd..b956e9ca34b 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -61,6 +61,13 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
61# include <sys/socket.h> 61# include <sys/socket.h>
62#endif 62#endif
63 63
64#ifdef HAVE_LINUX_SECCOMP_H
65# include <linux/seccomp.h>
66# include <linux/filter.h>
67# include <sys/prctl.h>
68# include <sys/syscall.h>
69#endif
70
64#ifdef HAVE_WINDOW_SYSTEM 71#ifdef HAVE_WINDOW_SYSTEM
65#include TERM_HEADER 72#include TERM_HEADER
66#endif /* HAVE_WINDOW_SYSTEM */ 73#endif /* HAVE_WINDOW_SYSTEM */
@@ -241,6 +248,11 @@ Initialization options:\n\
241--dump-file FILE read dumped state from FILE\n\ 248--dump-file FILE read dumped state from FILE\n\
242", 249",
243#endif 250#endif
251#ifdef HAVE_LINUX_SECCOMP_H
252 "\
253--sandbox=FILE read Seccomp BPF filter from FILE\n\
254"
255#endif
244 "\ 256 "\
245--no-build-details do not add build details such as time stamps\n\ 257--no-build-details do not add build details such as time stamps\n\
246--no-desktop do not load a saved desktop\n\ 258--no-desktop do not load a saved desktop\n\
@@ -938,6 +950,149 @@ load_pdump (int argc, char **argv)
938} 950}
939#endif /* HAVE_PDUMPER */ 951#endif /* HAVE_PDUMPER */
940 952
953#ifdef HAVE_LINUX_SECCOMP_H
954
955/* Wrapper function for the `seccomp' system call on GNU/Linux. This
956 system call usually doesn't have a wrapper function. See the
957 manual page of `seccomp' for the signature. */
958
959static int
960emacs_seccomp (unsigned int operation, unsigned int flags, void *args)
961{
962#ifdef SYS_seccomp
963 return syscall (SYS_seccomp, operation, flags, args);
964#else
965 errno = ENOSYS;
966 return -1;
967#endif
968}
969
970/* Attempt to load Secure Computing filters from FILE. Return false
971 if that doesn't work for some reason. */
972
973static bool
974load_seccomp (const char *file)
975{
976 bool success = false;
977 void *buffer = NULL;
978 int fd
979 = emacs_open_noquit (file, O_RDONLY | O_CLOEXEC | O_BINARY, 0);
980 if (fd < 0)
981 {
982 emacs_perror ("open");
983 goto out;
984 }
985 struct stat stat;
986 if (fstat (fd, &stat) != 0)
987 {
988 emacs_perror ("fstat");
989 goto out;
990 }
991 if (! S_ISREG (stat.st_mode))
992 {
993 fprintf (stderr, "seccomp file %s is not regular\n", file);
994 goto out;
995 }
996 enum
997 {
998 /* See MAX_RW_COUNT in sysdep.c. */
999#ifdef MAX_RW_COUNT
1000 max_read_size = MAX_RW_COUNT
1001#else
1002 max_read_size = INT_MAX >> 18 << 18
1003#endif
1004 };
1005 struct sock_fprog program;
1006 if (stat.st_size <= 0 || SIZE_MAX <= stat.st_size
1007 || PTRDIFF_MAX <= stat.st_size || max_read_size < stat.st_size
1008 || stat.st_size % sizeof *program.filter != 0)
1009 {
1010 fprintf (stderr, "seccomp filter %s has invalid size %ld\n",
1011 file, (long) stat.st_size);
1012 goto out;
1013 }
1014 size_t size = stat.st_size;
1015 size_t count = size / sizeof *program.filter;
1016 eassert (0 < count && count < SIZE_MAX);
1017 if (USHRT_MAX < count)
1018 {
1019 fprintf (stderr, "seccomp filter %s is too big\n", file);
1020 goto out;
1021 }
1022 /* Try reading one more byte to detect file size changes. */
1023 buffer = malloc (size + 1);
1024 if (buffer == NULL)
1025 {
1026 emacs_perror ("malloc");
1027 goto out;
1028 }
1029 ptrdiff_t read = emacs_read (fd, buffer, size + 1);
1030 if (read < 0)
1031 {
1032 emacs_perror ("read");
1033 goto out;
1034 }
1035 if (read != count)
1036 {
1037 fprintf (stderr,
1038 "seccomp filter %s changed size while reading\n",
1039 file);
1040 goto out;
1041 }
1042 if (emacs_close (fd) < 0)
1043 emacs_perror ("close"); /* not a fatal error */
1044 fd = -1;
1045 program.len = count;
1046 program.filter = buffer;
1047
1048 /* See man page of `seccomp' why this is necessary. Note that we
1049 intentionally don't check the return value: a parent process
1050 might have made this call before, in which case it would fail;
1051 or, if enabling privilege-restricting mode fails, the `seccomp'
1052 syscall will fail anyway. */
1053 prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1054 /* Install the filter. Make sure that potential other threads can't
1055 escape it. */
1056 if (emacs_seccomp (SECCOMP_SET_MODE_FILTER,
1057 SECCOMP_FILTER_FLAG_TSYNC, &program)
1058 != 0)
1059 {
1060 emacs_perror ("seccomp");
1061 goto out;
1062 }
1063 success = true;
1064
1065 out:
1066 if (fd < 0)
1067 emacs_close (fd);
1068 free (buffer);
1069 return success;
1070}
1071
1072/* Load Secure Computing filter from file specified with the --seccomp
1073 option. Exit if that fails. */
1074
1075static void
1076maybe_load_seccomp (int argc, char **argv)
1077{
1078 int skip_args = 0;
1079 char *file = NULL;
1080 while (skip_args < argc - 1)
1081 {
1082 if (argmatch (argv, argc, "-seccomp", "--seccomp", 9, &file,
1083 &skip_args)
1084 || argmatch (argv, argc, "--", NULL, 2, NULL, &skip_args))
1085 break;
1086 ++skip_args;
1087 }
1088 if (file == NULL)
1089 return;
1090 if (! load_seccomp (file))
1091 fatal ("cannot enable seccomp filter from %s", file);
1092}
1093
1094#endif /* HAVE_LINUX_SECCOMP_H */
1095
941int 1096int
942main (int argc, char **argv) 1097main (int argc, char **argv)
943{ 1098{
@@ -945,6 +1100,13 @@ main (int argc, char **argv)
945 for pointers. */ 1100 for pointers. */
946 void *stack_bottom_variable; 1101 void *stack_bottom_variable;
947 1102
1103 /* First, check whether we should apply a seccomp filter. This
1104 should come at the very beginning to allow the filter to protect
1105 the initialization phase. */
1106#ifdef HAVE_LINUX_SECCOMP_H
1107 maybe_load_seccomp (argc, argv);
1108#endif
1109
948 bool no_loadup = false; 1110 bool no_loadup = false;
949 char *junk = 0; 1111 char *junk = 0;
950 char *dname_arg = 0; 1112 char *dname_arg = 0;
@@ -2133,12 +2295,15 @@ static const struct standard_args standard_args[] =
2133 { "-color", "--color", 5, 0}, 2295 { "-color", "--color", 5, 0},
2134 { "-no-splash", "--no-splash", 3, 0 }, 2296 { "-no-splash", "--no-splash", 3, 0 },
2135 { "-no-desktop", "--no-desktop", 3, 0 }, 2297 { "-no-desktop", "--no-desktop", 3, 0 },
2136 /* The following two must be just above the file-name args, to get 2298 /* The following three must be just above the file-name args, to get
2137 them out of our way, but without mixing them with file names. */ 2299 them out of our way, but without mixing them with file names. */
2138 { "-temacs", "--temacs", 1, 1 }, 2300 { "-temacs", "--temacs", 1, 1 },
2139#ifdef HAVE_PDUMPER 2301#ifdef HAVE_PDUMPER
2140 { "-dump-file", "--dump-file", 1, 1 }, 2302 { "-dump-file", "--dump-file", 1, 1 },
2141#endif 2303#endif
2304#ifdef HAVE_LINUX_SECCOMP_H
2305 { "-seccomp", "--seccomp", 1, 1 },
2306#endif
2142#ifdef HAVE_NS 2307#ifdef HAVE_NS
2143 { "-NSAutoLaunch", 0, 5, 1 }, 2308 { "-NSAutoLaunch", 0, 5, 1 },
2144 { "-NXAutoLaunch", 0, 5, 1 }, 2309 { "-NXAutoLaunch", 0, 5, 1 },