diff options
Diffstat (limited to 'pam')
-rw-r--r-- | pam/gkr-pam-client.c | 146 |
1 files changed, 118 insertions, 28 deletions
diff --git a/pam/gkr-pam-client.c b/pam/gkr-pam-client.c index 25c32fed..f0444ce6 100644 --- a/pam/gkr-pam-client.c +++ b/pam/gkr-pam-client.c @@ -51,7 +51,74 @@ #define PAM_APP_NAME_LEN (sizeof (PAM_APP_NAME) - 1) static int -connect_to_daemon (const char *path) +check_peer_same_uid (int sock) +{ + uid_t uid = -1; + + /* + * Certain OS require a message to be sent over the unix socket for the + * otherside to get the process credentials. Most uncool. + * + * The normal gnome-keyring protocol accomodates this and the client + * sends a message/byte before sending anything else. This only works + * for the daemon verifying the client. + * + * This code here is used by a client to verify the daemon is running + * as the right user. Since we cannot modify the protocol, this only + * works on OSs that can do this credentials lookup transparently. + */ + +/* Linux */ +#if defined(SO_PEERCRED) + struct ucred cr; + socklen_t cr_len = sizeof (cr); + + if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 && + cr_len == sizeof (cr)) { + uid = cr.uid; + } else { + syslog (GKR_LOG_ERR, "could not get gnome-keyring-daemon socket credentials, " + "(returned len %d/%d)\n", cr_len, (int) sizeof (cr)); + return -1; + } + + +/* The BSDs */ +#elif defined(LOCAL_PEERCRED) + uid_t gid; + struct xucred xuc; + socklen_t xuc_len = sizeof (xuc); + + if (getsockopt (sock, SOL_SOCKET, LOCAL_PEERCRED, &xuc, &xuc_len) == 0 && + xuc_len == sizeof (xuc) { + uid = xuc.cr_uid; + } else { + syslog (GKR_LOG_ERR, "could not get gnome-keyring-daemon socket credentials, " + "(returned len %d/%d)\n", xuc_len, (int)sizeof (xuc)); + return -1; + } + + +/* NOTE: Add more here */ +#else + syslog (GKR_LOG_WARN, "Cannot verify that the process to which we are passing the login" + " password is genuinely running as the same user login: not supported on this OS."); + uid = geteuid (); + + +#endif + + if (uid != geteuid ()) { + syslog (GKR_LOG_ERR, "The gnome keyring socket is not running with the same " + "credentials as the user login. Disconnecting."); + return 0; + } + + return 1; +} + +static int +write_credentials_byte (int sock) { #if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__)) union { @@ -62,32 +129,16 @@ connect_to_daemon (const char *path) struct msghdr msg; #endif - struct sockaddr_un addr; - int sock, bytes_written; - char buf; + int bytes_written; + char buf = 0; - addr.sun_family = AF_UNIX; - strncpy (addr.sun_path, path, sizeof (addr.sun_path)); - - sock = socket (AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - syslog (GKR_LOG_ERR, "couldn't create socket: %s", strerror (errno)); - return -1; - } - - /* close on exec */ - fcntl (sock, F_SETFD, 1); + /* + * All this fuss is for certain OS's, as a byte needs to have + * gone through the socket before they know who is on the + * other side. :( + */ - if (connect (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) { - syslog (GKR_LOG_ERR, "couldn't connect to daemon at: %s: %s", - path, strerror (errno)); - close (sock); - return -1; - } - - /* Write the credentials byte */ - buf = 0; -#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__)) +#if !defined(HAVE_GETPEEREID) && defined(HAVE_CMSGCRED) iov.iov_base = &buf; iov.iov_len = 1; @@ -105,7 +156,7 @@ connect_to_daemon (const char *path) again: -#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__)) +#if !defined(HAVE_GETPEEREID) && defined(HAVE_CMSGCRED) bytes_written = sendmsg (sock, &msg, 0); #else bytes_written = write (sock, &buf, 1); @@ -114,12 +165,51 @@ again: if (bytes_written < 0) { if (errno == EINTR || errno == EAGAIN) goto again; - syslog (GKR_LOG_ERR, "couldn't send credentials to: %s: %s", + syslog (GKR_LOG_ERR, "couldn't send credentials to daemon: %s", + strerror (errno)); + return -1; + } + + return 0; +} + +static int +connect_to_daemon (const char *path) +{ + struct sockaddr_un addr; + int sock; + + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, path, sizeof (addr.sun_path)); + + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + syslog (GKR_LOG_ERR, "couldn't create socket: %s", strerror (errno)); + return -1; + } + + /* close on exec */ + fcntl (sock, F_SETFD, 1); + + if (connect (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) { + syslog (GKR_LOG_ERR, "couldn't connect to daemon at: %s: %s", path, strerror (errno)); close (sock); return -1; } - + + /* Verify the server is running as the right user */ + if (check_peer_same_uid (sock) <= 0) { + close (sock); + return -1; + } + + /* This lets the server verify us */ + if (write_credentials_byte (sock) < 0) { + close (sock); + return -1; + } + return sock; } |