Credential Passing (SO_PEERCRED)

When D-Bus checks if your process is allowed to access a secret, it asks the kernel — not your process. SO_PEERCRED provides unforgeable identity by reading directly from the kernel’s task_struct. This is authentication without passwords, enforced at the syscall level.

Credential Passing: Kernel-Enforced Identity

Kernel Foundation

DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

D-Bus is a message-passing system. The session bus (dbus-daemon or dbus-broker) listens on a Unix socket. Applications like gopass use it to talk to gnome-keyring or secret-service for credential storage.

The Kernel Primitive: SO_PEERCRED

struct ucred cred;
getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len);
// cred.uid  -- kernel-verified, unforgeable
// cred.pid  -- kernel-verified, unforgeable
// cred.gid  -- kernel-verified, unforgeable

When a process connects to the D-Bus socket, the kernel fills in the peer credentials from the process’s task_struct — the kernel’s representation of a process (include/linux/sched.h). The D-Bus daemon uses this to enforce access policies: it knows exactly which UID/PID is making each request, and the connecting process cannot lie about this.

This is kernel-enforced identity, not userspace trust. The credentials come from the kernel’s own process table, not from anything the connecting process sends.

SO_PEERCRED vs SCM_CREDENTIALS

Mechanism How It Works When to Use

SO_PEERCRED

Kernel auto-fills from task_struct at connect time

Simple: "who connected to me?" (D-Bus, systemd)

SCM_CREDENTIALS

Sender includes struct ucred in ancillary data; kernel validates and may override the values

Advanced: per-message credential tagging

With SCM_CREDENTIALS, the sender can claim a UID/GID, but the kernel overrides the values unless the sender has CAP_SYS_ADMIN. A non-root process sending SCM_CREDENTIALS with a fake UID gets its real UID stamped by the kernel. This is the "trust but verify" pattern implemented at the kernel level.

Why gopass Needs D-Bus

gopass show -c performs two IPC operations:

  1. Retrieves the secret via D-Bus to the keyring daemon (gnome-keyring or secret-service)

  2. Copies to clipboard via wl-copy to the Wayland compositor

Without DBUS_SESSION_BUS_ADDRESS, gopass cannot find the bus socket, so it cannot reach the keyring daemon. The credential check (SO_PEERCRED) happens transparently — the D-Bus daemon verifies gopass’s identity before allowing access to stored secrets.

The task_struct Connection

SO_PEERCRED reads from the process’s task_struct, which is the kernel’s central data structure for process state:

// include/linux/sched.h (simplified)
struct task_struct {
    pid_t pid;                    // Process ID
    const struct cred *cred;      // Process credentials
    struct mm_struct *mm;         // Memory map
    struct files_struct *files;   // Open file descriptors
    // ... hundreds more fields
};

The cred field points to a struct cred containing the process’s real/effective/saved UIDs and GIDs, capabilities, and security context. This is the same structure that the kernel checks on every file access, every signal delivery, and every syscall that requires privilege. D-Bus simply leverages the same infrastructure.

Kernel Source Map

Source File What It Does Key Functions

net/unix/af_unix.c

SO_PEERCRED fills credentials at connect time

unix_stream_connect() stores creds in sk→sk_peer_cred

include/linux/sched.h

task_struct definition

Process state, credentials, memory, files

net/core/sock.c

SO_PEERCRED getsockopt handler

sock_getsockopt() case SO_PEERCRED

include/linux/cred.h

Credential structure

struct cred — uid, gid, capabilities, security context

kernel/cred.c

Credential management

prepare_creds(), commit_creds(), override_creds()

Exploration Exercises

# See D-Bus peers and their credentials
busctl --user list

# Trace D-Bus socket operations
strace -e connect,getsockopt gopass show -c v3/test 2>&1 | grep -E 'PEER|unix'

# Inspect the D-Bus socket
ss -xlp | grep bus

# Read your own task_struct via procfs
cat /proc/self/status | grep -E '^(Pid|Uid|Gid|Cap)'