Browse Source

Add chroot capability

tags/1.0
Timo Röhling 12 years ago
parent
commit
c29c38d622
5 changed files with 56 additions and 12 deletions
  1. 2
    0
      CMakeLists.txt
  2. 6
    0
      postinstall.cmake.in
  3. 42
    10
      postsrsd.c
  4. 3
    1
      postsrsd.init.in
  5. 3
    1
      postsrsd.upstart.in

+ 2
- 0
CMakeLists.txt View File

3
 
3
 
4
 option(GENERATE_SRS_SECRET "Generate a random SRS secret if none exists during install" ON)
4
 option(GENERATE_SRS_SECRET "Generate a random SRS secret if none exists during install" ON)
5
 
5
 
6
+set(CHROOT_DIR "${CMAKE_INSTALL_PREFIX}/lib/${PROJECT_NAME}" CACHE PATH "Chroot jail for daemon")
7
+
6
 find_program(HELP2MAN help2man DOC "path to help2man executable")
8
 find_program(HELP2MAN help2man DOC "path to help2man executable")
7
 find_program(DD dd DOC "path to dd executable")
9
 find_program(DD dd DOC "path to dd executable")
8
 find_program(BASE64 base64 DOC "path to base64 executable")
10
 find_program(BASE64 base64 DOC "path to base64 executable")

+ 6
- 0
postinstall.cmake.in View File

1
 set(GENERATE_SRS_SECRET "@GENERATE_SRS_SECRET@")
1
 set(GENERATE_SRS_SECRET "@GENERATE_SRS_SECRET@")
2
+set(CHROOT_DIR "@CHROOT_DIR@")
2
 set(INIT_FLAVOR "@INIT_FLAVOR@")
3
 set(INIT_FLAVOR "@INIT_FLAVOR@")
3
 set(SECRET_FILE "@PROJECT_NAME@.secret")
4
 set(SECRET_FILE "@PROJECT_NAME@.secret")
4
 set(DD "@DD@")
5
 set(DD "@DD@")
5
 set(BASE64 "@BASE64@")
6
 set(BASE64 "@BASE64@")
6
 set(INSSERV "@INSSERV@")
7
 set(INSSERV "@INSSERV@")
7
 
8
 
9
+if(CHROOT_DIR AND NOT EXISTS "$ENV{DESTDIR}${CHROOT_DIR}")
10
+	message(STATUS "Chroot jail: $ENV{DESTDIR}${CHROOT_DIR}")
11
+	file(MAKE_DIRECTORY "$ENV{DESTDIR}${CHROOT_DIR}")
12
+endif()
13
+
8
 if(INIT_FLAVOR AND NOT EXISTS "$ENV{DESTDIR}/etc/default/@PROJECT_NAME@")
14
 if(INIT_FLAVOR AND NOT EXISTS "$ENV{DESTDIR}/etc/default/@PROJECT_NAME@")
9
 	file(INSTALL "@CMAKE_CURRENT_SOURCE_DIR@/@PROJECT_NAME@.default" DESTINATION "/etc/default" RENAME "@PROJECT_NAME@")
15
 	file(INSTALL "@CMAKE_CURRENT_SOURCE_DIR@/@PROJECT_NAME@.default" DESTINATION "/etc/default" RENAME "@PROJECT_NAME@")
10
 endif()
16
 endif()

+ 42
- 10
postsrsd.c View File

193
     "   -f<port>       set port for the forward SRS lookup (default: 10001)\n"
193
     "   -f<port>       set port for the forward SRS lookup (default: 10001)\n"
194
     "   -r<port>       set port for the reverse SRS lookup (default: 10002)\n"
194
     "   -r<port>       set port for the reverse SRS lookup (default: 10002)\n"
195
     "   -p<pidfile>    write process ID to pidfile (default: none)\n"
195
     "   -p<pidfile>    write process ID to pidfile (default: none)\n"
196
+    "   -c<dir>        chroot to <dir> (default: none)\n"
196
     "   -u<user>       switch user id after port bind (default: none)\n"
197
     "   -u<user>       switch user id after port bind (default: none)\n"
197
     "   -t<seconds>    timeout for idle client connections (default: 1800)\n"
198
     "   -t<seconds>    timeout for idle client connections (default: 1800)\n"
198
     "   -D             fork into background\n"
199
     "   -D             fork into background\n"
212
   int opt, timeout = 1800, family = AF_UNSPEC;
213
   int opt, timeout = 1800, family = AF_UNSPEC;
213
   int daemonize = FALSE;
214
   int daemonize = FALSE;
214
   char *forward_service = NULL, *reverse_service = NULL,
215
   char *forward_service = NULL, *reverse_service = NULL,
215
-       *user = NULL, *domain = NULL;
216
+       *user = NULL, *domain = NULL, *chroot_dir = NULL;
216
   int forward_sock, reverse_sock;
217
   int forward_sock, reverse_sock;
217
   char *secret_file = NULL, *pid_file = NULL;
218
   char *secret_file = NULL, *pid_file = NULL;
218
   FILE *pf = NULL;
219
   FILE *pf = NULL;
