|  | @@ -193,6 +193,7 @@ static void show_help ()
 | 
		
	
		
			
			| 193 | 193 |      "   -f<port>       set port for the forward SRS lookup (default: 10001)\n"
 | 
		
	
		
			
			| 194 | 194 |      "   -r<port>       set port for the reverse SRS lookup (default: 10002)\n"
 | 
		
	
		
			
			| 195 | 195 |      "   -p<pidfile>    write process ID to pidfile (default: none)\n"
 | 
		
	
		
			
			|  | 196 | +    "   -c<dir>        chroot to <dir> (default: none)\n"
 | 
		
	
		
			
			| 196 | 197 |      "   -u<user>       switch user id after port bind (default: none)\n"
 | 
		
	
		
			
			| 197 | 198 |      "   -t<seconds>    timeout for idle client connections (default: 1800)\n"
 | 
		
	
		
			
			| 198 | 199 |      "   -D             fork into background\n"
 | 
		
	
	
		
			
			|  | @@ -212,10 +213,11 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 212 | 213 |    int opt, timeout = 1800, family = AF_UNSPEC;
 | 
		
	
		
			
			| 213 | 214 |    int daemonize = FALSE;
 | 
		
	
		
			
			| 214 | 215 |    char *forward_service = NULL, *reverse_service = NULL,
 | 
		
	
		
			
			| 215 |  | -       *user = NULL, *domain = NULL;
 | 
		
	
		
			
			|  | 216 | +       *user = NULL, *domain = NULL, *chroot_dir = NULL;
 | 
		
	
		
			
			| 216 | 217 |    int forward_sock, reverse_sock;
 | 
		
	
		
			
			| 217 | 218 |    char *secret_file = NULL, *pid_file = NULL;
 | 
		
	
		
			
			| 218 | 219 |    FILE *pf = NULL;
 | 
		
	
		
			
			|  | 220 | +  struct passwd *pwd = NULL;
 | 
		
	
		
			
			| 219 | 221 |    char secret[1024];
 | 
		
	
		
			
			| 220 | 222 |    char *tmp;
 | 
		
	
		
			
			| 221 | 223 |    srs_t *srs;
 | 
		
	
	
		
			
			|  | @@ -225,7 +227,7 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 225 | 227 |    tmp = strrchr(argv[0], '/');
 | 
		
	
		
			
			| 226 | 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 | 231 |      switch (opt) {
 | 
		
	
		
			
			| 230 | 232 |        case '?':
 | 
		
	
		
			
			| 231 | 233 |          return EXIT_FAILURE;
 | 
		
	
	
		
			
			|  | @@ -256,6 +258,9 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 256 | 258 |        case 'u':
 | 
		
	
		
			
			| 257 | 259 |          user = strdup(optarg);
 | 
		
	
		
			
			| 258 | 260 |          break;
 | 
		
	
		
			
			|  | 261 | +      case 'c':
 | 
		
	
		
			
			|  | 262 | +	chroot_dir = strdup(optarg);
 | 
		
	
		
			
			|  | 263 | +	break;
 | 
		
	
		
			
			| 259 | 264 |        case 'D':
 | 
		
	
		
			
			| 260 | 265 |          daemonize = TRUE;
 | 
		
	
		
			
			| 261 | 266 |          break;
 | 
		
	
	
		
			
			|  | @@ -267,6 +272,15 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 267 | 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 | 284 |    if (pid_file) {
 | 
		
	
		
			
			| 271 | 285 |      pf = fopen (pid_file, "w");
 | 
		
	
		
			
			| 272 | 286 |      if (pf == NULL) {
 | 
		
	
	
		
			
			|  | @@ -274,11 +288,7 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 274 | 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 | 292 |    if (secret_file != NULL) {
 | 
		
	
		
			
			| 283 | 293 |      size_t len;
 | 
		
	
		
			
			| 284 | 294 |      FILE *fp = fopen(secret_file, "rb");
 | 
		
	
	
		
			
			|  | @@ -298,6 +308,7 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 298 | 308 |      show_help();
 | 
		
	
		
			
			| 299 | 309 |      return EXIT_FAILURE;
 | 
		
	
		
			
			| 300 | 310 |    }
 | 
		
	
		
			
			|  | 311 | +  /* Bind ports. May require privileges if the config specifies ports below 1024 */
 | 
		
	
		
			
			| 301 | 312 |    if (forward_service != NULL) {
 | 
		
	
		
			
			| 302 | 313 |      forward_sock = bind_service(forward_service, family);
 | 
		
	
		
			
			| 303 | 314 |      free (forward_service);
 | 
		
	
	
		
			
			|  | @@ -312,8 +323,11 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 312 | 323 |      reverse_sock = bind_service("10002", family);
 | 
		
	
		
			
			| 313 | 324 |    }
 | 
		
	
		
			
			| 314 | 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 | 330 |    if (user) {
 | 
		
	
		
			
			| 316 |  | -    struct passwd *pwd;
 | 
		
	
		
			
			| 317 | 331 |      errno = 0;
 | 
		
	
		
			
			| 318 | 332 |      pwd = getpwnam(user);
 | 
		
	
		
			
			| 319 | 333 |      if (pwd == NULL) {
 | 
		
	
	
		
			
			|  | @@ -323,23 +337,38 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 323 | 337 |          fprintf (stderr, "%s: No such user: %s\n", self, user);
 | 
		
	
		
			
			| 324 | 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 | 354 |      if (setuid(pwd->pw_uid) < 0) {
 | 
		
	
		
			
			| 327 | 355 |        fprintf (stderr, "%s: Failed to switch user id: %s\n", self, strerror(errno));
 | 
		
	
		
			
			| 328 | 356 |        return EXIT_FAILURE;
 | 
		
	
		
			
			| 329 | 357 |      }
 | 
		
	
		
			
			| 330 | 358 |    }
 | 
		
	
		
			
			|  | 359 | +  /* Standard double fork technique to disavow all knowledge about the controlling terminal */
 | 
		
	
		
			
			| 331 | 360 |    if (daemonize) {
 | 
		
	
		
			
			| 332 | 361 |      close(0); close(1); close(2);
 | 
		
	
		
			
			| 333 | 362 |      if (fork() != 0) return EXIT_SUCCESS;
 | 
		
	
		
			
			| 334 | 363 |      setsid();
 | 
		
	
		
			
			| 335 | 364 |      if (fork() != 0) return EXIT_SUCCESS;
 | 
		
	
		
			
			| 336 | 365 |    }
 | 
		
	
		
			
			|  | 366 | +  /* Make note of our actual process ID */
 | 
		
	
		
			
			| 337 | 367 |    if (pf) {
 | 
		
	
		
			
			| 338 | 368 |      fprintf (pf, "%d", (int)getpid());
 | 
		
	
		
			
			| 339 | 369 |      fclose (pf);
 | 
		
	
		
			
			| 340 | 370 |    }
 | 
		
	
		
			
			| 341 | 371 |  
 | 
		
	
		
			
			| 342 |  | -  openlog ("postsrsd", LOG_PID, LOG_MAIL);
 | 
		
	
		
			
			| 343 | 372 |    srs = srs_new();
 | 
		
	
		
			
			| 344 | 373 |    srs_add_secret (srs, secret);
 | 
		
	
		
			
			| 345 | 374 |    srs_set_separator (srs, '+');
 | 
		
	
	
		
			
			|  | @@ -356,7 +385,10 @@ int main (int argc, char **argv)
 | 
		
	
		
			
			| 356 | 385 |      char keybuf[1024], *key;
 | 
		
	
		
			
			| 357 | 386 |  
 | 
		
	
		
			
			| 358 | 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 | 392 |        return EXIT_FAILURE;
 | 
		
	
		
			
			| 361 | 393 |      }
 | 
		
	
		
			
			| 362 | 394 |      for (i = 0; i < 2; ++i) {
 |