X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-sysdeps-unix.c;h=07b761b4686e63c151cdb35caa21ccc895f6fce0;hb=757b80b9711d9733798c927495d74c7323e95400;hp=22868e2ebb8dced605d7564f2a90e03ad71631c7;hpb=88498b706a39bbe520f9591d8d52b54fb1f8e378;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 22868e2..07b761b 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef HAVE_ERRNO_H #include @@ -71,6 +72,9 @@ #ifdef HAVE_GETPEERUCRED #include #endif +#ifdef HAVE_ALLOCA_H +#include +#endif #ifdef HAVE_ADT #include @@ -78,6 +82,10 @@ #include "sd-daemon.h" +#if !DBUS_USE_SYNC +#include +#endif + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -866,6 +874,96 @@ _dbus_connect_unix_socket (const char *path, } /** + * Creates a UNIX domain socket and connects it to the specified + * process to execute. + * + * This will set FD_CLOEXEC for the socket returned. + * + * @param path the path to the executable + * @param argv the argument list for the process to execute. + * argv[0] typically is identical to the path of the executable + * @param error return location for error code + * @returns connection file descriptor or -1 on error + */ +int +_dbus_connect_exec (const char *path, + char *const argv[], + DBusError *error) +{ + int fds[2]; + pid_t pid; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("connecting to process %s\n", path); + + if (socketpair (AF_UNIX, SOCK_STREAM +#ifdef SOCK_CLOEXEC + |SOCK_CLOEXEC +#endif + , 0, fds) < 0) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to create socket pair: %s", + _dbus_strerror (errno)); + return -1; + } + + _dbus_fd_set_close_on_exec (fds[0]); + _dbus_fd_set_close_on_exec (fds[1]); + + pid = fork (); + if (pid < 0) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to fork() to call %s: %s", + path, _dbus_strerror (errno)); + close (fds[0]); + close (fds[1]); + return -1; + } + + if (pid == 0) + { + /* child */ + close (fds[0]); + + dup2 (fds[1], STDIN_FILENO); + dup2 (fds[1], STDOUT_FILENO); + + if (fds[1] != STDIN_FILENO && + fds[1] != STDOUT_FILENO) + close (fds[1]); + + /* Inherit STDERR and the controlling terminal from the + parent */ + + _dbus_close_all (); + + execvp (path, argv); + + fprintf (stderr, "Failed to execute process %s: %s\n", path, _dbus_strerror (errno)); + + _exit(1); + } + + /* parent */ + close (fds[1]); + + if (!_dbus_set_fd_nonblocking (fds[0], error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + + close (fds[0]); + return -1; + } + + return fds[0]; +} + +/** * Enables or disables the reception of credentials on the given socket during * the next message transmission. This is only effective if the #LOCAL_CREDS * system feature exists, in which case the other side of the connection does @@ -1772,7 +1870,7 @@ _dbus_read_credentials_socket (int client_fd, if (pid_read != DBUS_PID_UNSET) { - if (!_dbus_credentials_add_unix_pid (credentials, pid_read)) + if (!_dbus_credentials_add_pid (credentials, pid_read)) { _DBUS_SET_OOM (error); return FALSE; @@ -2227,7 +2325,7 @@ _dbus_credentials_add_from_current_process (DBusCredentials *credentials) _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t)); _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t)); - if (!_dbus_credentials_add_unix_pid(credentials, _dbus_getpid())) + if (!_dbus_credentials_add_pid(credentials, _dbus_getpid())) return FALSE; if (!_dbus_credentials_add_unix_uid(credentials, _dbus_geteuid())) return FALSE; @@ -2334,7 +2432,12 @@ _dbus_parse_uid (const DBusString *uid_str, } #if !DBUS_USE_SYNC -_DBUS_DEFINE_GLOBAL_LOCK (atomic); +/* To be thread-safe by default on platforms that don't necessarily have + * atomic operations (notably Debian armel, which is armv4t), we must + * use a mutex that can be initialized statically, like this. + * GLib >= 2.32 uses a similar system. + */ +static pthread_mutex_t atomic_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /** @@ -2350,10 +2453,12 @@ _dbus_atomic_inc (DBusAtomic *atomic) return __sync_add_and_fetch(&atomic->value, 1)-1; #else dbus_int32_t res; - _DBUS_LOCK (atomic); + + pthread_mutex_lock (&atomic_mutex); res = atomic->value; atomic->value += 1; - _DBUS_UNLOCK (atomic); + pthread_mutex_unlock (&atomic_mutex); + return res; #endif } @@ -2372,10 +2477,11 @@ _dbus_atomic_dec (DBusAtomic *atomic) #else dbus_int32_t res; - _DBUS_LOCK (atomic); + pthread_mutex_lock (&atomic_mutex); res = atomic->value; atomic->value -= 1; - _DBUS_UNLOCK (atomic); + pthread_mutex_unlock (&atomic_mutex); + return res; #endif } @@ -2396,24 +2502,14 @@ _dbus_atomic_get (DBusAtomic *atomic) #else dbus_int32_t res; - _DBUS_LOCK (atomic); + pthread_mutex_lock (&atomic_mutex); res = atomic->value; - _DBUS_UNLOCK (atomic); + pthread_mutex_unlock (&atomic_mutex); + return res; #endif } -#ifdef DBUS_BUILD_TESTS -/** Gets our GID - * @returns process GID - */ -dbus_gid_t -_dbus_getgid (void) -{ - return getgid (); -} -#endif - /** * Wrapper for poll(). * @@ -2519,11 +2615,11 @@ _dbus_poll (DBusPollFD *fds, * available, to avoid problems when the system time changes. * * @param tv_sec return location for number of seconds - * @param tv_usec return location for number of microseconds (thousandths) + * @param tv_usec return location for number of microseconds */ void -_dbus_get_current_time (long *tv_sec, - long *tv_usec) +_dbus_get_monotonic_time (long *tv_sec, + long *tv_usec) { #ifdef HAVE_MONOTONIC_CLOCK struct timespec ts; @@ -2546,6 +2642,27 @@ _dbus_get_current_time (long *tv_sec, } /** + * Get current time, as in gettimeofday(). Never uses the monotonic + * clock. + * + * @param tv_sec return location for number of seconds + * @param tv_usec return location for number of microseconds + */ +void +_dbus_get_real_time (long *tv_sec, + long *tv_usec) +{ + struct timeval t; + + gettimeofday (&t, NULL); + + if (tv_sec) + *tv_sec = t.tv_sec; + if (tv_usec) + *tv_usec = t.tv_usec; +} + +/** * Creates a directory; succeeds if the directory * is created or already existed. * @@ -3018,8 +3135,11 @@ _dbus_printf_string_upper_bound (const char *format, char static_buf[1024]; int bufsize = sizeof (static_buf); int len; + va_list args_copy; - len = vsnprintf (static_buf, bufsize, format, args); + DBUS_VA_COPY (args_copy, args); + len = vsnprintf (static_buf, bufsize, format, args_copy); + va_end (args_copy); /* If vsnprintf() returned non-negative, then either the string fits in * static_buf, or this OS has the POSIX and C99 behaviour where vsnprintf @@ -3035,8 +3155,12 @@ _dbus_printf_string_upper_bound (const char *format, * or the real length could be coincidentally the same. Which is it? * If vsnprintf returns the truncated length, we'll go to the slow * path. */ - if (vsnprintf (static_buf, 1, format, args) == 1) + DBUS_VA_COPY (args_copy, args); + + if (vsnprintf (static_buf, 1, format, args_copy) == 1) len = -1; + + va_end (args_copy); } /* If vsnprintf() returned negative, we have to do more work. @@ -3052,7 +3176,10 @@ _dbus_printf_string_upper_bound (const char *format, if (buf == NULL) return -1; - len = vsnprintf (buf, bufsize, format, args); + DBUS_VA_COPY (args_copy, args); + len = vsnprintf (buf, bufsize, format, args_copy); + va_end (args_copy); + dbus_free (buf); /* If the reported length is exactly the buffer size, round up to the @@ -3103,6 +3230,7 @@ _dbus_get_tmpdir(void) return tmpdir; } +#if defined(DBUS_ENABLE_X11_AUTOLAUNCH) || defined(DBUS_ENABLE_LAUNCHD) /** * Execute a subprocess, returning up to 1024 bytes of output * into @p result. @@ -3135,7 +3263,6 @@ _read_subprocess_line_argv (const char *progpath, int ret; int status; int orig_len; - int i; dbus_bool_t retval; sigset_t new_set, old_set; @@ -3188,7 +3315,6 @@ _read_subprocess_line_argv (const char *progpath, if (pid == 0) { /* child process */ - int maxfds; int fd; fd = open ("/dev/null", O_RDWR); @@ -3201,26 +3327,15 @@ _read_subprocess_line_argv (const char *progpath, /* set-up stdXXX */ close (result_pipe[READ_END]); close (errors_pipe[READ_END]); - close (0); /* close stdin */ - close (1); /* close stdout */ - close (2); /* close stderr */ - if (dup2 (fd, 0) == -1) + if (dup2 (fd, 0) == -1) /* setup stdin */ _exit (1); - if (dup2 (result_pipe[WRITE_END], 1) == -1) + if (dup2 (result_pipe[WRITE_END], 1) == -1) /* setup stdout */ _exit (1); - if (dup2 (errors_pipe[WRITE_END], 2) == -1) + if (dup2 (errors_pipe[WRITE_END], 2) == -1) /* setup stderr */ _exit (1); - maxfds = sysconf (_SC_OPEN_MAX); - /* Pick something reasonable if for some reason sysconf - * says unlimited. - */ - if (maxfds < 0) - maxfds = 1024; - /* close all inherited fds */ - for (i = 3; i < maxfds; i++) - close (i); + _dbus_close_all (); sigprocmask (SIG_SETMASK, &old_set, NULL); @@ -3317,6 +3432,7 @@ _read_subprocess_line_argv (const char *progpath, return retval; } +#endif /** * Returns the address of a new session bus. @@ -3344,6 +3460,13 @@ _dbus_get_autolaunch_address (const char *scope, DBusString uuid; dbus_bool_t retval; + if (_dbus_check_setuid ()) + { + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to autolaunch when setuid"); + return FALSE; + } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); retval = FALSE; @@ -3461,6 +3584,13 @@ _dbus_lookup_launchd_socket (DBusString *socket_path, _DBUS_ASSERT_ERROR_IS_CLEAR (error); + if (_dbus_check_setuid ()) + { + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to find launchd socket when setuid"); + return FALSE; + } + i = 0; argv[i] = "launchctl"; ++i; @@ -3501,6 +3631,13 @@ _dbus_lookup_session_address_launchd (DBusString *address, DBusError *error) dbus_bool_t valid_socket; DBusString socket_path; + if (_dbus_check_setuid ()) + { + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to find launchd socket when setuid"); + return FALSE; + } + if (!_dbus_string_init (&socket_path)) { _DBUS_SET_OOM (error); @@ -3691,54 +3828,27 @@ _dbus_get_standard_session_servicedirs (DBusList **dirs) dbus_bool_t _dbus_get_standard_system_servicedirs (DBusList **dirs) { - const char *xdg_data_dirs; - DBusString servicedir_path; - - if (!_dbus_string_init (&servicedir_path)) - return FALSE; - - xdg_data_dirs = _dbus_getenv ("XDG_DATA_DIRS"); - - if (xdg_data_dirs != NULL) - { - if (!_dbus_string_append (&servicedir_path, xdg_data_dirs)) - goto oom; - - if (!_dbus_string_append (&servicedir_path, ":")) - goto oom; - } - else - { - if (!_dbus_string_append (&servicedir_path, "/usr/local/share:/usr/share:")) - goto oom; - } - /* - * Add configured datadir to defaults. This may be the same as one - * of the XDG directories. However, the config parser should take - * care of the duplicates. + * DBUS_DATADIR may be the same as one of the standard directories. However, + * the config parser should take care of the duplicates. * * Also, append /lib as counterpart of /usr/share on the root * directory (the root directory does not know /share), in order to * facilitate early boot system bus activation where /usr might not * be available. */ - if (!_dbus_string_append (&servicedir_path, - DBUS_DATADIR":" - "/lib:")) - goto oom; - - if (!_dbus_split_paths_and_append (&servicedir_path, - DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR, - dirs)) - goto oom; + static const char standard_search_path[] = + "/usr/local/share:" + "/usr/share:" + DBUS_DATADIR ":" + "/lib"; + DBusString servicedir_path; - _dbus_string_free (&servicedir_path); - return TRUE; + _dbus_string_init_const (&servicedir_path, standard_search_path); - oom: - _dbus_string_free (&servicedir_path); - return FALSE; + return _dbus_split_paths_and_append (&servicedir_path, + DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR, + dirs); } /** @@ -3958,4 +4068,189 @@ _dbus_replace_install_prefix (const char *configure_time_path) return configure_time_path; } +/** + * Closes all file descriptors except the first three (i.e. stdin, + * stdout, stderr). + */ +void +_dbus_close_all (void) +{ + int maxfds, i; + +#ifdef __linux__ + DIR *d; + + /* On Linux we can optimize this a bit if /proc is available. If it + isn't available, fall back to the brute force way. */ + + d = opendir ("/proc/self/fd"); + if (d) + { + for (;;) + { + struct dirent buf, *de; + int k, fd; + long l; + char *e = NULL; + + k = readdir_r (d, &buf, &de); + if (k != 0 || !de) + break; + + if (de->d_name[0] == '.') + continue; + + errno = 0; + l = strtol (de->d_name, &e, 10); + if (errno != 0 || e == NULL || *e != '\0') + continue; + + fd = (int) l; + if (fd < 3) + continue; + + if (fd == dirfd (d)) + continue; + + close (fd); + } + + closedir (d); + return; + } +#endif + + maxfds = sysconf (_SC_OPEN_MAX); + + /* Pick something reasonable if for some reason sysconf says + * unlimited. + */ + if (maxfds < 0) + maxfds = 1024; + + /* close all inherited fds */ + for (i = 3; i < maxfds; i++) + close (i); +} + +/** + * **NOTE**: If you modify this function, please also consider making + * the corresponding change in GLib. See + * glib/gutils.c:g_check_setuid(). + * + * Returns TRUE if the current process was executed as setuid (or an + * equivalent __libc_enable_secure is available). See: + * http://osdir.com/ml/linux.lfs.hardened/2007-04/msg00032.html + */ +dbus_bool_t +_dbus_check_setuid (void) +{ + /* TODO: get __libc_enable_secure exported from glibc. + * See http://www.openwall.com/lists/owl-dev/2012/08/14/1 + */ +#if 0 && defined(HAVE_LIBC_ENABLE_SECURE) + { + /* See glibc/include/unistd.h */ + extern int __libc_enable_secure; + return __libc_enable_secure; + } +#elif defined(HAVE_ISSETUGID) + /* BSD: http://www.freebsd.org/cgi/man.cgi?query=issetugid&sektion=2 */ + return issetugid (); +#else + uid_t ruid, euid, suid; /* Real, effective and saved user ID's */ + gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */ + + static dbus_bool_t check_setuid_initialised; + static dbus_bool_t is_setuid; + + if (_DBUS_UNLIKELY (!check_setuid_initialised)) + { +#ifdef HAVE_GETRESUID + if (getresuid (&ruid, &euid, &suid) != 0 || + getresgid (&rgid, &egid, &sgid) != 0) +#endif /* HAVE_GETRESUID */ + { + suid = ruid = getuid (); + sgid = rgid = getgid (); + euid = geteuid (); + egid = getegid (); + } + + check_setuid_initialised = TRUE; + is_setuid = (ruid != euid || ruid != suid || + rgid != egid || rgid != sgid); + + } + return is_setuid; +#endif +} + +/** + * Read the address from the socket and append it to the string + * + * @param fd the socket + * @param address + * @param error return location for error code + */ +dbus_bool_t +_dbus_append_address_from_socket (int fd, + DBusString *address, + DBusError *error) +{ + union { + struct sockaddr sa; + struct sockaddr_storage storage; + struct sockaddr_un un; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } socket; + char hostip[INET6_ADDRSTRLEN]; + int size = sizeof (socket); + + if (getsockname (fd, &socket.sa, &size)) + goto err; + + switch (socket.sa.sa_family) + { + case AF_UNIX: + if (socket.un.sun_path[0]=='\0') + { + if (_dbus_string_append_printf (address, "unix:abstract=%s", &(socket.un.sun_path[1]))) + return TRUE; + } + else + { + if (_dbus_string_append_printf (address, "unix:path=%s", socket.un.sun_path)) + return TRUE; + } + break; + case AF_INET: + if (inet_ntop (AF_INET, &socket.ipv4.sin_addr, hostip, sizeof (hostip))) + if (_dbus_string_append_printf (address, "tcp:family=ipv4,host=%s,port=%u", + hostip, ntohs (socket.ipv4.sin_port))) + return TRUE; + break; +#ifdef AF_INET6 + case AF_INET6: + if (inet_ntop (AF_INET6, &socket.ipv6.sin6_addr, hostip, sizeof (hostip))) + if (_dbus_string_append_printf (address, "tcp:family=ipv6,host=%s,port=%u", + hostip, ntohs (socket.ipv6.sin6_port))) + return TRUE; + break; +#endif + default: + dbus_set_error (error, + _dbus_error_from_errno (EINVAL), + "Failed to read address from socket: Unknown socket type."); + return FALSE; + } + err: + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to open socket: %s", + _dbus_strerror (errno)); + return FALSE; +} + /* tests in dbus-sysdeps-util.c */