220
+  struct passwd *pwd = NULL;
219
   char secret[1024];
221
   char secret[1024];
220
   char *tmp;
222
   char *tmp;
221
   srs_t *srs;
223
   srs_t *srs;
225
   tmp = strrchr(argv[0], '/');
227
   tmp = strrchr(argv[0], '/');
226
   if (tmp) self = strdup(tmp + 1); else self = strdup(argv[0]);
228
   if (tmp) self = strdup(tmp + 1); else self = strdup(argv[0]);
227
 
229
 
228
-  while ((opt = getopt(argc, argv, "46d:f:r:s:u:t:p:Dhv")) != -1) {
230
+  while ((opt = getopt(argc, argv, "46d:f:r:s:u:t:p:c:Dhv")) != -1) {
229
     switch (opt) {
231
     switch (opt) {
230
       case '?':
232
       case '?':
231
         return EXIT_FAILURE;
233
         return EXIT_FAILURE;
256
       case 'u':
258
       case 'u':
257
         user = strdup(optarg);
259
         user = strdup(optarg);
258
         break;
260
         break;
261
+      case 'c':
262
+	chroot_dir = strdup(optarg);
263
+	break;
259
       case 'D':
264
       case 'D':
260
         daemonize = TRUE;
265
         daemonize = TRUE;
261
         break;
266
         break;
267
 	return EXIT_SUCCESS;
272
 	return EXIT_SUCCESS;
268
     }
273
     }
269
   }
274
   }
275
+  if (domain == NULL) {
276
+    fprintf (stderr, "%s: You must set a home domain (-d)\n", self);
277
+    show_help();
278
+    return EXIT_FAILURE;
279
+  }
280
+
281
+  /* The stuff we do first may not be possible from within chroot or without privileges */
282
+
283
+  /* Open pid file for writing (the actual process ID is filled in later) */
270
   if (pid_file) {
284
   if (pid_file) {
271
     pf = fopen (pid_file, "w");
285
     pf = fopen (pid_file, "w");
272
     if (pf == NULL) {
286
     if (pf == NULL) {
274
       return EXIT_FAILURE;
288
       return EXIT_FAILURE;
275
     }
289
     }
276
   }
290
   }
277
-  if (domain == NULL) {
278
-    fprintf (stderr, "%s: You must set a home domain (-d)\n", self);
279
-    show_help();
280
-    return EXIT_FAILURE;
281
-  }
291
+  /* Read secret. The default installation makes this root accessible only. */
282
   if (secret_file != NULL) {
292
   if (secret_file != NULL) {
283
     size_t len;
293
     size_t len;
284
     FILE *fp = fopen(secret_file, "rb");
294
     FILE *fp = fopen(secret_file, "rb");
298
     show_help();
308
     show_help();
299
     return EXIT_FAILURE;
309
     return EXIT_FAILURE;
300
   }
310
   }
311
+  /* Bind ports. May require privileges if the config specifies ports below 1024 */
301
   if (forward_service != NULL) {
312
   if (forward_service != NULL) {
302
     forward_sock = bind_service(forward_service, family);
313
     forward_sock = bind_service(forward_service, family);
303
     free (forward_service);
314
     free (forward_service);
312
     reverse_sock = bind_service("10002", family);
323
     reverse_sock = bind_service("10002", family);
313
   }
324
   }
314
   if (reverse_sock < 0) return EXIT_FAILURE;
325
   if (reverse_sock < 0) return EXIT_FAILURE;
326
+
327
+  /* Open syslog now (NDELAY), because it may no longer reachable from chroot */
328
+  openlog (self, LOG_PID | LOG_NDELAY, LOG_MAIL);
329
+  /* We also have to lookup the uid of the unprivileged user for the same reason. */
315
   if (user) {
330
   if (user) {
316
-    struct passwd *pwd;
317
     errno = 0;
331
     errno = 0;
318
     pwd = getpwnam(user);
332
     pwd = getpwnam(user);
319
     if (pwd == NULL) {
333
     if (pwd == NULL) {
323
         fprintf (stderr, "%s: No such user: %s\n", self, user);
337
         fprintf (stderr, "%s: No such user: %s\n", self, user);
324
       return EXIT_FAILURE;
338
       return EXIT_FAILURE;
325
     }
339
     }
340
+  }
341
+  /* Now we can chroot, which again requires root privileges */
342
+  if (chroot_dir) {
343
+    if (chdir(chroot_dir) < 0) {
344
+      fprintf (stderr, "%s: Cannot change to chroot: %s\n", self, strerror(errno));
345
+      return EXIT_FAILURE;
346
+    }
347
+    if (chroot(chroot_dir) < 0) {
348
+      fprintf (stderr, "%s: Failed to enable chroot: %s\n", self, strerror(errno));
349
+      return EXIT_FAILURE;
350
+    }
351
+  }
352
+  /* Finally, we revert to the unprivileged user */
353
+  if (pwd) {
326
     if (setuid(pwd->pw_uid) < 0) {
354
     if (setuid(pwd->pw_uid) < 0) {
327
       fprintf (stderr, "%s: Failed to switch user id: %s\n", self, strerror(errno));
355
       fprintf (stderr, "%s: Failed to switch user id: %s\n", self, strerror(errno));
328
       return EXIT_FAILURE;
356
       return EXIT_FAILURE;
329
     }
357
     }
330
   }
358
   }
