pledge.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. #include <seccomp.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <sys/ioctl.h>
  5. enum {
  6. PLEDGE_STDIO = 1 << 0,
  7. PLEDGE_RPATH = 1 << 1,
  8. PLEDGE_WPATH = 1 << 2,
  9. PLEDGE_CPATH = 1 << 3,
  10. PLEDGE_DPATH = 1 << 4,
  11. PLEDGE_TMPPATH = 1 << 5,
  12. PLEDGE_INET = 1 << 6,
  13. PLEDGE_FATTR = 1 << 7,
  14. PLEDGE_FLOCK = 1 << 8,
  15. PLEDGE_UNIX = 1 << 9,
  16. PLEDGE_DNS = 1 << 10,
  17. PLEDGE_GETPW = 1 << 11,
  18. PLEDGE_SENDFD = 1 << 12,
  19. PLEDGE_RECVFD = 1 << 13,
  20. PLEDGE_IOCTL = 1 << 14,
  21. PLEDGE_TTY = 1 << 15,
  22. PLEDGE_PROC = 1 << 16,
  23. PLEDGE_EXEC = 1 << 17,
  24. PLEDGE_PROT_EXEC = 1 << 18,
  25. PLEDGE_SETTIME = 1 << 19,
  26. PLEDGE_PS = 1 << 20,
  27. PLEDGE_VMINFO = 1 << 21,
  28. PLEDGE_ID = 1 << 22,
  29. PLEDGE_PF = 1 << 23,
  30. PLEDGE_AUDIO = 1 << 24,
  31. };
  32. typedef struct {
  33. char *name;
  34. int mask;
  35. } promise;
  36. promise ptable[] = {
  37. { .name = "stdio", .mask = PLEDGE_STDIO },
  38. { .name = "rpath", .mask = PLEDGE_RPATH },
  39. { .name = "wpath", .mask = PLEDGE_WPATH },
  40. { .name = "cpath", .mask = PLEDGE_CPATH },
  41. { .name = "dpath", .mask = PLEDGE_DPATH },
  42. { .name = "tmppath", .mask = PLEDGE_TMPPATH },
  43. { .name = "inet", .mask = PLEDGE_INET },
  44. { .name = "fattr", .mask = PLEDGE_FATTR },
  45. { .name = "flock", .mask = PLEDGE_FLOCK },
  46. { .name = "unix", .mask = PLEDGE_UNIX },
  47. { .name = "dns", .mask = PLEDGE_DNS },
  48. { .name = "getpw", .mask = PLEDGE_GETPW },
  49. { .name = "sendfd", .mask = PLEDGE_SENDFD },
  50. { .name = "recvfd", .mask = PLEDGE_RECVFD },
  51. { .name = "ioctl", .mask = PLEDGE_IOCTL },
  52. { .name = "tty", .mask = PLEDGE_TTY },
  53. { .name = "proc", .mask = PLEDGE_PROC },
  54. { .name = "exec", .mask = PLEDGE_EXEC },
  55. { .name = "prot_exec", .mask = PLEDGE_PROT_EXEC },
  56. { .name = "settime", .mask = PLEDGE_SETTIME },
  57. { .name = "ps", .mask = PLEDGE_PS },
  58. { .name = "vminfo", .mask = PLEDGE_VMINFO },
  59. { .name = "id", .mask = PLEDGE_ID },
  60. { .name = "pf", .mask = PLEDGE_PF },
  61. { .name = "audio", .mask = PLEDGE_AUDIO },
  62. };
  63. int
  64. pledge(const char *promises, const char *paths[])
  65. {
  66. int i, n, f;
  67. int flags = 0;
  68. int rc = -1;
  69. scmp_filter_ctx ctx;
  70. ctx = seccomp_init(SCMP_ACT_TRAP);
  71. if (!ctx) {
  72. errno = EACCES;
  73. goto out;
  74. }
  75. while (*promises) {
  76. // skip spaces
  77. while (*promises && *promises == ' ') promises++;
  78. // look for a token
  79. f = 0;
  80. for (i = 0; i < sizeof(ptable)/sizeof(*ptable); i++) {
  81. n = strlen(ptable[i].name);
  82. if (!strncmp(promises, ptable[i].name, n)) {
  83. // this can be removed once every promise has been implemented
  84. if (!ptable[i].mask) {
  85. errno = ENOSYS;
  86. goto out;
  87. }
  88. flags |= ptable[i].mask;
  89. promises += n;
  90. f = 1;
  91. break;
  92. }
  93. }
  94. // what we saw was not any valid token
  95. if (!f) {
  96. errno = EINVAL;
  97. goto out;
  98. }
  99. // ensure the token is terminated by a space or end of string
  100. if (*promises && *promises != ' ') {
  101. errno = EINVAL;
  102. goto out;
  103. }
  104. }
  105. #define RULE(syscall) \
  106. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(syscall), 0); \
  107. if (rc < 0) goto out
  108. // everyone is allowed to exit
  109. RULE(exit_group);
  110. if (flags & PLEDGE_STDIO) {
  111. RULE(clock_getres);
  112. RULE(clock_gettime);
  113. RULE(close);
  114. /* RULE(closefrom); */
  115. RULE(dup);
  116. RULE(dup2);
  117. RULE(dup3);
  118. RULE(fchdir);
  119. RULE(fcntl);
  120. RULE(fstat);
  121. RULE(fsync);
  122. RULE(ftruncate);
  123. RULE(getdents);
  124. /* RULE(getdtablecount); */
  125. RULE(getegid);
  126. /* RULE(getentropy); */
  127. RULE(geteuid);
  128. RULE(getgid);
  129. RULE(getgroups);
  130. RULE(getitimer);
  131. /* RULE(getlogin); */
  132. RULE(getpgid);
  133. RULE(getpgrp);
  134. RULE(getpid);
  135. RULE(getppid);
  136. RULE(getresgid);
  137. RULE(getresuid);
  138. RULE(getrlimit);
  139. RULE(getsid);
  140. /* RULE(getthrid); */
  141. RULE(gettimeofday);
  142. RULE(getuid);
  143. RULE(getuid);
  144. /* RULE(issetugid); */
  145. /* RULE(kevent); */
  146. /* RULE(kqueue); */
  147. RULE(lseek);
  148. RULE(madvise);
  149. /* RULE(minherit); */
  150. RULE(mmap);
  151. RULE(mprotect);
  152. /* RULE(mquery); */
  153. RULE(munmap);
  154. RULE(nanosleep);
  155. RULE(pipe);
  156. RULE(pipe2);
  157. RULE(poll);
  158. /* RULE(pread); */
  159. RULE(preadv);
  160. /* RULE(pwrite); */
  161. RULE(pwritev);
  162. RULE(read);
  163. RULE(readv);
  164. RULE(recvfrom);
  165. RULE(recvmsg);
  166. RULE(select);
  167. RULE(sendmsg);
  168. /* RULE(sendsyslog); */
  169. /* sendto: only if dest sockaddr is NULL */
  170. RULE(sendto);
  171. RULE(setitimer);
  172. RULE(shutdown);
  173. RULE(sigaction);
  174. RULE(sigprocmask);
  175. RULE(sigreturn);
  176. RULE(socketpair);
  177. RULE(umask);
  178. RULE(wait4);
  179. RULE(write);
  180. RULE(writev);
  181. }
  182. if (flags & PLEDGE_RPATH) {
  183. /* Allowed if the only cause read-only effects on file system */
  184. RULE(chdir);
  185. RULE(getcwd);
  186. RULE(openat);
  187. /* RULE(fstatat); */
  188. RULE(faccessat);
  189. RULE(readlinkat);
  190. RULE(lstat);
  191. RULE(chmod);
  192. RULE(fchmod);
  193. RULE(fchmodat);
  194. /* RULE(chflags); */
  195. /* RULE(chflagsat); */
  196. RULE(chown);
  197. RULE(fchown);
  198. RULE(fchownat);
  199. RULE(fstat);
  200. /* RULE(getfsstat); */
  201. }
  202. if (flags & PLEDGE_WPATH) {
  203. /* system calls may have write effects on file system */
  204. RULE(getcwd);
  205. RULE(openat);
  206. /* RULE(fstatat); */
  207. RULE(faccessat);
  208. RULE(readlinkat);
  209. RULE(lstat);
  210. RULE(chmod);
  211. RULE(fchmod);
  212. RULE(fchmodat);
  213. /* RULE(chflags); */
  214. /* RULE(chflagsat); */
  215. RULE(chown);
  216. RULE(fchown);
  217. RULE(fchownat);
  218. RULE(fstat);
  219. }
  220. if (flags & PLEDGE_CPATH) {
  221. /* system calls may create new files or directories on file system */
  222. RULE(rename);
  223. RULE(rmdir);
  224. RULE(renameat);
  225. RULE(link);
  226. RULE(linkat);
  227. RULE(symlink);
  228. RULE(unlink);
  229. RULE(unlinkat);
  230. RULE(mkdir);
  231. RULE(mkdirat);
  232. }
  233. if (flags & PLEDGE_DPATH) {
  234. /* may create special files */
  235. /* RULE(mkfifo); */
  236. RULE(mknod);
  237. }
  238. if (flags & PLEDGE_TMPPATH) {
  239. /* system calls permitted to read, write, and create in /tmp directory */
  240. RULE(lstat);
  241. RULE(chmod);
  242. /* RULE(chflags); */
  243. RULE(chown);
  244. RULE(unlink);
  245. RULE(fstat);
  246. }
  247. if (flags & PLEDGE_INET) {
  248. /* system calls may operate in AF_INET and AF_INET6 domains */
  249. RULE(socket);
  250. RULE(listen);
  251. RULE(bind);
  252. RULE(connect);
  253. RULE(accept4);
  254. RULE(accept);
  255. RULE(getpeername);
  256. RULE(getsockname);
  257. /* setsockopt: substantially reduced in functionality */
  258. RULE(setsockopt);
  259. RULE(getsockopt);
  260. }
  261. if (flags & PLEDGE_FATTR) {
  262. /* system calls may make explicit changes in struct stat of a file */
  263. RULE(socket);
  264. RULE(listen);
  265. RULE(bind);
  266. RULE(connect);
  267. RULE(accept4);
  268. RULE(accept);
  269. RULE(getpeername);
  270. RULE(getsockname);
  271. RULE(setsockopt);
  272. RULE(getsockopt);
  273. }
  274. if (flags & PLEDGE_FLOCK) {
  275. /* no distinction btw shared/exclusive locks; required for lock/unlock */
  276. RULE(fcntl);
  277. RULE(flock);
  278. /* RULE(lockf); */
  279. RULE(open);
  280. }
  281. if (flags & PLEDGE_UNIX) {
  282. /* system calls may operate in AF_UNIX domain */
  283. RULE(socket);
  284. RULE(listen);
  285. RULE(bind);
  286. RULE(connect);
  287. RULE(accept4);
  288. RULE(accept);
  289. RULE(getpeername);
  290. RULE(getsockname);
  291. RULE(setsockopt);
  292. RULE(getsockopt);
  293. }
  294. if (flags & PLEDGE_DNS) {
  295. /* subsequent to successful open of /etc/resolv.conf; allow DNS xaction */
  296. RULE(sendto);
  297. RULE(recvfrom);
  298. RULE(socket);
  299. RULE(connect);
  300. }
  301. if (flags & PLEDGE_GETPW) {
  302. /* ro opening of files in /etc */
  303. /* RULE(getpwnam); */
  304. /* RULE(getgrnam); */
  305. /* RULE(getgrouplist); */
  306. /* RULE(initgroups); */
  307. }
  308. if (flags & PLEDGE_SENDFD) {
  309. /* directory FDs not permitted */
  310. RULE(sendmsg);
  311. }
  312. if (flags & PLEDGE_RECVFD) {
  313. /* directory FDs not permitted */
  314. RULE(recvmsg);
  315. }
  316. if (flags & PLEDGE_IOCTL) {
  317. /* allows subset of ioctl:
  318. * FIOCLEX
  319. * FIONCLEX
  320. * FIOASYNC
  321. * FIOGETOWN
  322. * FIOSETOWN
  323. * TIOCGETA succeed on tty device, otherwise fail with EPERM
  324. * TIOCGPGRP and TIOCGWINSZ allowed on tty device
  325. * Rather than test for tty device, just let ioctl fail on ENOTTY
  326. * A few other operations are allowed, but not listed here (?)
  327. */
  328. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  329. SCMP_A1(SCMP_CMP_EQ, FIOCLEX));
  330. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  331. SCMP_A1(SCMP_CMP_EQ, FIONCLEX));
  332. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  333. SCMP_A1(SCMP_CMP_EQ, FIOASYNC));
  334. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  335. SCMP_A1(SCMP_CMP_EQ, FIOGETOWN)); */
  336. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  337. SCMP_A1(SCMP_CMP_EQ, FIOSETOWN)); */
  338. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 2,
  339. SCMP_A1(SCMP_CMP_EQ, TIOCGETA)); */
  340. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 2,
  341. SCMP_A1(SCMP_CMP_EQ, TIOCGPGRP));
  342. }
  343. if (flags & PLEDGE_TTY) {
  344. /* allows subset of ioctl:
  345. * TIOCSPGRP
  346. * TIOCGETA
  347. * TIOCGPGRP
  348. * TIOCGWINSZ
  349. * TIOCSWINSZ
  350. * TIOCSBRK
  351. * TIOCCDTR
  352. * TIOCSETA
  353. * TIOCSETAW
  354. * TIOCSETAF
  355. */
  356. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  357. SCMP_A1(SCMP_CMP_EQ, TIOCSPGRP));
  358. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  359. SCMP_A1(SCMP_CMP_EQ, TIOCGETA)); */
  360. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  361. SCMP_A1(SCMP_CMP_EQ, TIOCGPGRP));
  362. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  363. SCMP_A1(SCMP_CMP_EQ, TIOCGWINSZ));
  364. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  365. SCMP_A1(SCMP_CMP_EQ, TIOCSWINSZ));
  366. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  367. SCMP_A1(SCMP_CMP_EQ, TIOCSBRK));
  368. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  369. SCMP_A1(SCMP_CMP_EQ, TIOCCDTR)); */
  370. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  371. SCMP_A1(SCMP_CMP_EQ, TIOCSETA)); */
  372. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  373. SCMP_A1(SCMP_CMP_EQ, TIOCSETAW)); */
  374. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  375. SCMP_A1(SCMP_CMP_EQ, TIOCSETAF)); */
  376. /* Also, allow r/w operations on /dev/tty */
  377. }
  378. if (flags & PLEDGE_TTY & PLEDGE_RPATH) {
  379. /* RULE(revoke); */
  380. }
  381. if (flags & PLEDGE_PROC) {
  382. RULE(fork);
  383. RULE(vfork);
  384. RULE(kill);
  385. RULE(getpriority);
  386. RULE(setpriority);
  387. RULE(setrlimit);
  388. RULE(setpgid);
  389. RULE(setsid);
  390. }
  391. if (flags & PLEDGE_EXEC) {
  392. RULE(execve);
  393. }
  394. if (flags & PLEDGE_PROT_EXEC) {
  395. /* Allows use of PROT_EXEC with the following system calls */
  396. RULE(mmap);
  397. RULE(mprotect);
  398. }
  399. if (flags & PLEDGE_SETTIME) {
  400. /* Allows setting of system time via the following system calls */
  401. RULE(settimeofday);
  402. /* RULE(adjtime); */
  403. /* RULE(and adjfreq); */
  404. }
  405. if (flags & PLEDGE_PS) {
  406. /* Allows sufficient sysctl access for inspection of procs, as in ps */
  407. /* RULE(sysctl); */
  408. }
  409. if (flags & PLEDGE_VMINFO) {
  410. /* Allows sufficient sysctl access for inspection of virtual mem,
  411. * as in top, vmstat */
  412. /* RULE(sysctl); */
  413. }
  414. if (flags & PLEDGE_ID) {
  415. RULE(setuid);
  416. /* RULE(seteuid); */
  417. RULE(setreuid);
  418. RULE(setresuid);
  419. RULE(setgid);
  420. /* RULE(setegid); */
  421. RULE(setregid);
  422. RULE(setresgid);
  423. RULE(setgroups);
  424. /* RULE(setlogin); */
  425. RULE(setrlimit);
  426. RULE(getpriority);
  427. RULE(setpriority);
  428. }
  429. if (flags & PLEDGE_PF) {
  430. /* Allows the following subset of ioctl operations on the pf(4) device:
  431. * DIOCADDRULE
  432. * DIOCGETSTATUS
  433. * DIOCNATLOOK
  434. * DIOCRADDTABLES
  435. * DIOCRCLRADDRS
  436. * DIOCRCLRTABLES
  437. * DIOCRCLRTSTATS
  438. * DIOCRGETTSTATS
  439. * DIOCRSETADDRS
  440. * DIOCXBEGIN
  441. * DIOCXCOMMIT
  442. */
  443. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  444. SCMP_A1(SCMP_CMP_EQ, DIOCADDRULE)); */
  445. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  446. SCMP_A1(SCMP_CMP_EQ, DIOCGETSTATUS)); */
  447. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  448. SCMP_A1(SCMP_CMP_EQ, DIOCNATLOOK)); */
  449. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  450. SCMP_A1(SCMP_CMP_EQ, DIOCRADDTABLES)); */
  451. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  452. SCMP_A1(SCMP_CMP_EQ, DIOCRCLRADDRS)); */
  453. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  454. SCMP_A1(SCMP_CMP_EQ, DIOCRCLRTABLES)); */
  455. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  456. SCMP_A1(SCMP_CMP_EQ, DIOCRCLRTSTATS)); */
  457. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  458. SCMP_A1(SCMP_CMP_EQ, DIOCRGETTSTATS)); */
  459. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  460. SCMP_A1(SCMP_CMP_EQ, DIOCRSETADDRS)); */
  461. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  462. SCMP_A1(SCMP_CMP_EQ, DIOCXBEGIN)); */
  463. }
  464. if (flags & PLEDGE_AUDIO) {
  465. /* Allows the following subset of ioctl operations on the audio(4) device:
  466. * AUDIO_GETPOS
  467. * AUDIO_SETINFO
  468. * AUDIO_GETINFO
  469. * AUDIO_GETENC
  470. * AUDIO_SETFD
  471. * AUDIO_GETPROPS
  472. */
  473. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  474. SCMP_A1(SCMP_CMP_EQ, AUDIO_GETPOS)); */
  475. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  476. SCMP_A1(SCMP_CMP_EQ, AUDIO_SETINFO)); */
  477. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  478. SCMP_A1(SCMP_CMP_EQ, AUDIO_GETINFO)); */
  479. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  480. SCMP_A1(SCMP_CMP_EQ, AUDIO_GETENC)); */
  481. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  482. SCMP_A1(SCMP_CMP_EQ, AUDIO_SETFD)); */
  483. /* rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
  484. SCMP_A1(SCMP_CMP_EQ, AUDIO_GETPROPS)); */
  485. }
  486. rc = seccomp_load(ctx);
  487. if (rc < 0) goto out;
  488. rc = 0;
  489. out:
  490. seccomp_release(ctx);
  491. return rc;
  492. }