| # Macintosh Development

[Home] [About Us] [People] [MIT Support Library] [MIT Kerberos for Macintosh]
[Applications] [Mac OS X] [Miscellaneous Documentation] [Information Systems]

| Credentials Cache API v3 Specification

> - Abstract
> - New in revision 6
> - Constants
> - Types and functions
>   - Basic types
>   - Opaque types
>     - cc_context_t
>     - cc_ccache_t
>     - cc_ccache_iterator_t
>     - cc_credentials_iterator_t
>     - cc_credentials_t
>     - cc_string_t
>     - cc_lock_t
> - Implementation requirements
> - Revision history

> ## Abstract

> This is the specification for an API which provides Credentials Cache services for both Kerberos v5 and v4. The idea behind this API is that multiple Kerberos implementations can share a single collection of credentials caches, mediated by this API specification. On the Mac OS and Microsoft Windows platforms this will allow single-login, even when more than one Kerberos shared library is in use on a particular system.

> Abstractly, a credentials cache collection contains one or more credentials caches, or ccaches. A ccache is uniquely identified by its name, which is a string internal to the API and not intended to be presented to users. The user presentable identifier of a ccache is its principal.

> Unlike the previous versions of the API, version 3 of the API stores both Kerberos v4 and v5 credentials in the same ccache.

> At any given time, one ccache is the "default" ccache. The exact meaning of a default ccache is OS-specific; refer to implementation requirements for details.

> ##

> ## New in revision 7 (API Version 3 second draft)

> March 11, 2000, by meeroh

> - Opaque types are not pointers as the should have been
> - Kerberos versions are now a bitfield
> - Lock type agrument broken up
> - Error constant explanations tweaked
> - Added an example of changing and extending the API
> - Specified API behavior on error returns
> - Clarified `cc_context_open_default_ccache`
> - Fixed a bunch of typos and pasteos
> - Moved API behavior specification near the top
> - Added `cc_v4_key_length`
> - Added `skip`, `clone`, and `reset` to iterators
> - Added `clone` to ccaches
> - Changed iterators to lock the iteration object on creation and release it when released.

> ## New in revision 6 (API Version 3 draft)

> October 11, 1999, by meeroh

> - Renamed "NCs" back to credentials caches. Despite good intentions on Steve's part, noone really called them NCs. Much better to rename the agglomerate object (now a "collection"), to which very few references are made.
> - Renamed iterator functions for consistency
> - Changed all opaque types to new consistent names and one fewer level of indirection
> - Renamed most other types for consistency
> - Added "Columbia special" string-to-key type (Columbia University has some weird case where they want to truncate the password at 8th character before hashing it with the MIT hash.)
> - Changed the abstraction of a ccache to hold v4 and v5
> - Added the default ccache API
> - Changed the API to object-like approach to allow for easier future extensions
> - Added `cc_context_get_version()`, `cc_context_clone()`, `cc_ccache_get_change_time`
> - Added specification for locking
> - Added change time and default time accessors to ccaches
> - Changed credentials removal to remove exact credentials
> - Added `cc_ccache_move()`
> - Added `ccErrCCacheNotFound` errors to all ccache functions

> Also see the revision history for previous versions

> ## Constants

> ### CCache API version constants

>     // enums for API versions used in cc_initialize()
>     enum {
>        ccapi_version_2 = 2,
>        ccapi_version_3 = 3
>     };

> These constants are passed into `cc_initialize()` to indicate the version of the API the caller wants to use.

> CCache API v1 and v2 are incompatible, and therefore the v1 version number has been removed. Clients shouldn't be using the v1 API.

> ### Credentials versions constants

>     enum {
>         cc_credentials_v4 = 1,             // Kerberos v4 credentials
>         cc_credentials_v5 = 2,             // Kerberos v5 credentials
>         cc_credentials_v4_v5 = cc_credentials_v4 | cc_credentials_v5
>                                            // Kerberos v4 and v5 credentials
>     };

> These constants are used in several places in the API to discern between Kerberos v4 and Kerberos v5. Not all values are valid inputs and outputs for all functions; function specifications below detail the allowed values.

> Kerberos version constants will always be a bit-field, and can be tested as such; for example:

> `if ((ccacheVersion & cc_credentials_v5) != 0) /* ccache has v5 credentials */`

> ### Lock types

>     enum cc_lock_type {
>         cc_lock_read = 0,
>         cc_lock_write = 1,
>         cc_lock_block = 2,
>         cc_lock_noblock = 3,
>     };

> These constants are used in locking functions to describe the type of lock requested. Read locks, when granted, give the owner of the lock the ability to inspect the locked object. Write locks, when granted, give the owner of the lock the ability to modify the locked object. Attempting to acquire a lock with a non-blocking call will result in an error if the lock cannot be acquired; otherewise, the call will block until the lock can be acquired.

> ### Error constants

> 0 | `ccNoError` | Successful return
> 201 | `ccIteratorEnd` | Iterator is done iterating
> 202 | `ccErrBadParam` | Bad parameter (NULL or invalid pointer where valid pointer expected)
> 203 | `ccErrNoMem` | Not enough memory to complete the operation
> 204 | `ccErrInvalidContext` | Context is invalid (e.g., it was released)
> 205 | `ccErrInvalidCCache` | CCache is invalid (e.g., it was released or destroyed)
> 206 | `ccErrInvalidString` | String is invalid (e.g., it was released)
> 207 | `ccErrInvalidCredentials` | Credentials are invalid (e.g., they were released), or they have a bad version
> 208 | `ccErrInvalidCCacheIterator` | CCache iterator is invalid (e.g., it was released)
> 209 | `ccErrInvalidCredentialsIterator` | Credentials iterator is invalid (e.g., it was released)
> 210 | `ccErrInvalidLock` | Lock is invalid (e.g., it was released)
> 211 | `ccErrBadName` | Bad credential cache name format
> 212 | `ccErrBadCredentialsVersion` | Credentials version is invalid
> 213 | `ccErrBadAPIVersion` | Unsupported API version
> 214 | `ccErrContextLocked` | Context is already locked
> 215 | `ccErrContextUnlocked` | Context is not locked by the caller
> 216 | `ccErrCCacheLocked` | CCache is already locked
> 217 | `ccErrCCacheUnlocked` | CCache is not locked by the caller
> 218 | `ccErrBadLockType` | Bad lock type
> 219 | `ccErrNeverDefault` | CCache was never default
> 220 | `ccErrCredentialsNotFound` | Matching credentials not found in the ccache
> 221 | `ccErrCCacheNotFound` | Matching ccache not found in the collection (either a name was supplied to a function, and no ccache with that name exists, or a `cc_ccache_t` was supplied and the corresponding ccache has been destroyed).

> ## API behavior

> ### Error handling

> All functions of the API return some of the error constants listed above; the exact list of error constants returned by any API function is provided in the function descriptions below.

> When returning an error constant other than `ccNoError` or `ccIteratorEnd`, API functions never modify any of the values passed in by reference.

> ### Synchronization and atomicity

> Every function in the API is atomic. Every function acquires a lock before accessing any ccache or the collection, and releases the lock before returning; the scope of the lock is the smallest possible i.e. only as few individual ccaches as needed are locked; otherwise, the entire collection is locked. The restrictions of the lock are the narrowest possible, i.e. the lock is a read lock unless the ccache or the collection need to be modified.

> Iterators, in addition to every function being atomic, iterate over ccacches or credentials atomically. That means that the caller is guaranteed that the set of ccaches or credentials that is iterated over will not change during the lifetime of the iterator (except as noted below). The lock is acquired when the iterator is created. The lock is released when the iterator is released. The lock is a reader lock, which means that if you intend to make modifications to the iteration set while iterating, you need to upgrade to a writer lock first.

