Documentation
¶
Overview ¶
Package ipc — the private socket's codes, range 0.3.91.* (ADR 0148).
The range was allocated to internal/service/proc/ipc, the engine, and is declared here, at the same path in the core, since ADR 0160. The values did not change with the move, and the LL byte still records the service layer that allocated them.
Package ipc — the sentinels internal/service/proc/ipc refuses with. Each var's name equals its errs.Define Reason in SCREAMING_SNAKE form. None quotes a path in its Public half: the path travels in the "path" field, for the log. The Private strings name the service package, where each condition is detected.
Package ipc is the private socket's contract (ADR 0148, ADR 0160): who is at the other end of a connection, the connection that carries that answer, the two ports a private socket is reached through — the Listener that accepts and the Dialer that connects — and the codes every refusal carries.
The engine is internal/service/proc/ipc: a Unix socket in a 0700 directory whose whole path is audited, the kernel's word on the peer where it gives one, a named pipe with its own DACL on Windows. Nothing here opens a socket. The ports exist so a caller that holds a Listener or a Dialer can be handed a double in a test — connections from net.Pipe with the peer the test chooses — instead of a socket on disk.
Index ¶
Constants ¶
const CodeClosed errs.Code = 0x00_03_5B_08 // 0.3.91.8
CodeClosed identifies an Accept on a listener that was closed.
const CodeDialFailed errs.Code = 0x00_03_5B_06 // 0.3.91.6
CodeDialFailed identifies a dial that did not reach a listener.
const CodeDirectoryUnsafe errs.Code = 0x00_03_5B_02 // 0.3.91.2
CodeDirectoryUnsafe identifies a socket directory another account could plant an entry in or read through: not a directory, a link, not owned by this process's user, or writable by its group or by anyone.
const CodeEndpointForeign errs.Code = 0x00_03_5B_07 // 0.3.91.7
CodeEndpointForeign identifies a socket file owned by another account: a client refuses to talk to what may be an impostor.
const CodeEndpointSquatted errs.Code = 0x00_03_5B_0B // 0.3.91.11
CodeEndpointSquatted identifies a Windows pipe name another account holds: the name exists, and the pipe is not this account's — its owner is another account, or its DACL admits no instance of this account. A squatter can compute the name, since the account it is derived from is no secret, so the listen cannot succeed while the squatter holds it.
It is its own code, not IN_USE, because the remedy differs: IN_USE says this account's daemon already runs (talk to it), this says another account took the name first (find the process that holds it).
const CodeFDPassRefused errs.Code = 0x00_03_5B_0A // 0.3.91.10
CodeFDPassRefused identifies descriptors not passed: the peer is not one the kernel named, the payload is empty, max is not positive, the platform passes none, or the send or receive failed.
const CodeInUse errs.Code = 0x00_03_5B_03 // 0.3.91.3
CodeInUse identifies a socket path a live process already answers on.
const CodeListenFailed errs.Code = 0x00_03_5B_04 // 0.3.91.4
CodeListenFailed identifies a listen the kernel refused.
const CodeMisconfigured errs.Code = 0x00_03_5B_01 // 0.3.91.1
CodeMisconfigured identifies a configuration no endpoint can be built from: an empty or relative path, one longer than the kernel's sun_path, a UID or GID list holding a negative value, a SID list off Windows, or an entry of it that is not a SID or stands for every caller.
const CodePathUnsafe errs.Code = 0x00_03_5B_09 // 0.3.91.9
CodePathUnsafe identifies a socket path another account could steer: a component ABOVE the socket's directory, or the directory holding it, lets another account decide where the path leads — a link planted where anybody can write, a directory another account created there, or an entry anybody can replace. The socket directory's own entry stays CodeDirectoryUnsafe's.
It is its own code, as LOCK_PATH_REDIRECTED is lock's (ADR 0083), because the remedy differs: DIRECTORY_UNSAFE asks for a chmod or a chown, this asks a human to look at a component that may be an attack in progress — or to move the socket under the directory the engine's RuntimeDir names.
const CodePeerRefused errs.Code = 0x00_03_5B_05 // 0.3.91.5
CodePeerRefused identifies a peer whose kernel-reported identity is not this process's user nor in the allow-lists.
Variables ¶
var ( // Misconfigured refuses a configuration no endpoint can be built from. Misconfigured = errs.Static(CodeMisconfigured, "MISCONFIGURED", "The private socket is misconfigured", "service/proc/ipc: empty or relative path, path beyond sun_path, a negative UID/GID, a SID list off Windows, or a SID entry that is not one or names every caller; the rule field says which, the sid field the entry", 0, 78) // DirectoryUnsafe refuses a socket directory another account could reach. DirectoryUnsafe = errs.Static(CodeDirectoryUnsafe, "DIRECTORY_UNSAFE", "The private socket's directory is not private", "service/proc/ipc: the directory is a link, not a directory, not ours, or group/world writable; the rule field says which", 0, 78) // InUse refuses to listen where a live process already answers. InUse = errs.Static(CodeInUse, "IN_USE", "Another process already listens on this private socket", "service/proc/ipc: a dial to the existing socket succeeded, so it was not removed", 0, 0) // ListenFailed reports a listen the kernel refused. ListenFailed = errs.Static(CodeListenFailed, "LISTEN_FAILED", "The private socket could not be opened", "service/proc/ipc: the socket, its bind, the chown or chmod after it, or the listen failed; the cause is in the chain", 0, 0) // PeerRefused reports a connection from an account the listener does not // admit. It is returned to no one but the listener's observer: the // connection is closed without a word. PeerRefused = errs.Static(CodePeerRefused, "PEER_REFUSED", "The peer is not allowed on this private socket", "service/proc/ipc: the kernel-reported UID/GID is neither ours nor in the allow-lists, uid and gid fields; on Windows the client's token names neither our account nor a listed SID, or cannot be read, sid and reason fields", 0, 0) // DialFailed reports a dial that reached no listener. DialFailed = errs.Static(CodeDialFailed, "DIAL_FAILED", "The private socket did not answer", "service/proc/ipc: the dial failed or timed out; the cause is in the chain", 0, 0) // EndpointForeign refuses a socket file another account owns, unless that // account is in AllowUIDs or the file's group in AllowGIDs; the uid and // gid fields name the owner. On Windows it refuses a pipe served by // another account than the one it stands for — this account's, or the // listed account whose pipe it is —, or owned by another; the sid or // owner field names it. EndpointForeign = errs.Static(CodeEndpointForeign, "ENDPOINT_FOREIGN", "The private socket belongs to another account", "service/proc/ipc: the socket file's owner is not this process's user nor an allowed one, the uid field names it; on Windows the pipe's server or owner is not the account it stands for, the sid or owner field names it", 0, 78) // FDPassRefused refuses to pass descriptors: to a peer the kernel did not // name (Peer.Verified false), so a descriptor never reaches an account // nobody vouched for; with an empty payload or a max that is not // positive; on a platform that passes none; or when the send or receive // failed, the errno joined. The reason field says which. FDPassRefused = errs.Static(CodeFDPassRefused, "FD_PASS_REFUSED", "The descriptors could not be passed", "service/proc/ipc: SendFiles/RecvFiles refused or failed; the reason field says which", 0, 0) // EndpointSquatted refuses to listen on a Windows pipe name another // account holds; the owner field names it when its owner could be read, // the cause field says why the pipe could not be joined otherwise. EndpointSquatted = errs.Static(CodeEndpointSquatted, "ENDPOINT_SQUATTED", "Another account holds this private socket's name", "service/proc/ipc: the pipe's name exists and is not this account's: its owner field names who holds it, or its cause field says why it admits no instance of ours", 0, 78) // Closed reports an Accept on a closed listener. Closed = errs.Static(CodeClosed, "CLOSED", "The private socket is closed", "service/proc/ipc: Accept after Close", 0, 0) // PathUnsafe refuses a socket path another account could steer. PathUnsafe = errs.Static(CodePathUnsafe, "PATH_UNSAFE", "The private socket's path runs through a directory another account controls", "service/proc/ipc: a component above the socket's directory is a link planted where anybody can write, a directory another account owns there, or an entry anybody can replace; the path, dir, kind and container fields say which, target and uid where they apply", 0, 78) )
Functions ¶
This section is empty.
Types ¶
type Conn ¶
type Conn struct {
net.Conn
// Peer is who connected (for a listener) or who listens (for a dialer).
Peer PeerValue
}
Conn is a connection with its peer's identity: what a Listener's Accept returns to a listener and a Dialer's Dial to a client, Peer naming the other end.
type Dialer ¶
type Dialer interface {
// Dial connects within ctx. Peer names the listener where the kernel
// does; a listener of an account that is not admitted is refused.
Dial(ctx context.Context) (*Conn, error)
}
Dialer is the connecting end of a private socket: it reaches the listener its configuration names and refuses — before a byte is sent — an endpoint it cannot trust. It is frozen at one method (ADR 0039).
type FileReceiver ¶ added in v0.28.0
type FileReceiver interface {
// RecvFiles reads one message into buf and the descriptors attached to it,
// at most max (refused at zero), each close-on-exec — not inheritable on
// Windows — and wrapped as an *os.File the caller owns and closes.
// Descriptors beyond max are closed by the kernel on Unix and on Windows in
// the sender's process — a socket, already the peer's, closed here —, never
// leaked into this process;
// FDPassRefused with the reason "truncated" says so, beside the files that
// fit.
RecvFiles(buf []byte, max int) (n int, files []*os.File, err error)
}
FileReceiver is FileSender's other end, asserted on Conn.Conn. On Windows the files travel in band: RecvFiles must be called where the peer called SendFiles, and the stream is refused anywhere else.
type FileSender ¶ added in v0.28.0
type FileSender interface {
// SendFiles sends payload with files attached; the peer receives copies of
// the descriptors, and the sender keeps and closes its own. A peer the
// kernel did not name, or an empty payload, is FDPassRefused and nothing is
// written.
SendFiles(payload []byte, files ...*os.File) error
}
FileSender is the sibling a private connection may offer (ADR 0039), asserted on Conn.Conn: open files passed to the peer's process with SCM_RIGHTS on Unix, and on Windows in a frame the receiver takes each handle out of the sender's process from, with DuplicateHandle — a socket by WSADuplicateSocketW for the peer's process and WSASocketW there, since a socket is not duplicated as a handle.
type Listener ¶
type Listener interface {
// Accept returns the next connection of an admitted peer. A peer the
// kernel names and the configuration does not admit is closed at once,
// counted by Refused, and never returned. After Close it returns the
// CLOSED sentinel.
Accept() (*Conn, error)
// Addr is the endpoint's address.
Addr() net.Addr
// Close stops accepting and releases the endpoint.
Close() error
// Path is the endpoint's configured path — the socket file, or the path
// a Windows pipe's name is derived from.
Path() string
// Refused is how many connections Accept closed because their peer was
// not admitted.
Refused() int64
}
Listener is the accepting end of a private socket: it hands out the connections of admitted peers only, and closes and counts the others.
It is frozen at these five methods (ADR 0039): a method added to a published interface breaks every double a caller wrote, so a capability that comes later is a sibling interface a Listener may also implement.
type PeerValue ¶
type PeerValue struct {
// UID and GID are the peer's effective user and group; -1 when the
// kernel does not say (Verified is then false).
UID, GID int
// PID is the peer's process, 0 when the kernel does not say.
PID int
// SID is the peer's account on Windows — a client's read from the token
// its pipe captured, a listener's from its process token, or from the
// pipe's owner when that token cannot be read —; empty elsewhere.
SID string
// Verified is true when the kernel named the peer — UID and GID by
// SO_PEERCRED on Linux, LOCAL_PEERCRED on FreeBSD and DragonFly,
// LOCAL_PEEREID on NetBSD, SID from a token on Windows —, false
// when only the directory's permissions admitted it (darwin, OpenBSD,
// illumos, Solaris), or on Windows when a dialer named another
// account's listener by its pipe's owner alone. On Linux, the three BSDs
// and at a Windows listener an unnamed peer is refused, never admitted.
Verified bool
}
PeerValue is who is at the other end of a connection, as the kernel says: the peer's user, group and process where the kernel names them, and whether it did (Verified) — false where only the directory's permissions admitted it.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package rpc is the contract of a call between two processes over the private socket (ADR 0218): the Caller a client is, the Handler that answers one method, the Router that names it, the Request a handler reads, the Stream a caller reads a server's items from, and the codes every refusal carries — range 0.3.106.*, declared here at the engine's path (ADR 0160).
|
Package rpc is the contract of a call between two processes over the private socket (ADR 0218): the Caller a client is, the Handler that answers one method, the Router that names it, the Request a handler reads, the Stream a caller reads a server's items from, and the codes every refusal carries — range 0.3.106.*, declared here at the engine's path (ADR 0160). |