2.21 Conformance
A conforming implementation MUST satisfy every requirement in this chapter. This section collects them by role.
2.21.1 Authority obligations #
An authority federating over PSI MUST satisfy all of the following.
2.21.1.1 Channel #
- Listen; never dial out to a source (§2.3).
- Never rely on the socket's descriptor as the access control, and bound unregistered connections and registration time independently of it (§2.6).
- Bound registered sources, and conversations per source (§2.6).
- Tear down the connection on a framing error or a failed write, rather than attempting resynchronisation (§2.6).
2.21.1.2 Registration #
- Require
Registeron conversation0as the first message (§2.8). - Establish the source's identity from the peer's token, never from
source_name, and refuse a mismatch rather than correcting it (§2.8, §2.9). - Accept only configured sources, treating an empty configuration as no source may register (§2.9).
- Refuse a source that declares no domain (§2.10).
- Refuse a domain that is not a well-formed locally-issued domain SID (§2.10).
- Refuse a domain another registered source claims (§2.10).
- Refuse a source declaring a different domain from the one it declared before (§2.10).
- Refuse a source whose declared domain contradicts a configured pin, and treat an unparseable pin as refusing rather than as absent (§2.10).
- Send
Registeredonly once the source is routable (§2.8). - Report, where an administrator will see it, a source registering
with no identifier range, and a source registering without
QUERIES(§2.8).
2.21.1.3 Conversations #
- Allocate conversation identifiers, never accepting one from a
source, never opening one with an identifier already live, and
reserve
0for registration (§2.7). - Route each logon to exactly one source, resolved before any credential is collected (§2.11).
- Never fall back to another source when the owning source is unreachable (§2.11).
- Relay the verified
originator, taken from the client's socket (§2.11). - Refuse to relay a prompt for a credential type the client did not advertise (§2.12).
- Enforce its own round and time limits on the relayed exchange (§2.12).
- Send
Abandonfor any conversation that will not reach a terminal state (§2.14).
2.21.1.4 Assertions #
- Validate every SID structurally before treating it as identity, including SIDs carried inside claim values (§2.13).
- Drop an asserted logon SID, and drop duplicates first-mention-wins, before applying any scope test (§2.13).
- Enforce identity confinement, with no configuration lifting it, on
an
Assertion, on aQueryResult, and on thesidof aChanged(§2.18). - Enforce membership scope, subject only to a per-source permission
covering memberships alone, and apply it to a source-asserted
primary_groupas well as to listed groups — promoting an unlisted one into the membership set before the test (§2.19, §2.13). - Never apply membership scope to a
primary_groupit substituted itself for an empty field (§2.13, §2.19). - Fail the logon on a malformed group SID, an unusable claim, a
canonical_namePGSS §2.15 forbids, or an out-of-range identifier (§2.13). - Place
primary_groupon the token even when the source did not list it amonggroups(§2.13). - Perform derivation itself, and never accept privileges, integrity, or a token from a source (§2.4).
- Never relay a refusal reason revealing a distinction the source was required not to make (§2.13).
2.21.1.5 Identifiers #
- Apply the source's base to every relative identifier it accepts, and never accept one already rebased (§2.20).
- Refuse a relative identifier of
0or one at or past the source's count, rather than clamping or wrapping it (§2.20). - Reserve a band of identifiers below every source's base for its own, and use its own value for any SID within that band regardless of what the source sent (§2.20).
2.21.1.6 Queries #
- Never send a message a source did not declare it answers, and never set a field bit gating a capability it did not declare (§2.8).
- Never send more keys in one
Querythan the source'smax_batch, nor more than 64 whatever it declared (§2.8). - Resolve an absolute POSIX identifier to a source and a relative identifier itself, and never send an absolute one to a source (§2.15).
- Apply identity confinement, membership scope and numeric scope to a
QueryResultexactly as to anAssertion, and validate every SID in one structurally — including those of references inside a value (§2.15). - Rebase every identifier in a result, under the rules of §2.20 (§2.15).
- Qualify a source's
canonical_nameitself; never require a source to (§2.15). - Consult no further source on a
Refusedresult, treat onlyNotFoundas leave to continue, and never relay a source'sRefusedoutward as PGSS Logon'sRefusedoutcome (§2.15). - Never record a source it may not ask — one that declared no
QUERIES— as having answeredNotFound(§2.15). - Relay cursors opaquely, never construct or modify a source's, and never restart an enumeration on a source's behalf (§2.16).
- Record a source that declined or could not be reached, and not retry it for the remainder of that enumeration, across pages as well as within one (§2.16).
- Never infer from a source declining to enumerate that it holds nothing (§2.16).
2.21.1.7 Caching #
- Not cache a source's answers at all unless it declared
PUSHES_CHANGESor a non-zeroentry_ttl(§2.8, §2.17). - Not hold an answer beyond a declared
entry_ttl(§2.8). - Accept
Changedat any time afterRegistered, and never reply to it (§2.17). - Re-read through
Queryafter an invalidation, and never take a new value fromChanged(§2.17). - Treat the loss of a source's connection as
Allfor that source (§2.17).
An authority that holds nothing satisfies 45 to 49 trivially.
2.21.2 Source obligations #
A principal source MUST satisfy all of the following.
2.21.2.1 Connection #
- Connect to the authority; never listen for it (§2.3).
- Open with
Registeron conversation0, carrying its name and its domain (§2.8). - Send nothing else before receiving
Registered(§2.8). - Report itself ready — to an init system or equivalent — only after
Registered(§2.3). - Declare the same domain on every registration, for the life of the machine's configuration (§2.10).
- Tear down the connection on a framing error or a failed write, on
every path including an unsolicited
Changed(§2.6, §2.17).
2.21.2.2 Conversations #
- Reply on the conversation identifier it was given, and never invent one (§2.7).
- Decline to act on a message on a conversation it does not know, and never treat it as opening one — without replying on it, since the identifier may since have been reused (§2.7).
- Refuse an
Authenticate,QueryorEnumerateSourcearriving on conversation0(§2.7). - Bound the conversations it tracks itself, rather than relying on the authority's limit (§2.6).
- Refuse beyond that bound with
AuthorityUnavailable, rather than dropping silently (§2.11). - Discard conversation state on
Abandon, and not reply (§2.14).
2.21.2.3 Answering #
- Send exactly one terminal message —
AssertionorRefusal— per conversation (§2.13). - Assert only principals within its declared domain (§2.18).
- Assert group SIDs and identifiers only, never attributes (§2.13).
- Carry the canonical spelling of the principal's name in
canonical_name(§2.13), and never assert a name that PGSS §2.15 forbids — validating what it asserts, not only what it creates. - Never distinguish an unknown principal from a bad credential — by denial code, by reason, or by timing (§2.13).
2.21.2.4 Identifiers #
- Assert relative identifiers only, and never apply its own base (§2.20).
- Allocate from a single counter across every kind of object it holds, so that no number is issued twice (§2.20).
- Send
0for any object it does not number, including every group it does not own (§2.20). - Never issue an identifier at or past the count it was given (§2.20).
2.21.2.5 Queries #
A source declaring no capabilities (§2.8) is exempt from this section entirely.
- Declare only capabilities it implements, and answer every message type it declared (§2.8).
- Answer every key of a
Query, in order, without reordering, merging or omitting — or refuse the conversation whole (§2.15). - Send
Found,NotFoundorRefusedin a result, and neverUnavailableorMalformed(§2.15). - Return its own canonical spelling of a name, unqualified (§2.15).
- Return relative identifiers in a result, exactly as in an
Assertion(§2.15, §2.20). - Answer only for principals within its declared domain, on a query as on a logon (§2.18).
- Answer
Refused, never an emptyFound, wherever it is declining rather than reporting an absence — including a membership it will not expose, a key naming an object it does not hold, and a key of the wrong kind (§2.16). - Refuse a cursor it can no longer honour, rather than restarting or answering from a changed store (§2.16).
- Hold no per-cursor state it is unwilling to discard unasked (§2.16).
- Size a page against the smaller of PSI's message ceiling and PGSS Logon's, since the authority must re-encode it into the latter (§2.16, §2.A).
- Send
Changedbefore the altered answer becomes observable, if it declaredPUSHES_CHANGES(§2.17). - Declare a non-zero
entry_ttlif it does not push changes and can tolerate its answers being held, and SHOULD declare one even if it does (§2.8, §2.17).
2.21.2.6 Credentials #
- Store verifiers that are not usable as credential material — nothing a challenge could be recomputed from (PGSS §2.11).
- Erase credential material, and every buffer it was decoded through, before that memory is released (PGSS §2.12).
- Never write credential material to a log, audit record, or diagnostic (PGSS §2.12).
2.21.3 What a source is not required to do #
A source is not required to store anything, to be local, to be persistent, or to know what a token is. It answers one question: given this identifier and whatever it chose to ask for, who is this?
Nor is it required to trust the authority beyond the connection. A
source that refuses logons on the strength of originator, or declines
to answer for principals it holds but does not wish to expose, is
conforming.