> The only case when the iteration set will be modified while the iterator is active is when the owner of the iterator makes modifications. The only guarantee made in this case is that deleting an element on the iteration set has no effect on which elements will be subsequently returned by the iterator (note that their order can be affected, however). This allows one to iterate over an iteration set and delete all its elements.

> Locking implementation should not use copy-on-write techniques to implement locks, because those techniques imply that same parts of the ccache collection remain visible to some callers even though they are not present in the collection, which is a potential security risk. For example, a copy-on-write technique might make a copy of the entire collection when a read lock is acquired, so as to allow the owner of the lock to access the collection in an apparently unmodified state, while also allowing others to make modifications to the collection. However, this would also enable the owner of the lock to indefinitely (until the expiration time) use credentials that have actually been deleted from the collection.

> ### Object lifetimes and management

> The lifetime of an object returned by the API is until `release()` is called for it. Releasing one object has no effect on existence of any other object. For example, a ccache obtained within a context continue to exist when the context is released.

> Every object returned by the API (`cc_context_t`, `cc_ccache_t`, `cc_ccache_iterator_t`, `cc_credentials_t`, `cc_credentials_iterator_t`, `cc_string_t`) is owned by the caller of the API, and it is the responsibility of the caller to call `release()` for every object to prevent memory leaks.

> ### Platform-specific behaviors and requirements

> #### Mac OS

