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 implementation claiming to be a PGSS Logon authority MUST satisfy all of the following.
2.21.1.1 Channel #
- Listen on
/run/logon.sockas aSOCK_STREAMUnix domain socket (§2.5). - Control access to it with a security descriptor, not with process integrity and not with POSIX permission bits (§2.4).
- Serve conversations concurrently, so that one stalled logon does not block others (§2.5).
- Support
SCM_RIGHTSon the channel (§2.9).
2.21.1.2 Framing #
- Reject any message whose magic is not
PGSL, as a whole-connection failure (§2.6). - Reject any message whose version it does not implement, answering
UnsupportedVersionwhere it can still encode a denial (§2.6). - Reject any message declaring more than 65536 bytes, or fewer than a header, without reading the body (§2.6).
- Skip to a structure's declared end after reading known fields, rather than assuming its length, and substitute a documented default for an optional trailing field a peer did not write (§2.6).
2.21.1.3 Conversation #
- Require
LogonStart,CredentialChangeStart,CredentialEnrollStart,SessionEndorSessionEndQueryas the first message, and reject a conversation opening otherwise (§2.3). - Send exactly one terminal message, nothing after it, and close the connection (§2.3).
- Bound the number of rounds, the time spent awaiting an answer, and
the time a conversation may remain open, and terminate with
ConversationLimiton exhausting any of them (§2.3, §2.5). A bound reached MUST produce a terminal message; closing the connection silently is what the code exists to prevent. - Reject a
CredentialResponseit did not solicit (§2.3). - Match answers to prompts by
credential_ref, never by position, and treat a missing answer as a failed logon rather than as grounds to tear down the connection (§2.8).
2.21.1.4 Trust #
- Establish peer identity from the connected socket's peer token,
never from a message body, and never via
SO_PEERCRED(§2.4). - Treat
logon_typeas a proposal and constrain it against the verified peer (§2.4). - Treat
identifier,ttyandremote_hostas unverified claims (§2.4).
2.21.1.5 Derivation #
- Perform derivation itself, and never accept a token, privilege set or integrity level from another party (§2.1).
- Never fork or exec on the client's behalf; never learn about terminals, environments or session leadership (§2.1).
2.21.1.6 Prompting #
- Never send a prompt for a credential type absent from the client's
supported_credential_types, and deny the logon instead if it cannot proceed within them (§2.8). - Ensure
credential_refis unique among the prompts of one request (§2.8).
2.21.1.7 Result #
- Transfer the token as a file descriptor in the ancillary data of
AccessGranted, never by name (§2.9). - Never distinguish an unknown principal from a bad credential — by denial code, by reason text, or by timing (§2.10, §2.12).
- Send
homeandshellas absolute paths, or empty, whatever their provenance; an authority with no value for a profile field MUST leave it empty rather than invent one (§2.9).
2.21.1.8 Credential handling #
- Erase credential material, and every buffer it was encoded and decoded through — including an allocation abandoned by a buffer that grew — before that memory is released (§2.12).
- Never write credential material to a log, audit record, error message or diagnostic (§2.12).
2.21.1.9 Identity lookup #
- Listen on
/run/ident.sockas well as/run/logon.sock, as aSOCK_STREAMUnix domain socket (§2.14). - Grant connect access to that socket, by security descriptor, to every principal that runs ordinary programs (§2.14).
- Refuse a message of one socket's range received on the other (§2.14).
- Echo each request's
tag, and never interpret it otherwise (§2.14). - Serve requests concurrently, and never let a slow answer on one connection delay another (§2.14).
- Bound the time spent answering a request, and answer
Unavailablerather than waiting indefinitely (§2.14). - Ignore ancillary data received on the identity socket (§2.14).
2.21.1.10 Resolution #
- Interpret names itself, and never require a client to parse, qualify or case-fold one (§2.15).
- Compare names ASCII case-insensitively, establishing the comparison itself rather than delegating it to a source (§2.15).
- Refuse a name containing a reserved character, a byte outside
0x20–0x7e, or a leading or trailing space — whether created locally, received in a request, asserted by a source, or carried alongside a SID in a reference (§2.15). - Resolve bare names in a configured order, never in registration order, stopping at the first source that answers (§2.15).
- Carry the resolved SID and the canonical qualified name on every successful reply, whatever form the request used (§2.15).
2.21.1.11 Answers #
- Never answer a
Principalrequest with a group, or aGrouprequest with a principal, and establish the kind itself rather than relaying a source's (§2.16). - Ignore a field bit it does not implement, and never report it as present (§2.16).
- Report each requested field it implements as present or as withheld with a reason, never as neither (§2.16).
- Withhold
MEMBERSwith a reason rather than returning a partial or empty list, where it will not or cannot produce it (§2.16). - Report
Absentrather thanDeclinedfor a group whose membership is a rule rather than a record, and gather aBUILTINgroup's recorded members from the sources rather than reporting itAbsent(§2.16). - Encode "no POSIX identifier" as zero, and never substitute a number a caller could mistake for a real one (§2.16).
- Answer a
passwdrecord, agrouprecord, a principal's memberships, or a name for a SID, each in one request (§2.16). - Never report
NotFoundunless every source in the search order that could have answered was consulted and answered (§2.18). - Report
Unavailablewhere a source earlier in the search order could not be reached (§2.18). - Never report
NotFoundin place ofRefusedor of aRestrictedfield, and never reportRefusedfor anything other than a caller that may not make the request (§2.18). - List every source that did not contribute to an enumeration, and
never report
Foundon a page it could not produce (§2.17, §2.18). - Reject a cursor it did not issue, or can no longer honour, with
Malformed— never by restarting the walk and never by reporting it complete (§2.17). - Serve member enumeration where it withholds
MEMBERSasTooLarge(§2.17).
2.21.1.12 Service attestation #
An authority that implements ServiceAttest MUST satisfy all of the
following. An authority that does not implement it MUST refuse the
message, and is conforming.
- Never hold, store, or accept a credential for a service identity, and never offer a credential exchange for one (§2.19).
- Refuse a
ServiceAttestunless both the peer's principal and the peer's being the platform's service manager are established from the connection, and never from the message (§2.19). - Answer a peer that satisfies neither condition, or only one, with
PermissionDenied(§2.19). - Never mint a token under this message for a principal that has not
been designated as usable for a service logon, and refuse with
AccountRestrictedwhere it cannot determine whether a principal carries that designation (§2.19). - Never let a refusal distinguish "no such principal" from "not designated for service logon" (§2.19, §2.10).
- Reject an empty or unusable
servicewithMalformedRequest(§2.19). - Record the logon type as
Service, and leave everyprofilefield empty rather than inventing a value (§2.19, §2.9). - Issue a token that cannot be used to authenticate to another system on the authority's behalf, at a level below the one a credentialled logon receives and bounding everything derived from it (§2.19).
- Record that the logon was attested rather than authenticated, wherever it records what vouched for a credentialled logon (§2.19).
- Reject a
LogonStartthat would complete with no credential exchange for an identity it accepts underServiceAttest, and reject aServiceAttestnaming an identity it accepts underLogonStart(§2.19).
2.21.1.13 Logon type restrictions #
- Refuse a logon whose type the principal's holder does not permit,
with
AccountRestricted, after authentication rather than instead of it (§2.16, §2.10). - Read an unstated
LOGON_TYPESas its own default rather than as "nothing permitted", and never includeServicein that default (§2.16, §2.19). - Never widen what a principal's holder permits. Local policy may narrow it further; nothing may add to it (§2.16).
- Refuse a
logon_typeit does not recognise, withMalformedRequest(§2.7, §2.10).
2.21.1.14 Credential change #
An authority that implements CredentialChangeStart MUST satisfy all of
the following. An authority that does not implement it MUST refuse the
message with PermissionDenied, and is conforming. Obligations 10 to
13, 19, 20, 24 and 25 bind a change conversation as they bind a logon.
- Change only the credential of the principal the connected peer's token names, established from the socket and never from a message (§2.20, §2.4).
- Establish, within the conversation, that the principal can present
their current credential before changing it, and refuse with
AuthenticationFailedwhere they cannot (§2.20). - Refuse with
AccountRestricteda principal it holds no changeable credential for, including every service identity (§2.20, §2.19). - Send
CredentialChangedonly once the new credential is the one a subsequent logon will be tested against (§2.20). - Mint no token and create no session for a change, and send no
descriptor with
CredentialChanged(§2.20). - End with
ConversationLimita change it gives up on for want of an acceptable new credential (§2.20). - Refuse a
CredentialChangeStartthat ends beforesupported_credential_typeswithMalformedRequest(§2.20). - Where the socket's descriptor admits peers that may not originate logons, decide from each peer's identity whether it may originate one, and never treat reaching the socket as that permission (§2.20, §2.4).
2.21.1.15 Ending a session #
An authority that implements SessionEnd and SessionEndQuery MUST
satisfy all of the following. An authority that does not implement them
MUST refuse both with PermissionDenied, and is conforming. Obligations
5 to 8, 10 and 14 bind these requests as they bind a logon.
- Never accept
SessionEndQueryasSessionEndor the reverse, and change nothing in answeringSessionEndQuery(§2.22). - Answer
SessionEndQuerywithSessionEndAllowedexactly where aSessionEndfrom the same peer would be permitted, and otherwise with the denial it would draw (§2.22). - Refuse, with
PermissionDenied, a peer whose token is restricted, the SYSTEM and Anonymous sessions the kernel creates, and every session of logon typeService, whoever asks (§2.22). - Permit a caller to end a session whose user is the caller, of a logon type a person has, where the caller is a principal of an issued domain (§2.22).
- Decide any other request by an access check of the caller's token against a descriptor local policy states — SYSTEM and Administrators alone where it states none, and nobody where it states one that cannot be used (§2.22).
- Answer
NoSuchSessiononly to a caller who may end other principals' sessions, andPermissionDeniedto any other where the session does not exist (§2.22, §2.10). - End every process whose primary token belongs to the session — asking first, then, after a grace period of at most 10 seconds, without asking — and never a process only because one of its threads impersonates a token of the session (§2.22).
- Address each signal by a reference that cannot come to name a different process, never by a process identifier looked up again (§2.22).
- Look again after each round, bound the number of rounds, and answer within 30 seconds (§2.22).
- Count in
remainingevery process it could not examine or end, and never fail the request for one (§2.22). - Hold no token of the session longer than it takes to read which session it belongs to, and never end its own process (§2.22).
- Where the session is the one the connected peer's token belongs to,
send
SessionEndedbefore signalling anything, carrying what it found to end and aremainingof zero (§2.22). - Start nothing in ending a session (§2.1, §2.22).
2.21.1.16 Credential enrolment #
An authority that implements CredentialEnrollStart MUST satisfy all of
the following. An authority that does not implement it MUST refuse the
message with PermissionDenied, and is conforming. Obligations 10 to
13, 19, 20, 24, 25, 68, 69 and 72 bind an enrolment as they bind a
credential change.
- Add or remove only the credentials of the principal the connected peer's token names, established from the socket and never from a message (§2.23, §2.4).
- For both
AddandRemove, establish within the conversation that the principal can present their current credential before changing anything, and refuse withAuthenticationFailedwhere they cannot (§2.23). - Refuse with
AccountRestricteda principal it holds no credential for that it can ask for as proof, including every service identity (§2.23, §2.19). - Refuse an
actionorcredential_typeit does not recognise withMalformedRequest, acredential_typeit does not enrol withPermissionDenied, and aCredentialEnrollStartthat ends beforematerialwithMalformedRequest(§2.23). - Refuse material it will not accept with
CredentialRejected, check it again when applying the change, and never sendCredentialRejectedfor a failed proof or in answer to any other message (§2.23, §2.B). - Leave unchanged which credentials the principal may sign in with (§2.23).
2.21.2 Client obligations #
There are four client roles, and they are independent. A program may be any of them, several, or none: a logon originator never looks a principal up; a name resolver does the reverse.
An implementation originating logons MUST satisfy obligations 1 to 20.
An implementation performing identity lookup MUST satisfy 21 to 27. An
implementation changing its own principal's credential MUST satisfy
obligations 2 to 12 and 18 to 20, reading CredentialChangeStart
wherever they say LogonStart, and 28 and 29. An implementation adding
or removing its own principal's credentials MUST satisfy the same, reading
CredentialEnrollStart for CredentialChangeStart, and 34 and 35. An
implementation ending logon sessions MUST satisfy obligations 4 to 7 and
30 to 33.
2.21.2.1 Conversation #
- Send exactly one
LogonStart, as the first message (§2.3). - Answer each
CredentialRequestwith exactly oneCredentialResponse(§2.3). - Never send a
CredentialResponsethat was not solicited (§2.3). - Treat a connection that closes without a terminal message as a failed logon, and not retry automatically (§2.3).
2.21.2.2 Framing #
- Reject any message whose magic is not
PGSL(§2.6). - Reject any message whose version it does not implement (§2.6).
- Skip to a structure's declared end after reading known fields (§2.6).
2.21.2.3 Capabilities #
- Never declare in
supported_credential_typesa credential type it cannot render (§2.7). Declaring fewer than it can render is permitted, and an empty list is how a client asks for a logon requiring no interaction. - Fail the conversation, rather than guess, if it receives a prompt it cannot render (§2.8).
2.21.2.4 Rendering #
- Display received messages, in order, before the prompts of the same request (§2.8).
- Not interpret or branch on message text,
credential_name, orreason(§2.8, §2.10). - Echo
credential_refback unchanged (§2.8).
2.21.2.5 Result #
- Read the token descriptor from the ancillary data of
AccessGranted, and treat anAccessGrantedwithout one as a failed logon (§2.9). - Close the descriptor if it does not install the token (§2.9).
- Have a fallback for every
profilefield, and treat an empty field, and an absentprofile, identically and not as an error (§2.9). - Never execute a relative
shell, nor resolve a relativehomeagainst its own working directory (§2.9). Ashellcontaining no separator is relative, and MUST NOT be resolved against a search path. - Never treat
profileas a directory lookup, nor cache it as an answer about any principal other than the one who just authenticated (§2.9).
2.21.2.6 Credential handling #
- Collect a credential only where it can establish that its collection
method meets the type's requirements, and never collect a
Passwordwith echo enabled (§2.12). - Never silently truncate an answer to fit its own buffer (§2.12).
- Erase credential material, and every buffer it was collected and encoded through, before that memory is released, and never write it to a log, error message or diagnostic (§2.12).
2.21.2.7 Identity lookup #
- Forward a name unchanged, and never parse, split, qualify or case-fold one (§2.15).
- Never reuse a
tagwhile a request bearing it is outstanding, and never assume replies arrive in request order (§2.14). - Treat a cursor as opaque: never construct, parse or modify one, nor present one to a different authority or across a reconnection (§2.17).
- Continue an enumeration until
nextis empty, never infer completion from an empty page, and never present a walk it abandoned for any other reason as a complete one (§2.17). - Surface an enumeration's
incompletelist rather than discarding it (§2.17). - Distinguish
NotFoundfromUnavailable, and never cache the second as though it were the first (§2.18). - Never use enumeration to test whether a principal exists (§2.17).
Obligations 24 and 26 bind a client that caches for the lifetime of a single process just as they bind an authority. A resolver that remembers "no such user" through an outage will keep reporting it after the outage ends, and one that reports a failed walk as an empty system does the same thing to every principal at once.
2.21.2.8 Credential change #
- Send exactly one
CredentialChangeStart, as the first message, and name no principal in it — there is no field for one (§2.20). - Treat
CredentialChangedas the only successful outcome, expect no token with it, and close any descriptor that arrives with it (§2.20).
2.21.2.9 Ending a session #
- Send exactly one
SessionEndorSessionEndQuery, as the only message, and useSessionEndQuery— neverSessionEnd— to learn whether ending a session would be permitted (§2.22). - Wait up to 60 seconds for the answer to
SessionEndbefore treating it as unanswered, and then not assume either outcome (§2.22). - Treat a non-zero
remainingas a session that may still exist, and never read the counts as an outcome where the session was the caller's own (§2.22). - Treat a denial code it does not recognise as a refusal, not as an unreadable answer (§2.B).
2.21.2.10 Credential enrolment #
- Send exactly one
CredentialEnrollStart, as the first message, with every field, and name no principal in it (§2.23). - Tell
CredentialRejected(the material) apart fromAuthenticationFailed(the proof) where it reports a refusal, and treat a denial code it does not recognise as a refusal (§2.23, §2.B).
2.21.3 What a client is not required to do #
A client is not required to understand what any credential type means, what any message says, or why a logon was denied. It renders what it is given and returns what it collects.
That is the property the whole conversational shape exists to produce, and a client that starts reasoning about the content of prompts has given it up — it will need changing the next time an authority's policy does.