X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-sysdeps-util-unix.c;h=26fcb5bc99cf7ef48e3484baa25181210c456167;hb=1962bd7b478b21ae054b836f41e944f2a45f6621;hp=d104e41c31797d2cb55439073f4a533183302336;hpb=a05ef3b12927aa40b4d322f8d0efa7bd3490d033;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-sysdeps-util-unix.c b/dbus/dbus-sysdeps-util-unix.c index d104e41..26fcb5b 100644 --- a/dbus/dbus-sysdeps-util-unix.c +++ b/dbus/dbus-sysdeps-util-unix.c @@ -26,6 +26,7 @@ #include "dbus-sysdeps.h" #include "dbus-sysdeps-unix.h" #include "dbus-internals.h" +#include "dbus-list.h" #include "dbus-pipe.h" #include "dbus-protocol.h" #include "dbus-string.h" @@ -50,10 +51,6 @@ #include #include -#ifdef HAVE_SYSLOG_H -#include -#endif - #ifdef HAVE_SYS_SYSLIMITS_H #include #endif @@ -89,7 +86,7 @@ _dbus_become_daemon (const DBusString *pidfile, { const char *s; pid_t child_pid; - int dev_null_fd; + DBusEnsureStandardFdsFlags flags; _dbus_verbose ("Becoming a daemon...\n"); @@ -114,23 +111,18 @@ _dbus_become_daemon (const DBusString *pidfile, case 0: _dbus_verbose ("in child, closing std file descriptors\n"); - /* silently ignore failures here, if someone - * doesn't have /dev/null we may as well try - * to continue anyhow - */ - - dev_null_fd = open ("/dev/null", O_RDWR); - if (dev_null_fd >= 0) + flags = DBUS_FORCE_STDIN_NULL | DBUS_FORCE_STDOUT_NULL; + s = _dbus_getenv ("DBUS_DEBUG_OUTPUT"); + + if (s == NULL || *s == '\0') + flags |= DBUS_FORCE_STDERR_NULL; + else + _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n"); + + if (!_dbus_ensure_standard_fds (flags, &s)) { - dup2 (dev_null_fd, 0); - dup2 (dev_null_fd, 1); - - s = _dbus_getenv ("DBUS_DEBUG_OUTPUT"); - if (s == NULL || *s == '\0') - dup2 (dev_null_fd, 2); - else - _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n"); - close (dev_null_fd); + _dbus_warn ("%s: %s", s, _dbus_strerror (errno)); + _exit (1); } if (!keep_umask) @@ -354,7 +346,7 @@ _dbus_change_to_daemon_user (const char *user, * is going to work then setgroups() should also work. */ if (setgroups (0, NULL) < 0) - _dbus_warn ("Failed to drop supplementary groups: %s\n", + _dbus_warn ("Failed to drop supplementary groups: %s", _dbus_strerror (errno)); /* Set GID first, or the setuid may remove our permission @@ -380,139 +372,152 @@ _dbus_change_to_daemon_user (const char *user, } #endif /* !HAVE_LIBAUDIT */ +#ifdef HAVE_SETRLIMIT -/** - * Attempt to ensure that the current process can open - * at least @p limit file descriptors. - * - * If @p limit is lower than the current, it will not be - * lowered. No error is returned if the request can - * not be satisfied. - * - * @param limit number of file descriptors +/* We assume that if we have setrlimit, we also have getrlimit and + * struct rlimit. */ -void -_dbus_request_file_descriptor_limit (unsigned int limit) + +struct DBusRLimit { + struct rlimit lim; +}; + +DBusRLimit * +_dbus_rlimit_save_fd_limit (DBusError *error) { -#ifdef HAVE_SETRLIMIT - struct rlimit lim; - struct rlimit target_lim; - - /* No point to doing this practically speaking - * if we're not uid 0. We expect the system - * bus to use this before we change UID, and - * the session bus takes the Linux default - * of 1024 for both cur and max. - */ - if (getuid () != 0) - return; + DBusRLimit *self; - if (getrlimit (RLIMIT_NOFILE, &lim) < 0) - return; + self = dbus_new0 (DBusRLimit, 1); - if (lim.rlim_cur >= limit) - return; + if (self == NULL) + { + _DBUS_SET_OOM (error); + return NULL; + } - /* Ignore "maximum limit", assume we have the "superuser" - * privileges. On Linux this is CAP_SYS_RESOURCE. - */ - target_lim.rlim_cur = target_lim.rlim_max = limit; - /* Also ignore errors; if we fail, we will at least work - * up to whatever limit we had, which seems better than - * just outright aborting. - * - * However, in the future we should probably log this so OS builders - * have a chance to notice any misconfiguration like dbus-daemon - * being started without CAP_SYS_RESOURCE. - */ - setrlimit (RLIMIT_NOFILE, &target_lim); -#endif + if (getrlimit (RLIMIT_NOFILE, &self->lim) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to get fd limit: %s", _dbus_strerror (errno)); + dbus_free (self); + return NULL; + } + + return self; } -void -_dbus_init_system_log (dbus_bool_t is_daemon) +/* Enough fds that we shouldn't run out, even if several uids work + * together to carry out a denial-of-service attack. This happens to be + * the same number that systemd < 234 would normally use. */ +#define ENOUGH_FDS 65536 + +dbus_bool_t +_dbus_rlimit_raise_fd_limit (DBusError *error) { -#ifdef HAVE_SYSLOG_H - int logopts = LOG_PID; + struct rlimit old, lim; -#if HAVE_DECL_LOG_PERROR -#ifdef HAVE_SYSTEMD - if (!is_daemon || sd_booted () <= 0) -#endif - logopts |= LOG_PERROR; -#endif + if (getrlimit (RLIMIT_NOFILE, &lim) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to get fd limit: %s", _dbus_strerror (errno)); + return FALSE; + } - openlog ("dbus", logopts, LOG_DAEMON); -#endif + old = lim; + + if (getuid () == 0) + { + /* We are privileged, so raise the soft limit to at least + * ENOUGH_FDS, and the hard limit to at least the desired soft + * limit. This assumes we can exercise CAP_SYS_RESOURCE on Linux, + * or other OSs' equivalents. */ + if (lim.rlim_cur != RLIM_INFINITY && + lim.rlim_cur < ENOUGH_FDS) + lim.rlim_cur = ENOUGH_FDS; + + if (lim.rlim_max != RLIM_INFINITY && + lim.rlim_max < lim.rlim_cur) + lim.rlim_max = lim.rlim_cur; + } + + /* Raise the soft limit to match the hard limit, which we can do even + * if we are unprivileged. In particular, systemd >= 240 will normally + * set rlim_cur to 1024 and rlim_max to 512*1024, recent Debian + * versions end up setting rlim_cur to 1024 and rlim_max to 1024*1024, + * and older and non-systemd Linux systems would typically set rlim_cur + * to 1024 and rlim_max to 4096. */ + if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max) + lim.rlim_cur = lim.rlim_max; + + /* Early-return if there is nothing to do. */ + if (lim.rlim_max == old.rlim_max && + lim.rlim_cur == old.rlim_cur) + return TRUE; + + if (setrlimit (RLIMIT_NOFILE, &lim) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set fd limit to %lu: %s", + (unsigned long) lim.rlim_cur, + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; } -/** - * Log a message to the system log file (e.g. syslog on Unix). - * - * @param severity a severity value - * @param msg a printf-style format string - */ -void -_dbus_system_log (DBusSystemLogSeverity severity, const char *msg, ...) +dbus_bool_t +_dbus_rlimit_restore_fd_limit (DBusRLimit *saved, + DBusError *error) { - va_list args; + if (setrlimit (RLIMIT_NOFILE, &saved->lim) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to restore old fd limit: %s", + _dbus_strerror (errno)); + return FALSE; + } - va_start (args, msg); + return TRUE; +} - _dbus_system_logv (severity, msg, args); +#else /* !HAVE_SETRLIMIT */ - va_end (args); +static void +fd_limit_not_supported (DBusError *error) +{ + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "cannot change fd limit on this platform"); } -/** - * Log a message to the system log file (e.g. syslog on Unix). - * - * @param severity a severity value - * @param msg a printf-style format string - * @param args arguments for the format string - * - * If the FATAL severity is given, this function will terminate the program - * with an error code. - */ -void -_dbus_system_logv (DBusSystemLogSeverity severity, const char *msg, va_list args) +DBusRLimit * +_dbus_rlimit_save_fd_limit (DBusError *error) { - va_list tmp; -#ifdef HAVE_SYSLOG_H - int flags; - switch (severity) - { - case DBUS_SYSTEM_LOG_INFO: - flags = LOG_DAEMON | LOG_NOTICE; - break; - case DBUS_SYSTEM_LOG_SECURITY: - flags = LOG_AUTH | LOG_NOTICE; - break; - case DBUS_SYSTEM_LOG_FATAL: - flags = LOG_DAEMON|LOG_CRIT; - break; - default: - return; - } + fd_limit_not_supported (error); + return NULL; +} - DBUS_VA_COPY (tmp, args); - vsyslog (flags, msg, tmp); - va_end (tmp); -#endif +dbus_bool_t +_dbus_rlimit_raise_fd_limit (DBusError *error) +{ + fd_limit_not_supported (error); + return FALSE; +} + +dbus_bool_t +_dbus_rlimit_restore_fd_limit (DBusRLimit *saved, + DBusError *error) +{ + fd_limit_not_supported (error); + return FALSE; +} -#if !defined(HAVE_SYSLOG_H) || !HAVE_DECL_LOG_PERROR - { - /* vsyslog() won't write to stderr, so we'd better do it */ - DBUS_VA_COPY (tmp, args); - fprintf (stderr, "dbus[" DBUS_PID_FORMAT "]: ", _dbus_getpid ()); - vfprintf (stderr, msg, tmp); - fputc ('\n', stderr); - va_end (tmp); - } #endif - if (severity == DBUS_SYSTEM_LOG_FATAL) - exit (1); +void +_dbus_rlimit_free (DBusRLimit *lim) +{ + dbus_free (lim); } /** Installs a UNIX signal handler @@ -555,7 +560,7 @@ dbus_bool_t _dbus_user_at_console (const char *username, DBusError *error) { - +#ifdef DBUS_CONSOLE_AUTH_DIR DBusString u, f; dbus_bool_t result; @@ -586,6 +591,9 @@ _dbus_user_at_console (const char *username, _dbus_string_free (&f); return result; +#else + return FALSE; +#endif } @@ -1087,7 +1095,7 @@ string_squash_nonprintable (DBusString *str) unsigned char *buf; int i, len; - buf = _dbus_string_get_data (str); + buf = _dbus_string_get_udata (str); len = _dbus_string_get_length (str); for (i = 0; i < len; i++) @@ -1182,23 +1190,177 @@ fail: return FALSE; } -/* - * replaces the term DBUS_PREFIX in configure_time_path by the - * current dbus installation directory. On unix this function is a noop +/** + * Replace the DBUS_PREFIX in the given path, in-place, by the + * current D-Bus installation directory. On Unix this function + * does nothing, successfully. * - * @param configure_time_path - * @return real path + * @param path path to edit + * @return #FALSE on OOM */ -const char * -_dbus_replace_install_prefix (const char *configure_time_path) +dbus_bool_t +_dbus_replace_install_prefix (DBusString *path) { - return configure_time_path; + return TRUE; +} + +static dbus_bool_t +ensure_owned_directory (const char *label, + const DBusString *string, + dbus_bool_t create, + DBusError *error) +{ + const char *dir = _dbus_string_get_const_data (string); + struct stat buf; + + if (create && !_dbus_ensure_directory (string, error)) + return FALSE; + + /* + * The stat()-based checks in this function are to protect against + * mistakes, not malice. We are working in a directory that is meant + * to be trusted; but if a user has used `su` or similar to escalate + * their privileges without correctly clearing the environment, the + * XDG_RUNTIME_DIR in the environment might still be the user's + * and not root's. We don't want to write root-owned files into that + * directory, so just warn and don't provide support for transient + * services in that case. + * + * In particular, we use stat() and not lstat() so that if we later + * decide to use a different directory name for transient services, + * we can drop in a compatibility symlink without breaking older + * libdbus. + */ + + if (stat (dir, &buf) != 0) + { + int saved_errno = errno; + + dbus_set_error (error, _dbus_error_from_errno (saved_errno), + "%s \"%s\" not available: %s", label, dir, + _dbus_strerror (saved_errno)); + return FALSE; + } + + if (!S_ISDIR (buf.st_mode)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, "%s \"%s\" is not a directory", + label, dir); + return FALSE; + } + + if (buf.st_uid != geteuid ()) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "%s \"%s\" is owned by uid %ld, not our uid %ld", + label, dir, (long) buf.st_uid, (long) geteuid ()); + return FALSE; + } + + /* This is just because we have the stat() results already, so we might + * as well check opportunistically. */ + if ((S_IWOTH | S_IWGRP) & buf.st_mode) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "%s \"%s\" can be written by others (mode 0%o)", + label, dir, buf.st_mode); + return FALSE; + } + + return TRUE; } #define DBUS_UNIX_STANDARD_SESSION_SERVICEDIR "/dbus-1/services" #define DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR "/dbus-1/system-services" /** + * Returns the standard directories for a session bus to look for + * transient service activation files. + * + * @param dirs the directory list we are returning + * @returns #FALSE on error + */ +dbus_bool_t +_dbus_set_up_transient_session_servicedirs (DBusList **dirs, + DBusError *error) +{ + const char *xdg_runtime_dir; + DBusString services; + DBusString dbus1; + DBusString xrd; + dbus_bool_t ret = FALSE; + char *data = NULL; + + if (!_dbus_string_init (&dbus1)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_init (&services)) + { + _dbus_string_free (&dbus1); + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_init (&xrd)) + { + _dbus_string_free (&dbus1); + _dbus_string_free (&services); + _DBUS_SET_OOM (error); + return FALSE; + } + + xdg_runtime_dir = _dbus_getenv ("XDG_RUNTIME_DIR"); + + /* Not an error, we just can't have transient session services */ + if (xdg_runtime_dir == NULL) + { + _dbus_verbose ("XDG_RUNTIME_DIR is unset: transient session services " + "not available here\n"); + ret = TRUE; + goto out; + } + + if (!_dbus_string_append (&xrd, xdg_runtime_dir) || + !_dbus_string_append_printf (&dbus1, "%s/dbus-1", + xdg_runtime_dir) || + !_dbus_string_append_printf (&services, "%s/dbus-1/services", + xdg_runtime_dir)) + { + _DBUS_SET_OOM (error); + goto out; + } + + if (!ensure_owned_directory ("XDG_RUNTIME_DIR", &xrd, FALSE, error) || + !ensure_owned_directory ("XDG_RUNTIME_DIR subdirectory", &dbus1, TRUE, + error) || + !ensure_owned_directory ("XDG_RUNTIME_DIR subdirectory", &services, + TRUE, error)) + goto out; + + if (!_dbus_string_steal_data (&services, &data) || + !_dbus_list_append (dirs, data)) + { + _DBUS_SET_OOM (error); + goto out; + } + + _dbus_verbose ("Transient service directory is %s\n", data); + /* Ownership was transferred to @dirs */ + data = NULL; + ret = TRUE; + +out: + _dbus_string_free (&dbus1); + _dbus_string_free (&services); + _dbus_string_free (&xrd); + dbus_free (data); + return ret; +} + +/** * Returns the standard directories for a session bus to look for service * activation files * @@ -1334,27 +1496,71 @@ _dbus_get_standard_system_servicedirs (DBusList **dirs) } /** - * Append the absolute path of the system.conf file + * Get the absolute path of the system.conf file * (there is no system bus on Windows so this can just * return FALSE and print a warning or something) * - * @param str the string to append to + * @param str the string to append to, which must be empty on entry * @returns #FALSE if no memory */ dbus_bool_t -_dbus_append_system_config_file (DBusString *str) +_dbus_get_system_config_file (DBusString *str) { + _dbus_assert (_dbus_string_get_length (str) == 0); + return _dbus_string_append (str, DBUS_SYSTEM_CONFIG_FILE); } /** - * Append the absolute path of the session.conf file. + * Get the absolute path of the session.conf file. * - * @param str the string to append to + * @param str the string to append to, which must be empty on entry * @returns #FALSE if no memory */ dbus_bool_t -_dbus_append_session_config_file (DBusString *str) +_dbus_get_session_config_file (DBusString *str) { + _dbus_assert (_dbus_string_get_length (str) == 0); + return _dbus_string_append (str, DBUS_SESSION_CONFIG_FILE); } + +#ifdef DBUS_ENABLE_EMBEDDED_TESTS + +/* + * Set uid to a machine-readable authentication identity (numeric Unix + * uid or ConvertSidToStringSid-style Windows SID) that is likely to exist, + * and differs from the identity of the current process. + * + * @param uid Populated with a machine-readable authentication identity + * on success + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_test_append_different_uid (DBusString *uid) +{ + if (geteuid () == 0) + return _dbus_string_append (uid, "65534"); + else + return _dbus_string_append (uid, "0"); +} + +/* + * Set uid to a human-readable authentication identity (login name) + * that is likely to exist, and differs from the identity of the current + * process. This function currently only exists on Unix platforms. + * + * @param uid Populated with a machine-readable authentication identity + * on success + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_test_append_different_username (DBusString *username) +{ + if (geteuid () == 0) + return _dbus_string_append (username, "nobody"); + else + return _dbus_string_append (username, "root"); +} + +#endif