summaryrefslogtreecommitdiff
path: root/dbus/dbus-sysdeps-unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/dbus-sysdeps-unix.c')
-rw-r--r--dbus/dbus-sysdeps-unix.c598
1 files changed, 443 insertions, 155 deletions
diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c
index fe891ab7..dc0418cb 100644
--- a/dbus/dbus-sysdeps-unix.c
+++ b/dbus/dbus-sysdeps-unix.c
@@ -29,6 +29,7 @@
#include "dbus-sysdeps-unix.h"
#include "dbus-threads.h"
#include "dbus-protocol.h"
+#include "dbus-file.h"
#include "dbus-transport.h"
#include "dbus-string.h"
#include "dbus-userdb.h"
@@ -53,6 +54,7 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <netdb.h>
#include <grp.h>
#include <arpa/inet.h>
@@ -80,7 +82,9 @@
#include <bsm/adt.h>
#endif
-#include "sd-daemon.h"
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
#if !DBUS_USE_SYNC
#include <pthread.h>
@@ -130,6 +134,9 @@
#endif /* Solaris */
+static dbus_bool_t _dbus_set_fd_nonblocking (int fd,
+ DBusError *error);
+
static dbus_bool_t
_dbus_open_socket (int *fd_p,
int domain,
@@ -198,10 +205,10 @@ _dbus_open_unix_socket (int *fd,
* @returns #FALSE if error is set
*/
dbus_bool_t
-_dbus_close_socket (int fd,
+_dbus_close_socket (DBusSocket fd,
DBusError *error)
{
- return _dbus_close (fd, error);
+ return _dbus_close (fd.fd, error);
}
/**
@@ -214,11 +221,11 @@ _dbus_close_socket (int fd,
* @returns number of bytes appended to the string
*/
int
-_dbus_read_socket (int fd,
+_dbus_read_socket (DBusSocket fd,
DBusString *buffer,
int count)
{
- return _dbus_read (fd, buffer, count);
+ return _dbus_read (fd.fd, buffer, count);
}
/**
@@ -232,7 +239,7 @@ _dbus_read_socket (int fd,
* @returns the number of bytes written or -1 on error
*/
int
-_dbus_write_socket (int fd,
+_dbus_write_socket (DBusSocket fd,
const DBusString *buffer,
int start,
int len)
@@ -245,7 +252,7 @@ _dbus_write_socket (int fd,
again:
- bytes_written = send (fd, data, len, MSG_NOSIGNAL);
+ bytes_written = send (fd.fd, data, len, MSG_NOSIGNAL);
if (bytes_written < 0 && errno == EINTR)
goto again;
@@ -253,7 +260,7 @@ _dbus_write_socket (int fd,
return bytes_written;
#else
- return _dbus_write (fd, buffer, start, len);
+ return _dbus_write (fd.fd, buffer, start, len);
#endif
}
@@ -270,7 +277,7 @@ _dbus_write_socket (int fd,
* @returns number of bytes appended to string
*/
int
-_dbus_read_socket_with_unix_fds (int fd,
+_dbus_read_socket_with_unix_fds (DBusSocket fd,
DBusString *buffer,
int count,
int *fds,
@@ -328,7 +335,7 @@ _dbus_read_socket_with_unix_fds (int fd,
again:
- bytes_read = recvmsg(fd, &m, 0
+ bytes_read = recvmsg (fd.fd, &m, 0
#ifdef MSG_CMSG_CLOEXEC
|MSG_CMSG_CLOEXEC
#endif
@@ -430,7 +437,7 @@ _dbus_read_socket_with_unix_fds (int fd,
}
int
-_dbus_write_socket_with_unix_fds(int fd,
+_dbus_write_socket_with_unix_fds(DBusSocket fd,
const DBusString *buffer,
int start,
int len,
@@ -451,7 +458,7 @@ _dbus_write_socket_with_unix_fds(int fd,
}
int
-_dbus_write_socket_with_unix_fds_two(int fd,
+_dbus_write_socket_with_unix_fds_two(DBusSocket fd,
const DBusString *buffer1,
int start1,
int len1,
@@ -511,7 +518,7 @@ _dbus_write_socket_with_unix_fds_two(int fd,
again:
- bytes_written = sendmsg (fd, &m, 0
+ bytes_written = sendmsg (fd.fd, &m, 0
#if HAVE_DECL_MSG_NOSIGNAL
|MSG_NOSIGNAL
#endif
@@ -543,7 +550,7 @@ _dbus_write_socket_with_unix_fds_two(int fd,
* @returns total bytes written from both buffers, or -1 on error
*/
int
-_dbus_write_socket_two (int fd,
+_dbus_write_socket_two (DBusSocket fd,
const DBusString *buffer1,
int start1,
int len1,
@@ -586,7 +593,7 @@ _dbus_write_socket_two (int fd,
again:
- bytes_written = sendmsg (fd, &m, MSG_NOSIGNAL);
+ bytes_written = sendmsg (fd.fd, &m, MSG_NOSIGNAL);
if (bytes_written < 0 && errno == EINTR)
goto again;
@@ -594,17 +601,11 @@ _dbus_write_socket_two (int fd,
return bytes_written;
#else
- return _dbus_write_two (fd, buffer1, start1, len1,
+ return _dbus_write_two (fd.fd, buffer1, start1, len1,
buffer2, start2, len2);
#endif
}
-dbus_bool_t
-_dbus_socket_is_invalid (int fd)
-{
- return fd < 0 ? TRUE : FALSE;
-}
-
/**
* Thin wrapper around the read() system call that appends
* the data it reads to the DBusString buffer. It appends
@@ -1102,7 +1103,7 @@ _dbus_listen_unix_socket (const char *path,
if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
{
dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
- "Abstract socket name too long\n");
+ "Socket name too long\n");
_dbus_close (listen_fd, NULL);
return -1;
}
@@ -1156,12 +1157,13 @@ _dbus_listen_unix_socket (const char *path,
* @returns the number of file descriptors
*/
int
-_dbus_listen_systemd_sockets (int **fds,
- DBusError *error)
+_dbus_listen_systemd_sockets (DBusSocket **fds,
+ DBusError *error)
{
+#ifdef HAVE_SYSTEMD
int r, n;
- unsigned fd;
- int *new_fds;
+ int fd;
+ DBusSocket *new_fds;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -1203,7 +1205,7 @@ _dbus_listen_systemd_sockets (int **fds,
/* OK, the file descriptors are all good, so let's take posession of
them then. */
- new_fds = dbus_new (int, n);
+ new_fds = dbus_new (DBusSocket, n);
if (!new_fds)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
@@ -1219,7 +1221,7 @@ _dbus_listen_systemd_sockets (int **fds,
goto fail;
}
- new_fds[fd - SD_LISTEN_FDS_START] = fd;
+ new_fds[fd - SD_LISTEN_FDS_START].fd = fd;
}
*fds = new_fds;
@@ -1234,6 +1236,11 @@ _dbus_listen_systemd_sockets (int **fds,
dbus_free (new_fds);
return -1;
+#else
+ dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+ "dbus was compiled without systemd support");
+ return -1;
+#endif
}
/**
@@ -1249,7 +1256,7 @@ _dbus_listen_systemd_sockets (int **fds,
* @param error return location for error code
* @returns connection file descriptor or -1 on error
*/
-int
+DBusSocket
_dbus_connect_tcp_socket (const char *host,
const char *port,
const char *family,
@@ -1258,7 +1265,7 @@ _dbus_connect_tcp_socket (const char *host,
return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error);
}
-int
+DBusSocket
_dbus_connect_tcp_socket_with_nonce (const char *host,
const char *port,
const char *family,
@@ -1266,7 +1273,8 @@ _dbus_connect_tcp_socket_with_nonce (const char *host,
DBusError *error)
{
int saved_errno = 0;
- int fd = -1, res;
+ DBusSocket fd = DBUS_SOCKET_INIT;
+ int res;
struct addrinfo hints;
struct addrinfo *ai, *tmp;
@@ -1285,7 +1293,7 @@ _dbus_connect_tcp_socket_with_nonce (const char *host,
dbus_set_error (error,
DBUS_ERROR_BAD_ADDRESS,
"Unknown address family %s", family);
- return -1;
+ return _dbus_socket_get_invalid ();
}
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
@@ -1297,25 +1305,25 @@ _dbus_connect_tcp_socket_with_nonce (const char *host,
_dbus_error_from_errno (errno),
"Failed to lookup host/port: \"%s:%s\": %s (%d)",
host, port, gai_strerror(res), res);
- return -1;
+ return _dbus_socket_get_invalid ();
}
tmp = ai;
while (tmp)
{
- if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
+ if (!_dbus_open_socket (&fd.fd, tmp->ai_family, SOCK_STREAM, 0, error))
{
freeaddrinfo(ai);
_DBUS_ASSERT_ERROR_IS_SET(error);
- return -1;
+ return _dbus_socket_get_invalid ();
}
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
- if (connect (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
+ if (connect (fd.fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
{
saved_errno = errno;
- _dbus_close(fd, NULL);
- fd = -1;
+ _dbus_close (fd.fd, NULL);
+ fd.fd = -1;
tmp = tmp->ai_next;
continue;
}
@@ -1324,13 +1332,13 @@ _dbus_connect_tcp_socket_with_nonce (const char *host,
}
freeaddrinfo(ai);
- if (fd == -1)
+ if (fd.fd == -1)
{
dbus_set_error (error,
_dbus_error_from_errno (saved_errno),
"Failed to connect to socket \"%s:%s\" %s",
host, port, _dbus_strerror(saved_errno));
- return -1;
+ return _dbus_socket_get_invalid ();
}
if (noncefile != NULL)
@@ -1342,16 +1350,16 @@ _dbus_connect_tcp_socket_with_nonce (const char *host,
_dbus_string_free (&noncefileStr);
if (!ret)
- {
- _dbus_close (fd, NULL);
- return -1;
+ {
+ _dbus_close (fd.fd, NULL);
+ return _dbus_socket_get_invalid ();
}
}
- if (!_dbus_set_fd_nonblocking (fd, error))
+ if (!_dbus_set_fd_nonblocking (fd.fd, error))
{
- _dbus_close (fd, NULL);
- return -1;
+ _dbus_close (fd.fd, NULL);
+ return _dbus_socket_get_invalid ();
}
return fd;
@@ -1378,11 +1386,12 @@ _dbus_listen_tcp_socket (const char *host,
const char *port,
const char *family,
DBusString *retport,
- int **fds_p,
+ DBusSocket **fds_p,
DBusError *error)
{
int saved_errno;
- int nlisten_fd = 0, *listen_fd = NULL, res, i;
+ int nlisten_fd = 0, res, i;
+ DBusSocket *listen_fd = NULL;
struct addrinfo hints;
struct addrinfo *ai, *tmp;
unsigned int reuseaddr;
@@ -1424,7 +1433,9 @@ _dbus_listen_tcp_socket (const char *host,
tmp = ai;
while (tmp)
{
- int fd = -1, *newlisten_fd;
+ int fd = -1, tcp_nodelay_on;
+ DBusSocket *newlisten_fd;
+
if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
{
_DBUS_ASSERT_ERROR_IS_SET(error);
@@ -1439,15 +1450,33 @@ _dbus_listen_tcp_socket (const char *host,
host ? host : "*", port, _dbus_strerror (errno));
}
+ /* Nagle's algorithm imposes a huge delay on the initial messages
+ going over TCP. */
+ tcp_nodelay_on = 1;
+ if (setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay_on, sizeof (tcp_nodelay_on)) == -1)
+ {
+ _dbus_warn ("Failed to set TCP_NODELAY socket option \"%s:%s\": %s",
+ host ? host : "*", port, _dbus_strerror (errno));
+ }
+
if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
{
saved_errno = errno;
_dbus_close(fd, NULL);
if (saved_errno == EADDRINUSE)
{
- /* Depending on kernel policy, it may or may not
- be neccessary to bind to both IPv4 & 6 addresses
- so ignore EADDRINUSE here */
+ /* Depending on kernel policy, binding to an IPv6 address
+ might implicitly bind to a corresponding IPv4
+ address or vice versa, resulting in EADDRINUSE for the
+ other one (e.g. bindv6only=0 on Linux).
+
+ Also, after we "goto redo_lookup_with_port" after binding
+ a port on one of the possible addresses, we will
+ try to bind that same port on every address, including the
+ same address again for a second time; that one will
+ also fail with EADDRINUSE.
+
+ For both those reasons, ignore EADDRINUSE here */
tmp = tmp->ai_next;
continue;
}
@@ -1467,7 +1496,7 @@ _dbus_listen_tcp_socket (const char *host,
goto failed;
}
- newlisten_fd = dbus_realloc(listen_fd, sizeof(int)*(nlisten_fd+1));
+ newlisten_fd = dbus_realloc(listen_fd, sizeof(DBusSocket)*(nlisten_fd+1));
if (!newlisten_fd)
{
saved_errno = errno;
@@ -1478,7 +1507,7 @@ _dbus_listen_tcp_socket (const char *host,
goto failed;
}
listen_fd = newlisten_fd;
- listen_fd[nlisten_fd] = fd;
+ listen_fd[nlisten_fd].fd = fd;
nlisten_fd++;
if (!_dbus_string_get_length(retport))
@@ -1500,7 +1529,7 @@ _dbus_listen_tcp_socket (const char *host,
if (result == -1 ||
(res = getnameinfo ((struct sockaddr*)&addr, addrlen, NULL, 0,
portbuf, sizeof(portbuf),
- NI_NUMERICHOST)) != 0)
+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to resolve port \"%s:%s\": %s (%s)",
@@ -1544,7 +1573,7 @@ _dbus_listen_tcp_socket (const char *host,
for (i = 0 ; i < nlisten_fd ; i++)
{
- if (!_dbus_set_fd_nonblocking (listen_fd[i], error))
+ if (!_dbus_set_fd_nonblocking (listen_fd[i].fd, error))
{
goto failed;
}
@@ -1558,7 +1587,7 @@ _dbus_listen_tcp_socket (const char *host,
if (ai)
freeaddrinfo(ai);
for (i = 0 ; i < nlisten_fd ; i++)
- _dbus_close(listen_fd[i], NULL);
+ _dbus_close(listen_fd[i].fd, NULL);
dbus_free(listen_fd);
return -1;
}
@@ -1639,12 +1668,130 @@ write_credentials_byte (int server_fd,
}
}
+/* return FALSE on OOM, TRUE otherwise, even if no credentials were found */
+static dbus_bool_t
+add_linux_security_label_to_credentials (int client_fd,
+ DBusCredentials *credentials)
+{
+#if defined(__linux__) && defined(SO_PEERSEC)
+ DBusString buf;
+ socklen_t len = 1024;
+ dbus_bool_t oom = FALSE;
+
+ if (!_dbus_string_init_preallocated (&buf, len) ||
+ !_dbus_string_set_length (&buf, len))
+ return FALSE;
+
+ while (getsockopt (client_fd, SOL_SOCKET, SO_PEERSEC,
+ _dbus_string_get_data (&buf), &len) < 0)
+ {
+ int e = errno;
+
+ _dbus_verbose ("getsockopt failed with %s, len now %lu\n",
+ _dbus_strerror (e), (unsigned long) len);
+
+ if (e != ERANGE || len <= _dbus_string_get_length_uint (&buf))
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERSEC): %s\n",
+ _dbus_strerror (e));
+ goto out;
+ }
+
+ /* If not enough space, len is updated to be enough.
+ * Try again with a large enough buffer. */
+ if (!_dbus_string_set_length (&buf, len))
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+ _dbus_verbose ("will try again with %lu\n", (unsigned long) len);
+ }
+
+ if (len <= 0)
+ {
+ _dbus_verbose ("getsockopt(SO_PEERSEC) yielded <= 0 bytes: %lu\n",
+ (unsigned long) len);
+ goto out;
+ }
+
+ if (len > _dbus_string_get_length_uint (&buf))
+ {
+ _dbus_verbose ("%lu > %u", (unsigned long) len,
+ _dbus_string_get_length_uint (&buf));
+ _dbus_assert_not_reached ("getsockopt(SO_PEERSEC) overflowed");
+ }
+
+ if (_dbus_string_get_byte (&buf, len - 1) == 0)
+ {
+ /* the kernel included the trailing \0 in its count,
+ * but DBusString always has an extra \0 after the data anyway */
+ _dbus_verbose ("subtracting trailing \\0\n");
+ len--;
+ }
+
+ if (!_dbus_string_set_length (&buf, len))
+ {
+ _dbus_assert_not_reached ("shortening string should not lead to OOM");
+ oom = TRUE;
+ goto out;
+ }
+
+ if (strlen (_dbus_string_get_const_data (&buf)) != len)
+ {
+ /* LSM people on the linux-security-module@ mailing list say this
+ * should never happen: the label should be a bytestring with
+ * an optional trailing \0 */
+ _dbus_verbose ("security label from kernel had an embedded \\0, "
+ "ignoring it\n");
+ goto out;
+ }
+
+ _dbus_verbose ("getsockopt(SO_PEERSEC): %lu bytes excluding \\0: %s\n",
+ (unsigned long) len,
+ _dbus_string_get_const_data (&buf));
+
+ if (!_dbus_credentials_add_linux_security_label (credentials,
+ _dbus_string_get_const_data (&buf)))
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+out:
+ _dbus_string_free (&buf);
+ return !oom;
+#else
+ /* no error */
+ return TRUE;
+#endif
+}
+
/**
* Reads a single byte which must be nul (an error occurs otherwise),
* and reads unix credentials if available. Clears the credentials
* object, then adds pid/uid if available, so any previous credentials
* stored in the object are lost.
*
+ * DBusServer makes the security assumption that the credentials
+ * returned by this method are the credentials that were active
+ * at the time the socket was opened. Do not add APIs to this
+ * method that would break that assumption.
+ *
+ * In particular, it is incorrect to use any API of the form
+ * "get the process ID at the other end of the connection, then
+ * determine its uid, gid, or other credentials from the pid"
+ * (e.g. looking in /proc on Linux). If we did that, we would
+ * be vulnerable to several attacks. A malicious process could
+ * queue up the rest of the authentication handshake and a malicious
+ * message that it should not be allowed to send, then race with
+ * the DBusServer to exec() a more privileged (e.g. setuid) binary that
+ * would have been allowed to send that message; or it could exit,
+ * and arrange for enough setuid processes to be started that its
+ * pid would be recycled for one of those processes with high
+ * probability; or it could fd-pass the connection to a more
+ * privileged process.
+ *
* Return value indicates whether a byte was read, not whether
* we got valid credentials. On some systems, such as Linux,
* reading/writing the byte isn't actually required, but we do it
@@ -1661,7 +1808,7 @@ write_credentials_byte (int server_fd,
* @returns #TRUE on success
*/
dbus_bool_t
-_dbus_read_credentials_socket (int client_fd,
+_dbus_read_credentials_socket (DBusSocket client_fd,
DBusCredentials *credentials,
DBusError *error)
{
@@ -1679,18 +1826,18 @@ _dbus_read_credentials_socket (int client_fd,
} cmsg;
#endif
- uid_read = DBUS_UID_UNSET;
- pid_read = DBUS_PID_UNSET;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
/* The POSIX spec certainly doesn't promise this, but
* we need these assertions to fail as soon as we're wrong about
* it so we can do the porting fixups
*/
- _dbus_assert (sizeof (pid_t) <= sizeof (dbus_pid_t));
- _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t));
- _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t));
+ _DBUS_STATIC_ASSERT (sizeof (pid_t) <= sizeof (dbus_pid_t));
+ _DBUS_STATIC_ASSERT (sizeof (uid_t) <= sizeof (dbus_uid_t));
+ _DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
+
+ uid_read = DBUS_UID_UNSET;
+ pid_read = DBUS_PID_UNSET;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
_dbus_credentials_clear (credentials);
@@ -1708,7 +1855,7 @@ _dbus_read_credentials_socket (int client_fd,
#endif
again:
- bytes_read = recvmsg (client_fd, &msg, 0);
+ bytes_read = recvmsg (client_fd.fd, &msg, 0);
if (bytes_read < 0)
{
@@ -1760,16 +1907,41 @@ _dbus_read_credentials_socket (int client_fd,
#endif
int cr_len = sizeof (cr);
- if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
- cr_len == sizeof (cr))
+ if (getsockopt (client_fd.fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) != 0)
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERCRED): %s\n",
+ _dbus_strerror (errno));
+ }
+ else if (cr_len != sizeof (cr))
+ {
+ _dbus_verbose ("Failed to getsockopt(SO_PEERCRED), returned %d bytes, expected %d\n",
+ cr_len, (int) sizeof (cr));
+ }
+ else
+ {
+ pid_read = cr.pid;
+ uid_read = cr.uid;
+ }
+#elif defined(HAVE_UNPCBID) && defined(LOCAL_PEEREID)
+ /* Another variant of the above - used on NetBSD
+ */
+ struct unpcbid cr;
+ socklen_t cr_len = sizeof (cr);
+
+ if (getsockopt (client_fd.fd, 0, LOCAL_PEEREID, &cr, &cr_len) != 0)
+ {
+ _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID): %s\n",
+ _dbus_strerror (errno));
+ }
+ else if (cr_len != sizeof (cr))
{
- pid_read = cr.pid;
- uid_read = cr.uid;
+ _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID), returned %d bytes, expected %d\n",
+ cr_len, (int) sizeof (cr));
}
else
{
- _dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n",
- cr_len, (int) sizeof (cr), _dbus_strerror (errno));
+ pid_read = cr.unp_pid;
+ uid_read = cr.unp_euid;
}
#elif defined(HAVE_CMSGCRED)
/* We only check for HAVE_CMSGCRED, but we're really assuming that the
@@ -1803,7 +1975,7 @@ _dbus_read_credentials_socket (int client_fd,
* up this list, because it carries the pid and we use this code path
* for audit data. */
ucred_t * ucred = NULL;
- if (getpeerucred (client_fd, &ucred) == 0)
+ if (getpeerucred (client_fd.fd, &ucred) == 0)
{
pid_read = ucred_getpid (ucred);
uid_read = ucred_geteuid (ucred);
@@ -1869,7 +2041,7 @@ _dbus_read_credentials_socket (int client_fd,
*/
uid_t euid;
gid_t egid;
- if (getpeereid (client_fd, &euid, &egid) == 0)
+ if (getpeereid (client_fd.fd, &euid, &egid) == 0)
{
uid_read = euid;
}
@@ -1922,6 +2094,12 @@ _dbus_read_credentials_socket (int client_fd,
}
}
+ if (!add_linux_security_label_to_credentials (client_fd.fd, credentials))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
return TRUE;
}
@@ -1943,12 +2121,12 @@ _dbus_read_credentials_socket (int client_fd,
* @returns #TRUE if the byte was sent
*/
dbus_bool_t
-_dbus_send_credentials_socket (int server_fd,
+_dbus_send_credentials_socket (DBusSocket server_fd,
DBusError *error)
{
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
- if (write_credentials_byte (server_fd, error))
+ if (write_credentials_byte (server_fd.fd, error))
return TRUE;
else
return FALSE;
@@ -1963,10 +2141,10 @@ _dbus_send_credentials_socket (int server_fd,
* @param listen_fd the listen file descriptor
* @returns the connection fd of the client, or -1 on error
*/
-int
-_dbus_accept (int listen_fd)
+DBusSocket
+_dbus_accept (DBusSocket listen_fd)
{
- int client_fd;
+ DBusSocket client_fd;
struct sockaddr addr;
socklen_t addrlen;
#ifdef HAVE_ACCEPT4
@@ -1983,28 +2161,28 @@ _dbus_accept (int listen_fd)
* libc headers, SOCK_CLOEXEC is too. At runtime, it is still
* not necessarily true that either is supported by the running kernel.
*/
- client_fd = accept4 (listen_fd, &addr, &addrlen, SOCK_CLOEXEC);
- cloexec_done = client_fd >= 0;
+ client_fd.fd = accept4 (listen_fd.fd, &addr, &addrlen, SOCK_CLOEXEC);
+ cloexec_done = client_fd.fd >= 0;
- if (client_fd < 0 && (errno == ENOSYS || errno == EINVAL))
+ if (client_fd.fd < 0 && (errno == ENOSYS || errno == EINVAL))
#endif
{
- client_fd = accept (listen_fd, &addr, &addrlen);
+ client_fd.fd = accept (listen_fd.fd, &addr, &addrlen);
}
- if (client_fd < 0)
+ if (client_fd.fd < 0)
{
if (errno == EINTR)
goto retry;
}
- _dbus_verbose ("client fd %d accepted\n", client_fd);
+ _dbus_verbose ("client fd %d accepted\n", client_fd.fd);
#ifdef HAVE_ACCEPT4
if (!cloexec_done)
#endif
{
- _dbus_fd_set_close_on_exec(client_fd);
+ _dbus_fd_set_close_on_exec(client_fd.fd);
}
return client_fd;
@@ -2371,9 +2549,9 @@ _dbus_credentials_add_from_current_process (DBusCredentials *credentials)
* we need these assertions to fail as soon as we're wrong about
* it so we can do the porting fixups
*/
- _dbus_assert (sizeof (pid_t) <= sizeof (dbus_pid_t));
- _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t));
- _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t));
+ _DBUS_STATIC_ASSERT (sizeof (pid_t) <= sizeof (dbus_pid_t));
+ _DBUS_STATIC_ASSERT (sizeof (uid_t) <= sizeof (dbus_uid_t));
+ _DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
if (!_dbus_credentials_add_pid(credentials, _dbus_getpid()))
return FALSE;
@@ -2592,6 +2770,11 @@ _dbus_poll (DBusPollFD *fds,
_DBUS_STRUCT_OFFSET (DBusPollFD, revents) ==
_DBUS_STRUCT_OFFSET (struct pollfd, revents))
{
+ if (timeout_milliseconds < -1)
+ {
+ timeout_milliseconds = -1;
+ }
+
return poll ((struct pollfd*) fds,
n_fds,
timeout_milliseconds);
@@ -2820,61 +3003,55 @@ _dbus_sleep_milliseconds (int milliseconds)
#endif
}
-static dbus_bool_t
-_dbus_generate_pseudorandom_bytes (DBusString *str,
- int n_bytes)
-{
- int old_len;
- char *p;
-
- old_len = _dbus_string_get_length (str);
-
- if (!_dbus_string_lengthen (str, n_bytes))
- return FALSE;
-
- p = _dbus_string_get_data_len (str, old_len, n_bytes);
-
- _dbus_generate_pseudorandom_bytes_buffer (p, n_bytes);
-
- return TRUE;
-}
-
/**
- * Generates the given number of random bytes,
+ * Generates the given number of securely random bytes,
* using the best mechanism we can come up with.
*
* @param str the string
* @param n_bytes the number of random bytes to append to string
- * @returns #TRUE on success, #FALSE if no memory
+ * @param error location to store reason for failure
+ * @returns #TRUE on success, #FALSE on error
*/
dbus_bool_t
_dbus_generate_random_bytes (DBusString *str,
- int n_bytes)
+ int n_bytes,
+ DBusError *error)
{
int old_len;
int fd;
-
- /* FALSE return means "no memory", if it could
- * mean something else then we'd need to return
- * a DBusError. So we always fall back to pseudorandom
- * if the I/O fails.
- */
+ int result;
old_len = _dbus_string_get_length (str);
fd = -1;
/* note, urandom on linux will fall back to pseudorandom */
fd = open ("/dev/urandom", O_RDONLY);
+
if (fd < 0)
- return _dbus_generate_pseudorandom_bytes (str, n_bytes);
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not open /dev/urandom: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
_dbus_verbose ("/dev/urandom fd %d opened\n", fd);
- if (_dbus_read (fd, str, n_bytes) != n_bytes)
+ result = _dbus_read (fd, str, n_bytes);
+
+ if (result != n_bytes)
{
+ if (result < 0)
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not read /dev/urandom: %s",
+ _dbus_strerror (errno));
+ else
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "Short read from /dev/urandom");
+
_dbus_close (fd, NULL);
_dbus_string_set_length (str, old_len);
- return _dbus_generate_pseudorandom_bytes (str, n_bytes);
+ return FALSE;
}
_dbus_verbose ("Read %d bytes from /dev/urandom\n",
@@ -2933,7 +3110,7 @@ _dbus_disable_sigpipe (void)
* @param fd the file descriptor
*/
void
-_dbus_fd_set_close_on_exec (intptr_t fd)
+_dbus_fd_set_close_on_exec (int fd)
{
int val;
@@ -3025,6 +3202,13 @@ _dbus_dup(int fd,
* @returns #TRUE on success.
*/
dbus_bool_t
+_dbus_set_socket_nonblocking (DBusSocket fd,
+ DBusError *error)
+{
+ return _dbus_set_fd_nonblocking (fd.fd, error);
+}
+
+static dbus_bool_t
_dbus_set_fd_nonblocking (int fd,
DBusError *error)
{
@@ -3093,22 +3277,22 @@ _dbus_print_backtrace (void)
}
/**
- * Creates a full-duplex pipe (as in socketpair()).
- * Sets both ends of the pipe nonblocking.
+ * Creates pair of connect sockets (as in socketpair()).
+ * Sets both ends of the pair nonblocking.
*
* Marks both file descriptors as close-on-exec
*
* @param fd1 return location for one end
* @param fd2 return location for the other end
- * @param blocking #TRUE if pipe should be blocking
+ * @param blocking #TRUE if pair should be blocking
* @param error error return
* @returns #FALSE on failure (if error is set)
*/
dbus_bool_t
-_dbus_full_duplex_pipe (int *fd1,
- int *fd2,
- dbus_bool_t blocking,
- DBusError *error)
+_dbus_socketpair (DBusSocket *fd1,
+ DBusSocket *fd2,
+ dbus_bool_t blocking,
+ DBusError *error)
{
#ifdef HAVE_SOCKETPAIR
int fds[2];
@@ -3156,17 +3340,17 @@ _dbus_full_duplex_pipe (int *fd1,
return FALSE;
}
- *fd1 = fds[0];
- *fd2 = fds[1];
+ fd1->fd = fds[0];
+ fd2->fd = fds[1];
_dbus_verbose ("full-duplex pipe %d <-> %d\n",
- *fd1, *fd2);
+ fd1->fd, fd2->fd);
return TRUE;
#else
- _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n");
+ _dbus_warn ("_dbus_socketpair() not implemented on this OS\n");
dbus_set_error (error, DBUS_ERROR_FAILED,
- "_dbus_full_duplex_pipe() not implemented on this OS");
+ "_dbus_socketpair() not implemented on this OS");
return FALSE;
#endif
}
@@ -3513,6 +3697,7 @@ _dbus_get_autolaunch_address (const char *scope,
* but that's done elsewhere, and if it worked, this function wouldn't
* be called.) */
const char *display;
+ char *progpath;
char *argv[6];
int i;
DBusString uuid;
@@ -3530,7 +3715,12 @@ _dbus_get_autolaunch_address (const char *scope,
/* fd.o #19997: if $DISPLAY isn't set to something useful, then
* dbus-launch-x11 is just going to fail. Rather than trying to
- * run it, we might as well bail out early with a nice error. */
+ * run it, we might as well bail out early with a nice error.
+ *
+ * This is not strictly true in a world where the user bus exists,
+ * because dbus-launch --autolaunch knows how to connect to that -
+ * but if we were going to connect to the user bus, we'd have done
+ * so before trying autolaunch: in any case. */
display = _dbus_getenv ("DISPLAY");
if (display == NULL || display[0] == '\0')
@@ -3546,19 +3736,24 @@ _dbus_get_autolaunch_address (const char *scope,
return FALSE;
}
- if (!_dbus_get_local_machine_uuid_encoded (&uuid))
+ if (!_dbus_get_local_machine_uuid_encoded (&uuid, error))
{
- _DBUS_SET_OOM (error);
goto out;
}
- i = 0;
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
if (_dbus_getenv ("DBUS_USE_TEST_BINARY") != NULL)
- argv[i] = TEST_BUS_LAUNCH_BINARY;
+ progpath = TEST_BUS_LAUNCH_BINARY;
else
#endif
- argv[i] = DBUS_BINDIR "/dbus-launch";
+ progpath = DBUS_BINDIR "/dbus-launch";
+ /*
+ * argv[0] is always dbus-launch, that's the name what we'll
+ * get from /proc, or ps(1), regardless what the progpath is,
+ * see fd.o#69716
+ */
+ i = 0;
+ argv[i] = "dbus-launch";
++i;
argv[i] = "--autolaunch";
++i;
@@ -3573,7 +3768,7 @@ _dbus_get_autolaunch_address (const char *scope,
_dbus_assert (i == _DBUS_N_ELEMENTS (argv));
- retval = _read_subprocess_line_argv (argv[0],
+ retval = _read_subprocess_line_argv (progpath,
TRUE,
argv, address, error);
@@ -3642,7 +3837,10 @@ _dbus_read_local_machine_uuid (DBusGUID *machine_id,
/* if none found, try to make a new one */
dbus_error_free (error);
_dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
- _dbus_generate_uuid (machine_id);
+
+ if (!_dbus_generate_uuid (machine_id, error))
+ return FALSE;
+
return _dbus_write_uuid_file (&filename, machine_id, error);
}
@@ -3759,6 +3957,77 @@ _dbus_lookup_session_address_launchd (DBusString *address, DBusError *error)
}
#endif
+dbus_bool_t
+_dbus_lookup_user_bus (dbus_bool_t *supported,
+ DBusString *address,
+ DBusError *error)
+{
+ const char *runtime_dir = _dbus_getenv ("XDG_RUNTIME_DIR");
+ dbus_bool_t ret = FALSE;
+ struct stat stbuf;
+ DBusString user_bus_path;
+
+ if (runtime_dir == NULL)
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR not found in environment");
+ *supported = FALSE;
+ return TRUE; /* Cannot use it, but not an error */
+ }
+
+ if (!_dbus_string_init (&user_bus_path))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append_printf (&user_bus_path, "%s/bus", runtime_dir))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (lstat (_dbus_string_get_const_data (&user_bus_path), &stbuf) == -1)
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR/bus not available: %s",
+ _dbus_strerror (errno));
+ *supported = FALSE;
+ ret = TRUE; /* Cannot use it, but not an error */
+ goto out;
+ }
+
+ if (stbuf.st_uid != getuid ())
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR/bus owned by uid %ld, not our uid %ld",
+ (long) stbuf.st_uid, (long) getuid ());
+ *supported = FALSE;
+ ret = TRUE; /* Cannot use it, but not an error */
+ goto out;
+ }
+
+ if ((stbuf.st_mode & S_IFMT) != S_IFSOCK)
+ {
+ _dbus_verbose ("XDG_RUNTIME_DIR/bus is not a socket: st_mode = 0o%lo",
+ (long) stbuf.st_mode);
+ *supported = FALSE;
+ ret = TRUE; /* Cannot use it, but not an error */
+ goto out;
+ }
+
+ if (!_dbus_string_append (address, "unix:path=") ||
+ !_dbus_address_append_escaped (address, &user_bus_path))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ *supported = TRUE;
+ ret = TRUE;
+
+out:
+ _dbus_string_free (&user_bus_path);
+ return ret;
+}
+
/**
* Determines the address of the session bus by querying a
* platform-specific method.
@@ -3787,11 +4056,18 @@ _dbus_lookup_session_address (dbus_bool_t *supported,
*supported = TRUE;
return _dbus_lookup_session_address_launchd (address, error);
#else
- /* On non-Mac Unix platforms, if the session address isn't already
- * set in DBUS_SESSION_BUS_ADDRESS environment variable, we punt and
- * fall back to the autolaunch: global default; see
- * init_session_address in dbus/dbus-bus.c. */
*supported = FALSE;
+
+ if (!_dbus_lookup_user_bus (supported, address, error))
+ return FALSE;
+ else if (*supported)
+ return TRUE;
+
+ /* On non-Mac Unix platforms, if the session address isn't already
+ * set in DBUS_SESSION_BUS_ADDRESS environment variable and the
+ * $XDG_RUNTIME_DIR/bus can't be used, we punt and fall back to the
+ * autolaunch: global default; see init_session_address in
+ * dbus/dbus-bus.c. */
return TRUE;
#endif
}
@@ -3907,12 +4183,12 @@ _dbus_daemon_unpublish_session_bus_address (void)
* See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently
* for Winsock so is abstracted)
*
- * @returns #TRUE if errno == EAGAIN or errno == EWOULDBLOCK
+ * @returns #TRUE if e == EAGAIN or e == EWOULDBLOCK
*/
dbus_bool_t
-_dbus_get_is_errno_eagain_or_ewouldblock (void)
+_dbus_get_is_errno_eagain_or_ewouldblock (int e)
{
- return errno == EAGAIN || errno == EWOULDBLOCK;
+ return e == EAGAIN || e == EWOULDBLOCK;
}
/**
@@ -3951,8 +4227,8 @@ _dbus_delete_directory (const DBusString *filename,
*
*/
dbus_bool_t
-_dbus_socket_can_pass_unix_fd(int fd) {
-
+_dbus_socket_can_pass_unix_fd (DBusSocket fd)
+{
#ifdef SCM_RIGHTS
union {
struct sockaddr sa;
@@ -3964,7 +4240,7 @@ _dbus_socket_can_pass_unix_fd(int fd) {
_DBUS_ZERO(sa_buf);
- if (getsockname(fd, &sa_buf.sa, &sa_len) < 0)
+ if (getsockname(fd.fd, &sa_buf.sa, &sa_len) < 0)
return FALSE;
return sa_buf.sa.sa_family == AF_UNIX;
@@ -4103,7 +4379,7 @@ _dbus_check_setuid (void)
* @param error return location for error code
*/
dbus_bool_t
-_dbus_append_address_from_socket (int fd,
+_dbus_append_address_from_socket (DBusSocket fd,
DBusString *address,
DBusError *error)
{
@@ -4118,7 +4394,7 @@ _dbus_append_address_from_socket (int fd,
int size = sizeof (socket);
DBusString path_str;
- if (getsockname (fd, &socket.sa, &size))
+ if (getsockname (fd.fd, &socket.sa, &size))
goto err;
switch (socket.sa.sa_family)
@@ -4169,4 +4445,16 @@ _dbus_append_address_from_socket (int fd,
return FALSE;
}
+int
+_dbus_save_socket_errno (void)
+{
+ return errno;
+}
+
+void
+_dbus_restore_socket_errno (int saved_errno)
+{
+ errno = saved_errno;
+}
+
/* tests in dbus-sysdeps-util.c */