> It is an error to hold any lock across a call to `WaitNextEvent()` under Mac OS, whether the lock is explicit (acquired by calling `lock()`) or implicit (acquired by an iterator or other API functions). If you do that, all your locks might be automatically unlocked, to allow other processes to access the cache. (In other words, blocking locks can't be used on Mac OS for inter-process synchronization.)

> Under Mac OS 7, 8, and 9, the default cache is maintained on a system-wide basis, since all the processes running on the machine have the same privileges anyway (the privileges of the currently logged in user).

> For Mac OS X Server, see the UNIX section below.

> #### MS Windows

> DLLs should be named KrbCC16.dll and KrbCC32.dll.

> The semantics of default ccaches under MS Windows are unspecified so far (will be fixed before final version of the API specification).

> UNIX

> The semantics of default ccaches on various variant of UNIX are unspecified so far (will be fixed before final version of the API specification).

> ## Types and functions

> ### Basic types

> The `cc_int32` and `cc_uint32` are the signed and unsigned 32-bit integer types. `cc_uint32` is `unsigned int` on platforms where `4 <= sizeof (unsigned int) < sizeof (unsigned long)` and `long` on all other platforms. `cc_int32` is `int` on platforms where `4 <= sizeof (int) < sizeof (long)` and `long` on all other platforms.

>     typedef cc_int32 cc_time_t;

> The `cc_time_t` type is used to represent a time in seconds. The time must be stored as the number of seconds since midnight GMT on January 1, 1970.

> ### Opaque types

> All of the opaque high-level types in CCache API are implemented as structures of function pointers and private data. To perform some operation on a type, the caller of the API has to first obtain an instance of that type, and then call the appropriate function pointer from that instance. For example, to call `get_change_time()` on a `cc_context_t`, one would call `cc_initialize()` (which creates a new `cc_context_t`) and then call its `get_change_time()`, like this:

>     cc_context_t context;
>     cc_int32 err = cc_initialize (&context, ccapi_version_3, nil, nil);
>     if (err == ccNoError)
>         time = context -> functions -> get_change_time (context)

> All API functions also have convenience preprocessor macros, which make the API seem completely function-based. For example, `cc_context_get_change_time (context, time)` is equivalent to `context -> functions -> get_change_time (context, time)`. The convenience macros follow the following naming convention: the function call

>     cc_type_t opaque_pointer;
>     result = opaque_pointer -> functions -> somefunction (opaque_pointer, args)

> can be written using a convenience macro as:

>     cc_type_t opaque_pointer;
>     result = cc_type_somefunction (opaque_pointer, args)

> The specifications below include the names for both the functions and the convenience macros, in that order. For clarity, it is recommended that clients using the API use the convenience macros, but that is merely a stylistic choice.

> Implementing the API in this manner allows us to extend and change the interface in the future, while preserving compatibility with older clients.

> For example, consider the case when the signature or the semantics of a `cc_ccache_t` function is changed. The API version number is incremented. The library implementation contains both a function with the old signature and semantics and a function with the new signature and semantics. When a context is created, the API version number used in that context is stored in the context, and therefore it can be used whenever a ccache is created in that context. When a ccache is created in a context with the old API version number, the function pointer structure for the ccache is filled with pointers to functions implementing the old semantics; when a ccache is created in a context with the new API version number, the function pointer structure for the ccache is filled with poitners to functions implementing the new semantics.

> Similarly, if a function is added to the API, the version number in the context can be used to decide whether to include the implementation of the new function in the appropriate function pointer structure or not.

> ###

> cc_context_t

>     typedef struct {
>         cc_int32    (*release) (
>                                     cc_context_t context);
>         cc_int32    (*clone) (
>                                     cc_context_t context,
>                                     cc_context_t* new_context,
>                                     cc_int32 api_version,
>                                     cc_int32 supported_version,
>                                     char const** vendor);
>         cc_int32    (*get_version) (
>                                     cc_context_t context,
>                                     cc_int32* version);
>         cc_int32    (*get_change_time) (
>                                     cc_context_t context,
>                                     cc_time_t* time);
>         cc_int32    (*get_default_ccache_name) (
>                                     cc_context_t context,
>                                     cc_string_t* name);
>         cc_int32    (*open_ccache) (
>                                     cc_context_t context,
>                                     const char* name,
>                                     cc_ccache_t* ccache);
>         cc_int32    (*open_default_ccache) (
>                                     cc_context_t context,
>                                     cc_ccache_t* ccache);
>         cc_int32    (*create_ccache) (
>                                     cc_context_t context,
>                                     const char* name,
>                                     cc_int_32 cred_vers,
>                                     const char* principal,
>                                     cc_ccache_t* ccache);
>         cc_int32    (*create_default_ccache) (
>                                     cc_context_t context,
>                                     cc_int_32 cred_vers,
>                                     const char* principal,
>                                     cc_ccache_t* ccache);
>         cc_int32    (*create_new_ccache) (
>                                     cc_context_t context,
>                                     cc_int_32 cred_vers,
>                                     const char* principal,
>                                     cc_ccache_t* ccache);
>         cc_int32    (*new_ccache_iterator) (
>                                     cc_context_t context,
>                                     cc_ccache_iterator_t* iterator);
>         cc_uint32   (*lock) (
>                                     cc_context_t context,
>                                     cc_uint32 lock_type,
>                                     cc_uint32 block,
>                                     cc_lock_t* lock);
>     } cc_context_f;

>     typedef struct {
>         const cc_context_f* functions;
>     }* cc_context_t;

> The `cc_context_t` type gives the caller access to a ccache collection. Before being able to call any functions in the CCache API, the caller needs to acquire an instance of `cc_context_t` by calling `cc_initialize` or `cc_context_clone`. The `cc_context_t` type has the following functions:

> #### cc_initialize

>     cc_int32 cc_initialize (
>         cc_context_t*         cc_context,
>         cc_int32              api_version,
>         cc_int32*             api_supported,
>         char**                vendor)

> This function performs any initialization required by the API. It must be called before any other function in the API is called.

> The application must pass in the maximum version number of the API it supports in the `api_version` parameter.

> If `api_supported` is non-NULL, then `cc_initialize` will store the maximum API version number supported by the library implementing the API there.

> If the version requested by `api_version` is not equal to a version supported by the library, `ccErrBadAPIVersion` will be returned as the error code (along with the version the library does support in `api_supported`) and `cc_initialize()` will not allocate any memory.

> If `vendor` is non-NULL, then `cc_initialize()` will store in `*vendor` a pointer to a read-only C string which contains a string describing the vendor which implemented the credentials cache API.

> Possible return values: `ccNoError`, `ccErrNoMem`, `ccErrBadAPIVersion`, `ccErrBadParam`

> #### cc_context_get_version

>     cc_int32 get_version (
>         cc_context_t          cc_context,
>         cc_int32*             api_version)
>     cc_int32 cc_context_get_version (
>         cc_context_t          cc_context,
>         cc_int32*             api_version)

> This function returns the API version with which a context was initialized.

> This is useful for cases where a context is passed between different pieces of code (an application and a library, for example), and the recipient of a context wants to ascertain that the context was initialized with the appropriate version of the API.

> Possible return values: `ccNoError`, `ccErrInvalidContext`, `ccErrBadParam`

> #### cc_context_release

>     cc_int32 release (
>         cc_context_t          cc_context)
>     cc_int32 cc_context_release (
>         cc_context_t          cc_context)

> This function releases the context and performs any cleanup required by the API.

> Possible return values: `ccNoError`, `ccErrInvalidContext`

> #### cc_context_clone

>     cc_int32 clone (
>         cc_context_t          cc_context,
>         cc_context_t*         new_context,
>         cc_int32              api_version,
>         cc_int32*             supported_version,
>         char**                vendor)
>     cc_int32 cc_context_clone (
>         cc_context_t          cc_context,
>         cc_context_t*         new_context,
>         cc_int32              api_version,
>         cc_int32*             supported_version,
>         char**                vendor)

> This function creates a new context by cloning an existing context. The new context is initialized for API version in `api_version`.

> This function should be used by any code that wants to use the API, but also wants to receive a context from other code (for example, a library which takes a context from an application). Since the new context is initialized to the specified API version, the caller of `cc_context_clone()` can continue to use it, knowing its API version.

> Values returned in `supported_version` and `vendor` are the same as for `cc_initialize()`.

> Possible return values: `ccNoError`, `ccErrNoMem`, `ccErrBadAPIVersion`, `ccErrBadParam`, `ccErrInvalidContext`

> #### cc_context_get_change_time

>     cc_int32 get_change_time (
>         cc_context_t          cc_context,
>         cc_time_t*            time)
>     cc_int32 cc_context_get_change_time (
>         cc_context_t          cc_context,
>         cc_time_t*            time)

> This function returns the time of the most recent change for the entire ccache collection. By maintaining a local copy the caller can deduce whether the ccache collection has been modified since the previous call to `cc_context_get_change_time()` or not.

> The time returned by `cc_context_get_changed_time()` increases whenever:

> - a ccache is created
> - a ccache is destroyed
> - a credential is stored
> - a credential is removed
> - a ccache principal is changed
> - the default ccache is changed

> Possible return values: `ccNoError`, `ccErrInvalidContext`, `ccBadParam`

> #### cc_context_get_default_ccache_name

>     cc_int32 get_default_ccache_name (
>         cc_context_t          cc_context,
>         cc_string_t*          name)
>     cc_int32 cc_context_get_default_ccache_name (
>         cc_context_t          cc_context,
>         cc_string_t*          name)

> This function returns the name of the default ccache. When the default ccache exists, its name is returned. If there are no ccaches in the collection, and thus there is no default ccache, the name that the default ccache should have is returned. The ccache with that name will be used as the default ccache by all processes which initialized Kerberos libraries before the ccache was created.

> If there is no default ccache, and the client is creating a new ccache, it should be created with the default name. If there already is a default ccache, and the client wants to create a new ccache (as opposed to reusing an existing ccache), it should be created with any unique name; `cc_context_create_new_ccache()` can be used to accomplish that more easily.

> If the first ccache is created with a name other than the default name, then the processes already running will not notice the credentials stored in the new ccache, which is normally undesirable.

> Possible return values: `ccNoError`, `ccErrInvalidContext`, `ccBadParam`, `ccErrNoMem`

> #### cc_context_open_ccache

>     cc_int32 open_ccache (
>         cc_context_t          cc_context,
>         const char*           name,
>         cc_ccache_t*          ccache)
>     cc_int32 cc_context_open_ccache (
>         cc_context_t          cc_context,
>         const char*           name,
>         cc_ccache_t*          ccache)

> Opens an already existing ccache identified by its name. It returns a reference to the ccache in `*ccache`.

> The list of all ccache names, principals, and credentials versions may be retrieved by calling `cc_context_new_cache_iterator()`, `cc_ccache_get_name()`, `cc_ccache_get_principal()`, and `cc_ccache_get_cred_version()`.

> Possible error codes: `ccNoError`, `ccErrBadName`, `ccErrInvalidContext`, `ccErrNoMem`, `ccErrCCacheNotFound`, `ccErrBadParam`

> #### cc_context_open_default_ccache

>     cc_int32 open_default_ccache (
>         cc_context_t          cc_context,
>         cc_ccache_t*          ccache)
>     cc_int32 cc_context_open_default_ccache (
>         cc_context_t          cc_context,
>         cc_ccache_t*          ccache)

> Opens the default ccache. It returns a reference to the ccache in `*ccache`.

> This function performs the same function as calling `cc_context_get_default_ccache_name` followed by `cc_context_open_ccache`, but it performs it atomically.

> Possible error codes: `ccNoError`, `ccErrInvalidContext`, `ccErrNoMem`, `ccErrCCacheNotFound`, `ccErrBadParam`

> #### cc_context_create_ccache

>     cc_int32 create_ccache (
>         cc_context_t          cc_context,
>         const char*           name,
>         cc_int32              cred_vers,
>         const char*           principal,
>         cc_ccache_t*          ccache)
>     cc_int32 cc_context_create_ccache (
>         cc_context_t          cc_context,
>         const char*           name,
>         cc_int32              cred_vers,
>         const char*           principal,
>         cc_ccache_t*          ccache)

> Create a new credentials cache. The ccache is uniquely identified by its name. The principal given is also associated with the ccache and the credentials version specified. A NULL name is not allowed (and `ccErrBadName` is returned if one is passed in). Only `cc_credentials_v4` and `cc_credentials_v5` are valid input values for `cred_vers`. If you want to create a new ccache that will hold both versions of credentials, call `cc_context_create_ccache()` with one version, and then `cc_ccache_set_principal()` with the other version.

> If you want to create a new ccache (with a unique name), you should use `cc_context_create_new_ccache()` instead. If you want to create or reinitialize the default cache, you should use `cc_context_create_default_ccache()`.

> If name is non-NULL and there is already a ccache named `name`:

> - the credentials in the ccache whose version is `cred_vers` are removed
> - the principal (of the existing ccache) associated with `cred_vers` is set to `principal`
> - a handle for the existing ccache is returned and all existing handles for the ccache remain valid

> If no ccache named name already exists:

> - a new empty ccache is created
> - the principal of the new ccache associated with `cred_vers` is set to `principal`
> - a handle for the new ccache is returned

> For a new ccache, the name should be any unique string. The name is not intended to be presented to users.

> If the created ccache is the first ccache in the collection, it is made the default ccache. Note that normally it is undesirable to create the first ccache with a name different from the default ccache name (as returned by `cc_context_get_default_ccache_name()`); see the description of `cc_context_get_default_ccache_name()` for details.

> The principal should be a C string containing an unparsed Kerberos principal in the format of the appropriate Kerberos version, i.e. foo.bar@BAZ for Kerberos v4 and foo/bar@BAZ for Kerberos v5.

> Possible error codes: `ccNoError`, `ccErrorBadName`, `ccErrBadParam`, `ccErrInvalidContext`, `ccErrNoMem`, `ccErrBadCredentialsVersion`

> #### cc_context_create_default_ccache

>     cc_int32 create_default_ccache (
>         cc_context_t          cc_context,
>         cc_int32              cred_vers,
>         const char*           principal,
>         cc_ccache_t*          ccache)
>     cc_int32 cc_context_create_default_ccache (
>         cc_context_t          cc_context,
>         cc_int32              cred_vers,
>         const char*           principal,
>         cc_ccache_t*          ccache)

> Create the default credentials cache. The behavior of this function is similar to that of `cc_create_ccache()`. If there is a default ccache (which is always the case except when there are no ccaches at all in the collection), it is initialized with the specified credentials version and principal, as per `cc_create_ccache()`; otherwise, a new ccache is created, and its name is the name returned by `cc_context_get_default_ccache_name()`.

> Possible error codes: `ccNoError`, `ccErrBadName`, `ccErrBadParam`, `ccErrInvalidContext`, `ccErrNoMem`, `ccErrBadCredentialsVersion`

> #### cc_context_create_new_ccache

>     cc_int32 create_new_ccache (
>         cc_context_t          cc_context,
>         cc_int32              cred_vers,
>         const char*           principal,
>         cc_ccache_t*          ccache)
>     cc_int32 cc_context_create_new_ccache (
>         cc_context_t          cc_context,
>         cc_int32              cred_vers,
>         const char*           principal,
>         cc_ccache_t*          ccache)

> Create a new unique credentials cache. The behavior of this function is similar to that of `cc_create_ccache()`. If there are no ccaches, and therefore no default ccache, the new ccache is created with the default ccache name as would be returned by `get_default_ccache_name()`. If there are some ccaches, and therefore there is a default ccache, the new ccache is created with a new unique name. Clearly, this function never reinitializes a ccache, since it always uses a unique name.

> Possible error codes: `ccNoError`, `ccErrBadName`, `ccErrBadParam`, `ccErrInvalidContext`, `ccErrNoMem`, `ccErrBadCredentialsVersion`

> #### cc_context_new_ccache_iterator

>     cc_int32 new_ccache_iterator (
>         cc_context_t          cc_context,
>         cc_ccache_iterator_t* iterator)
>     cc_int32 cc_context_new_ccache_iterator (
>         cc_context_t          cc_context,
>         cc_ccache_iterator_t* iterator)

> Used to allocate memory and initialize `iterator`. Successive calls to `iterator`'s `next()` function will return ccaches in the collection.

> If changes are made to the collection while an iterator is being used on it, the iterator must return at least the intersection, and at most the union, of the set of ccaches that were present when the iteration began and the set of ccaches that are present when it ends.

> Possible error codes: `ccNoError`, `ccBadParam`, `ccErrNoMem`, `ccErrInvalidContext`

> #### cc_context_lock

>     cc_int32 lock (
>         cc_context_t          context,
>         cc_int32              lock_type,
>         cc_int32              block,
>         cc_lock_t*            lock)
>     cc_int32 cc_context_lock (
>         cc_context_t          context,
>         cc_int32              lock_type,
>         cc_int32              block,
>         cc_lock_t*            lock)

> Attempts to acquire a lock for the ccache collection. Allowed values for `lock_type` are:

> - `cc_lock_read`: if acquired, the lock is a read lock, and the owner of the lock is guaranteed that the context will not be modified until the lock is released.
> - `cc_lock_write`: if acquired, the lock is a write lock, and the owner of the lock is guaranteed that noone else will be allowed to inspect or modify the context until the lock is released

> If `block` is `cc_lock_block`, `lock()` will not return until the lock is acquired. If `block` is `cc_lock_noblock`, `lock()` will return immediately, either acquiring the lock and returning `ccNoError`, or failing to acquire the lock and returning `ccErrContextLocked`.

> Attempting to acquire a write lock on a context that is already read-locked is allowed; if successful, the context acquires two different locks (a read and a write lock), which is the same as having just a write lock. To release them, `cc_lock_release()` should be called twice. If unsuccessful, the context still has one read lock.

> To avoid having to deal with differences between thread semantics on different platforms, locks are granted per context, rather than per thread or per process. That means that different threads of execution have to acquire separate contexts in order to be able to synchronize with each other.

> The lock object is returned in the lock parameter, and should be unlocked by using `cc_lock_release()`.

> Possible error codes: `ccNoError`, `ccErrContextLocked`, `ccErrBadLockType`, `ccErrInvalidContext`, ccErrNoMem

> ###

> cc_ccache_t

>     typedef struct {
>         cc_int32    (*release) (
>                                      cc_ccache_t ccache);
>         cc_int32    (*clone) (
>                                      cc_ccache_t ccache,
>                                      cc_ccache_t* new_ccache);
>         cc_int32    (*destroy) (
>                                      cc_ccache_t ccache);
>         cc_int32    (*set_default) (
>                                      cc_ccache_t ccache);
>         cc_int32    (*get_credentials_version) (
>                                      cc_ccache_t ccache,
>                                      cc_int32* credentials_version);
>         cc_int32    (*get_name) (
>                                      cc_ccache_t ccache,
>                                      cc_string_t* name);
>         cc_int32    (*get_principal) (
>                                      cc_ccache_t ccache,
>                                      cc_int32 credentials_version,
>                                      cc_string_t* principal);
>         cc_int32    (*set_principal) (
>                                      cc_ccache_t ccache,
>                                      cc_int32 credentials_version,
>                                      const char* principal);
>         cc_int32    (*store_credentials) (
>                                      cc_ccache_t ccache,
>                                      const cc_credentials_union* credentials);
>         cc_int32    (*remove_credentials) (
>                                      cc_ccache_t ccache,
>                                      cc_credentials_t credentials);
>         cc_int32    (*new_credentials_iterator) (
>                                      cc_ccache_t ccache,
>                                      cc_credentials_iterator_t* iterator);
>         cc_int32    (*lock) (
>                                      cc_ccache_t ccache,
>                                      cc_uint32 lock_type,
>                                      cc_uint32 block,
>                                      cc_lock_t* lock);
>         cc_int_32   (*get_last_default_time) (
>                                      cc_ccache_t ccache,
>                                      cc_time_t* time);
>         cc_int_32   (*get_change_time) (
>                                      cc_ccache_t ccache,
>                                      cc_time_t* time);
>         cc_int_32   (*move) (
>                                      cc_ccache_t source,
>                                      cc_ccache_t destination);
>     } cc_ccache_f;

>     typedef struct {
>         const cc_ccache_f* functions;
>     }* cc_ccache_t;

> The `cc_ccache_t` type represents a reference to a ccache. Callers can access a ccache and the credentials stored in it via a `cc_ccache_t.` A `cc_ccache_t` can be acquired via `cc_context_open_ccache()`, `cc_context_open_default_ccache()`, or `cc_ccache_iterator_next()`. The functions in a `cc_ccache_t` are:

> #### cc_ccache_release

>     cc_int32 release (
>         cc_ccache_t           ccache)
>     cc_int32 cc_ccache_release (
>         cc_ccache_t           ccache)

> Close the ccache reference. Memory associated with the ccache reference is deallocated; the ccache itself remains unchanged. The reference is invalid after being released.

> Possible return values: `ccNoError`, `ccErrInvalidCCache`

> #### cc_ccache_clone

>     cc_int32 clone (
>         cc_ccache_t           ccache)
>         cc_ccache_t*          new_ccache)
>     cc_int32 cc_ccache_clone (
>         cc_ccache_t           ccache)
>         cc_ccache_t*          new_ccache)

