pledge.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #include <seccomp.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. enum {
  5. PLEDGE_STDIO = 1 << 0,
  6. PLEDGE_PROC = 1 << 1,
  7. PLEDGE_EXEC = 1 << 2,
  8. };
  9. typedef struct {
  10. char *name;
  11. int mask;
  12. } promise;
  13. promise ptable[] = {
  14. { .name = "stdio", .mask = PLEDGE_STDIO },
  15. { .name = "rpath", .mask = 0 },
  16. { .name = "wpath", .mask = 0 },
  17. { .name = "cpath", .mask = 0 },
  18. { .name = "dpath", .mask = 0 },
  19. { .name = "tmppath", .mask = 0 },
  20. { .name = "inet", .mask = 0 },
  21. { .name = "fattr", .mask = 0 },
  22. { .name = "flock", .mask = 0 },
  23. { .name = "unix", .mask = 0 },
  24. { .name = "dns", .mask = 0 },
  25. { .name = "getpw", .mask = 0 },
  26. { .name = "sendfd", .mask = 0 },
  27. { .name = "recvfd", .mask = 0 },
  28. { .name = "ioctl", .mask = 0 },
  29. { .name = "tty", .mask = 0 },
  30. { .name = "proc", .mask = PLEDGE_PROC },
  31. { .name = "exec", .mask = PLEDGE_EXEC },
  32. { .name = "prot_exec", .mask = 0 },
  33. { .name = "settime", .mask = 0 },
  34. { .name = "ps", .mask = 0 },
  35. { .name = "vminfo", .mask = 0 },
  36. { .name = "id", .mask = 0 },
  37. { .name = "pf", .mask = 0 },
  38. { .name = "audio", .mask = 0 },
  39. };
  40. int
  41. pledge(const char *promises, const char *paths[])
  42. {
  43. int i, n, f;
  44. int flags = 0;
  45. int rc = -1;
  46. scmp_filter_ctx ctx;
  47. ctx = seccomp_init(SCMP_ACT_TRAP);
  48. if (!ctx) {
  49. errno = EACCES;
  50. goto out;
  51. }
  52. while (*promises) {
  53. // skip spaces
  54. while (*promises && *promises == ' ') promises++;
  55. // look for a token
  56. f = 0;
  57. for (i = 0; i < sizeof(ptable)/sizeof(*ptable); i++) {
  58. n = strlen(ptable[i].name);
  59. if (!strncmp(promises, ptable[i].name, n)) {
  60. // this can be removed once every promise has been implemented
  61. if (!ptable[i].mask) {
  62. errno = ENOSYS;
  63. goto out;
  64. }
  65. flags |= ptable[i].mask;
  66. promises += n;
  67. f = 1;
  68. break;
  69. }
  70. }
  71. // what we saw was not any valid token
  72. if (!f) {
  73. errno = EINVAL;
  74. goto out;
  75. }
  76. // ensure the token is terminated by a space or end of string
  77. if (*promises && *promises != ' ') {
  78. errno = EINVAL;
  79. goto out;
  80. }
  81. }
  82. #define RULE(syscall) \
  83. rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(syscall), 0); \
  84. if (rc < 0) goto out
  85. // everyone is allowed to exit
  86. RULE(exit_group);
  87. if (flags & PLEDGE_STDIO) {
  88. RULE(read);
  89. RULE(write);
  90. RULE(open);
  91. RULE(close);
  92. RULE(stat);
  93. RULE(fstat);
  94. RULE(lstat);
  95. RULE(poll);
  96. RULE(lseek);
  97. RULE(dup);
  98. RULE(dup2);
  99. }
  100. if (flags & PLEDGE_PROC) {
  101. RULE(fork);
  102. RULE(vfork);
  103. RULE(kill);
  104. RULE(getpriority);
  105. RULE(setpriority);
  106. RULE(setrlimit);
  107. RULE(setpgid);
  108. RULE(setsid);
  109. }
  110. if (flags & PLEDGE_EXEC) {
  111. RULE(execve);
  112. }
  113. rc = seccomp_load(ctx);
  114. if (rc < 0) goto out;
  115. rc = 0;
  116. out:
  117. seccomp_release(ctx);
  118. return rc;
  119. }