jack1 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
8.6KB

  1. /* Copyright (C) 2002 Fernando Lopez-Lezcano
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  13. Jackstart is based on code and concepts found in sucap.c, written by
  14. Finn Arne Gangstad <finnag@guardian.no> and givertcap.c, written by
  15. Tommi Ilmonen, Tommi.Ilmonen@hut.fi
  16. */
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <errno.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <pwd.h>
  23. #include <grp.h>
  24. #include <unistd.h>
  25. #include <sys/wait.h>
  26. #include <errno.h>
  27. #include <string.h>
  28. #include <config.h>
  29. #undef _POSIX_SOURCE
  30. #include <sys/capability.h>
  31. #include "jack/start.h"
  32. #include "md5.h"
  33. #include "jack_md5.h"
  34. #define READ_BLOCKSIZE 4096
  35. /* JACK_LOCATION must be passed on the gcc command line */
  36. static char *jackd_bin_path = JACK_LOCATION "/jackd";
  37. static char *jackd_md5_sum = JACKD_MD5_SUM;
  38. static int check_capabilities (void)
  39. {
  40. cap_t caps = cap_init ();
  41. cap_flag_value_t cap;
  42. pid_t pid;
  43. int have_all_caps = 1;
  44. if (caps == NULL) {
  45. fprintf (stderr, "jackstart: could not allocate capability working storage\n");
  46. return 0;
  47. }
  48. pid = getpid ();
  49. cap_clear (caps);
  50. if (capgetp (pid, caps)) {
  51. fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
  52. return 0;
  53. }
  54. /* check that we are able to give capabilites to other processes */
  55. cap_get_flag (caps, CAP_SETPCAP, CAP_EFFECTIVE, &cap);
  56. if (cap == CAP_CLEAR) {
  57. have_all_caps = 0;
  58. goto done;
  59. }
  60. /* check that we have the capabilities we want to transfer */
  61. cap_get_flag (caps, CAP_SYS_NICE, CAP_EFFECTIVE, &cap);
  62. if (cap == CAP_CLEAR) {
  63. have_all_caps = 0;
  64. goto done;
  65. }
  66. cap_get_flag (caps, CAP_SYS_RESOURCE, CAP_EFFECTIVE, &cap);
  67. if (cap == CAP_CLEAR) {
  68. have_all_caps = 0;
  69. goto done;
  70. }
  71. cap_get_flag (caps, CAP_IPC_LOCK, CAP_EFFECTIVE, &cap);
  72. if (cap == CAP_CLEAR) {
  73. have_all_caps = 0;
  74. goto done;
  75. }
  76. done:
  77. cap_free (caps);
  78. return have_all_caps;
  79. }
  80. static int give_capabilities (pid_t pid)
  81. {
  82. cap_t caps = cap_init ();
  83. const unsigned caps_size = 4;
  84. cap_value_t cap_list[] =
  85. { CAP_SETPCAP, CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_IPC_LOCK };
  86. if (caps == NULL) {
  87. fprintf (stderr, "jackstart: could not allocate capability working storage\n");
  88. return -1;
  89. }
  90. cap_clear (caps);
  91. if (capgetp (pid, caps)) {
  92. fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
  93. cap_clear (caps);
  94. }
  95. cap_set_flag (caps, CAP_EFFECTIVE, caps_size, cap_list, CAP_SET);
  96. cap_set_flag (caps, CAP_INHERITABLE, caps_size, cap_list, CAP_SET);
  97. cap_set_flag (caps, CAP_PERMITTED, caps_size, cap_list, CAP_SET);
  98. if (capsetp (pid, caps)) {
  99. fprintf (stderr, "jackstart: could not give capabilities: %s\n", strerror (errno));
  100. cap_free (caps);
  101. return -1;
  102. }
  103. cap_free (caps);
  104. return 0;
  105. }
  106. static int check_binary (const char *binpath)
  107. {
  108. struct stat status;
  109. FILE *binstream;
  110. if (lstat (jackd_bin_path, &status)) {
  111. fprintf (stderr, "jackstart: could not stat %s: %s\n",
  112. binpath, strerror (errno));
  113. return -1;
  114. }
  115. if (!(S_ISREG (status.st_mode))) {
  116. fprintf (stderr, "jackstart: %s is not a regular file\n",
  117. binpath);
  118. return -1;
  119. }
  120. if (status.st_uid != 0) {
  121. fprintf (stderr, "jackstart: %s is not owned by root\n",
  122. binpath);
  123. return -1;
  124. }
  125. if ((status.st_mode & 022) != 0) {
  126. fprintf (stderr,
  127. "jackstart: %s mode %o writeable by non-root users\n",
  128. binpath, status.st_mode & 07777);
  129. return -1;
  130. }
  131. if ((binstream = fopen (binpath, "r")) == NULL) {
  132. fprintf (stderr, "jackstart: can't open %s for reading: %s\n",
  133. binpath, strerror (errno));
  134. return -1;
  135. } else {
  136. /* md5sum the executable file, check man evp for more details */
  137. size_t sum;
  138. md5_t ctx;
  139. char buffer[READ_BLOCKSIZE + 72];
  140. unsigned char md_value[MD5_SIZE];
  141. char md_string[3];
  142. int i, j;
  143. md5_init (&ctx);
  144. while (1) {
  145. size_t n;
  146. sum = 0;
  147. do {
  148. n = fread (buffer + sum, 1, READ_BLOCKSIZE - sum, binstream);
  149. sum += n;
  150. } while (sum < READ_BLOCKSIZE && n != 0);
  151. if (n == 0 && ferror (binstream)) {
  152. fprintf (stderr, "jackstart: error while reading %s: %s\n", binpath, strerror (errno));
  153. return -1;
  154. }
  155. if (n == 0) {
  156. break;
  157. }
  158. md5_process (&ctx, buffer, READ_BLOCKSIZE);
  159. }
  160. if (sum > 0) {
  161. md5_process (&ctx, buffer, sum);
  162. }
  163. if (fclose (binstream)) {
  164. fprintf (stderr, "jackstart: could not close %s after reading: %s\n", binpath, strerror (errno));
  165. }
  166. md5_finish (&ctx, md_value);
  167. for (i = 0, j = 0; i < sizeof(md_value); i++, j += 2) {
  168. sprintf (md_string, "%02x", md_value[i]);
  169. if (md_string[0] != jackd_md5_sum[j] ||
  170. md_string[1] != jackd_md5_sum[j + 1]) {
  171. fprintf (stderr, "jackstart: md5 checksum for %s does not match\n", binpath);
  172. return -1;
  173. }
  174. }
  175. }
  176. return 0;
  177. }
  178. int main (int argc, char **argv)
  179. {
  180. uid_t uid, euid;
  181. pid_t pid, parent_pid;
  182. gid_t gid;
  183. int pipe_fds[2];
  184. int err;
  185. parent_pid = getpid ();
  186. /* get real user and group ids, effective user id */
  187. uid = getuid ();
  188. gid = getgid ();
  189. euid = geteuid ();
  190. /* are we running suid root? */
  191. if (uid != 0) {
  192. if (euid != 0) {
  193. fprintf (stderr, "jackstart: not running suid root, can't use capabilities\n");
  194. fprintf (stderr, " (currently running with uid=%d and euid=%d),\n", uid, euid);
  195. fprintf (stderr, " make jackstart suid root or start jackd directly\n\n");
  196. }
  197. }
  198. /* see if we can get the required capabilities */
  199. if (check_capabilities () == 0) {
  200. size_t size;
  201. cap_t cap = cap_init ();
  202. capgetp (0, cap);
  203. fprintf (stderr, "jackstart: cannot get realtime capabilities, current capabilities are:\n");
  204. fprintf (stderr, " %s\n", cap_to_text (cap, &size));
  205. fprintf (stderr, " probably running under a kernel with capabilities disabled,\n");
  206. fprintf (stderr, " a suitable kernel would have printed something like \"=eip\"\n\n");
  207. }
  208. /* check the executable, owner, permissions, md5 checksum */
  209. if (check_binary (jackd_bin_path)) {
  210. exit (1);
  211. }
  212. /* set process group to current pid */
  213. if (setpgid (0, getpid ())) {
  214. fprintf (stderr, "jackstart: failed to set process group: %s\n",
  215. strerror (errno));
  216. exit (1);
  217. }
  218. /* create pipe to synchronize with jackd */
  219. if (pipe (pipe_fds)) {
  220. fprintf (stderr, "jackstart: could not create pipe: %s\n",
  221. strerror (errno));
  222. exit (1);
  223. }
  224. /* make sure the file descriptors are the right ones,
  225. otherwise dup them, this is to make sure that both
  226. jackstart and jackd use the same fds
  227. */
  228. if (pipe_fds[0] != PIPE_READ_FD) {
  229. if (dup2 (pipe_fds[0], PIPE_READ_FD) != PIPE_READ_FD) {
  230. fprintf (stderr, "jackstart: could not dup pipe read file descriptor: %s\n",
  231. strerror (errno));
  232. exit (1);
  233. }
  234. }
  235. if (pipe_fds[1] != PIPE_WRITE_FD) {
  236. if (dup2 (pipe_fds[1], PIPE_WRITE_FD) != PIPE_WRITE_FD) {
  237. fprintf (stderr, "jackstart: could not dup pipe write file descriptor: %s\n",
  238. strerror (errno));
  239. exit (1);
  240. }
  241. }
  242. /* fork off a child to wait for jackd to start */
  243. fflush (NULL);
  244. pid = fork ();
  245. if (pid == -1) {
  246. fprintf (stderr, "jackstart: fork failed\n");
  247. exit (1);
  248. }
  249. if (pid) {
  250. /* mother process: drops privileges, execs jackd */
  251. close (PIPE_READ_FD);
  252. /* get rid of any supplemental groups */
  253. if (!getuid () && setgroups (0, 0)) {
  254. fprintf (stderr, "jackstart: setgroups failed: %s\n", strerror (errno));
  255. exit (1);
  256. }
  257. /* set gid and uid */
  258. setregid (gid, gid);
  259. setreuid (uid, uid);
  260. execvp (jackd_bin_path, argv);
  261. /* we could not start jackd, clean up and exit */
  262. fprintf (stderr, "jackstart: unable to execute %s: %s\n", jackd_bin_path, strerror (errno));
  263. close (PIPE_WRITE_FD);
  264. wait (&err);
  265. exit (1);
  266. } else {
  267. /* child process: grants privileges to jackd */
  268. close (PIPE_WRITE_FD);
  269. /* wait for jackd to start */
  270. while (1) {
  271. int ret;
  272. char c;
  273. /* picking up pipe closure is a tricky business.
  274. this seems to work as well as anything else.
  275. */
  276. ret = read (PIPE_READ_FD, &c, 1);
  277. fprintf (stderr, "back from read, ret = %d errno == %s\n", ret, strerror (errno));
  278. if (ret == 1) {
  279. break;
  280. } else if (errno != EINTR) {
  281. break;
  282. }
  283. }
  284. /* set privileges on jackd process */
  285. give_capabilities (parent_pid);
  286. }
  287. exit (0);
  288. }