Sync with dpkg 1.19.5

This commit is contained in:
Steffen Nurpmeso 2019-02-23 23:17:03 +01:00 committed by Juergen Daubert
parent 6def8c726a
commit 50939d7190
2 changed files with 89 additions and 66 deletions

View File

@ -122,11 +122,14 @@ Note: using this matching option alone might cause unintended processes to
be acted on, if the old process terminated without being able to remove the be acted on, if the old process terminated without being able to remove the
\fIpid-file\fP. \fIpid-file\fP.
.IP .IP
\fBWarning:\fP Using this match option alone with a daemon that writes the \fBWarning:\fP using this match option with a world-writable pidfile or using
pidfile as an unprivileged user is a security risk, because if the daemon it alone with a daemon that writes the pidfile as an unprivileged (non-root)
gets compromised the contents of the pidfile cannot be trusted, and then user will be refused with an error (since version 1.19.3) as this is a
security risk, because either any user can write to it, or if the daemon
gets compromised, the contents of the pidfile cannot be trusted, and then
a privileged runner (such as an init script executed as root) would end up a privileged runner (such as an init script executed as root) would end up
acting on any system process. acting on any system process.
Using \fI/dev/null\fP is excempt from these checks.
.TP .TP
.BR \-x ", " \-\-exec " \fIexecutable\fP" .BR \-x ", " \-\-exec " \fIexecutable\fP"
Check for processes that are instances of this \fIexecutable\fP. The Check for processes that are instances of this \fIexecutable\fP. The

View File