> This function returns a new reference to a ccache by cloning an existing reference to the ccache.

> Possible return values: `ccNoError`, `ccErrNoMem`, `ccErrInvalidCCache`

> #### cc_ccache_destroy

>     cc_int32 destroy (
>         cc_ccache_t           ccache)
>     cc_int32 cc_ccache_destroy (
>         cc_ccache_t           ccache)

> Destroy the ccache referenced by the ccache reference. All credentials in the ccache are removed. The associated memory is deallocated. The reference itself is released at the same time, and is therefore invalid after the call to `cc_ccache_destroy()`.

> If the default ccache is destroyed, the ccache that was most recently default before it is made the default ccache, unless the default ccache was the only ccache in the collection; in that case, no ccache is made default, as there are no ccaches left.

> Possible return values: `ccNoError`, `ccErrInvalidCCache`, `ccErrCCacheNotFound`

> #### cc_ccache_set_default_ccache

>     cc_int32 set_default (
>         cc_ccache_t           ccache)
>     cc_int32 cc_ccache_set_default (
>         cc_ccache_t           ccache)

> Makes the ccache referenced the default ccache.

> Possible return values: `ccNoError`, `ccErrInvalidCCache`, `ccErrCCacheNotFound`

> #### cc_cache_get_credentials_version

>     cc_int32 get_credentials_version (
>         cc_ccache_t           ccache,
>         cc_int32*             credentials_version)
>     cc_int32 cc_ccache_get_credentials_version (
>         cc_ccache_t           ccache,
>         cc_int32*             credentials_version)

> `cc_get_credentials_version()` returns one value of the enumerated type `cc_credentials_vers`. The possible return values are `cc_credentials_v4` (if ccache's v4 principal has been set), `cc_credentials_v5` (if ccache's v5 principal has been set), or `cc_credentials_v4_v5` (if both ccache's v4 and v5 principals have been set). A ccache's principal is set with one of `cc_context_create_ccache()`, `cc_context_create_new_ccache()`, `cc_create_default_ccache()`, or `cc_ccache_set_principal()`.

> Possible return values: `ccNoError`, `ccErrInvalidCCache`, `ccErrBadParam`, `ccErrCCacheNotFound`

> #### cc_ccache_get_name

>     cc_int32 get_name (
>         cc_ccache_t           ccache,
>         cc_string_t*          name)
>     cc_int32 cc_ccache_get_name (
>         cc_ccache_t           ccache,
>         cc_string_t*          name)

> `get_name()` returns the name of the ccache. The name can be used in `cc_context_open_ccache()` or `cc_context_create_ccache()`. The name uniquely identifies a ccache. The returned name should be freed via the `cc_string_release()` function.

> Possible error codes: `ccNoError`, `ccErrNoMem`, `ccErrBadParam`, `ccErrInvalidCCache`, `ccErrCCacheNotFound`

> #### cc_ccache_get_principal

>     cc_int32 get_principal (
>         cc_ccache_t           ccache,
>         cc_int32              cred_vers,
>         cc_string_t*          principal)
>     cc_int32 cc_ccache_get_principal (
>         cc_ccache_t           ccache,
>         cc_int32              cred_vers,
>         cc_string_t*          principal)

> Return the principal for the ccache that was set via `cc_context_create_ccache()`, `cc_context_create_default_ccache()`, `cc_context_create_new_ccache()`, or `cc_ccache_set_principal()`. The returned principal should be freed via the `cc_string_release()` function. Principals for v4 and v5 are separate, but should be kept synchronized for each ccache; they can be retrieved by passing `cc_credentials_v4` or `cc_credentials_v5` in `cred_vers`. Passing `cc_credentials_v4_v5` is an error (`ccErrBadCredentialsVersion`).

> Possible error codes: `ccNoError`, `ccErrNoMem`, `ccErrBadCredentialsVersion`, `ccErrBadParam`, `ccErrInvalidCCache`, `ccErrCCacheNotFound`

> #### cc_ccache_set_principal

>     cc_int32 set_principal (
>         cc_ccache_t           ccache,
>         cc_int32              cred_vers,
>         const char*           principal)
>     cc_int32 cc_ccache_set_principal (
>         cc_ccache_t           ccache,
>         cc_int32              cred_vers,
>         const char*           principal)

> Set the a principal for `ccache`. The v4 and v5 principals can be set independently, but they should always be kept equal, up to differences in string representation between v4 and v5. Passing `cc_credentials_v4_v5` in `cred_vers` is an error (`ccErrBadCredentialsVersion`).

> `principal` points to a nul-terminated string that will be copied into the ccache. This new principal will be returned if you call `cc_ccache_get_principal()` for this ccache.

> Possible error codes: `ccNoError`, `ccErrNoMem`, `ccErrInvalidCCache`, `ccErrBadCredentialsVersion`, `ccErrBadParam`, `ccErrCCacheNotFound`

> #### cc_ccache_store_credentials

>     cc_int32 store_credentials (
>         cc_ccache_t                   ccache,
>         const cc_credentials_union*   credentials)
>     cc_int32 cc_ccache_store_credentials (
>         cc_ccache_t                   ccache,
>         const cc_credentials_union*   credentials)

> Store a copy of `credentials` in the ccache.

> See the description of the credentials types for the meaning of `cc_credentials_union` fields.

> Before credentials of a specific credential type can be stored in a ccache, the corresponding principal version has to be set. I.e., before you can store Kerberos v4 credentials in a ccache, the Kerberos v4 principal has to be set either by `cc_context_create_ccache()`, `cc_context_create_default_ ccache()`, `cc_context_create_new_ccache()`, or `cc_set_principal()`; likewise for Kerberos v5. Otherwise, `ccErrBadCredentialsVersion` is returned.

> Possible error codes: `ccNoError`, `ccErrInvalidCCache`, `ccErrInvalidCredentials`, `ccErrBadCredentialsVersion`, `ccErrCCacheNotFound`

> #### cc_ccache_remove_credentials

>     cc_int32 remove_credentials (
>         cc_ccache_t                   ccache,
>         cc_credentials_t              credentials)
>     cc_int32 cc_ccache_remove_credentials (
>         cc_ccache_t                   ccache,
>         cc_credentials_t              credentials)

> Removes `credentials` from a ccache. Note that credentials must be previously acquired from the CCache API; only exactly matching credentials will be removed. (This places the burden of determining exactly which credentials to remove on the caller, but ensures there is no ambigity about which credentials will be removed.)

> If found, the credentials are removed from the ccache. The `credentials` parameter is not modified and should be freed by the caller. It is legitimate to call this function while an iterator is traversing the ccache, and the deletion of a credential already returned by `cc_credentials_iterator_next()` will not disturb sequence of credentials returned by `cc_credentials_iterator_next()`.

> Possible error codes: `ccNoError`, `ccErrInvalidCCache`, `ccErrInvalidCredentials`, `ccErrCredentialsNotFound`, `ccErrCCacheNotFound`

> #### cc_ccache_new_credentials_iterator

>     cc_int32 new_credentials_iterator (
>         cc_ccache_t           ccache,
>         cc_creds_iterator_t*  iterator)
>     cc_int32 cc_cccache_new_credentials_iterator (
>         cc_ccache_t           ccache,
>         cc_creds_iterator_t*  iterator)

> Allocates memory for `iterator` and initializes it. Successive calls to `cc_credentials_iterator_next()` will return credentials from the ccache.

> If changes are made to the ccache while an iterator is being used on it, the iterator must return at least the intersection, and at most the union, of the set of credentials that were in the ccache when the iteration began and the set of credentials that are in the ccache when it ends.

> Possible error codes: `ccNoError`, `ccErrBadParam`, `ccErrNoMem`, `ccErrCCacheNotFound`

> #### cc_ccache_lock

>     cc_int32 lock (
>         cc_ccache_t           ccache,
>         cc_int32              lock_type,
>         cc_uint32             block,
>         cc_lock_t*            lock)
>     cc_int32 cc_ccache_lock (
>         cc_ccache_t           ccache,
>         cc_int32              lock_type,
>         cc_uint32             block,
>         cc_lock_t*            lock)

> Attempts to acquire a lock for the ccache. Allowed values for `lock_type` are:

> - `cc_lock_read`: if acquired, the lock is a read lock, and the owner of the lock is guaranteed that the ccache will not be modified until the lock is released.
> - `cc_lock_write`: if acquired, the lock is a write lock, and the owner of the lock is guaranteed that noone else will be allowed to inspect or modify the ccache until the lock is released

> If `block` is `cc_lock_block`, `lock()` will not return until the lock is acquired. If `block` is `cc_lock_noblock`, `lock()` will return immediately, either acquiring the lock and returning `ccNoError`, or failing to acquire the lock and returning `ccErrCCacheLocked`.

> Attempting to acquire a write lock on a ccache that is already read-locked is allowed; if successful, the context acquires two different locks (a read and a write lock), which is the same as having just a write lock. To release them, `cc_lock_release()` should be called twice. If unsuccessful, the context still has one read lock.

> To avoid having to deal with differences between thread semantics on different platforms, locks are granted per context, rather than per thread or per process. That means that different threads of execution have to acquire separate contexts in order to be able to synchronize with each other.

> The lock object is returned in the lock parameter, and should be unlocked by using `cc_lock_release()`.

> Possible error codes: `ccNoError`, `ccErrCCacheLocked`, `ccErrBadLockType`, `ccErrNoMem`, `ccErrCCacheNotFound`

> #### cc_ccache_get_change_time

>     cc_int32 get_change_time (
>         cc_ccache_t           ccache,
>         cc_time_t*            time)
>     cc_int32 cc_ccache_get_change_time (
>         cc_ccache_t           ccache,
>         cc_time_t*            time)

> This function returns the time of the most recent change for the ccache. By maintaining a local copy the caller can deduce whether the ccache has been modified since the previous call to `cc_ccache_get_change_time()` or not.

> The time returned by `cc_ccache_get_changed_time()` increases whenever:

> - the ccache is created
> - a credential is stored
> - a credential is removed
> - a ccache principal is changed
> - the ccache is made default or not-default

> Possible return values: `ccNoError`, `ccErrInvalidCCache`, `ccBadParam`, `ccErrCCacheNotFound`

> #### cc_ccache_get_last_default_time

>     cc_int32 get_last_default_time (
>         cc_ccache_t           ccache,
>         cc_time_t*            time)
>     cc_int32 cc_ccache_get_last_default_time (
>         cc_ccache_t           ccache,
>         cc_time_t*            time)

> This function returns the last time when the ccache was made the default ccache. This allows clients to sort the ccaches by how recently they were default, which is useful for user listing of ccaches. If the ccache was never default, `ccErrNeverDefault` is returned.

> Possible return values: `ccNoError`, `ccErrInvalidCCache`, `ccBadParam`, `ccErrNeverDefault`, `ccErrCCacheNotFound`

> #### cc_ccache_move

>     cc_int32 move (
>         cc_ccache_t           source,
>         cc_ccache_t           destination)
>     cc_int32 cc_ccache_move (
>         cc_ccache_t           source,
>         cc_ccache_t           destination)

> This function takes one ccache (the source) and moves all of its credentials and all of its principals to a different ccache (the destination). All the credentials in the destination are destroyed; both principals of the destination are reset to the corresponding principals of the source ccache.

> Assuming that the ccaches are valid, the function cannot fail. This makes it extremely useful for callers that want to atomically and safely replace all the credentials in a ccache -- for example, to renew user's credentials. The new credentials can be placed in a new ccache, and when they are all available, atomically placed into the destination ccache.

> All ccache handles for the source ccache, including the one passed to `cc_ccache_move()`, become invalid, just as `cc_ccache_destroy` had been called.

> Any locks held on the source ccache are transferred to the destination ccache; this is okay because those locks must be held by the caller.

> Possible return values: `ccNoError`, `ccErrInvalidCCache`, `ccErrCCacheNotFound`

> ###

> cc_ccache_iterator_t

>     typedef struct {
>         cc_int32    (*release) (
>                                      cc_ccache_iterator_t iter);
>         cc_int32    (*clone) (
>                                      cc_ccache_iterator_t iter,
>                                      cc_ccache_iterator_t* new_iter);
>         cc_int32    (*next) (
>                                      cc_ccache_iterator_t iter,
>                                      cc_ccache_t* ccache);
>         cc_int32    (*skip) (
>                                      cc_ccache_iterator_t iter);
>         cc_int32    (*reset) (
>                                      cc_ccache_iterator_t iter);
>     } cc_ccache_iterator_f;

>     typedef struct {
>         const cc_ccache_iterator_f* functions;
>     }* cc_ccache_iterator_t;

> The `cc_ccache_iterator_t` type represents an iterator that iterates over a set of ccaches and returns them in all in some order. A new instance of this type can be obtained by calling `cc_context_new_ccache_iterator()`. The type has the following functions:

> #### cc_ccache_iterator_release

>     cc_int32 release (
>         cc_ccache_iterator_t  iterator)
>     cc_int32 cc_ccache_iterator_release (
>         cc_ccache_iterator_t  iterator)

> Deallocates the memory used by a ccache iterator.

> Possible error codes: `ccNoError`, `ccErrInvalidCCacheIterator`

> #### cc_ccache_iterator_clone

>     cc_int32 clone (
>         cc_ccache_iterator_t  iter,
>         cc_ccache_iterator_t* new_iter)
>     cc_int32 cc_ccache_iterator_clone (
>         cc_ccache_iterator_t  iter,
>         cc_ccache_iterator_t* new_iter)

> Creates a new iterator by cloning an existing one.

> Since the original iterator acquires a lock on the entire collection when it's created, the newly created iterator will always iterate over exactly the same set of ccaches. The newly created iterator will not return the ccaches that the original iterator had already returned, unless `cc_ccache_iterator_reset()` is called on the new iterator.

> Possible error codes: `ccNoError`, `ccErrNoMem`, `ccErrInvalidCCacheIterator`

> #### cc_ccache_iterator_next

>     cc_int32 next (
>         cc_ccache_iterator_t  iterator,
>         cc_ccache_t*          ccache)
>     cc_int32 cc_ccache_iterator_next (
>         cc_ccache_iterator_t  iterator,
>         cc_ccache_t*          ccache)

> Used to sequentially open every ccache in the iteration set.

> `ccache` must be a pointer to a `cc_ccache_t`. The ccache returned may be used to get information about the ccache by calling `cc_ccache_get_name()`, `cc_ccache_get_cred_version()`, and `cc_ccache_get_principal()`. CCaches returned must be released between or after calls to `cc_ccache_iterator_next()`.

> When the last ccache in the sequence is returned, the return code from `next()` will be `ccIteratorEnd`.

> Possible error codes: `ccNoError`, `ccIteratorEnd`, `ccErrBadParam`, `ccErrNoMem`, `ccErrInvalidCCacheIterator`, `ccErrCCacheNotFound`

> #### cc_ccache_iterator_reset

>     cc_int32 reset (
>         cc_ccache_iterator_t  iter)
>     cc_int32 cc_ccache_iterator_reset (
>         cc_ccache_iterator_t  iter)

> Resets the iterator. After being reset, the iterator will return again all the ccaches it had already returned, except for the ccaches that had been deleted during a previous iteration.

> Possible error codes: `ccNoError`, `ccErrInvalidCCacheIterator`

> #### cc_ccache_iterator_skip

>     cc_int32 skip (
>         cc_ccache_iterator_t  iter)
>     cc_int32 cc_ccache_iterator_skip (
>         cc_ccache_iterator_t  iter)

> Makes the iterator skip the next ccache in its iteration set. The ccache will not be subsequently returned by the iterator, unless `cc_ccache_iterator_reset()` is called. If the iterator has already returned all the ccaches in its iteration set, `ccIteratorEnd` is returned.

> Possible error codes: `ccNoError`, `ccIteratorEnd`, `ccErrInvalidCCacheIterator`

> ###

> cc_credentials_iterator_t

>     typedef struct {
>         void        (*release) (
>                                      cc_credentials_iterator_t iterator);
>         cc_int32    (*clone) (
>                                      cc_credentials_iterator_t iter,
>                                      cc_credentials_iterator_t* new_iter);
>         cc_int32    (*next) (
>                                      cc_credentials_iterator_t iterator,
>                                      cc_credentials_t* ccache);
>         cc_int32    (*skip) (
>                                      cc_credentials_iterator_t iter);
>         cc_int32    (*reset) (
>                                      cc_credentials_iterator_t iter);
>     } cc_credentials_iterator_f;

>     typedef struct {
>         const cc_credentials_iterator_f* functions;
>     }* cc_credentials_iterator_t;

> The `cc_credentials_iterator_t` type represents an iterator that iterates over a set of credentials. A new instance of this type can be obtained by calling `cc_ccache_new_credentials_iterator()`. The type has the following functions:

> #### cc_ccache_credentials_iterator_release

>     cc_int32 release (
>         cc_credentials_iterator_t  iterator)
>     cc_int32 cc_ccache_iterator_release (
>         cc_credentials_iterator_t  iterator)

> Deallocates memory used by a credentials iterator.

> Possible error codes: `ccNoErr`, `ccErrInvalidCredentialsIterator`

> #### cc_credentials_iterator_clone

>     cc_int32 clone (
>         cc_credentials_iterator_t  iter,
>         cc_credentials_iterator_t* new_iter)
>     cc_int32 cc_credentials_iterator_clone (
>         cc_credentials_iterator_t  iter,
>         cc_credentials_iterator_t* new_iter)

> Creates a new iterator by cloning an existing one.

> Since the original iterator acquires a lock on the entire ccache when it's created, the newly created iterator will always iterate over exactly the same set of credentials. The newly created iterator will not return the credentials that the original iterator had already returned, unless `cc_credentials_iterator_reset()` is called on the new iterator.

> Possible error codes: `ccNoError`, `ccErrNoMem`, `ccErrInvalidCredentialsIterator`

> #### cc_credentials_iterator_next

>     cc_int32 next (
>         cc_credentials_iterator_t  iterator,
>         cc_credentials_t*     credentials)
>     cc_int32 cc_credentials_iterator_next (
>         cc_credentials_iterator_t  iterator,
>         cc_credentials_t*     credentials)

> `cc_ccache_creds_iterator_t -> next()` is used to sequentially access a set of credentials. Both Kerberos v4 and v5 credentials are returned by the iterator; callers interested in only one kind need to check the version of the returned credentials and ignore the ones they don't care about.

> The credentials are returned in a `cc_credentials_t` pointed to by `credentials`.

> When the last credential in the sequence is returned, the return code from `next()` will be `ccIteratorEnd`.

> Possible error codes: `ccNoErr`, `ccIteratorEnd`, `ccErrBadParam`, `ccErrNoMem`, `ccErrInvalidCredentialsIterator`

> #### cc_credentials_iterator_reset

>     cc_int32 reset (
>         cc_credentials_iterator_t  iter)
>     cc_int32 cc_credentials_iterator_reset (
>         cc_credentials_iterator_t  iter)

> Resets the iterator. After being reset, the iterator will return again all the credentials it had already returned, except for the credentials that had been deleted during a previous iteration.

> Possible error codes: `ccNoError`, `ccErrInvalidCredentialsIterator`

> #### cc_credentials_iterator_skip

>     cc_int32 skip (
>         cc_credentials_iterator_t  iter)
>     cc_int32 cc_credentials_iterator_skip (
>         cc_credentials_iterator_t  iter)

> Makes the iterator skip next credentials in its iteration set. The credentials will not be subsequently returned by the iterator, unless `cc_credentials_iterator_reset()` is called. If the iterator has already returned all the credentials in its iteration set, `ccIteratorEnd` is returned.

> Possible error codes: `ccNoError`, `ccIteratorEnd`, `ccErrInvalidCredentialsIterator`

> ###

> cc_credentials_t

>     typedef struct {
>         cc_int32                  version;
>         union {
>             cc_credentials_v4_t*  credentials_v4;
>             cc_credentials_v5_t*  credentials_v5;
>         } credentials;
>     } cc_credentials_union;

>     typedef struct {
>         cc_int32                  (*release) (
>                                      cc_credentials_t credentials);
>     } cc_credentials_f;

>     typedef struct {
>         const cc_credentials_union* data;
>         const cc_credentials_f* functions;
>     }* cc_credentials_t;

> The `cc_credentials_t` type is used to store a single set of credentials for either Kerberos v4 or Kerberos v5. In addition to its only function, `release()`, it contains a pointer to a `cc_credentials_union` structure. A `cc_credentials_union` structure contains an integer of the enumerator type `cc_credentials_version`, which is either `cc_credentials_v4` or `cc_credentials_v5`, and a pointer union, which contains either a `cc_credentials_v4_t` pointer or a `cc_credentials_v5_t` pointer, depending on the value in `version`.

> Variables of the type `cc_credentials_t` are allocated by the CCache implementation, and should be released with their `release()` function. API functions which receive credentials structures from the caller always accept `cc_credentials_union`, which is allocated by the caller, and accordingly disposed by the caller.

> If a `cc_credentials_t` variable is used to store Kerberos v4 credentials, then `credentials.credentials_v4` points to a v4 credentials structure:

>     enum {
>       cc_v4_ticket_size = 1250
>       cc_v4_name_size = 40,
>       cc_v4_instance_size = 40,
>       cc_v4_realm_size = 40,
>       cc_v4_key_size = 8
>     };

>     enum cc_string_to_key_type {
>        cc_v4_stk_unknown = 0,
>        cc_v4_stk_afs = 1,
>        cc_v4_stk_des = 2,
>        cc_v4_stk_columbia_special = 3
>     };

>     typedef struct {
>         unsigned char              version;
>         char                       principal[cc_v4_name_size];
>         char                       principal_instance[cc_v4_instance_size];
>         char                       service[cc_v4_name_size];
>         char                       service_instance[cc_v4_instance_size];
>         char                       realm[cc_v4_realm_size];
>         unsigned char              session_key[cc_v4_key_size];
>         cc_int32                   kvno;
>         cc_int32                   string_to_key;
>         cc_time_t                  issue_date;
>         cc_int32                   lifetime;
>         cc_uint32                  address;
>         cc_int32                   ticket_size;
>         unsigned char              ticket[cc_v4_ticket_size];
>     } cc_cred_v4;

> The `realm`, `principal`, `principal_instance`, `service`, and `service_instance` fields should contain (properly quoted) string representation of the realm, the principal, and the instance of the ticket and the service it was issued for. The `session_key` field should contain the session key for the ticket. The `kvno` field should contain the key version number of the ticket. The `string_to_key` field should contain a value of the enumerated type `cc_string_to_key_type`. The `issue_date` field should contain the issue time of the ticket. The `lifetime` field should contain the lifetime of the ticket in units of 5 minutes. The `address` field should contain the IP address that the ticket was issued for as an unsigned 32-bit integer. The `ticket` field should contain the ticket itself, and its size should be in the `ticket_size` field.

> Otherwise, `cc_credentials_t` is used to store Kerberos v5 credentials, and then `credentials.credentials_v5` points to a a v5 credentials structure. In a v5 credentials structure, `cc_data` structures are used to store tagged variable-length binary data. Specifically, for `cc_credentials_v5.ticket` and `cc_credentials_v5.second_ticket`, the `cc_data.type` field must be zero. For the `cc_credentials_v5.addresses`, `cc_credentials_v5.authdata`, and `cc_credentials_v5.keyblock`, the `cc_data.type` field should be the address type, authorization data type, and encryption type, as defined by the Kerberos v5 protocol definition.

>     typedef struct {
>         cc_uint32            type;
>         cc_uint32            length;
>         unsigned char*      data;
>     } cc_data;

>     typedef struct {
>         char*       client;
>         char*       server;
>         cc_data     keyblock;
>         cc_time_t   authtime;
>         cc_time_t   starttime;
>         cc_time_t   endtime;
>         cc_time_t   renew_till;
>         cc_uint32   is_skey;
>         cc_uint32   ticket_flags;
>         cc_data**   addresses;
>         cc_data     ticket;
>         cc_data     second_ticket;
>         cc_data**   authdata;
>     } cc_cred_v5;

> The `client` and the `server` field should contain the client's and the server's principal identifier, respectively, in properly quoted string representation. The `keyblock` fiels should contain the session encryption key info. The `authtime` field should contain the time when the ticket was issued. The `starttime` and the `endtime` fields should contain the beginning and the end of the time period during which the credentials are valid. The `renew_till` field should contain the maximal time until which the credentials can be renewed. The `is_skey` field should be set to 1 if the ticket is encrypted in another ticket's key, or 0 otherwise. The `ticket_flags` field should contain the ticket flags of the ticket, as presented by the Kerberos v5 API. The `addresses` field should contain the list of network addresses of hosts that are allowed to authenticate using this ticket. The `ticket` field should contain the ticket data, and the `second_ticket` field should contain the data for the second ticket, if there is one (for example, if the ticket is encrypted in second ticket's key). The `authdata` field should contain the autorization data.

> #### cc_credentials_release

>     cc_int32 release (
>         cc_credentials_t  credentials)
>     cc_int32 cc_credentials_release (
>         cc_credentials_t  credentials)

> This function frees all storage associated with a variable of `cc_credentials_t`.

> Possible error codes: `ccNoError`, `ccErrInvalidCredentials`

> ###

> cc_string_t

>     typedef struct {
>         cc_int32                  (*release) (
>                                      cc_string_t string);
>     } cc_string_f;

>     typedef struct {
>         const char* data;
>         const cc_string_f* functions;
>     }* cc_string_t;

> The `cc_string_t` represents a C string returned by the API. It has a pointer to the string data and a `release()` function. This type is used for both principal names and ccache names returned by the API. Principal names may contain UTF-8 encoded strings for internationalization purposes.

> #### cc_string_release

>     cc_int32 release (
>         cc_string_t  string)
>     cc_int32 cc_string_release (
>         cc_string_t  string)

> This function frees the string returned by an API function.

> Possible error codes: `ccNoError`, `ccErrInvalidString`

> ###

> cc_lock_t

>     typedef struct {
>         cc_int32                  (*release) (
>                                      cc_lock_t lock);
>     } cc_lock_f;

>     typedef struct {
>         const cc_lock_f* functions;
>     } cc_lock_t;

> The `cc_lock_t` represents a lock held on a context or a ccache.

> #### cc_lock_release

>     cc_int32 release (
>         cc_lock_t  lock)
>     cc_int32 cc_lock_release (
>         cc_lock_t  lock)

> This function releases (unlocks) the lock and frees other resources associated with it.

> Possible error codes: `ccNoError`, `ccErrInvalidLock`

> ## Revision History and Notes

> #### Original version (Draft Version 1)

> 1/27/96 by Theodore Ts'o

> #### Revision 2 (Draft Version 1)

> 970628 by Steve Rothwell for the V4Cache Team (Paul Hill, Jenny Khuon, Jean Luker, Dave Detlefs, Allan Bjorklund, & Steve Rothwell)

> ### Revision 3 (Draft Version 1)

> 970725 by Steve Rothwell after initial implementation and alpha release. The term "credentials cache" was previously used to mean both "the main cache" and individual "named cache"s within the main cache. I have started using the term "NC" for "named cache" to make the distinction clearer and to reduce the overloading of the word "cache".

> ##### Changes made for revision 3 of this API:

> - Added cred version type to cc_create() & cc_open()
> - New functions
>   - cc_get_NC_info(), returns NC_info list for all NCs
>   - cc_free_NC_info(), frees NC_info list
>   - cc_get_cred_version(), returns version type of NC
>   - cc_get_name(), returns name of NC
>   - cc_free_name(), frees name aquired via cc_get_name()
>   - cc_seq_fetch_NCs(), iterate over all NCs
> - New return codes
>   - `CC_BAD_PARAM`
>   - CC_ERR_CACHE_ATTACH
>   - CC_ERR_CACHE_RELEASE
>   - CC_ERR_CACHE_FULL
>   - `CC_ERR_CRED_VERSION`
> - Modified functions
>   - cc_create(), cc_open(), pass version type of NC
>   - cc_store(), cc_remove(), cc_
> - New & Modified typedefs & data structures
>   - cc_cred_vers { CC_CRED_VUNKNOWN, CC_CRED_V4, CC_CRED_V5 }
>   - cred_ptr_union : contains pointer to credentials (either V4 or V5)
>   - cred_union : contains version type and cred_ptr_union
>   - modified V4Cred_type
>   - enum StringToKey_Type { STK_AFS or STK_DES }
>   - copies of the maximum V4 string size indicators KRB_PRINCIPAL_SZ, KRB_SERVICE_SZ, KRB_INSTANCE_SZ, KRB_REALM_SZ, ADDR_SZ

> #### Revision 4 (Draft Version 1)

> 970908 by Steve Rothwell to incorporate changes initiated by Ted Tso. Further changes are expected in the comments for cc_create() and cc_get_change_time().

> #### Revision 4a (Final Version 1)

> 980603 by Scott McGuire to correct typographical errors, HTML errors, and minor clarifications. Final API Version 1 spec.

> #### Revision 5 (Draft Version 2)

> 990201 by Scott McGuire.

> - Increased API version number to 2.
> - Added enum's defining version numbers.
> - Changes to cc_initialize() to specify how to deal with different API version numbers.
> - Added description of cc_int32 and cc_uint32 types.
> - Change some cc_int32's to cc_uint32's.
> - Changed way cc_create() will behave when called on an existing cache.
> - Replaced cc_seq_fetch_NCs() with cc_seq_fetch_NCs_begin(), cc_seq_fetch_NCs_next(), and cc_seq_fetch_NCs_end();
> - Replaced cc_seq_fetch_creds() with cc_seq_fetch_creds_begin(), cc_seq_fetch_creds_next(), and cc_seq_fetch_creds_end();
> - Replaced enum type references in structs and function paramenters with cc_int32 references;
> - Replaced int type references in function parameters with cc_int32;
> - Added return type of cc_int32 to all functions;
> - Removed #ifdef from cred_union structure;
> - Constant definitions and changes to V4Cred_type structure;
> - Removed incorrect const ccache_p * parameters from cc_store() and cc_remove_cred();
> - Added `CC_NO_ERROR` and `CC_BAD_PARAM` as possible return codes from all functions (except no `CC_BAD_PARAM` from cc_shutdown() );
> - Added `CC_ERR_CRED_VERSION` as possible return code from cc_open() and cc_create();
> - Moved infoNC structure definition up to be with rest of structure definitions;
> - Changed "struct _infoNC" to "infoNC" in parameter type references.
> - cc_free_principal() and cc_free_name() now take char ** instead of char * for final parameter. (This change was made between rev 4a and rev 5, but I'm re-emphasizing it here.)
> - Added Implementation Notes section with requirement that all functions must be atomic and name requirements for Windows DLL's.
> - Renamed "the proposed changes to this API are" section to "Ideas for Future Versions" -- but removed all items but one because they'd all been done.
> - Removed most of the notes about differences with the Win NT/95 implementation of the API -- the differences have been reconciled.
> - Removed unnecessary and inconsistent italicizing.

> #### Revision 5a (Final Version 2)

> 990723 by Scott McGuire.

> - cc_create(): Removed text about "expected" form of name. Removed note about "the alpha version does not do this."
> - cc_destroy(): Clarified that you do not need to call cc_close() on the cache_pointer after calling this function.
> - Removed note about Windows cc_get_instance() and cc_set_instance() functions, they are no longer part of the Windows code!Function definitions

Questions or comments? Send mail to macdev@mit.edu
Last updated on $Date: 2000/06/15 15:15:15 $
Last modified by $Author: dalmeida $