3.2 Opening and creating tokens
Each of these returns a token fd (or -1 with errno).
int ;
int ;
int ;
int ;
int ;
| Function | Opens |
|---|---|
peios_token_open_self | The calling thread's token. flags may be KACS_TOKEN_OPEN_REAL to get the primary token even while the thread is impersonating; otherwise you get the effective (impersonation-aware) token. access is the desired handle rights. |
peios_token_open_process | The primary token of the process named by pidfd. Subject to a process-query access check and PIP dominance over the target. |
peios_token_open_thread | Thread tid's impersonation token if it is impersonating, else the process primary token. |
peios_token_open_peer | The peer-identity token captured at connect() on a connected Unix stream/seqpacket socket conn_fd — how a server learns who is on the other end of a socket. Reads the KACS_SO_PEER_TOKEN socket option (level SOL_KACS, <pkm/socket.h>). The handle carries fixed `QUERY |
peios_token_create_raw | Mints a token from a pre-built token-spec buffer. This is the escape hatch — prefer the builder below. Requires SeCreateTokenPrivilege. |
Errors, per call:
peios_token_open_self—EINVAL(unknownflags; empty or unknownaccessbits),EACCES(the token's own SD deniesaccess).peios_token_open_process—EACCES(any of the three checks failed — process-query right, PIP dominance, or the token SD; deliberately indistinguishable),EBADF(invalid pidfd),ESRCH(target exited),EINVAL(empty or unknownaccessbits).peios_token_open_thread— the_open_processset, plusESRCH(thread exited, or not inpidfd's process) andEINVAL(tid <= 0).peios_token_open_peer—ENOTCONN(not connected),ENODATA(connected but no captured identity — a socketpair end),EOPNOTSUPP(a datagram or non-Unix socket),ENOTSOCK(not a socket),EBADF(invalid fd).peios_token_create_raw—EPERM(privilege missing),EINVAL(spec failed kernel validation),EFAULT(bad spec pointer),ENOMEM(allocation failed).
peios_token_open_peer is the cornerstone of local authentication: accept a connection, open the peer token, and you have the caller's identity to query or impersonate — no password, no handshake, just the kernel's word for who connected.
3.2.1 Peer identity on sockets #
int ;
int ;
int ;
int ;
int ;
peios_token_impersonate_peerispeios_token_open_peerfollowed bypeios_token_impersonateand aclose— the one-call form for a handler that impersonates, works, and reverts on a single thread. It returns 0 or -1; the errno is whichever step failed.peios_socket_set_impersonation_levelis the client's side of the exchange: it sets theKACS_SO_IMPERSONATION_LEVELsocket option (KACS_IMLEVEL_*), bounding the level at which the peer may capture this socket's identity — Identification to be known but not acted as, Anonymous to withhold identity entirely. Usually set beforeconnect(); it may change at any time and bounds every capture from then on. The default isKACS_IMLEVEL_IMPERSONATION. Errors:EINVAL(unknown level),EOPNOTSUPP(a non-Unix socket).peios_socket_get_impersonation_levelreads the level back into*level.peios_socket_restampreplaces the identity a listening socket conveys to connecting clients — captured whenlisten()was called — with the caller's own (KACS_SO_RESTAMP;EINVALif the socket is not listening). Capture is symmetric: a client'speios_token_open_peeron its own socket right afterconnect()returns the listener's identity at Identification level, which is how a client verifies the service it reached. A service that receives listeners it did not create (from the descriptor store after a restart, or from a broker) restamps them before accepting.peios_socket_set_pass_tokenturns on per-message identity for a sending socket (KACS_SO_PASS_TOKEN): every send then carries the sender's effective identity as aKACS_SCM_TOKENancillary message, and the peer'speios_token_open_peerfollows whoever was impersonating at each send. This is what makes a pooled or multiplexed connection convey the right identity per request without the client library knowing. To attach a specific token to one message instead — a router forwarding for many clients, or a submitter handing a job manager the identity a job should run as — usepeios_socket_send_messagebelow.
3.2.2 Per-message identity and descriptors #
A message on a Unix socket can carry two things beside its bytes: a token the kernel attests the sender could act as (KACS_SCM_TOKEN, level SOL_KACS) and a set of file descriptors (SCM_RIGHTS). Building the control buffer by hand and walking it back is the same twenty lines in every program that does it, and a wrong CMSG_ALIGN is silent; these calls do it once.
;
ssize_t ;
ssize_t ;
int ;
peios_socket_send_messagesendslenbytes frombufonsock_fdwith onesendmsg(2), attachingtoken_fdas aKACS_SCM_TOKENwhen it is>= 0andfds[0..fd_count]asSCM_RIGHTSwhenfd_count > 0;flagsaresendmsgflags. Returns the bytes sent. The kernel gates the token attach exactly as it gates impersonating it — the handle needsTOKEN_IMPERSONATE, and the token travels at the sending socket's impersonation level (peios_socket_set_impersonation_level, default Impersonation), which can only lower what the token already carries: if attaching would have to lower the token's level the send fails rather than downgrading. Ownership is unchanged by a send — the caller keepstoken_fdand every entry offds; the kernel duplicates them into the receiver on delivery.peios_socket_recv_messagereceives one message withrecvmsg(2)intobuf/cap, with room for one attached token andmsg->fd_capdescriptors written into the caller-ownedmsg->fds(passNULLwithfd_cap == 0to accept none).MSG_CMSG_CLOEXECis always added toflags, so every descriptor delivered isO_CLOEXEC. Returns the bytes received (0at end of stream). On return the caller ownsmsg->token_fd(when>= 0) andmsg->fds[0..fd_count]and closes each; the token handle carriesQUERY | IMPERSONATE | DUPLICATE, the same fixed rights aspeios_token_open_peer. Nothing leaks on any path: a second token in one message, or a descriptor pastfd_cap, is closed by the library before it returns, andmsg->flagsreports the loss asPEIOS_SOCKET_MSG_CTRUNCATED;PEIOS_SOCKET_MSG_TRUNCATEDmeans the bytes did not fitcap(on aSOCK_SEQPACKETsocket the tail is gone). A receiver that cannot accept a truncated message closes what it was handed. On-1nothing was consumed and*msgis untouched.fd_capabove the kernel'sSCM_MAX_FD(253) is capped silently. The truncation flag also covers handles that fit in the syscall's ancillary buffer but exceed the caller's capacity. For example,fd_cap == 0closes any received rights and reports control truncation. This flag is retained alongside payload truncation when both occur.peios_socket_peer_pidfdreturns a new pidfd (O_CLOEXEC) for the process on the other end of a connected Unix socket —getsockopt(SOL_SOCKET, SO_PEERPIDFD), a Linux ≥ 6.5 facility that libpeios only wraps; the option itself comes from the kernel UAPI and glibc. It is the kernel's handle on the peer, which is whatpeios_token_open_processtakes to open the peer's primary token — the identity a process has, as distinct from the identity it conveyed atconnect()(peios_token_open_peer) — and never a PID the peer named, so a recycled PID can never be opened by mistake.
Errors: peios_socket_send_message — EINVAL (NULL buffer with len > 0, NULL fds with fd_count > 0, or fd_count above SCM_MAX_FD), EACCES (token_fd lacks TOKEN_IMPERSONATE), EPERM (attaching would lower the token's level), EBADF (a descriptor in fds is not open), plus the sendmsg set (ENOTCONN, EPIPE, EMSGSIZE, EAGAIN). peios_socket_recv_message — EINVAL (NULL msg), plus the recvmsg set (ENOTCONN, EAGAIN, EBADF). peios_socket_peer_pidfd — ENODATA (no connected peer to name), ENOPROTOOPT (a kernel without SO_PEERPIDFD), ENOTSOCK, EBADF, EMFILE.
The client side of a job submission (PSPU §7) is the archetype — one record, one identity, two descriptors:
const char *request = "{\"command\":\"submit\",\"image_path\":\"/usr/bin/backup\","
"\"descriptors\":[\"control\"],\"output\":true}";
int fds = ; /* named descriptors first, output sink last */
/* `job_token` is a token I hold with TOKEN_IMPERSONATE; the job runs as it. */
if
/* I still own job_token, control_sock and my stdout. */
and the manager's side, receiving it:
char buf;
int fds;
struct peios_socket_message msg = ;
ssize_t n = ;
if
if
if
if else
/* msg.fds[0..msg.fd_count] and msg.token_fd are mine to use and close */