359
+  /* Standard double fork technique to disavow all knowledge about the controlling terminal */
331
   if (daemonize) {
360
   if (daemonize) {
332
     close(0); close(1); close(2);
361
     close(0); close(1); close(2);
333
     if (fork() != 0) return EXIT_SUCCESS;
362
     if (fork() != 0) return EXIT_SUCCESS;
334
     setsid();
363
     setsid();
335
     if (fork() != 0) return EXIT_SUCCESS;
364
     if (fork() != 0) return EXIT_SUCCESS;
336
   }
365
   }
366
+  /* Make note of our actual process ID */
337
   if (pf) {
367
   if (pf) {
338
     fprintf (pf, "%d", (int)getpid());
368
     fprintf (pf, "%d", (int)getpid());
339
     fclose (pf);
369
     fclose (pf);
340
   }
370
   }
341
 
371
 
342
-  openlog ("postsrsd", LOG_PID, LOG_MAIL);
343
   srs = srs_new();
372
   srs = srs_new();
344
   srs_add_secret (srs, secret);
373
   srs_add_secret (srs, secret);
345
   srs_set_separator (srs, '+');
374
   srs_set_separator (srs, '+');
356
     char keybuf[1024], *key;
385
     char keybuf[1024], *key;
357
 
386
 
358
     if (poll(fds, 2, 1000) < 0) {
387
     if (poll(fds, 2, 1000) < 0) {
359
-      if (!daemonize) fprintf (stderr, "%s: Poll failure: %s\n", self, strerror(errno));
388
+      if (daemonize)
389
+        syslog (LOG_MAIL | LOG_ERR, "Poll failure: %s", strerror(errno));
390
+      else
391
+        fprintf (stderr, "%s: Poll failure: %s\n", self, strerror(errno));
360
       return EXIT_FAILURE;
392
       return EXIT_FAILURE;
361
     }
393
     }
362
     for (i = 0; i < 2; ++i) {
394
     for (i = 0; i < 2; ++i) {

+ 3
- 1
postsrsd.init.in View File

33
 SRS_REVERSE_PORT=10002
33
 SRS_REVERSE_PORT=10002
34
 SRS_SECRET=/etc/@PROJECT_NAME@.secret
34
 SRS_SECRET=/etc/@PROJECT_NAME@.secret
35
 RUN_AS=nobody
35
 RUN_AS=nobody
36
+CHROOT=@CHROOT_DIR@
36
 
37
 
37
 # Read config file if it is present.
38
 # Read config file if it is present.
38
 if [ -r /etc/default/$NAME ]
39
 if [ -r /etc/default/$NAME ]
40
     . /etc/default/$NAME
41
     . /etc/default/$NAME
41
 fi
42
 fi
42
 
43
 
43
-POSTSRS_OPTS="-4 -f$SRS_FORWARD_PORT -r$SRS_REVERSE_PORT -d$SRS_DOMAIN -s$SRS_SECRET -u$RUN_AS -p$PIDFILE -D"
44
+POSTSRS_OPTS="-4 -f$SRS_FORWARD_PORT -r$SRS_REVERSE_PORT -d$SRS_DOMAIN -s$SRS_SECRET -u$RUN_AS -p$PIDFILE -c$CHROOT -D"
44
 
45
 
45
 test -r "$SRS_SECRET" -a -n "$SRS_DOMAIN" || exit 0
46
 test -r "$SRS_SECRET" -a -n "$SRS_DOMAIN" || exit 0
46
 
47
 
83
 esac
84
 esac
84
 
85
 
85
 exit $ret
86
 exit $ret
87
+

+ 3
- 1
postsrsd.upstart.in View File

13
 	SRS_REVERSE_PORT=10002
13
 	SRS_REVERSE_PORT=10002
14
 	SRS_SECRET=/etc/@PROJECT_NAME@.secret
14
 	SRS_SECRET=/etc/@PROJECT_NAME@.secret
15
 	RUN_AS=nobody
15
 	RUN_AS=nobody
16
+	CHROOT=@CHROOT_DIR@
16
 	if [ -r "$DEFAULTFILE" ]; then
17
 	if [ -r "$DEFAULTFILE" ]; then
17
 		. "$DEFAULTFILE"
18
 		. "$DEFAULTFILE"
18
 	fi
19
 	fi
19
-	exec @CMAKE_INSTALL_PREFIX@/sbin/@POSTSRSD@ -4 -f$SRS_FORWARD_PORT -r$SRS_REVERSE_PORT -d$SRS_DOMAIN -s$SRS_SECRET -u$RUN_AS
20
+	exec @CMAKE_INSTALL_PREFIX@/sbin/@POSTSRSD@ -4 -f$SRS_FORWARD_PORT -r$SRS_REVERSE_PORT -d$SRS_DOMAIN -s$SRS_SECRET -u$RUN_AS -c$CHROOT
20
 end script
21
 end script
22
+

Loading…
Cancel
Save