mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-06-19 04:58:27 +02:00
2003-01-04 Havoc Pennington <hp@pobox.com>
* test/watch.c (error_handler): make it safe if the error handler is called multiple times (if we s/error handler/disconnect handler/ we should just guarantee it's called only once) * dbus/dbus-transport.c (_dbus_transport_disconnect): call the error handler on disconnect (it's quite possible we should just change the error handler to a "disconnect handler," I'm not sure we have any other meaningful errors) * configure.in: check for getpwnam_r * dbus/dbus-transport.c, dbus/dbus-transport-unix.c, dbus/dbus-auth.c: add credentials support, add EXTERNAL auth mechanism as in SASL spec, using socket credentials * dbus/dbus-sysdeps.c (_dbus_read_credentials_unix_socket): new function (_dbus_send_credentials_unix_socket): new function * dbus/dbus-sysdeps.c (_dbus_accept_unix_socket): rename just dbus_accept() (_dbus_write): only check errno if <0 returned (_dbus_write_two): ditto
This commit is contained in:
parent
1ed128b524
commit
01af5ff410
12 changed files with 817 additions and 41 deletions
25
ChangeLog
25
ChangeLog
|
|
@ -1,3 +1,28 @@
|
|||
2003-01-04 Havoc Pennington <hp@pobox.com>
|
||||
|
||||
* test/watch.c (error_handler): make it safe if the error handler
|
||||
is called multiple times (if we s/error handler/disconnect
|
||||
handler/ we should just guarantee it's called only once)
|
||||
|
||||
* dbus/dbus-transport.c (_dbus_transport_disconnect): call the
|
||||
error handler on disconnect (it's quite possible we should
|
||||
just change the error handler to a "disconnect handler," I'm
|
||||
not sure we have any other meaningful errors)
|
||||
|
||||
* configure.in: check for getpwnam_r
|
||||
|
||||
* dbus/dbus-transport.c, dbus/dbus-transport-unix.c,
|
||||
dbus/dbus-auth.c: add credentials support, add EXTERNAL auth
|
||||
mechanism as in SASL spec, using socket credentials
|
||||
|
||||
* dbus/dbus-sysdeps.c (_dbus_read_credentials_unix_socket): new function
|
||||
(_dbus_send_credentials_unix_socket): new function
|
||||
|
||||
* dbus/dbus-sysdeps.c (_dbus_accept_unix_socket): rename just
|
||||
dbus_accept()
|
||||
(_dbus_write): only check errno if <0 returned
|
||||
(_dbus_write_two): ditto
|
||||
|
||||
2003-01-02 Anders Carlsson <andersca@codefactory.se>
|
||||
|
||||
* dbus/dbus-marshal.c: (_dbus_marshal_utf8_string),
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ AC_CHECK_SIZEOF(__int64)
|
|||
## byte order
|
||||
AC_C_BIGENDIAN
|
||||
|
||||
AC_CHECK_FUNCS(vsnprintf vasprintf)
|
||||
AC_CHECK_FUNCS(vsnprintf vasprintf getpwnam_r)
|
||||
|
||||
dnl check for writev header and writev function so we're
|
||||
dnl good to go if HAVE_WRITEV gets defined.
|
||||
|
|
|
|||
268
dbus/dbus-auth.c
268
dbus/dbus-auth.c
|
|
@ -64,6 +64,12 @@ typedef struct
|
|||
DBusProcessAuthCommandFunction func;
|
||||
} DBusAuthCommandHandler;
|
||||
|
||||
/**
|
||||
* This function appends an initial client response to the given string
|
||||
*/
|
||||
typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth,
|
||||
DBusString *response);
|
||||
|
||||
/**
|
||||
* This function processes a block of data received from the peer.
|
||||
* i.e. handles a DATA command.
|
||||
|
|
@ -97,6 +103,7 @@ typedef struct
|
|||
DBusAuthEncodeFunction server_encode_func;
|
||||
DBusAuthDecodeFunction server_decode_func;
|
||||
DBusAuthShutdownFunction server_shutdown_func;
|
||||
DBusInitialResponseFunction client_initial_response_func;
|
||||
DBusAuthDataFunction client_data_func;
|
||||
DBusAuthEncodeFunction client_encode_func;
|
||||
DBusAuthDecodeFunction client_decode_func;
|
||||
|
|
@ -116,6 +123,14 @@ struct DBusAuth
|
|||
const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
|
||||
|
||||
const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */
|
||||
|
||||
DBusString identity; /**< Current identity we're authorizing
|
||||
* as.
|
||||
*/
|
||||
|
||||
DBusCredentials credentials; /**< Credentials, fields may be -1 */
|
||||
|
||||
DBusCredentials authorized_identity; /**< Credentials that are authorized */
|
||||
|
||||
unsigned int needed_memory : 1; /**< We needed memory to continue since last
|
||||
* successful getting something done
|
||||
|
|
@ -125,6 +140,7 @@ struct DBusAuth
|
|||
unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
|
||||
unsigned int authenticated_pending_begin : 1; /**< Authenticated once we get BEGIN */
|
||||
unsigned int already_got_mechanisms : 1; /**< Client already got mech list */
|
||||
unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
|
||||
};
|
||||
|
||||
typedef struct
|
||||
|
|
@ -228,6 +244,14 @@ _dbus_auth_new (int size)
|
|||
|
||||
auth->refcount = 1;
|
||||
|
||||
auth->credentials.pid = -1;
|
||||
auth->credentials.uid = -1;
|
||||
auth->credentials.gid = -1;
|
||||
|
||||
auth->authorized_identity.pid = -1;
|
||||
auth->authorized_identity.uid = -1;
|
||||
auth->authorized_identity.gid = -1;
|
||||
|
||||
/* note that we don't use the max string length feature,
|
||||
* because you can't use that feature if you're going to
|
||||
* try to recover from out-of-memory (it creates
|
||||
|
|
@ -244,6 +268,14 @@ _dbus_auth_new (int size)
|
|||
|
||||
if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
|
||||
{
|
||||
_dbus_string_free (&auth->incoming);
|
||||
dbus_free (auth);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
|
||||
{
|
||||
_dbus_string_free (&auth->incoming);
|
||||
_dbus_string_free (&auth->outgoing);
|
||||
dbus_free (auth);
|
||||
return NULL;
|
||||
|
|
@ -278,6 +310,11 @@ shutdown_mech (DBusAuth *auth)
|
|||
/* Cancel any auth */
|
||||
auth->authenticated_pending_begin = FALSE;
|
||||
auth->authenticated = FALSE;
|
||||
auth->already_asked_for_initial_response = FALSE;
|
||||
_dbus_string_set_length (&auth->identity, 0);
|
||||
auth->authorized_identity.pid = -1;
|
||||
auth->authorized_identity.uid = -1;
|
||||
auth->authorized_identity.gid = -1;
|
||||
|
||||
if (auth->mech != NULL)
|
||||
{
|
||||
|
|
@ -353,6 +390,163 @@ handle_decode_stupid_test_mech (DBusAuth *auth,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
do_rejection (DBusAuth *auth)
|
||||
{
|
||||
if (_dbus_string_append (&auth->outgoing,
|
||||
"REJECTED\r\n"))
|
||||
{
|
||||
shutdown_mech (auth);
|
||||
_dbus_verbose ("rejected client auth\n");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
handle_server_data_external_mech (DBusAuth *auth,
|
||||
const DBusString *data)
|
||||
{
|
||||
DBusCredentials desired_identity;
|
||||
|
||||
if (auth->credentials.uid < 0)
|
||||
{
|
||||
_dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
|
||||
return do_rejection (auth);
|
||||
}
|
||||
|
||||
if (_dbus_string_get_length (data) > 0)
|
||||
{
|
||||
if (_dbus_string_get_length (&auth->identity) > 0)
|
||||
{
|
||||
/* Tried to send two auth identities, wtf */
|
||||
return do_rejection (auth);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this is our auth identity */
|
||||
if (!_dbus_string_copy (data, 0, &auth->identity, 0))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Poke client for an auth identity, if none given */
|
||||
if (_dbus_string_get_length (&auth->identity) == 0 &&
|
||||
!auth->already_asked_for_initial_response)
|
||||
{
|
||||
if (_dbus_string_append (&auth->outgoing,
|
||||
"DATA\r\n"))
|
||||
{
|
||||
_dbus_verbose ("sending empty challenge asking client for auth identity\n");
|
||||
auth->already_asked_for_initial_response = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
desired_identity.pid = -1;
|
||||
desired_identity.uid = -1;
|
||||
desired_identity.gid = -1;
|
||||
|
||||
/* If auth->identity is still empty here, then client
|
||||
* responded with an empty string after we poked it for
|
||||
* an initial response. This means to try to auth the
|
||||
* identity provided in the credentials.
|
||||
*/
|
||||
if (_dbus_string_get_length (&auth->identity) == 0)
|
||||
{
|
||||
desired_identity.uid = auth->credentials.uid;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_dbus_credentials_from_uid_string (&auth->identity,
|
||||
&desired_identity))
|
||||
return do_rejection (auth);
|
||||
}
|
||||
|
||||
if (desired_identity.uid < 0)
|
||||
{
|
||||
_dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
|
||||
return do_rejection (auth);
|
||||
}
|
||||
|
||||
if (_dbus_credentials_match (&auth->credentials,
|
||||
&desired_identity))
|
||||
{
|
||||
/* client has authenticated */
|
||||
_dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n",
|
||||
desired_identity.uid,
|
||||
auth->credentials.uid);
|
||||
|
||||
if (!_dbus_string_append (&auth->outgoing,
|
||||
"OK\r\n"))
|
||||
return FALSE;
|
||||
|
||||
auth->authorized_identity.uid = desired_identity.uid;
|
||||
|
||||
auth->authenticated_pending_begin = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return do_rejection (auth);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_server_shutdown_external_mech (DBusAuth *auth)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
handle_client_initial_response_external_mech (DBusAuth *auth,
|
||||
DBusString *response)
|
||||
{
|
||||
/* We always append our UID as an initial response, so the server
|
||||
* doesn't have to send back an empty challenge to check whether we
|
||||
* want to specify an identity. i.e. this avoids a round trip that
|
||||
* the spec for the EXTERNAL mechanism otherwise requires.
|
||||
*/
|
||||
DBusString plaintext;
|
||||
|
||||
if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
|
||||
return FALSE;
|
||||
|
||||
if (!_dbus_string_append_our_uid (&plaintext))
|
||||
goto failed;
|
||||
|
||||
if (!_dbus_string_base64_encode (&plaintext, 0,
|
||||
response,
|
||||
_dbus_string_get_length (response)))
|
||||
goto failed;
|
||||
|
||||
_dbus_string_free (&plaintext);
|
||||
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
_dbus_string_free (&plaintext);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
handle_client_data_external_mech (DBusAuth *auth,
|
||||
const DBusString *data)
|
||||
{
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_client_shutdown_external_mech (DBusAuth *auth)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* Put mechanisms here in order of preference.
|
||||
* What I eventually want to have is:
|
||||
*
|
||||
|
|
@ -364,11 +558,21 @@ handle_decode_stupid_test_mech (DBusAuth *auth,
|
|||
*/
|
||||
static const DBusAuthMechanismHandler
|
||||
all_mechanisms[] = {
|
||||
{ "EXTERNAL",
|
||||
handle_server_data_external_mech,
|
||||
NULL, NULL,
|
||||
handle_server_shutdown_external_mech,
|
||||
handle_client_initial_response_external_mech,
|
||||
handle_client_data_external_mech,
|
||||
NULL, NULL,
|
||||
handle_client_shutdown_external_mech },
|
||||
/* Obviously this has to die for production use */
|
||||
{ "DBUS_STUPID_TEST_MECH",
|
||||
handle_server_data_stupid_test_mech,
|
||||
handle_encode_stupid_test_mech,
|
||||
handle_decode_stupid_test_mech,
|
||||
handle_server_shutdown_stupid_test_mech,
|
||||
NULL,
|
||||
handle_client_data_stupid_test_mech,
|
||||
handle_encode_stupid_test_mech,
|
||||
handle_decode_stupid_test_mech,
|
||||
|
|
@ -496,8 +700,9 @@ process_auth (DBusAuth *auth,
|
|||
auth->mech = find_mech (&mech);
|
||||
if (auth->mech != NULL)
|
||||
{
|
||||
_dbus_verbose ("Trying mechanism %s\n",
|
||||
auth->mech->mechanism);
|
||||
_dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
|
||||
auth->mech->mechanism,
|
||||
_dbus_string_get_length (&decoded_response));
|
||||
|
||||
if (!(* auth->mech->server_data_func) (auth,
|
||||
&decoded_response))
|
||||
|
|
@ -702,15 +907,30 @@ client_try_next_mechanism (DBusAuth *auth)
|
|||
{
|
||||
_dbus_string_free (&auth_command);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!_dbus_string_append (&auth_command,
|
||||
mech->mechanism))
|
||||
{
|
||||
_dbus_string_free (&auth_command);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (mech->client_initial_response_func != NULL)
|
||||
{
|
||||
if (!_dbus_string_append (&auth_command, " "))
|
||||
{
|
||||
_dbus_string_free (&auth_command);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(* mech->client_initial_response_func) (auth, &auth_command))
|
||||
{
|
||||
_dbus_string_free (&auth_command);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_dbus_string_append (&auth_command,
|
||||
"\r\n"))
|
||||
{
|
||||
|
|
@ -1327,4 +1547,42 @@ _dbus_auth_decode_data (DBusAuth *auth,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets credentials received via reliable means from the operating
|
||||
* system.
|
||||
*
|
||||
* @param auth the auth conversation
|
||||
* @param credentials the credentials received
|
||||
*/
|
||||
void
|
||||
_dbus_auth_set_credentials (DBusAuth *auth,
|
||||
const DBusCredentials *credentials)
|
||||
{
|
||||
auth->credentials = *credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identity we authorized the client as. Apps may have
|
||||
* different policies as to what identities they allow.
|
||||
*
|
||||
* @param auth the auth conversation
|
||||
* @param credentials the credentials we've authorized
|
||||
*/
|
||||
void
|
||||
_dbus_auth_get_identity (DBusAuth *auth,
|
||||
DBusCredentials *credentials)
|
||||
{
|
||||
if (auth->authenticated)
|
||||
{
|
||||
*credentials = auth->authorized_identity;
|
||||
}
|
||||
else
|
||||
{
|
||||
credentials->pid = -1;
|
||||
credentials->uid = -1;
|
||||
credentials->gid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @} */
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <dbus/dbus-macros.h>
|
||||
#include <dbus/dbus-errors.h>
|
||||
#include <dbus/dbus-string.h>
|
||||
#include <dbus/dbus-sysdeps.h>
|
||||
|
||||
DBUS_BEGIN_DECLS;
|
||||
|
||||
|
|
@ -43,26 +44,30 @@ typedef enum
|
|||
|
||||
DBusAuth* _dbus_auth_server_new (void);
|
||||
DBusAuth* _dbus_auth_client_new (void);
|
||||
void _dbus_auth_ref (DBusAuth *auth);
|
||||
void _dbus_auth_unref (DBusAuth *auth);
|
||||
DBusAuthState _dbus_auth_do_work (DBusAuth *auth);
|
||||
dbus_bool_t _dbus_auth_get_bytes_to_send (DBusAuth *auth,
|
||||
const DBusString **str);
|
||||
void _dbus_auth_bytes_sent (DBusAuth *auth,
|
||||
int bytes_sent);
|
||||
dbus_bool_t _dbus_auth_bytes_received (DBusAuth *auth,
|
||||
const DBusString *str);
|
||||
dbus_bool_t _dbus_auth_get_unused_bytes (DBusAuth *auth,
|
||||
DBusString *str);
|
||||
dbus_bool_t _dbus_auth_needs_encoding (DBusAuth *auth);
|
||||
dbus_bool_t _dbus_auth_encode_data (DBusAuth *auth,
|
||||
const DBusString *plaintext,
|
||||
DBusString *encoded);
|
||||
dbus_bool_t _dbus_auth_needs_decoding (DBusAuth *auth);
|
||||
dbus_bool_t _dbus_auth_decode_data (DBusAuth *auth,
|
||||
const DBusString *encoded,
|
||||
DBusString *plaintext);
|
||||
void _dbus_auth_ref (DBusAuth *auth);
|
||||
void _dbus_auth_unref (DBusAuth *auth);
|
||||
DBusAuthState _dbus_auth_do_work (DBusAuth *auth);
|
||||
dbus_bool_t _dbus_auth_get_bytes_to_send (DBusAuth *auth,
|
||||
const DBusString **str);
|
||||
void _dbus_auth_bytes_sent (DBusAuth *auth,
|
||||
int bytes_sent);
|
||||
dbus_bool_t _dbus_auth_bytes_received (DBusAuth *auth,
|
||||
const DBusString *str);
|
||||
dbus_bool_t _dbus_auth_get_unused_bytes (DBusAuth *auth,
|
||||
DBusString *str);
|
||||
dbus_bool_t _dbus_auth_needs_encoding (DBusAuth *auth);
|
||||
dbus_bool_t _dbus_auth_encode_data (DBusAuth *auth,
|
||||
const DBusString *plaintext,
|
||||
DBusString *encoded);
|
||||
dbus_bool_t _dbus_auth_needs_decoding (DBusAuth *auth);
|
||||
dbus_bool_t _dbus_auth_decode_data (DBusAuth *auth,
|
||||
const DBusString *encoded,
|
||||
DBusString *plaintext);
|
||||
void _dbus_auth_set_credentials (DBusAuth *auth,
|
||||
const DBusCredentials *credentials);
|
||||
|
||||
void _dbus_auth_get_identity (DBusAuth *auth,
|
||||
DBusCredentials *credentials);
|
||||
|
||||
DBUS_END_DECLS;
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ unix_handle_watch (DBusServer *server,
|
|||
|
||||
listen_fd = dbus_watch_get_fd (watch);
|
||||
|
||||
client_fd = _dbus_accept_unix_socket (listen_fd);
|
||||
client_fd = _dbus_accept (listen_fd);
|
||||
|
||||
if (client_fd < 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <pwd.h>
|
||||
#ifdef HAVE_WRITEV
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
|
@ -151,7 +152,7 @@ _dbus_write (int fd,
|
|||
|
||||
bytes_written = write (fd, data, len);
|
||||
|
||||
if (errno == EINTR)
|
||||
if (bytes_written < 0 && errno == EINTR)
|
||||
goto again;
|
||||
|
||||
#if 0
|
||||
|
|
@ -226,7 +227,7 @@ _dbus_write_two (int fd,
|
|||
vectors,
|
||||
data2 ? 2 : 1);
|
||||
|
||||
if (errno == EINTR)
|
||||
if (bytes_written < 0 && errno == EINTR)
|
||||
goto again;
|
||||
|
||||
return bytes_written;
|
||||
|
|
@ -367,17 +368,184 @@ _dbus_listen_unix_socket (const char *path,
|
|||
return listen_fd;
|
||||
}
|
||||
|
||||
/* try to read a single byte and return #TRUE if we read it
|
||||
* and it's equal to nul.
|
||||
*/
|
||||
static dbus_bool_t
|
||||
read_credentials_byte (int client_fd,
|
||||
DBusResultCode *result)
|
||||
{
|
||||
char buf[1];
|
||||
int bytes_read;
|
||||
|
||||
again:
|
||||
bytes_read = read (client_fd, buf, 1);
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
else
|
||||
{
|
||||
dbus_set_result (result, _dbus_result_from_errno (errno));
|
||||
_dbus_verbose ("Failed to read credentials byte: %s\n",
|
||||
_dbus_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (bytes_read == 0)
|
||||
{
|
||||
dbus_set_result (result, DBUS_RESULT_IO_ERROR);
|
||||
_dbus_verbose ("EOF reading credentials byte\n");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_assert (bytes_read == 1);
|
||||
|
||||
if (buf[0] != '\0')
|
||||
{
|
||||
dbus_set_result (result, DBUS_RESULT_FAILED);
|
||||
_dbus_verbose ("Credentials byte was not nul\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_dbus_verbose ("read credentials byte\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
write_credentials_byte (int server_fd,
|
||||
DBusResultCode *result)
|
||||
{
|
||||
int bytes_written;
|
||||
char buf[1] = { '\0' };
|
||||
|
||||
again:
|
||||
|
||||
bytes_written = write (server_fd, buf, 1);
|
||||
|
||||
if (bytes_written < 0 && errno == EINTR)
|
||||
goto again;
|
||||
|
||||
if (bytes_written < 0)
|
||||
{
|
||||
dbus_set_result (result, _dbus_result_from_errno (errno));
|
||||
_dbus_verbose ("Failed to write credentials byte: %s\n",
|
||||
_dbus_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
else if (bytes_written == 0)
|
||||
{
|
||||
dbus_set_result (result, DBUS_RESULT_IO_ERROR);
|
||||
_dbus_verbose ("wrote zero bytes writing credentials byte\n");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_assert (bytes_written == 1);
|
||||
_dbus_verbose ("wrote credentials byte\n");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a connection on a listening UNIX socket.
|
||||
* Specific to UNIX domain sockets because we might
|
||||
* add extra args to this function later to get client
|
||||
* credentials. Handles EINTR for you.
|
||||
* Reads a single byte which must be nul (an error occurs otherwise),
|
||||
* and reads unix credentials if available. Fills in pid/uid/gid with
|
||||
* -1 if no credentials are available. 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 anyway just to avoid multiple codepaths.
|
||||
*
|
||||
* Fails if no byte is available, so you must select() first.
|
||||
*
|
||||
* The point of the byte is that on some systems we have to
|
||||
* use sendmsg()/recvmsg() to transmit credentials.
|
||||
*
|
||||
* @param client_fd the client file descriptor
|
||||
* @param credentials struct to fill with credentials of client
|
||||
* @param result location to store result code
|
||||
* @returns #TRUE on success
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_read_credentials_unix_socket (int client_fd,
|
||||
DBusCredentials *credentials,
|
||||
DBusResultCode *result)
|
||||
{
|
||||
credentials->pid = -1;
|
||||
credentials->uid = -1;
|
||||
credentials->gid = -1;
|
||||
|
||||
#ifdef SO_PEERCRED
|
||||
if (read_credentials_byte (client_fd, result))
|
||||
{
|
||||
struct ucred cr;
|
||||
int cr_len = sizeof (cr);
|
||||
|
||||
if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
|
||||
cr_len == sizeof (cr))
|
||||
{
|
||||
credentials->pid = cr.pid;
|
||||
credentials->uid = cr.uid;
|
||||
credentials->gid = cr.gid;
|
||||
_dbus_verbose ("Got credentials pid %d uid %d gid %d\n",
|
||||
credentials->pid,
|
||||
credentials->uid,
|
||||
credentials->gid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n",
|
||||
cr_len, (int) sizeof (cr), _dbus_strerror (errno));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
#else /* !SO_PEERCRED */
|
||||
_dbus_verbose ("Socket credentials not supported on this OS\n");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a single nul byte with our UNIX credentials as ancillary
|
||||
* data. Returns #TRUE if the data was successfully written. On
|
||||
* systems that don't support sending credentials, just writes a byte,
|
||||
* doesn't send any credentials. On some systems, such as Linux,
|
||||
* reading/writing the byte isn't actually required, but we do it
|
||||
* anyway just to avoid multiple codepaths.
|
||||
*
|
||||
* Fails if no byte can be written, so you must select() first.
|
||||
*
|
||||
* The point of the byte is that on some systems we have to
|
||||
* use sendmsg()/recvmsg() to transmit credentials.
|
||||
*
|
||||
* @param server_fd file descriptor for connection to server
|
||||
* @param result return location for error code
|
||||
* @returns #TRUE if the byte was sent
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_send_credentials_unix_socket (int server_fd,
|
||||
DBusResultCode *result)
|
||||
{
|
||||
if (write_credentials_byte (server_fd, result))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a connection on a listening socket.
|
||||
* Handles EINTR for you.
|
||||
*
|
||||
* @param listen_fd the listen file descriptor
|
||||
* @returns the connection fd of the client, or -1 on error
|
||||
*/
|
||||
int
|
||||
_dbus_accept_unix_socket (int listen_fd)
|
||||
_dbus_accept (int listen_fd)
|
||||
{
|
||||
int client_fd;
|
||||
|
||||
|
|
@ -389,7 +557,7 @@ _dbus_accept_unix_socket (int listen_fd)
|
|||
if (errno == EINTR)
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
||||
return client_fd;
|
||||
}
|
||||
|
||||
|
|
@ -559,4 +727,168 @@ _dbus_string_parse_double (const DBusString *str,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/** @} end of DBusString */
|
||||
/**
|
||||
* Gets the credentials corresponding to the given username.
|
||||
*
|
||||
* @param username the username
|
||||
* @param credentials credentials to fill in
|
||||
* @returns #TRUE if the username existed and we got some credentials
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_credentials_from_username (const DBusString *username,
|
||||
DBusCredentials *credentials)
|
||||
{
|
||||
const char *username_c_str;
|
||||
|
||||
credentials->pid = -1;
|
||||
credentials->uid = -1;
|
||||
credentials->gid = -1;
|
||||
|
||||
_dbus_string_get_const_data (username, &username_c_str);
|
||||
|
||||
#ifdef HAVE_GETPWNAM_R
|
||||
{
|
||||
struct passwd *p;
|
||||
int result;
|
||||
char buf[1024];
|
||||
struct passwd p_str;
|
||||
|
||||
p = NULL;
|
||||
result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf),
|
||||
&p);
|
||||
|
||||
if (result == 0 && p == &p_str)
|
||||
{
|
||||
credentials->uid = p->pw_uid;
|
||||
credentials->gid = p->pw_gid;
|
||||
|
||||
_dbus_verbose ("Username %s has uid %d gid %d\n",
|
||||
username_c_str, credentials->uid, credentials->gid);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_verbose ("User %s unknown\n", username_c_str);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
#else /* ! HAVE_GETPWNAM_R */
|
||||
{
|
||||
/* I guess we're screwed on thread safety here */
|
||||
struct passwd *p;
|
||||
|
||||
p = getpwnam (username_c_str);
|
||||
|
||||
if (p != NULL)
|
||||
{
|
||||
credentials->uid = p->pw_uid;
|
||||
credentials->gid = p->pw_gid;
|
||||
|
||||
_dbus_verbose ("Username %s has uid %d gid %d\n",
|
||||
username_c_str, credentials->uid, credentials->gid);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_verbose ("User %s unknown\n", username_c_str);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets credentials from a UID string. (Parses a string to a UID
|
||||
* and converts to a DBusCredentials.)
|
||||
*
|
||||
* @param uid_str the UID in string form
|
||||
* @param credentials credentials to fill in
|
||||
* @returns #TRUE if successfully filled in some credentials
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_credentials_from_uid_string (const DBusString *uid_str,
|
||||
DBusCredentials *credentials)
|
||||
{
|
||||
int end;
|
||||
long uid;
|
||||
|
||||
credentials->pid = -1;
|
||||
credentials->uid = -1;
|
||||
credentials->gid = -1;
|
||||
|
||||
if (_dbus_string_get_length (uid_str) == 0)
|
||||
{
|
||||
_dbus_verbose ("UID string was zero length\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
uid = -1;
|
||||
end = 0;
|
||||
if (!_dbus_string_parse_int (uid_str, 0, &uid,
|
||||
&end))
|
||||
{
|
||||
_dbus_verbose ("could not parse string as a UID\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (end != _dbus_string_get_length (uid_str))
|
||||
{
|
||||
_dbus_verbose ("string contained trailing stuff after UID\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
credentials->uid = uid;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the credentials of the current process.
|
||||
*
|
||||
* @param credentials credentials to fill in.
|
||||
*/
|
||||
void
|
||||
_dbus_credentials_from_current_process (DBusCredentials *credentials)
|
||||
{
|
||||
credentials->pid = getpid ();
|
||||
credentials->uid = getuid ();
|
||||
credentials->gid = getgid ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the provided_credentials are allowed to log in
|
||||
* as the expected_credentials.
|
||||
*
|
||||
* @param expected_credentials credentials we're trying to log in as
|
||||
* @param provided_credentials credentials we have
|
||||
* @returns #TRUE if we can log in
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_credentials_match (const DBusCredentials *expected_credentials,
|
||||
const DBusCredentials *provided_credentials)
|
||||
{
|
||||
if (provided_credentials->uid < 0)
|
||||
return FALSE;
|
||||
else if (expected_credentials->uid < 0)
|
||||
return FALSE;
|
||||
else if (provided_credentials->uid == 0)
|
||||
return TRUE;
|
||||
else if (provided_credentials->uid == expected_credentials->uid)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the uid of the current process to the given string.
|
||||
*
|
||||
* @param str the string to append to
|
||||
* @returns #TRUE on success
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_string_append_our_uid (DBusString *str)
|
||||
{
|
||||
return _dbus_string_append_int (str, getuid ());
|
||||
}
|
||||
|
||||
/** @} end of sysdeps */
|
||||
|
|
|
|||
|
|
@ -65,12 +65,36 @@ int _dbus_write_two (int fd,
|
|||
int start2,
|
||||
int len2);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* -1 if not available */
|
||||
int pid;
|
||||
int uid;
|
||||
int gid;
|
||||
} DBusCredentials;
|
||||
|
||||
int _dbus_connect_unix_socket (const char *path,
|
||||
DBusResultCode *result);
|
||||
int _dbus_listen_unix_socket (const char *path,
|
||||
DBusResultCode *result);
|
||||
int _dbus_accept_unix_socket (int listen_fd);
|
||||
int _dbus_accept (int listen_fd);
|
||||
|
||||
dbus_bool_t _dbus_read_credentials_unix_socket (int client_fd,
|
||||
DBusCredentials *credentials,
|
||||
DBusResultCode *result);
|
||||
dbus_bool_t _dbus_send_credentials_unix_socket (int server_fd,
|
||||
DBusResultCode *result);
|
||||
|
||||
|
||||
dbus_bool_t _dbus_credentials_from_username (const DBusString *username,
|
||||
DBusCredentials *credentials);
|
||||
dbus_bool_t _dbus_credentials_from_uid_string (const DBusString *uid_str,
|
||||
DBusCredentials *credentials);
|
||||
void _dbus_credentials_from_current_process (DBusCredentials *credentials);
|
||||
dbus_bool_t _dbus_credentials_match (const DBusCredentials *expected_credentials,
|
||||
const DBusCredentials *provided_credentials);
|
||||
|
||||
dbus_bool_t _dbus_string_append_our_uid (DBusString *str);
|
||||
|
||||
DBUS_END_DECLS;
|
||||
|
||||
|
|
|
|||
|
|
@ -77,9 +77,14 @@ struct DBusTransport
|
|||
|
||||
DBusAuth *auth; /**< Authentication conversation */
|
||||
|
||||
DBusCredentials credentials; /**< Credentials of other end */
|
||||
|
||||
unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */
|
||||
unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */
|
||||
unsigned int messages_need_sending : 1; /**< #TRUE if we need to write messages out */
|
||||
unsigned int send_credentials_pending : 1; /**< #TRUE if we need to send credentials */
|
||||
unsigned int receive_credentials_pending : 1; /**< #TRUE if we need to receive credentials */
|
||||
unsigned int is_server : 1; /**< #TRUE if on the server side */
|
||||
};
|
||||
|
||||
dbus_bool_t _dbus_transport_init_base (DBusTransport *transport,
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ check_write_watch (DBusTransport *transport)
|
|||
if (_dbus_transport_get_is_authenticated (transport))
|
||||
need_write_watch = transport->messages_need_sending;
|
||||
else
|
||||
need_write_watch = _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
|
||||
need_write_watch = transport->send_credentials_pending ||
|
||||
_dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
|
||||
|
||||
if (transport->disconnected)
|
||||
need_write_watch = FALSE;
|
||||
|
|
@ -390,16 +391,71 @@ recover_unused_bytes (DBusTransport *transport)
|
|||
do_io_error (transport);
|
||||
}
|
||||
|
||||
static void
|
||||
exchange_credentials (DBusTransport *transport,
|
||||
dbus_bool_t do_reading,
|
||||
dbus_bool_t do_writing)
|
||||
{
|
||||
DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
|
||||
|
||||
if (do_writing && transport->send_credentials_pending)
|
||||
{
|
||||
if (_dbus_send_credentials_unix_socket (unix_transport->fd,
|
||||
NULL))
|
||||
{
|
||||
transport->send_credentials_pending = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_verbose ("Failed to write credentials\n");
|
||||
do_io_error (transport);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_reading && transport->receive_credentials_pending)
|
||||
{
|
||||
if (_dbus_read_credentials_unix_socket (unix_transport->fd,
|
||||
&transport->credentials,
|
||||
NULL))
|
||||
{
|
||||
transport->receive_credentials_pending = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_verbose ("Failed to read credentials\n");
|
||||
do_io_error (transport);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(transport->send_credentials_pending ||
|
||||
transport->receive_credentials_pending))
|
||||
{
|
||||
_dbus_auth_set_credentials (transport->auth,
|
||||
&transport->credentials);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_authentication (DBusTransport *transport,
|
||||
dbus_bool_t do_reading,
|
||||
dbus_bool_t do_writing)
|
||||
{
|
||||
{
|
||||
_dbus_transport_ref (transport);
|
||||
|
||||
while (!_dbus_transport_get_is_authenticated (transport) &&
|
||||
_dbus_transport_get_is_connected (transport))
|
||||
{
|
||||
exchange_credentials (transport, do_reading, do_writing);
|
||||
|
||||
if (transport->send_credentials_pending ||
|
||||
transport->receive_credentials_pending)
|
||||
{
|
||||
_dbus_verbose ("send_credentials_pending = %d receive_credentials_pending = %d\n",
|
||||
transport->send_credentials_pending,
|
||||
transport->receive_credentials_pending);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (_dbus_auth_do_work (transport->auth))
|
||||
{
|
||||
case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
|
||||
|
|
@ -963,7 +1019,7 @@ _dbus_transport_new_for_domain_socket (const char *path,
|
|||
close (fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
|
||||
return transport;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,6 +105,13 @@ _dbus_transport_init_base (DBusTransport *transport,
|
|||
transport->authenticated = FALSE;
|
||||
transport->messages_need_sending = FALSE;
|
||||
transport->disconnected = FALSE;
|
||||
transport->send_credentials_pending = !server;
|
||||
transport->receive_credentials_pending = server;
|
||||
transport->is_server = server;
|
||||
|
||||
transport->credentials.pid = -1;
|
||||
transport->credentials.uid = -1;
|
||||
transport->credentials.gid = -1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -205,8 +212,12 @@ _dbus_transport_disconnect (DBusTransport *transport)
|
|||
|
||||
DBUS_TRANSPORT_HOLD_REF (transport);
|
||||
(* transport->vtable->disconnect) (transport);
|
||||
|
||||
|
||||
transport->disconnected = TRUE;
|
||||
|
||||
_dbus_connection_transport_error (transport->connection,
|
||||
DBUS_RESULT_DISCONNECTED);
|
||||
|
||||
DBUS_TRANSPORT_RELEASE_REF (transport);
|
||||
}
|
||||
|
||||
|
|
@ -238,9 +249,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport)
|
|||
return TRUE;
|
||||
else
|
||||
{
|
||||
if (transport->disconnected)
|
||||
return FALSE;
|
||||
|
||||
transport->authenticated =
|
||||
(!(transport->send_credentials_pending ||
|
||||
transport->receive_credentials_pending)) &&
|
||||
_dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED;
|
||||
|
||||
/* If we've authenticated as some identity, check that the auth
|
||||
* identity is the same as our own identity. In the future, we
|
||||
* may have API allowing applications to specify how this is
|
||||
* done, for example they may allow connection as any identity,
|
||||
* but then impose restrictions on certain identities.
|
||||
* Or they may give certain identities extra privileges.
|
||||
*/
|
||||
|
||||
if (transport->authenticated && transport->is_server)
|
||||
{
|
||||
DBusCredentials auth_identity;
|
||||
DBusCredentials our_identity;
|
||||
|
||||
_dbus_credentials_from_current_process (&our_identity);
|
||||
_dbus_auth_get_identity (transport->auth, &auth_identity);
|
||||
|
||||
if (!_dbus_credentials_match (&our_identity,
|
||||
&auth_identity))
|
||||
{
|
||||
_dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
|
||||
auth_identity.uid, our_identity.uid);
|
||||
_dbus_transport_disconnect (transport);
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
|
||||
auth_identity.uid, our_identity.uid);
|
||||
}
|
||||
}
|
||||
|
||||
return transport->authenticated;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
|
|||
in this document are to be interpreted as defined in "Key words for
|
||||
use in RFCs to Indicate Requirement Levels" [RFC 2119]
|
||||
|
||||
Overview
|
||||
Protocol Overview
|
||||
===
|
||||
|
||||
The protocol is a line-based protocol, where each line ends with
|
||||
|
|
@ -52,6 +52,27 @@ From server to client are as follows:
|
|||
|
||||
ERROR
|
||||
|
||||
|
||||
Special credentials-passing nul byte
|
||||
===
|
||||
|
||||
Immediately after connecting to the server, the client must send a
|
||||
single nul byte. This byte may be accompanied by credentials
|
||||
information on some operating systems that use sendmsg() with
|
||||
SCM_CREDS or SCM_CREDENTIALS to pass credentials over UNIX domain
|
||||
sockets. However, the nul byte MUST be sent even on other kinds of
|
||||
socket, and even on operating systems that do not require a byte to be
|
||||
sent in order to transmit credentials. The text protocol described in
|
||||
this document begins after the single nul byte. If the first byte
|
||||
received from the client is not a nul byte, the server may disconnect
|
||||
that client.
|
||||
|
||||
A nul byte in any context other than the initial byte is an error;
|
||||
the protocol is ASCII-only.
|
||||
|
||||
The credentials sent along with the nul byte may be used with the
|
||||
SASL mechanism EXTERNAL.
|
||||
|
||||
AUTH Command
|
||||
===
|
||||
|
||||
|
|
|
|||
|
|
@ -305,6 +305,9 @@ error_handler (DBusConnection *connection,
|
|||
"Error on connection: %s\n",
|
||||
dbus_result_to_string (error_code));
|
||||
|
||||
/* we don't want to be called again since we're dropping the connection */
|
||||
dbus_connection_set_error_function (connection, NULL, NULL, NULL);
|
||||
|
||||
_dbus_list_remove (&connections, connection);
|
||||
dbus_connection_unref (connection);
|
||||
quit_mainloop ();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue