Summary

  • RFC 1509 defined gss_cred_id_t as a caller-opaque local handle. The actual credential remained inside GSS-API, and the same handle value could designate different credentials for different callers.
  • Handle scope could be one process, a process and its children, or processes sharing a local identity such as a UID. Omitting the handle requested a locally selected default credential; it did not mean that no principal would be asserted.
  • A handle, a credential, an authenticated principal, a security context, a protected message and an authorized application action were separate receipts. Portability came from preserving those boundaries, not from making one small value universally meaningful.

Two equal values opened different doors

In an ordinary C program, equality feels decisive. Two integers compare equal. Two pointers have the same representation. A table lookup returns the same slot. It is tempting to conclude that the values name the same thing.

RFC 1509 denied that inference for a credential handle. It allowed gss_cred_id_t to be implemented as an arithmetic or pointer type, but called it a caller-opaque atomic datum. More unusually, it said that the same value could refer to different credentials when different callers presented it to GSS-API.

The difference was not encoded inside the handle. It lived in the environment around the call. An implementation might scope a handle to the process that acquired it, to that process and its children, or to processes sharing local identification such as a UID. A value observed in one process therefore could not be copied into an audit record and interpreted later as a universal credential identifier. The record was missing the resolver.

This was not sloppiness. It was the abstraction. The handle gave a caller a way to refer to security state without exposing the state’s representation. Its usefulness depended on a deliberately local join:

caller + implementation state + handle value + mechanism → credential

Remove the caller or the implementation epoch and the value loses its stable meaning. The number can survive while the referent changes. It can also remain equal across two callers while the referents differ.

The credential stayed behind the interface

RFC 1509 distinguished a credential from its handle with unusual clarity. Credentials described a principal and gave their holder the ability to act as that principal. The application, however, did not receive the actual credential. It received a local reference to a credential held inside GSS-API or its underlying mechanism.

That separation served security and portability at once. An application did not need to parse secret keys, tickets or mechanism-specific structures. A GSS-API implementation could support several mechanisms without teaching each caller their internal formats. A credential handle could remain the same C type even as the material behind it differed radically.

The document therefore said the handle itself contained no security-relevant information and required no special protection by the application. That sentence is easy to overread. It did not say the credential was harmless. It did not say any process could invoke the handle. It did not say copying the bits transferred authority. The protection boundary sat below the handle: the implementation and operating system had to decide which callers could resolve and use the credential.

RFC 1508, the language-independent companion, stated the consequence more directly. System-specific mechanisms and OS functions were responsible for constraining credential acquisition and use to appropriate processes. It treated the ability to use a principal’s credential as the ability to assert that principal’s identity. Transferability to another process was a local matter, not a universal GSS-API rule.

The apparent contradiction dissolves once the objects are separated. The handle bits need not be secret. Access to the resolver must still be controlled. A library index can be public while the circulation desk checks who may retrieve the sealed volume.

Scope was part of identity

The handle’s scope was not an implementation footnote. It was part of the evidence required to understand any use of the handle.

Suppose process A acquires a credential and receives handle value 7. A child process might inherit access under one implementation. A neighbouring service under another UID might present 7 and reach a different table. After a restart, a new credential might occupy the old slot. On another host, 7 could be meaningless. None of these cases violates the abstraction.

The only sound claim is local and dated: this caller, using this implementation state, resolved this handle to this credential for this operation. Even that claim does not establish the application result. It identifies a point in a longer chain.

RFC 1509 also allowed a credential to disappear when no handles remained through which it could be reached. Lifetime therefore did not belong to the value alone. It depended on handles, credential state and the mechanism’s own expiration rules. A log that retained only the small value could outlive every object needed to interpret it.

This is a recurring design choice in systems software. Indirection lets an implementation replace internals, revoke state and isolate callers. The price is that a reference cannot serve as a durable, cross-boundary identifier without the namespace and epoch that give it meaning. The API gained freedom precisely by refusing to promise that the value meant the same thing everywhere.

No credential meant “choose one”

The most dangerous name in the interface may have been GSS_C_NO_CREDENTIAL. In context-establishment calls, supplying it did not necessarily mean that no credential would be used. It requested default credential behavior.

RFC 1509 left the mechanism by which a default credential was established, and the scope of that default, to the implementation. RFC 2078 later made the resolution procedure more explicit. A platform might select the only principal the application was authorized to use, a default network identity, a network image of a default local identity, or a user-configured identity. Failure to resolve a usable default had its own error path.

This is ambient authority in a portable coat. The application omits an explicit reference so that it can run across mechanisms and platforms. The local environment supplies a principal. Portability improves because the caller knows less. Auditability can worsen for the same reason.

A trace that says “no credential” can therefore state the opposite of what happened. The meaningful receipt must say that default resolution was requested, which principal the local system selected, under which mechanism, for initiate or accept use, with what lifetime, and under whose authorization policy.

The specification did not make defaults illegitimate. It made them local. That distinction matters. A common API may define how to ask; it cannot make every machine choose the same principal or accept the same risk.

A context handle was not a connection

RFC 1509 applied a similar rule to gss_ctx_id_t. A context handle was another caller-opaque atomic value, and an equal value could identify different contexts for different callers. The context behind it held shared state, including cryptographic state, for one end of a peer relationship.

The context was also independent of a transport connection. A communications session could cross several connections, and several security contexts could exist successively or simultaneously inside one communications association. Socket identity and security-context identity were not interchangeable.

This design prevented a common collapse. An open TCP connection did not prove that a particular GSS context existed. A context handle did not prove which socket carried its tokens. Context completion did not prove that every later application message used integrity or confidentiality. Deleting a context at one end did not by itself prove that a remote application had completed its own work.

At each step the caller had work to do: carry tokens, associate them with the right context, inspect major and mechanism-specific statuses, request services, check the returned flags and decide what the authenticated peer was allowed to do.

The token crossed the boundary; the handle did not

RFC 1509’s authentication token looked superficially like another opaque value, but its job was different. A token was a bit string generated by the mechanism at one end for processing by the peer mechanism at the other. The application was responsible for transporting it. The token could be cryptographically protected; the local credential handle was not the transported credential.

This separation supplied a useful test. If an object is meant to cross a boundary, the protocol must define how the receiver can interpret and validate it. If it is a local handle, copying its representation is not a transfer procedure.

The Version 2 C binding made that lesson explicit for security contexts. RFC 2744 added gss_export_sec_context and gss_import_sec_context. Export deactivated the context in the source process and produced an interprocess token for another process. Only one instance of the context could be active at a time. The target’s ability to import could be restricted by local policy, such as a shared account or process group.

Unlike the old local handle value, the interprocess token could contain keys or other sensitive data. The application had to protect it in transit and give it only to a trustworthy process. The object that actually moved carried a different protection duty from the reference it replaced.

The later design did not prove that every implementation exported contexts or that transfer always succeeded. It clarified the contract: movement required an explicit state transition and a purpose-built artifact. Copying a handle was not movement.

A displayed name was another projection

Names exposed the same boundary in human-readable form. RFC 1509 distinguished printable names, intended for people, from internal names presented to the API. Printable syntax could depend on local configuration or individual preference. An object identifier accompanied the printable value to identify its namespace. Internal names retained enough type information for the implementation to avoid confusing one namespace with another.

Applications were told to use GSS-API operations to import, display and compare names. String equality was not the identity rule. RFC 2744 later stated plainly that displaying an imported internal name need not return the same string that was supplied, and even a namespace identifier might not survive the internal journey unchanged. A DNS name could be mapped into an X.500 form and later displayed that way.

This did not make display text useless. It made its role precise. A displayed name was a presentation selected by an implementation. An authenticated internal name was mechanism-bound state. A local account mapping was another decision. An application authorization rule was another still.

A screen can show the right-looking person while an authorization check uses the wrong principal. It can also show a different-looking string for the same internal entity. The evidence chain needs the namespace, mechanism and comparison result, not merely the characters seen by an operator.

Channel binding joined one more fact

Channel bindings were an intentional exception to GSS-API’s independence from communications associations. RFC 1509 let applications supply initiator and acceptor address types and addresses plus application data. Conceptually, the mechanism concatenated these values, signed them and bound the result to the context-establishment token. If the acceptor supplied different bindings, context establishment could fail with GSS_S_BAD_BINDINGS.

That was stronger than trusting a socket label. It still had a bounded meaning. The application chose the binding inputs. Address families could contain several forms. A mechanism could add checks, but did not have to prove every physical or administrative property of the channel. Some mechanisms could place the actual binding data in the token, which is why RFC 1509 warned against using confidential material as a binding component.

A successful binding comparison joined a claimed communications context to a security-context exchange. It did not prove legal identity, application entitlement, message delivery or observed effect. It closed one gap and left the later decisions visible.

The common interface stopped at the local machine

RFC 1511 described the Common Authentication Technology working group’s strategy as a division of labour. Security specialists could implement reusable mechanisms and services; protocol designers could integrate a generic interface without becoming experts in each cryptographic system. RFC 1508 defined the conceptual API. RFC 1509 gave C programs concrete types and calls.

The architecture was thin in exactly the sense that matters. It standardized references, operations and result vocabulary while leaving credential custody, process authorization, default identity, local name conversion and many placement choices to implementations and operating systems.

RFC 2078 revised the abstract API after implementation experience. RFCs 2743 and 2744 later replaced the early documents with Version 2 Update 1 and its C bindings. The successor work added operations and precision, but retained the deeper boundary: using GSS-API did not by itself provide a particular assurance. Security depended on the underlying mechanism and on callers correctly requesting services and reading the returned results.

This is a better history than “the standard created single sign-on.” The standard created a common way to ask local security machinery to establish and use contexts. It did not nationalize every credential into a global namespace. It did not erase caller identity. It did not turn handles into capabilities portable by copying.

What the sources prove

The record proves specification language and lineage. It proves that RFC 1509 deliberately allowed caller-relative handle meanings, separated handles from credentials and tokens, and placed essential control in local implementations. It proves that later GSS-API documents made default resolution, name behavior and explicit context transfer more detailed.

It does not prove how a named library represented handles, which scope a deployed operating system chose, whether an application logged the right facts, or whether any vulnerability, exploit or incident resulted. It does not prove broad adoption or universal conformance. A standards-track label is a publication state, not a deployment receipt.

The historical value lies in the negative space. In 1993, a security API already knew that equality of a small reference was not enough to establish equality of authority. The common layer worked because the decisive mapping stayed local and contestable. A durable claim required the caller, resolver, mechanism, principal, context and effect—not a lonely number copied from memory.

Sources