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:
Havoc Pennington 2003-01-04 07:28:54 +00:00
parent 1ed128b524
commit 01af5ff410
12 changed files with 817 additions and 41 deletions

View file

@ -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),

View file

@ -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.

View file

@ -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;
}
}
/** @} */

View file

@ -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;

View file

@ -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)
{

View file

@ -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 */

View file

@ -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;

View file

@ -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,

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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
===

View file

@ -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 ();