@ -323,16 +323,15 @@ warning(const char *format, ...)
va_end(arglist); va_end(arglist);
} }
static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1) static void DPKG_ATTR_NORET DPKG_ATTR_VPRINTF(2)
fatal(const char *format, ...) fatalv(int errno_fatal, const char *format, va_list args)
{ {
va_list arglist; va_list args_copy;
int errno_fatal = errno;
fprintf(stderr, "%s: ", progname); fprintf(stderr, "%s: ", progname);
va_start(arglist, format); va_copy(args_copy, args);
vfprintf(stderr, format, arglist); vfprintf(stderr, format, args_copy);
va_end(arglist); va_end(args_copy);
if (errno_fatal) if (errno_fatal)
fprintf(stderr, " (%s)\n", strerror(errno_fatal)); fprintf(stderr, " (%s)\n", strerror(errno_fatal));
else else
@ -344,6 +343,24 @@ fatal(const char *format, ...)
exit(2); exit(2);
} }
static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1)
fatal(const char *format, ...)
{
va_list args;
va_start(args, format);
fatalv(0, format, args);
}
static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1)
fatale(const char *format, ...)
{
va_list args;
va_start(args, format);
fatalv(errno, format, args);
}
#define BUG(...) bug(__FILE__, __LINE__, __func__, __VA_ARGS__) #define BUG(...) bug(__FILE__, __LINE__, __func__, __VA_ARGS__)
static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(4) static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(4)
@ -371,7 +388,7 @@ xmalloc(int size)
ptr = malloc(size); ptr = malloc(size);
if (ptr) if (ptr)
return ptr; return ptr;
fatal("malloc(%d) failed", size); fatale("malloc(%d) failed", size);
} }
static char * static char *
@ -382,7 +399,7 @@ xstrndup(const char *str, size_t n)
new_str = strndup(str, n); new_str = strndup(str, n);
if (new_str) if (new_str)
return new_str; return new_str;
fatal("strndup(%s, %zu) failed", str, n); fatale("strndup(%s, %zu) failed", str, n);
} }
static void static void
@ -391,12 +408,12 @@ timespec_gettime(struct timespec *ts)
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \ #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \
defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0 defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0
if (clock_gettime(CLOCK_MONOTONIC, ts) < 0) if (clock_gettime(CLOCK_MONOTONIC, ts) < 0)
fatal("clock_gettime failed"); fatale("clock_gettime failed");
#else #else
struct timeval tv; struct timeval tv;
if (gettimeofday(&tv, NULL) != 0) if (gettimeofday(&tv, NULL) != 0)
fatal("gettimeofday failed"); fatale("gettimeofday failed");
ts->tv_sec = tv.tv_sec; ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * NANOSEC_IN_MICROSEC; ts->tv_nsec = tv.tv_usec * NANOSEC_IN_MICROSEC;
@ -448,10 +465,10 @@ parse_unsigned(const char *string, int base, int *value_r)
long value; long value;
char *endptr; char *endptr;
errno = 0;
if (!string[0]) if (!string[0])
return -1; return -1;
errno = 0;
value = strtol(string, &endptr, base); value = strtol(string, &endptr, base);
if (string == endptr || *endptr != '\0' || errno != 0) if (string == endptr || *endptr != '\0' || errno != 0)
return -1; return -1;
@ -486,7 +503,7 @@ detach_controlling_tty(void)
return; return;
if (ioctl(tty_fd, TIOCNOTTY, 0) != 0) if (ioctl(tty_fd, TIOCNOTTY, 0) != 0)
fatal("unable to detach controlling tty"); fatale("unable to detach controlling tty");
close(tty_fd); close(tty_fd);
#endif #endif
@ -552,18 +569,18 @@ setup_socket_name(const char *suffix)
} }
if (asprintf(&notify_sockdir, "%s/%s.XXXXXX", basedir, suffix) < 0) if (asprintf(&notify_sockdir, "%s/%s.XXXXXX", basedir, suffix) < 0)
fatal("cannot allocate socket directory name"); fatale("cannot allocate socket directory name");
if (mkdtemp(notify_sockdir) == NULL) if (mkdtemp(notify_sockdir) == NULL)
fatal("cannot create socket directory %s", notify_sockdir); fatale("cannot create socket directory %s", notify_sockdir);
atexit(cleanup_socket_dir); atexit(cleanup_socket_dir);
if (chown(notify_sockdir, runas_uid, runas_gid)) if (chown(notify_sockdir, runas_uid, runas_gid))
fatal("cannot change socket directory ownership"); fatale("cannot change socket directory ownership");
if (asprintf(&notify_socket, "%s/notify", notify_sockdir) < 0) if (asprintf(&notify_socket, "%s/notify", notify_sockdir) < 0)
fatal("cannot allocate socket name"); fatale("cannot allocate socket name");
setenv("NOTIFY_SOCKET", notify_socket, 1); setenv("NOTIFY_SOCKET", notify_socket, 1);
@ -590,16 +607,16 @@ create_notify_socket(void)
/* Create notification socket. */ /* Create notification socket. */
fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
if (fd < 0) if (fd < 0)
fatal("cannot create notification socket"); fatale("cannot create notification socket");
/* We could set SOCK_CLOEXEC instead, but then we would need to /* We could set SOCK_CLOEXEC instead, but then we would need to
* check whether the socket call failed, try and then do this anyway, * check whether the socket call failed, try and then do this anyway,
* when we have no threading problems to worry about. */ * when we have no threading problems to worry about. */
flags = fcntl(fd, F_GETFD); flags = fcntl(fd, F_GETFD);
if (flags < 0) if (flags < 0)
fatal("cannot read fd flags for notification socket"); fatale("cannot read fd flags for notification socket");
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
fatal("cannot set close-on-exec flag for notification socket"); fatale("cannot set close-on-exec flag for notification socket");
sockname = setup_socket_name(".s-s-d-notify"); sockname = setup_socket_name(".s-s-d-notify");
@ -611,15 +628,15 @@ create_notify_socket(void)
rc = bind(fd, &su, sizeof(su)); rc = bind(fd, &su, sizeof(su));
if (rc < 0) if (rc < 0)
fatal("cannot bind to notification socket"); fatale("cannot bind to notification socket");
rc = chmod(su.sun_path, 0660); rc = chmod(su.sun_path, 0660);
if (rc < 0) if (rc < 0)
fatal("cannot change notification socket permissions"); fatale("cannot change notification socket permissions");
rc = chown(su.sun_path, runas_uid, runas_gid); rc = chown(su.sun_path, runas_uid, runas_gid);
if (rc < 0) if (rc < 0)
fatal("cannot change notification socket ownership"); fatale("cannot change notification socket ownership");
/* XXX: Verify we are talking to an expected child? Although it is not /* XXX: Verify we are talking to an expected child? Although it is not
* clear whether this is feasible given the knowledge we have got. */ * clear whether this is feasible given the knowledge we have got. */
@ -653,7 +670,7 @@ wait_for_notify(int fd)
/* Catch non-restartable errors, that is, not signals nor /* Catch non-restartable errors, that is, not signals nor
* kernel out of resources. */ * kernel out of resources. */
if (rc < 0 && (errno != EINTR && errno != EAGAIN)) if (rc < 0 && (errno != EINTR && errno != EAGAIN))
fatal("cannot monitor notification socket for activity"); fatale("cannot monitor notification socket for activity");
/* Timed-out. */ /* Timed-out. */
if (rc == 0) if (rc == 0)
@ -678,7 +695,7 @@ wait_for_notify(int fd)
nrecv = recv(fd, buf, sizeof(buf), 0); nrecv = recv(fd, buf, sizeof(buf), 0);
if (nrecv < 0 && (errno != EINTR && errno != EAGAIN)) if (nrecv < 0 && (errno != EINTR && errno != EAGAIN))
fatal("cannot receive notification packet"); fatale("cannot receive notification packet");
if (nrecv < 0) if (nrecv < 0)
break; break;
@ -694,7 +711,7 @@ wait_for_notify(int fd)
int extend_usec = 0; int extend_usec = 0;
if (parse_unsigned(line + 20, 10, &extend_usec) != 0) if (parse_unsigned(line + 20, 10, &extend_usec) != 0)
fatal("cannot parse extended timeout notification %s", line); fatale("cannot parse extended timeout notification %s", line);
/* Reset the current timeout. */ /* Reset the current timeout. */
timeout.tv_sec = extend_usec / 1000L; timeout.tv_sec = extend_usec / 1000L;
@ -707,9 +724,9 @@ wait_for_notify(int fd)
int suberrno = 0; int suberrno = 0;
if (parse_unsigned(line + 6, 10, &suberrno) != 0) if (parse_unsigned(line + 6, 10, &suberrno) != 0)
fatal("cannot parse errno notification %s", line); fatale("cannot parse errno notification %s", line);
errno = suberrno; errno = suberrno;
fatal("program failed to initialize"); fatale("program failed to initialize");
} else if (strcmp(line, "READY=1") == 0) { } else if (strcmp(line, "READY=1") == 0) {
debug("-> Notification => ready for service.\n"); debug("-> Notification => ready for service.\n");
return; return;
@ -734,19 +751,19 @@ write_pidfile(const char *filename, pid_t pid)
fp = fdopen(fd, "w"); fp = fdopen(fd, "w");
if (fp == NULL) if (fp == NULL)
fatal("unable to open pidfile '%s' for writing", filename); fatale("unable to open pidfile '%s' for writing", filename);
fprintf(fp, "%d\n", pid); fprintf(fp, "%d\n", pid);
if (fclose(fp)) if (fclose(fp))
fatal("unable to close pidfile '%s'", filename); fatale("unable to close pidfile '%s'", filename);
} }
static void static void
remove_pidfile(const char *filename) remove_pidfile(const char *filename)
{ {
if (unlink(filename) < 0 && errno != ENOENT) if (unlink(filename) < 0 && errno != ENOENT)
fatal("cannot remove pidfile '%s'", filename); fatale("cannot remove pidfile '%s'", filename);
} }
static void static void
@ -764,14 +781,14 @@ daemonize(void)
sigemptyset(&mask); sigemptyset(&mask);
sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1) if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1)
fatal("cannot block SIGCHLD"); fatale("cannot block SIGCHLD");
if (notify_await) if (notify_await)
notify_fd = create_notify_socket(); notify_fd = create_notify_socket();
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
fatal("unable to do first fork"); fatale("unable to do first fork");
else if (pid) { /* First Parent. */ else if (pid) { /* First Parent. */
/* Wait for the second parent to exit, so that if we need to /* Wait for the second parent to exit, so that if we need to
* perform any actions there, like creating a pidfile, we do * perform any actions there, like creating a pidfile, we do
@ -792,11 +809,11 @@ daemonize(void)
/* Create a new session. */ /* Create a new session. */
if (setsid() < 0) if (setsid() < 0)
fatal("cannot set session ID"); fatale("cannot set session ID");
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
fatal("unable to do second fork"); fatale("unable to do second fork");
else if (pid) { /* Second parent. */ else if (pid) { /* Second parent. */
/* Set a default umask for dumb programs, which might get /* Set a default umask for dumb programs, which might get
* overridden by the --umask option later on, so that we get * overridden by the --umask option later on, so that we get
@ -811,7 +828,7 @@ daemonize(void)
} }
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
fatal("cannot restore signal mask"); fatale("cannot restore signal mask");
debug("Detaching complete...\n"); debug("Detaching complete...\n");
} }
@ -1037,7 +1054,7 @@ parse_proc_schedule(const char *string)
if (string[policy_len] == ':' && if (string[policy_len] == ':' &&
parse_unsigned(string + policy_len + 1, 10, &prio) != 0) parse_unsigned(string + policy_len + 1, 10, &prio) != 0)
fatal("invalid process scheduler priority"); fatale("invalid process scheduler priority");
proc_sched = xmalloc(sizeof(*proc_sched)); proc_sched = xmalloc(sizeof(*proc_sched));
proc_sched->policy_name = policy_str; proc_sched->policy_name = policy_str;
@ -1069,7 +1086,7 @@ parse_io_schedule(const char *string)
if (string[class_len] == ':' && if (string[class_len] == ':' &&
parse_unsigned(string + class_len + 1, 10, &prio) != 0) parse_unsigned(string + class_len + 1, 10, &prio) != 0)
fatal("invalid IO scheduler priority"); fatale("invalid IO scheduler priority");
io_sched = xmalloc(sizeof(*io_sched)); io_sched = xmalloc(sizeof(*io_sched));
io_sched->policy_name = class_str; io_sched->policy_name = class_str;
@ -1101,7 +1118,7 @@ set_proc_schedule(struct res_schedule *sched)
param.sched_priority = sched->priority; param.sched_priority = sched->priority;
if (sched_setscheduler(getpid(), sched->policy, &param) == -1) if (sched_setscheduler(getpid(), sched->policy, &param) == -1)
fatal("unable to set process scheduler"); fatale("unable to set process scheduler");
#endif #endif
} }
@ -1487,7 +1504,7 @@ setup_options(void)
fullexecname = execname; fullexecname = execname;
if (stat(fullexecname, &exec_stat)) if (stat(fullexecname, &exec_stat))
fatal("unable to stat %s", fullexecname); fatale("unable to stat %s", fullexecname);
if (fullexecname != execname) if (fullexecname != execname)
free(fullexecname); free(fullexecname);
@ -1498,7 +1515,7 @@ setup_options(void)
pw = getpwnam(userspec); pw = getpwnam(userspec);
if (!pw) if (!pw)
fatal("user '%s' not found", userspec); fatale("user '%s' not found", userspec);
user_id = pw->pw_uid; user_id = pw->pw_uid;
} }
@ -1508,7 +1525,7 @@ setup_options(void)
gr = getgrnam(changegroup); gr = getgrnam(changegroup);
if (!gr) if (!gr)
fatal("group '%s' not found", changegroup); fatale("group '%s' not found", changegroup);
changegroup = gr->gr_name; changegroup = gr->gr_name;
runas_gid = gr->gr_gid; runas_gid = gr->gr_gid;
} }
@ -1521,7 +1538,7 @@ setup_options(void)
else else
pw = getpwnam(changeuser); pw = getpwnam(changeuser);
if (!pw) if (!pw)
fatal("user '%s' not found", changeuser); fatale("user '%s' not found", changeuser);
changeuser = pw->pw_name; changeuser = pw->pw_name;
runas_uid = pw->pw_uid; runas_uid = pw->pw_uid;
if (changegroup == NULL) { if (changegroup == NULL) {
@ -2226,7 +2243,7 @@ pid_is_running(pid_t pid)
else if (errno == ESRCH) else if (errno == ESRCH)
return false; return false;
else else
fatal("error checking pid %u status", pid); fatale("error checking pid %u status", pid);
} }
#endif #endif
@ -2267,22 +2284,25 @@ do_pidfile(const char *name)
* contents cannot be trusted, because the daemon might have * contents cannot be trusted, because the daemon might have
* been compromised. * been compromised.
* *
* If the pidfile is world-writable we refuse to parse it.
*
* If we got /dev/null specified as the pidfile, we ignore the * If we got /dev/null specified as the pidfile, we ignore the
* checks, as this is being used to run processes no matter * checks, as this is being used to run processes no matter
* what. */ * what. */
if (match_mode == MATCH_PIDFILE && if (strcmp(name, "/dev/null") != 0) {
strcmp(name, "/dev/null") != 0) {
struct stat st; struct stat st;
int fd = fileno(f); int fd = fileno(f);
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
fatal("cannot stat pidfile %s", name); fatale("cannot stat pidfile %s", name);
if ((st.st_uid != getuid() && st.st_uid != 0) || if (match_mode == MATCH_PIDFILE &&
(st.st_gid != getgid() && st.st_gid != 0)) ((st.st_uid != getuid() && st.st_uid != 0) ||
(st.st_gid != getgid() && st.st_gid != 0)))
fatal("matching only on non-root pidfile %s is insecure", name); fatal("matching only on non-root pidfile %s is insecure", name);
if (st.st_mode & 0002) if (st.st_mode & 0002)
fatal("matching only on world-writable pidfile %s is insecure", name); fatal("matching on world-writable pidfile %s is insecure", name);
} }
if (fscanf(f, "%d", &pid) == 1) if (fscanf(f, "%d", &pid) == 1)
@ -2298,7 +2318,7 @@ do_pidfile(const char *name)
} else if (errno == ENOENT) } else if (errno == ENOENT)
return STATUS_DEAD; return STATUS_DEAD;
else else
fatal("unable to open pidfile %s", name); fatale("unable to open pidfile %s", name);
} }
#if defined(OS_Linux) || defined(OS_Solaris) || defined(OS_AIX) #if defined(OS_Linux) || defined(OS_Solaris) || defined(OS_AIX)
@ -2313,7 +2333,7 @@ do_procinit(void)
procdir = opendir("/proc"); procdir = opendir("/proc");
if (!procdir) if (!procdir)
fatal("unable to opendir /proc"); fatale("unable to opendir /proc");
foundany = 0; foundany = 0;
while ((entry = readdir(procdir)) != NULL) { while ((entry = readdir(procdir)) != NULL) {
@ -2548,12 +2568,12 @@ do_start(int argc, char **argv)
if (background && close_io) { if (background && close_io) {
devnull_fd = open("/dev/null", O_RDWR); devnull_fd = open("/dev/null", O_RDWR);
if (devnull_fd < 0) if (devnull_fd < 0)
fatal("unable to open '%s'", "/dev/null"); fatale("unable to open '%s'", "/dev/null");
} }
if (nicelevel) { if (nicelevel) {
errno = 0; errno = 0;
if ((nice(nicelevel) == -1) && (errno != 0)) if ((nice(nicelevel) == -1) && (errno != 0))
fatal("unable to alter nice level by %i", nicelevel); fatale("unable to alter nice level by %i", nicelevel);
} }
if (proc_sched) if (proc_sched)
set_proc_schedule(proc_sched); set_proc_schedule(proc_sched);
@ -2563,19 +2583,19 @@ do_start(int argc, char **argv)
umask(umask_value); umask(umask_value);
if (changeroot != NULL) { if (changeroot != NULL) {
if (chdir(changeroot) < 0) if (chdir(changeroot) < 0)
fatal("unable to chdir() to %s", changeroot); fatale("unable to chdir() to %s", changeroot);
if (chroot(changeroot) < 0) if (chroot(changeroot) < 0)
fatal("unable to chroot() to %s", changeroot); fatale("unable to chroot() to %s", changeroot);
} }
if (chdir(changedir) < 0) if (chdir(changedir) < 0)
fatal("unable to chdir() to %s", changedir); fatale("unable to chdir() to %s", changedir);
rgid = getgid(); rgid = getgid();
ruid = getuid(); ruid = getuid();
if (changegroup != NULL) { if (changegroup != NULL) {
if (rgid != (gid_t)runas_gid) if (rgid != (gid_t)runas_gid)
if (setgid(runas_gid)) if (setgid(runas_gid))
fatal("unable to set gid to %d", runas_gid); fatale("unable to set gid to %d", runas_gid);
} }
if (changeuser != NULL) { if (changeuser != NULL) {
/* We assume that if our real user and group are the same as /* We assume that if our real user and group are the same as
@ -2583,12 +2603,12 @@ do_start(int argc, char **argv)
* will be already in place. */ * will be already in place. */
if (rgid != (gid_t)runas_gid || ruid != (uid_t)runas_uid) if (rgid != (gid_t)runas_gid || ruid != (uid_t)runas_uid)
if (initgroups(changeuser, runas_gid)) if (initgroups(changeuser, runas_gid))
fatal("unable to set initgroups() with gid %d", fatale("unable to set initgroups() with gid %d",
runas_gid); runas_gid);
if (ruid != (uid_t)runas_uid) if (ruid != (uid_t)runas_uid)
if (setuid(runas_uid)) if (setuid(runas_uid))
fatal("unable to set uid to %s", changeuser); fatale("unable to set uid to %s", changeuser);
} }
if (background && close_io) { if (background && close_io) {
@ -2603,7 +2623,7 @@ do_start(int argc, char **argv)
close(i); close(i);
} }
execv(startas, argv); execv(startas, argv);
fatal("unable to start %s", startas); fatale("unable to start %s", startas);
} }
static void static void
@ -2665,7 +2685,7 @@ set_what_stop(const char *format, ...)
va_end(arglist); va_end(arglist);
if (rc < 0) if (rc < 0)
fatal("cannot allocate formatted string"); fatale("cannot allocate formatted string");
} }
/* /*
@ -2730,7 +2750,7 @@ do_stop_timeout(int timeout, int *n_killed, int *n_notkilled)
rc = pselect(0, NULL, NULL, NULL, &interval, NULL); rc = pselect(0, NULL, NULL, NULL, &interval, NULL);
if (rc < 0 && errno != EINTR) if (rc < 0 && errno != EINTR)
fatal("select() failed for pause"); fatale("select() failed for pause");
} }
} }