tech-invite   World Map     

IETF     RFCs     Groups     SIP     ABNFs    |    3GPP     Specs     Glossaries     Architecture     IMS     UICC    |    search

RFC 1510

 
 
 

The Kerberos Network Authentication Service (V5)

Part 4 of 4, p. 74 to 112
Prev RFC Part

 


prevText      Top      Up      ToC       Page 74 
6.4.  Checksums

   The following is the ASN.1 definition used for a checksum:

            Checksum ::=   SEQUENCE {
                           cksumtype[0]   INTEGER,
                           checksum[1]    OCTET STRING
            }

   cksumtype This field indicates the algorithm used to generate the
             accompanying checksum.

   checksum  This field contains the checksum itself, encoded
             as an octet string.

   Detailed specification of selected checksum types appear later in
   this section.  Negative values for the checksum type are reserved for
   local use.  All non-negative values are reserved for officially
   assigned type fields and interpretations.

   Checksums used by Kerberos can be classified by two properties:
   whether they are collision-proof, and whether they are keyed.  It is
   infeasible to find two plaintexts which generate the same checksum
   value for a collision-proof checksum.  A key is required to perturb
   or initialize the algorithm in a keyed checksum.  To prevent
   message-stream modification by an active attacker, unkeyed checksums
   should only be used when the checksum and message will be
   subsequently encrypted (e.g., the checksums defined as part of the
   encryption algorithms covered earlier in this section).  Collision-
   proof checksums can be made tamper-proof as well if the checksum
   value is encrypted before inclusion in a message.  In such cases, the
   composition of the checksum and the encryption algorithm must be
   considered a separate checksum algorithm (e.g., RSA-MD5 encrypted
   using DES is a new checksum algorithm of type RSA-MD5-DES).  For most
   keyed checksums, as well as for the encrypted forms of collisionproof
   checksums, Kerberos prepends a confounder before the checksum is
   calculated.

6.4.1. The CRC-32 Checksum (crc32)

   The CRC-32 checksum calculates a checksum based on a cyclic
   redundancy check as described in ISO 3309 [14].  The resulting
   checksum is four (4) octets in length.  The CRC-32 is neither keyed
   nor collision-proof.  The use of this checksum is not recommended.
   An attacker using a probabilistic chosen-plaintext attack as
   described in [13] might be able to generate an alternative message
   that satisfies the checksum.  The use of collision-proof checksums is
   recommended for environments where such attacks represent a

Top      Up      ToC       Page 75 
   significant threat.

6.4.2. The RSA MD4 Checksum (rsa-md4)

   The RSA-MD4 checksum calculates a checksum using the RSA MD4
   algorithm [15].  The algorithm takes as input an input message of
   arbitrary length and produces as output a 128-bit (16 octet)
   checksum.  RSA-MD4 is believed to be collision-proof.

6.4.3. RSA MD4 Cryptographic Checksum Using DES (rsa-md4des)

   The RSA-MD4-DES checksum calculates a keyed collisionproof checksum
   by prepending an 8 octet confounder before the text, applying the RSA
   MD4 checksum algorithm, and encrypting the confounder and the
   checksum using DES in cipher-block-chaining (CBC) mode using a
   variant of the key, where the variant is computed by eXclusive-ORing
   the key with the constant F0F0F0F0F0F0F0F0 (A variant of the key is
   used to limit the use of a key to a particular function, separating
   the functions of generating a checksum from other encryption
   performed using the session key.  The constant F0F0F0F0F0F0F0F0 was
   chosen because it maintains key parity.  The properties of DES
   precluded the use of the complement.  The same constant is used for
   similar purpose in the Message Integrity Check in the Privacy
   Enhanced Mail standard.).  The initialization vector should be zero.
   The resulting checksum is 24 octets long (8 octets of which are
   redundant).  This checksum is tamper-proof and believed to be
   collision-proof.

   The DES specifications identify some "weak keys"; those keys shall
   not be used for generating RSA-MD4 checksums for use in Kerberos.

   The format for the checksum is described in the following diagram:

      +--+--+--+--+--+--+--+--
      |  des-cbc(confounder
      +--+--+--+--+--+--+--+--

                    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
                        rsa-md4(confounder+msg),key=var(key),iv=0)  |
                    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   The format cannot be described in ASN.1, but for those who prefer an
   ASN.1-like notation:

   rsa-md4-des-checksum ::=   ENCRYPTED       UNTAGGED SEQUENCE {
                              confounder[0]   UNTAGGED OCTET STRING(8),
                              check[1]        UNTAGGED OCTET STRING(16)
   }

Top      Up      ToC       Page 76 
6.4.4. The RSA MD5 Checksum (rsa-md5)

   The RSA-MD5 checksum calculates a checksum using the RSA MD5
   algorithm [16].  The algorithm takes as input an input message of
   arbitrary length and produces as output a 128-bit (16 octet)
   checksum.  RSA-MD5 is believed to be collision-proof.

6.4.5. RSA MD5 Cryptographic Checksum Using DES (rsa-md5des)

   The RSA-MD5-DES checksum calculates a keyed collisionproof checksum
   by prepending an 8 octet confounder before the text, applying the RSA
   MD5 checksum algorithm, and encrypting the confounder and the
   checksum using DES in cipher-block-chaining (CBC) mode using a
   variant of the key, where the variant is computed by eXclusive-ORing
   the key with the constant F0F0F0F0F0F0F0F0.  The initialization
   vector should be zero.  The resulting checksum is 24 octets long (8
   octets of which are redundant).  This checksum is tamper-proof and
   believed to be collision-proof.

   The DES specifications identify some "weak keys"; those keys shall
   not be used for encrypting RSA-MD5 checksums for use in Kerberos.

   The format for the checksum is described in the following diagram:

      +--+--+--+--+--+--+--+--
      |  des-cbc(confounder
      +--+--+--+--+--+--+--+--

                     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
                         rsa-md5(confounder+msg),key=var(key),iv=0)  |
                     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   The format cannot be described in ASN.1, but for those who prefer an
   ASN.1-like notation:

   rsa-md5-des-checksum ::=   ENCRYPTED       UNTAGGED SEQUENCE {
                              confounder[0]   UNTAGGED OCTET STRING(8),
                              check[1]        UNTAGGED OCTET STRING(16)
   }

6.4.6. DES cipher-block chained checksum (des-mac)

   The DES-MAC checksum is computed by prepending an 8 octet confounder
   to the plaintext, performing a DES CBC-mode encryption on the result
   using the key and an initialization vector of zero, taking the last
   block of the ciphertext, prepending the same confounder and
   encrypting the pair using DES in cipher-block-chaining (CBC) mode
   using a a variant of the key, where the variant is computed by

Top      Up      ToC       Page 77 
   eXclusive-ORing the key with the constant F0F0F0F0F0F0F0F0.  The
   initialization vector should be zero.  The resulting checksum is 128
   bits (16 octets) long, 64 bits of which are redundant. This checksum
   is tamper-proof and collision-proof.

   The format for the checksum is described in the following diagram:

      +--+--+--+--+--+--+--+--
      |   des-cbc(confounder
      +--+--+--+--+--+--+--+--

                     +-----+-----+-----+-----+-----+-----+-----+-----+
                       des-mac(conf+msg,iv=0,key),key=var(key),iv=0) |
                     +-----+-----+-----+-----+-----+-----+-----+-----+

   The format cannot be described in ASN.1, but for those who prefer an
   ASN.1-like notation:

   des-mac-checksum ::=    ENCRYPTED       UNTAGGED SEQUENCE {
                           confounder[0]   UNTAGGED OCTET STRING(8),
                           check[1]        UNTAGGED OCTET STRING(8)
   }

   The DES specifications identify some "weak" and "semiweak" keys;
   those keys shall not be used for generating DES-MAC checksums for use
   in Kerberos, nor shall a key be used whose veriant is "weak" or
   "semi-weak".

6.4.7. RSA MD4 Cryptographic Checksum Using DES alternative
       (rsa-md4-des-k)

   The RSA-MD4-DES-K checksum calculates a keyed collision-proof
   checksum by applying the RSA MD4 checksum algorithm and encrypting
   the results using DES in cipherblock-chaining (CBC) mode using a DES
   key as both key and initialization vector. The resulting checksum is
   16 octets long. This checksum is tamper-proof and believed to be
   collision-proof.  Note that this checksum type is the old method for
   encoding the RSA-MD4-DES checksum and it is no longer recommended.

6.4.8. DES cipher-block chained checksum alternative (desmac-k)

   The DES-MAC-K checksum is computed by performing a DES CBC-mode
   encryption of the plaintext, and using the last block of the
   ciphertext as the checksum value. It is keyed with an encryption key
   and an initialization vector; any uses which do not specify an
   additional initialization vector will use the key as both key and
   initialization vector.  The resulting checksum is 64 bits (8 octets)
   long. This checksum is tamper-proof and collision-proof.  Note that

Top      Up      ToC       Page 78 
   this checksum type is the old method for encoding the DESMAC checksum
   and it is no longer recommended.

   The DES specifications identify some "weak keys"; those keys shall
   not be used for generating DES-MAC checksums for use in Kerberos.

7.  Naming Constraints

7.1.  Realm Names

   Although realm names are encoded as GeneralStrings and although a
   realm can technically select any name it chooses, interoperability
   across realm boundaries requires agreement on how realm names are to
   be assigned, and what information they imply.

   To enforce these conventions, each realm must conform to the
   conventions itself, and it must require that any realms with which
   inter-realm keys are shared also conform to the conventions and
   require the same from its neighbors.

   There are presently four styles of realm names: domain, X500, other,
   and reserved.  Examples of each style follow:

        domain:   host.subdomain.domain (example)
          X500:   C=US/O=OSF (example)
         other:   NAMETYPE:rest/of.name=without-restrictions (example)
      reserved:   reserved, but will not conflict with above

   Domain names must look like domain names: they consist of components
   separated by periods (.) and they contain neither colons (:) nor
   slashes (/).

   X.500 names contain an equal (=) and cannot contain a colon (:)
   before the equal.  The realm names for X.500 names will be string
   representations of the names with components separated by slashes.
   Leading and trailing slashes will not be included.

   Names that fall into the other category must begin with a prefix that
   contains no equal (=) or period (.) and the prefix must be followed
   by a colon (:) and the rest of the name. All prefixes must be
   assigned before they may be used.  Presently none are assigned.

   The reserved category includes strings which do not fall into the
   first three categories.  All names in this category are reserved. It
   is unlikely that names will be assigned to this category unless there
   is a very strong argument for not using the "other" category.

   These rules guarantee that there will be no conflicts between the

Top      Up      ToC       Page 79 
   various name styles.  The following additional constraints apply to
   the assignment of realm names in the domain and X.500 categories: the
   name of a realm for the domain or X.500 formats must either be used
   by the organization owning (to whom it was assigned) an Internet
   domain name or X.500 name, or in the case that no such names are
   registered, authority to use a realm name may be derived from the
   authority of the parent realm.  For example, if there is no domain
   name for E40.MIT.EDU, then the administrator of the MIT.EDU realm can
   authorize the creation of a realm with that name.

   This is acceptable because the organization to which the parent is
   assigned is presumably the organization authorized to assign names to
   its children in the X.500 and domain name systems as well.  If the
   parent assigns a realm name without also registering it in the domain
   name or X.500 hierarchy, it is the parent's responsibility to make
   sure that there will not in the future exists a name identical to the
   realm name of the child unless it is assigned to the same entity as
   the realm name.

7.2.  Principal Names

   As was the case for realm names, conventions are needed to ensure
   that all agree on what information is implied by a principal name.
   The name-type field that is part of the principal name indicates the
   kind of information implied by the name.  The name-type should be
   treated as a hint.  Ignoring the name type, no two names can be the
   same (i.e., at least one of the components, or the realm, must be
   different).  This constraint may be eliminated in the future.  The
   following name types are defined:

      name-type      value   meaning
      NT-UNKNOWN       0     Name type not known
      NT-PRINCIPAL     1     Just the name of the principal as in
                             DCE, or for users
      NT-SRV-INST      2     Service and other unique instance (krbtgt)
      NT-SRV-HST       3     Service with host name as instance
                             (telnet, rcommands)
      NT-SRV-XHST      4     Service with host as remaining components
      NT-UID           5     Unique ID

   When a name implies no information other than its uniqueness at a
   particular time the name type PRINCIPAL should be used.  The
   principal name type should be used for users, and it might also be
   used for a unique server.  If the name is a unique machine generated
   ID that is guaranteed never to be reassigned then the name type of
   UID should be used (note that it is generally a bad idea to reassign
   names of any type since stale entries might remain in access control
   lists).

Top      Up      ToC       Page 80 
   If the first component of a name identifies a service and the
   remaining components identify an instance of the service in a server
   specified manner, then the name type of SRV-INST should be used.  An
   example of this name type is the Kerberos ticket-granting ticket
   which has a first component of krbtgt and a second component
   identifying the realm for which the ticket is valid.

   If instance is a single component following the service name and the
   instance identifies the host on which the server is running, then the
   name type SRV-HST should be used. This type is typically used for
   Internet services such as telnet and the Berkeley R commands.  If the
   separate components of the host name appear as successive components
   following the name of the service, then the name type SRVXHST should
   be used.  This type might be used to identify servers on hosts with
   X.500 names where the slash (/) might otherwise be ambiguous.

   A name type of UNKNOWN should be used when the form of the name is
   not known. When comparing names, a name of type UNKNOWN will match
   principals authenticated with names of any type.  A principal
   authenticated with a name of type UNKNOWN, however, will only match
   other names of type UNKNOWN.

   Names of any type with an initial component of "krbtgt" are reserved
   for the Kerberos ticket granting service.  See section 8.2.3 for the
   form of such names.

7.2.1. Name of server principals

   The principal identifier for a server on a host will generally be
   composed of two parts: (1) the realm of the KDC with which the server
   is registered, and (2) a two-component name of type NT-SRV-HST if the
   host name is an Internet domain name or a multi-component name of
   type NT-SRV-XHST if the name of the host is of a form such as X.500
   that allows slash (/) separators.  The first component of the two- or
   multi-component name will identify the service and the latter
   components will identify the host.  Where the name of the host is not
   case sensitive (for example, with Internet domain names) the name of
   the host must be lower case.  For services such as telnet and the
   Berkeley R commands which run with system privileges, the first
   component will be the string "host" instead of a service specific
   identifier.

8.  Constants and other defined values

8.1.  Host address types

   All negative values for the host address type are reserved for local
   use.  All non-negative values are reserved for officially assigned

Top      Up      ToC       Page 81 
   type fields and interpretations.

   The values of the types for the following addresses are chosen to
   match the defined address family constants in the Berkeley Standard
   Distributions of Unix.  They can be found in <sys/socket.h> with
   symbolic names AF_xxx (where xxx is an abbreviation of the address
   family name).


   Internet addresses

      Internet addresses are 32-bit (4-octet) quantities, encoded in MSB
      order.  The type of internet addresses is two (2).

   CHAOSnet addresses

      CHAOSnet addresses are 16-bit (2-octet) quantities, encoded in MSB
      order.  The type of CHAOSnet addresses is five (5).

   ISO addresses

      ISO addresses are variable-length.  The type of ISO addresses is
      seven (7).

   Xerox Network Services (XNS) addresses

      XNS addresses are 48-bit (6-octet) quantities, encoded in MSB
      order.  The type of XNS addresses is six (6).

   AppleTalk Datagram Delivery Protocol (DDP) addresses

      AppleTalk DDP addresses consist of an 8-bit node number and a 16-
      bit network number.  The first octet of the address is the node
      number; the remaining two octets encode the network number in MSB
      order. The type of AppleTalk DDP addresses is sixteen (16).

   DECnet Phase IV addresses

      DECnet Phase IV addresses are 16-bit addresses, encoded in LSB
      order.  The type of DECnet Phase IV addresses is twelve (12).

8.2.  KDC messages

8.2.1. IP transport

   When contacting a Kerberos server (KDC) for a KRB_KDC_REQ request
   using IP transport, the client shall send a UDP datagram containing
   only an encoding of the request to port 88 (decimal) at the KDC's IP

Top      Up      ToC       Page 82 
   address; the KDC will respond with a reply datagram containing only
   an encoding of the reply message (either a KRB_ERROR or a
   KRB_KDC_REP) to the sending port at the sender's IP address.

8.2.2. OSI transport

   During authentication of an OSI client to and OSI server, the mutual
   authentication of an OSI server to an OSI client, the transfer of
   credentials from an OSI client to an OSI server, or during exchange
   of private or integrity checked messages, Kerberos protocol messages
   may be treated as opaque objects and the type of the authentication
   mechanism will be:

   OBJECT IDENTIFIER ::= {iso (1), org(3), dod(5),internet(1),
                          security(5), kerberosv5(2)}

   Depending on the situation, the opaque object will be an
   authentication header (KRB_AP_REQ), an authentication reply
   (KRB_AP_REP), a safe message (KRB_SAFE), a private message
   (KRB_PRIV), or a credentials message (KRB_CRED).  The opaque data
   contains an application code as specified in the ASN.1 description
   for each message.  The application code may be used by Kerberos to
   determine the message type.

8.2.3. Name of the TGS

   The principal identifier of the ticket-granting service shall be
   composed of three parts: (1) the realm of the KDC issuing the TGS
   ticket (2) a two-part name of type NT-SRVINST, with the first part
   "krbtgt" and the second part the name of the realm which will accept
   the ticket-granting ticket.  For example, a ticket-granting ticket
   issued by the ATHENA.MIT.EDU realm to be used to get tickets from the
   ATHENA.MIT.EDU KDC has a principal identifier of "ATHENA.MIT.EDU"
   (realm), ("krbtgt", "ATHENA.MIT.EDU") (name).  A ticket-granting
   ticket issued by the ATHENA.MIT.EDU realm to be used to get tickets
   from the MIT.EDU realm has a principal identifier of "ATHENA.MIT.EDU"
   (realm), ("krbtgt", "MIT.EDU") (name).

8.3.  Protocol constants and associated values

   The following tables list constants used in the protocol and defines
   their meanings.

Top      Up      ToC       Page 83 
---------------+-----------+----------+----------------+---------------
Encryption type|etype value|block size|minimum pad size|confounder size
---------------+-----------+----------+----------------+---------------
NULL                0            1              0              0
des-cbc-crc         1            8              4              8
des-cbc-md4         2            8              0              8
des-cbc-md5         3            8              0              8

-------------------------------+-------------------+-------------
Checksum type                  |sumtype value      |checksum size
-------------------------------+-------------------+-------------
CRC32                           1                   4
rsa-md4                         2                   16
rsa-md4-des                     3                   24
des-mac                         4                   16
des-mac-k                       5                   8
rsa-md4-des-k                   6                   16
rsa-md5                         7                   16
rsa-md5-des                     8                   24

-------------------------------+-----------------
padata type                    |padata-type value
-------------------------------+-----------------
PA-TGS-REQ                      1
PA-ENC-TIMESTAMP                2
PA-PW-SALT                      3

-------------------------------+-------------
authorization data type        |ad-type value
-------------------------------+-------------
reserved values                 0-63
OSF-DCE                         64
SESAME                          65

-------------------------------+-----------------
alternate authentication type  |method-type value
-------------------------------+-----------------
reserved values                 0-63
ATT-CHALLENGE-RESPONSE          64

-------------------------------+-------------
transited encoding type        |tr-type value
-------------------------------+-------------
DOMAIN-X500-COMPRESS            1
reserved values                 all others

Top      Up      ToC       Page 84 
--------------+-------+-----------------------------------------
Label         |Value  |Meaning or MIT code
--------------+-------+-----------------------------------------

pvno             5     current Kerberos protocol version number

message types

KRB_AS_REQ      10     Request for initial authentication
KRB_AS_REP      11     Response to KRB_AS_REQ request
KRB_TGS_REQ     12     Request for authentication based on TGT
KRB_TGS_REP     13     Response to KRB_TGS_REQ request
KRB_AP_REQ      14     application request to server
KRB_AP_REP      15     Response to KRB_AP_REQ_MUTUAL
KRB_SAFE        20     Safe (checksummed) application message
KRB_PRIV        21     Private (encrypted) application message
KRB_CRED        22     Private (encrypted) message to forward
                       credentials
KRB_ERROR       30     Error response

name types

KRB_NT_UNKNOWN   0   Name type not known
KRB_NT_PRINCIPAL 1   Just the name of the principal as in DCE, or
                     for users
KRB_NT_SRV_INST  2   Service and other unique instance (krbtgt)
KRB_NT_SRV_HST   3   Service with host name as instance (telnet,
                     rcommands)
KRB_NT_SRV_XHST  4   Service with host as remaining components
KRB_NT_UID       5   Unique ID

error codes

KDC_ERR_NONE                   0   No error
KDC_ERR_NAME_EXP               1   Client's entry in database has
                                   expired
KDC_ERR_SERVICE_EXP            2   Server's entry in database has
                                   expired
KDC_ERR_BAD_PVNO               3   Requested protocol version number
                                   not supported
KDC_ERR_C_OLD_MAST_KVNO        4   Client's key encrypted in old
                                   master key
KDC_ERR_S_OLD_MAST_KVNO        5   Server's key encrypted in old
                                   master key
KDC_ERR_C_PRINCIPAL_UNKNOWN    6   Client not found in Kerberos database
KDC_ERR_S_PRINCIPAL_UNKNOWN    7   Server not found in Kerberos database
KDC_ERR_PRINCIPAL_NOT_UNIQUE   8   Multiple principal entries in
                                   database

Top      Up      ToC       Page 85 
KDC_ERR_NULL_KEY               9   The client or server has a null key
KDC_ERR_CANNOT_POSTDATE       10   Ticket not eligible for postdating
KDC_ERR_NEVER_VALID           11   Requested start time is later than
                                   end time
KDC_ERR_POLICY                12   KDC policy rejects request
KDC_ERR_BADOPTION             13   KDC cannot accommodate requested
                                   option
KDC_ERR_ETYPE_NOSUPP          14   KDC has no support for encryption
                                   type
KDC_ERR_SUMTYPE_NOSUPP        15   KDC has no support for checksum type
KDC_ERR_PADATA_TYPE_NOSUPP    16   KDC has no support for padata type
KDC_ERR_TRTYPE_NOSUPP         17   KDC has no support for transited type
KDC_ERR_CLIENT_REVOKED        18   Clients credentials have been revoked
KDC_ERR_SERVICE_REVOKED       19   Credentials for server have been
                                   revoked
KDC_ERR_TGT_REVOKED           20   TGT has been revoked
KDC_ERR_CLIENT_NOTYET         21   Client not yet valid - try again
                                   later
KDC_ERR_SERVICE_NOTYET        22   Server not yet valid - try again
                                   later
KDC_ERR_KEY_EXPIRED           23   Password has expired - change
                                   password to reset
KDC_ERR_PREAUTH_FAILED        24   Pre-authentication information
                                   was invalid
KDC_ERR_PREAUTH_REQUIRED      25   Additional pre-authentication
                                   required*
KRB_AP_ERR_BAD_INTEGRITY      31   Integrity check on decrypted field
                                   failed
KRB_AP_ERR_TKT_EXPIRED        32   Ticket expired
KRB_AP_ERR_TKT_NYV            33   Ticket not yet valid
KRB_AP_ERR_REPEAT             34   Request is a replay
KRB_AP_ERR_NOT_US             35   The ticket isn't for us
KRB_AP_ERR_BADMATCH           36   Ticket and authenticator don't match
KRB_AP_ERR_SKEW               37   Clock skew too great
KRB_AP_ERR_BADADDR            38   Incorrect net address
KRB_AP_ERR_BADVERSION         39   Protocol version mismatch
KRB_AP_ERR_MSG_TYPE           40   Invalid msg type
KRB_AP_ERR_MODIFIED           41   Message stream modified
KRB_AP_ERR_BADORDER           42   Message out of order
KRB_AP_ERR_BADKEYVER          44   Specified version of key is not
                                   available
KRB_AP_ERR_NOKEY              45   Service key not available
KRB_AP_ERR_MUT_FAIL           46   Mutual authentication failed
KRB_AP_ERR_BADDIRECTION       47   Incorrect message direction
KRB_AP_ERR_METHOD             48   Alternative authentication method
                                   required*
KRB_AP_ERR_BADSEQ             49   Incorrect sequence number in message
KRB_AP_ERR_INAPP_CKSUM        50   Inappropriate type of checksum in

Top      Up      ToC       Page 86 
                                   message
KRB_ERR_GENERIC               60   Generic error (description in e-text)
KRB_ERR_FIELD_TOOLONG         61   Field is too long for this
                                   implementation

   *This error carries additional information in the e-data field.  The
   contents of the e-data field for this message is described in section
   5.9.1.

9.  Interoperability requirements

   Version 5 of the Kerberos protocol supports a myriad of options.
   Among these are multiple encryption and checksum types, alternative
   encoding schemes for the transited field, optional mechanisms for
   pre-authentication, the handling of tickets with no addresses,
   options for mutual authentication, user to user authentication,
   support for proxies, forwarding, postdating, and renewing tickets,
   the format of realm names, and the handling of authorization data.

   In order to ensure the interoperability of realms, it is necessary to
   define a minimal configuration which must be supported by all
   implementations.  This minimal configuration is subject to change as
   technology does. For example, if at some later date it is discovered
   that one of the required encryption or checksum algorithms is not
   secure, it will be replaced.

9.1.  Specification 1

   This section defines the first specification of these options.
   Implementations which are configured in this way can be said to
   support Kerberos Version 5 Specification 1 (5.1).

   Encryption and checksum methods

   The following encryption and checksum mechanisms must be supported.
   Implementations may support other mechanisms as well, but the
   additional mechanisms may only be used when communicating with
   principals known to also support them: Encryption: DES-CBC-MD5
   Checksums: CRC-32, DES-MAC, DES-MAC-K, and DES-MD5

   Realm Names

   All implementations must understand hierarchical realms in both the
   Internet Domain and the X.500 style.  When a ticket granting ticket
   for an unknown realm is requested, the KDC must be able to determine
   the names of the intermediate realms between the KDCs realm and the
   requested realm.

Top      Up      ToC       Page 87 
   Transited field encoding

   DOMAIN-X500-COMPRESS (described in section 3.3.3.1) must be
   supported.  Alternative encodings may be supported, but they may be
   used only when that encoding is supported by ALL intermediate realms.

   Pre-authentication methods

   The TGS-REQ method must be supported.  The TGS-REQ method is not used
   on the initial request. The PA-ENC-TIMESTAMP method must be supported
   by clients but whether it is enabled by default may be determined on
   a realm by realm basis. If not used in the initial request and the
   error KDC_ERR_PREAUTH_REQUIRED is returned specifying PA-ENCTIMESTAMP
   as an acceptable method, the client should retry the initial request
   using the PA-ENC-TIMESTAMP preauthentication method. Servers need not
   support the PAENC-TIMESTAMP method, but if not supported the server
   should ignore the presence of PA-ENC-TIMESTAMP pre-authentication in
   a request.

   Mutual authentication

   Mutual authentication (via the KRB_AP_REP message) must be supported.

   Ticket addresses and flags

   All KDC's must pass on tickets that carry no addresses (i.e.,  if a
   TGT contains no addresses, the KDC will return derivative tickets),
   but each realm may set its own policy for issuing such tickets, and
   each application server will set its own policy with respect to
   accepting them. By default, servers should not accept them.

   Proxies and forwarded tickets must be supported.  Individual realms
   and application servers can set their own policy on when such tickets
   will be accepted.

   All implementations must recognize renewable and postdated tickets,
   but need not actually implement them.  If these options are not
   supported, the starttime and endtime in the ticket shall specify a
   ticket's entire useful life.  When a postdated ticket is decoded by a
   server, all implementations shall make the presence of the postdated
   flag visible to the calling server.

   User-to-user authentication

   Support for user to user authentication (via the ENC-TKTIN-SKEY KDC
   option) must be provided by implementations, but individual realms
   may decide as a matter of policy to reject such requests on a per-
   principal or realm-wide basis.

Top      Up      ToC       Page 88 
   Authorization data

   Implementations must pass all authorization data subfields from
   ticket-granting tickets to any derivative tickets unless directed to
   suppress a subfield as part of the definition of that registered
   subfield type (it is never incorrect to pass on a subfield, and no
   registered subfield types presently specify suppression at the KDC).

   Implementations must make the contents of any authorization data
   subfields available to the server when a ticket is used.
   Implementations are not required to allow clients to specify the
   contents of the authorization data fields.

9.2.  Recommended KDC values

   Following is a list of recommended values for a KDC implementation,
   based on the list of suggested configuration constants (see section
   4.4).

   minimum lifetime                5 minutes

   maximum renewable lifetime      1 week

   maximum ticket lifetime         1 day

   empty addresses                 only when suitable restrictions appear
                                   in authorization data

   proxiable, etc.                 Allowed.

10.  Acknowledgments

   Early versions of this document, describing version 4 of the
   protocol, were written by Jennifer Steiner (formerly at Project
   Athena); these drafts provided an excellent starting point for this
   current version 5 specification.  Many people in the Internet
   community have contributed ideas and suggested protocol changes for
   version 5. Notable contributions came from Ted Anderson, Steve
   Bellovin and Michael Merritt [17], Daniel Bernstein, Mike Burrows,
   Donald Davis, Ravi Ganesan, Morrie Gasser, Virgil Gligor, Bill
   Griffeth, Mark Lillibridge, Mark Lomas, Steve Lunt, Piers McMahon,
   Joe Pato, William Sommerfeld, Stuart Stubblebine, Ralph Swick, Ted
   T'so, and Stanley Zanarotti.  Many others commented and helped shape
   this specification into its current form.

Top      Up      ToC       Page 89 
11.  References

   [1]  Miller, S., Neuman, C., Schiller, J., and  J. Saltzer, "Section
        E.2.1: Kerberos  Authentication and Authorization System",
        M.I.T. Project Athena, Cambridge, Massachusetts, December 21,
        1987.

   [2]  Steiner, J., Neuman, C., and J. Schiller, "Kerberos: An
        Authentication Service for Open Network Systems", pp. 191-202 in
        Usenix Conference Proceedings, Dallas, Texas, February, 1988.

   [3]  Needham, R., and M. Schroeder, "Using Encryption for
        Authentication in Large Networks of Computers", Communications
        of the ACM, Vol. 21 (12), pp. 993-999, December 1978.

   [4]  Denning, D., and G. Sacco, "Time stamps in Key Distribution
        Protocols", Communications of the ACM, Vol. 24 (8), pp. 533-536,
        August 1981.

   [5]  Kohl, J., Neuman, C., and T. Ts'o, "The Evolution of the
        Kerberos Authentication Service", in an IEEE Computer Society
        Text soon to be published, June 1992.

   [6]  Davis, D., and R. Swick, "Workstation Services and Kerberos
        Authentication at Project Athena", Technical Memorandum TM-424,
        MIT Laboratory for Computer Science, February 1990.

   [7]  Levine, P., Gretzinger, M, Diaz, J., Sommerfeld, W., and K.
        Raeburn, "Section E.1: Service Management System, M.I.T.
        Project Athena, Cambridge, Mas sachusetts (1987).

   [8]  CCITT, Recommendation X.509: The Directory Authentication
        Framework, December 1988.

   [9]  Neuman, C., "Proxy-Based Authorization and Accounting for
        Distributed Systems," in Proceedings of the 13th International
        Conference on Distributed Computing Systems", Pittsburgh, PA,
        May 1993.

   [10] Pato, J., "Using Pre-Authentication to Avoid Password Guessing
        Attacks", Open Software Foundation DCE Request for Comments 26,
        December 1992.

   [11] National Bureau of Standards, U.S. Department of Commerce, "Data
        Encryption Standard", Federal Information Processing Standards
        Publication 46, Washington, DC (1977).

Top      Up      ToC       Page 90 
   [12] National Bureau of Standards, U.S. Department of Commerce, "DES
        Modes of Operation", Federal Information Processing Standards
        Publication 81, Springfield, VA, December 1980.

   [13] Stubblebine S., and V. Gligor, "On Message Integrity in
        Cryptographic Protocols", in Proceedings of the IEEE Symposium
        on Research in Security and Privacy, Oakland, California, May
        1992.

   [14] International Organization for Standardization, "ISO Information
        Processing Systems - Data Communication High-Level Data Link
        Control Procedure - Frame Structure", IS 3309, October 1984, 3rd
        Edition.

   [15] Rivest, R., "The MD4 Message Digest Algorithm", RFC 1320, MIT
        Laboratory for Computer Science, April 1992.

   [16] Rivest, R., "The MD5 Message Digest Algorithm", RFC 1321, MIT
        Laboratory for Computer Science, April 1992.

   [17] Bellovin S., and M. Merritt, "Limitations of the Kerberos
        Authentication System", Computer Communications Review, Vol.
        20(5), pp. 119-132, October 1990.

12.  Security Considerations

   Security issues are discussed throughout this memo.

13.  Authors' Addresses

   John Kohl
   Digital Equipment Corporation
   110 Spit Brook Road, M/S ZKO3-3/U14
   Nashua, NH  03062

   Phone: 603-881-2481
   EMail: jtkohl@zk3.dec.com


   B. Clifford Neuman
   USC/Information Sciences Institute
   4676 Admiralty Way #1001
   Marina del Rey, CA 90292-6695

   Phone: 310-822-1511
   EMail: bcn@isi.edu

Top      Up      ToC       Page 91 
A.  Pseudo-code for protocol processing

   This appendix provides pseudo-code describing how the messages are to
   be constructed and interpreted by clients and servers.

A.1.  KRB_AS_REQ generation
        request.pvno := protocol version; /* pvno = 5 */
        request.msg-type := message type; /* type = KRB_AS_REQ */

        if(pa_enc_timestamp_required) then
                request.padata.padata-type = PA-ENC-TIMESTAMP;
                get system_time;
                padata-body.patimestamp,pausec = system_time;
                encrypt padata-body into request.padata.padata-value
                        using client.key; /* derived from password */
        endif

        body.kdc-options := users's preferences;
        body.cname := user's name;
        body.realm := user's realm;
        body.sname := service's name; /* usually "krbtgt",
                                         "localrealm" */
        if (body.kdc-options.POSTDATED is set) then
                body.from := requested starting time;
        else
                omit body.from;
        endif
        body.till := requested end time;
        if (body.kdc-options.RENEWABLE is set) then
                body.rtime := requested final renewal time;
        endif
        body.nonce := random_nonce();
        body.etype := requested etypes;
        if (user supplied addresses) then
                body.addresses := user's addresses;
        else
                omit body.addresses;
        endif
        omit body.enc-authorization-data;
        request.req-body := body;

        kerberos := lookup(name of local kerberos server (or servers));
        send(packet,kerberos);

        wait(for response);
        if (timed_out) then
                retry or use alternate server;
        endif

Top      Up      ToC       Page 92 
A.2.  KRB_AS_REQ verification and KRB_AS_REP generation
        decode message into req;

        client := lookup(req.cname,req.realm);
        server := lookup(req.sname,req.realm);
        get system_time;
        kdc_time := system_time.seconds;

        if (!client) then
                /* no client in Database */
                error_out(KDC_ERR_C_PRINCIPAL_UNKNOWN);
        endif
        if (!server) then
                /* no server in Database */
                error_out(KDC_ERR_S_PRINCIPAL_UNKNOWN);
        endif

        if(client.pa_enc_timestamp_required and
           pa_enc_timestamp not present) then
                error_out(KDC_ERR_PREAUTH_REQUIRED(PA_ENC_TIMESTAMP));
        endif

        if(pa_enc_timestamp present) then
                decrypt req.padata-value into decrypted_enc_timestamp
                        using client.key;
                        using auth_hdr.authenticator.subkey;
                if (decrypt_error()) then
                        error_out(KRB_AP_ERR_BAD_INTEGRITY);
                if(decrypted_enc_timestamp is not within allowable
                        skew) then error_out(KDC_ERR_PREAUTH_FAILED);
                endif
                if(decrypted_enc_timestamp and usec is replay)
                        error_out(KDC_ERR_PREAUTH_FAILED);
                endif
                add decrypted_enc_timestamp and usec to replay cache;
        endif

        use_etype := first supported etype in req.etypes;

        if (no support for req.etypes) then
                error_out(KDC_ERR_ETYPE_NOSUPP);
        endif

        new_tkt.vno := ticket version; /* = 5 */
        new_tkt.sname := req.sname;
        new_tkt.srealm := req.srealm;
        reset all flags in new_tkt.flags;

Top      Up      ToC       Page 93 
        /* It should be noted that local policy may affect the  */
        /* processing of any of these flags.  For example, some */
        /* realms may refuse to issue renewable tickets         */

        if (req.kdc-options.FORWARDABLE is set) then
                set new_tkt.flags.FORWARDABLE;
        endif
        if (req.kdc-options.PROXIABLE is set) then
                set new_tkt.flags.PROXIABLE;
        endif
        if (req.kdc-options.ALLOW-POSTDATE is set) then
                set new_tkt.flags.ALLOW-POSTDATE;
        endif
        if ((req.kdc-options.RENEW is set) or
            (req.kdc-options.VALIDATE is set) or
            (req.kdc-options.PROXY is set) or
            (req.kdc-options.FORWARDED is set) or
            (req.kdc-options.ENC-TKT-IN-SKEY is set)) then
                error_out(KDC_ERR_BADOPTION);
        endif

        new_tkt.session := random_session_key();
        new_tkt.cname := req.cname;
        new_tkt.crealm := req.crealm;
        new_tkt.transited := empty_transited_field();

        new_tkt.authtime := kdc_time;

        if (req.kdc-options.POSTDATED is set) then
           if (against_postdate_policy(req.from)) then
                error_out(KDC_ERR_POLICY);
           endif
           set new_tkt.flags.INVALID;
           new_tkt.starttime := req.from;
        else
           omit new_tkt.starttime; /* treated as authtime when
                                      omitted */
        endif
        if (req.till = 0) then
                till := infinity;
        else
                till := req.till;
        endif

        new_tkt.endtime := min(till,
                              new_tkt.starttime+client.max_life,
                              new_tkt.starttime+server.max_life,
                              new_tkt.starttime+max_life_for_realm);

Top      Up      ToC       Page 94 
        if ((req.kdc-options.RENEWABLE-OK is set) and
            (new_tkt.endtime < req.till)) then
                /* we set the RENEWABLE option for later processing */
                set req.kdc-options.RENEWABLE;
                req.rtime := req.till;
        endif

        if (req.rtime = 0) then
                rtime := infinity;
        else
                rtime := req.rtime;
        endif

        if (req.kdc-options.RENEWABLE is set) then
                set new_tkt.flags.RENEWABLE;
                new_tkt.renew-till := min(rtime,
                new_tkt.starttime+client.max_rlife,
                new_tkt.starttime+server.max_rlife,
                new_tkt.starttime+max_rlife_for_realm);
        else
                omit new_tkt.renew-till; /* only present if RENEWABLE */
        endif

        if (req.addresses) then
                new_tkt.caddr := req.addresses;
        else
                omit new_tkt.caddr;
        endif

        new_tkt.authorization_data := empty_authorization_data();

        encode to-be-encrypted part of ticket into OCTET STRING;
        new_tkt.enc-part := encrypt OCTET STRING
            using etype_for_key(server.key), server.key, server.p_kvno;


        /* Start processing the response */

        resp.pvno := 5;
        resp.msg-type := KRB_AS_REP;
        resp.cname := req.cname;
        resp.crealm := req.realm;
        resp.ticket := new_tkt;

        resp.key := new_tkt.session;
        resp.last-req := fetch_last_request_info(client);
        resp.nonce := req.nonce;
        resp.key-expiration := client.expiration;

Top      Up      ToC       Page 95 
        resp.flags := new_tkt.flags;

        resp.authtime := new_tkt.authtime;
        resp.starttime := new_tkt.starttime;
        resp.endtime := new_tkt.endtime;

        if (new_tkt.flags.RENEWABLE) then
                resp.renew-till := new_tkt.renew-till;
        endif

        resp.realm := new_tkt.realm;
        resp.sname := new_tkt.sname;

        resp.caddr := new_tkt.caddr;

        encode body of reply into OCTET STRING;

        resp.enc-part := encrypt OCTET STRING
                         using use_etype, client.key, client.p_kvno;
        send(resp);

A.3.  KRB_AS_REP verification
        decode response into resp;

        if (resp.msg-type = KRB_ERROR) then
                if(error = KDC_ERR_PREAUTH_REQUIRED(PA_ENC_TIMESTAMP))
                        then set pa_enc_timestamp_required;
                        goto KRB_AS_REQ;
                endif
                process_error(resp);
                return;
        endif

        /* On error, discard the response, and zero the session key */
        /* from the response immediately */

        key = get_decryption_key(resp.enc-part.kvno, resp.enc-part.etype,
                                 resp.padata);
        unencrypted part of resp := decode of decrypt of resp.enc-part
                                using resp.enc-part.etype and key;
        zero(key);

        if (common_as_rep_tgs_rep_checks fail) then
                destroy resp.key;
                return error;
        endif

        if near(resp.princ_exp) then

Top      Up      ToC       Page 96 
                print(warning message);
        endif
        save_for_later(ticket,session,client,server,times,flags);

A.4.  KRB_AS_REP and KRB_TGS_REP common checks
        if (decryption_error() or
            (req.cname != resp.cname) or
            (req.realm != resp.crealm) or
            (req.sname != resp.sname) or
            (req.realm != resp.realm) or
            (req.nonce != resp.nonce) or
            (req.addresses != resp.caddr)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif

        /* make sure no flags are set that shouldn't be, and that  */
        /* all that should be are set                              */
        if (!check_flags_for_compatability(req.kdc-options,resp.flags))
                then destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif

        if ((req.from = 0) and
            (resp.starttime is not within allowable skew)) then
                destroy resp.key;
                return KRB_AP_ERR_SKEW;
        endif
        if ((req.from != 0) and (req.from != resp.starttime)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif
        if ((req.till != 0) and (resp.endtime > req.till)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif

        if ((req.kdc-options.RENEWABLE is set) and
            (req.rtime != 0) and (resp.renew-till > req.rtime)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif
        if ((req.kdc-options.RENEWABLE-OK is set) and
            (resp.flags.RENEWABLE) and
            (req.till != 0) and
            (resp.renew-till > req.till)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;

Top      Up      ToC       Page 97 
        endif

A.5.  KRB_TGS_REQ generation
        /* Note that make_application_request might have to     */
        /* recursivly call this routine to get the appropriate  */
        /* ticket-granting ticket                               */

        request.pvno := protocol version; /* pvno = 5 */
        request.msg-type := message type; /* type = KRB_TGS_REQ */

        body.kdc-options := users's preferences;
        /* If the TGT is not for the realm of the end-server  */
        /* then the sname will be for a TGT for the end-realm */
        /* and the realm of the requested ticket (body.realm) */
        /* will be that of the TGS to which the TGT we are    */
        /* sending applies                                    */
        body.sname := service's name;
        body.realm := service's realm;

        if (body.kdc-options.POSTDATED is set) then
                body.from := requested starting time;
        else
                omit body.from;
        endif
        body.till := requested end time;
        if (body.kdc-options.RENEWABLE is set) then
                body.rtime := requested final renewal time;
        endif
        body.nonce := random_nonce();
        body.etype := requested etypes;
        if (user supplied addresses) then
                body.addresses := user's addresses;
        else
                omit body.addresses;
        endif

        body.enc-authorization-data := user-supplied data;
        if (body.kdc-options.ENC-TKT-IN-SKEY) then
                body.additional-tickets_ticket := second TGT;
        endif

        request.req-body := body;
        check := generate_checksum (req.body,checksumtype);

        request.padata[0].padata-type := PA-TGS-REQ;
        request.padata[0].padata-value := create a KRB_AP_REQ using
                                      the TGT and checksum

Top      Up      ToC       Page 98 
        /* add in any other padata as required/supplied */

        kerberos := lookup(name of local kerberose server (or servers));
        send(packet,kerberos);

        wait(for response);
        if (timed_out) then
                retry or use alternate server;
        endif

A.6.  KRB_TGS_REQ verification and KRB_TGS_REP generation
        /* note that reading the application request requires first
        determining the server for which a ticket was issued, and
        choosing the correct key for decryption.  The name of the
        server appears in the plaintext part of the ticket. */

        if (no KRB_AP_REQ in req.padata) then
                error_out(KDC_ERR_PADATA_TYPE_NOSUPP);
        endif
        verify KRB_AP_REQ in req.padata;

        /* Note that the realm in which the Kerberos server is
        operating is determined by the instance from the
        ticket-granting ticket.  The realm in the ticket-granting
        ticket is the realm under which the ticket granting ticket was
        issued.  It is possible for a single Kerberos server to
        support more than one realm. */

        auth_hdr := KRB_AP_REQ;
        tgt := auth_hdr.ticket;

        if (tgt.sname is not a TGT for local realm and is not
                req.sname) then error_out(KRB_AP_ERR_NOT_US);

        realm := realm_tgt_is_for(tgt);

        decode remainder of request;

        if (auth_hdr.authenticator.cksum is missing) then
                error_out(KRB_AP_ERR_INAPP_CKSUM);
        endif
        if (auth_hdr.authenticator.cksum type is not supported) then
                error_out(KDC_ERR_SUMTYPE_NOSUPP);
        endif
        if (auth_hdr.authenticator.cksum is not both collision-proof
            and keyed)  then
                error_out(KRB_AP_ERR_INAPP_CKSUM);
        endif

Top      Up      ToC       Page 99 
        set computed_checksum := checksum(req);
        if (computed_checksum != auth_hdr.authenticatory.cksum) then
                error_out(KRB_AP_ERR_MODIFIED);
        endif

        server := lookup(req.sname,realm);

        if (!server) then
                if (is_foreign_tgt_name(server)) then
                        server := best_intermediate_tgs(server);
                else
                        /* no server in Database */
                        error_out(KDC_ERR_S_PRINCIPAL_UNKNOWN);
                endif
        endif

        session := generate_random_session_key();


        use_etype := first supported etype in req.etypes;

        if (no support for req.etypes) then
                error_out(KDC_ERR_ETYPE_NOSUPP);
        endif

        new_tkt.vno := ticket version; /* = 5 */
        new_tkt.sname := req.sname;
        new_tkt.srealm := realm;
        reset all flags in new_tkt.flags;

        /* It should be noted that local policy may affect the  */
        /* processing of any of these flags.  For example, some */
        /* realms may refuse to issue renewable tickets         */

        new_tkt.caddr := tgt.caddr;
        resp.caddr := NULL; /* We only include this if they change */
        if (req.kdc-options.FORWARDABLE is set) then
                if (tgt.flags.FORWARDABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.FORWARDABLE;
        endif
        if (req.kdc-options.FORWARDED is set) then
                if (tgt.flags.FORWARDABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.FORWARDED;
                new_tkt.caddr := req.addresses;

Top      Up      ToC       Page 100 
                resp.caddr := req.addresses;
        endif
        if (tgt.flags.FORWARDED is set) then
                set new_tkt.flags.FORWARDED;
        endif

        if (req.kdc-options.PROXIABLE is set) then
                if (tgt.flags.PROXIABLE is reset)
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.PROXIABLE;
        endif
        if (req.kdc-options.PROXY is set) then
                if (tgt.flags.PROXIABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.PROXY;
                new_tkt.caddr := req.addresses;
                resp.caddr := req.addresses;
        endif

        if (req.kdc-options.POSTDATE is set) then
                if (tgt.flags.POSTDATE is reset)
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.POSTDATE;
        endif
        if (req.kdc-options.POSTDATED is set) then
                if (tgt.flags.POSTDATE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.POSTDATED;
                set new_tkt.flags.INVALID;
                if (against_postdate_policy(req.from)) then
                        error_out(KDC_ERR_POLICY);
                endif
                new_tkt.starttime := req.from;
        endif


        if (req.kdc-options.VALIDATE is set) then
                if (tgt.flags.INVALID is reset) then
                        error_out(KDC_ERR_POLICY);
                endif
                if (tgt.starttime > kdc_time) then
                        error_out(KRB_AP_ERR_NYV);
                endif
                if (check_hot_list(tgt)) then

Top      Up      ToC       Page 101 
                        error_out(KRB_AP_ERR_REPEAT);
                endif
                tkt := tgt;
                reset new_tkt.flags.INVALID;
        endif

        if (req.kdc-options.(any flag except ENC-TKT-IN-SKEY, RENEW,
                             and those already processed) is set) then
                error_out(KDC_ERR_BADOPTION);
        endif

        new_tkt.authtime := tgt.authtime;

        if (req.kdc-options.RENEW is set) then
          /* Note that if the endtime has already passed, the ticket */
          /* would have been rejected in the initial authentication  */
          /* stage, so there is no need to check again here          */
                if (tgt.flags.RENEWABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                if (tgt.renew-till >= kdc_time) then
                        error_out(KRB_AP_ERR_TKT_EXPIRED);
                endif
                tkt := tgt;
                new_tkt.starttime := kdc_time;
                old_life := tgt.endttime - tgt.starttime;
                new_tkt.endtime := min(tgt.renew-till,
                                       new_tkt.starttime + old_life);
        else
                new_tkt.starttime := kdc_time;
                if (req.till = 0) then
                        till := infinity;
                else
                        till := req.till;
                endif
                new_tkt.endtime := min(till,
                                   new_tkt.starttime+client.max_life,
                                   new_tkt.starttime+server.max_life,
                                   new_tkt.starttime+max_life_for_realm,
                                   tgt.endtime);

                if ((req.kdc-options.RENEWABLE-OK is set) and
                    (new_tkt.endtime < req.till) and
                    (tgt.flags.RENEWABLE is set) then
                        /* we set the RENEWABLE option for later  */
                        /* processing                             */
                        set req.kdc-options.RENEWABLE;
                        req.rtime := min(req.till, tgt.renew-till);

Top      Up      ToC       Page 102 
                endif
        endif

        if (req.rtime = 0) then
                rtime := infinity;
        else
                rtime := req.rtime;
        endif

        if ((req.kdc-options.RENEWABLE is set) and
            (tgt.flags.RENEWABLE is set)) then
                set new_tkt.flags.RENEWABLE;
                new_tkt.renew-till := min(rtime,
                new_tkt.starttime+client.max_rlife,
                new_tkt.starttime+server.max_rlife,
                new_tkt.starttime+max_rlife_for_realm,
                tgt.renew-till);
        else
                new_tkt.renew-till := OMIT;
                              /* leave the renew-till field out */
        endif
        if (req.enc-authorization-data is present) then
                decrypt req.enc-authorization-data
                        into    decrypted_authorization_data
                        using auth_hdr.authenticator.subkey;
                if (decrypt_error()) then
                        error_out(KRB_AP_ERR_BAD_INTEGRITY);
                endif
        endif
        new_tkt.authorization_data :=
        req.auth_hdr.ticket.authorization_data +
                                 decrypted_authorization_data;

        new_tkt.key := session;
        new_tkt.crealm := tgt.crealm;
        new_tkt.cname := req.auth_hdr.ticket.cname;

        if (realm_tgt_is_for(tgt) := tgt.realm) then
                /* tgt issued by local realm */
                new_tkt.transited := tgt.transited;
        else
                /* was issued for this realm by some other realm */
                if (tgt.transited.tr-type not supported) then
                        error_out(KDC_ERR_TRTYPE_NOSUPP);
                endif
                new_tkt.transited
                   := compress_transited(tgt.transited + tgt.realm)
        endif

Top      Up      ToC       Page 103 
        encode encrypted part of new_tkt into OCTET STRING;
        if (req.kdc-options.ENC-TKT-IN-SKEY is set) then
                if (server not specified) then
                        server = req.second_ticket.client;
                endif
                if ((req.second_ticket is not a TGT) or
                    (req.second_ticket.client != server)) then
                        error_out(KDC_ERR_POLICY);
                endif

                new_tkt.enc-part := encrypt OCTET STRING using
                        using etype_for_key(second-ticket.key),
                                                      second-ticket.key;
        else
                new_tkt.enc-part := encrypt OCTET STRING
                        using etype_for_key(server.key), server.key,
                                                      server.p_kvno;
        endif

        resp.pvno := 5;
        resp.msg-type := KRB_TGS_REP;
        resp.crealm := tgt.crealm;
        resp.cname := tgt.cname;
        resp.ticket := new_tkt;

        resp.key := session;
        resp.nonce := req.nonce;
        resp.last-req := fetch_last_request_info(client);
        resp.flags := new_tkt.flags;

        resp.authtime := new_tkt.authtime;
        resp.starttime := new_tkt.starttime;
        resp.endtime := new_tkt.endtime;

        omit resp.key-expiration;

        resp.sname := new_tkt.sname;
        resp.realm := new_tkt.realm;

        if (new_tkt.flags.RENEWABLE) then
                resp.renew-till := new_tkt.renew-till;
        endif


        encode body of reply into OCTET STRING;

        if (req.padata.authenticator.subkey)
                resp.enc-part := encrypt OCTET STRING using use_etype,

Top      Up      ToC       Page 104 
                        req.padata.authenticator.subkey;
        else resp.enc-part := encrypt OCTET STRING
                              using use_etype, tgt.key;

        send(resp);

A.7.  KRB_TGS_REP verification
        decode response into resp;

        if (resp.msg-type = KRB_ERROR) then
                process_error(resp);
                return;
        endif

        /* On error, discard the response, and zero the session key from
        the response immediately */

        if (req.padata.authenticator.subkey)
                unencrypted part of resp :=
                        decode of decrypt of resp.enc-part
                        using resp.enc-part.etype and subkey;
        else unencrypted part of resp :=
                        decode of decrypt of resp.enc-part
                        using resp.enc-part.etype and tgt's session key;
        if (common_as_rep_tgs_rep_checks fail) then
                destroy resp.key;
                return error;
        endif

        check authorization_data as necessary;
        save_for_later(ticket,session,client,server,times,flags);

A.8.  Authenticator generation
        body.authenticator-vno := authenticator vno; /* = 5 */
        body.cname, body.crealm := client name;
        if (supplying checksum) then
                body.cksum := checksum;
        endif
        get system_time;
        body.ctime, body.cusec := system_time;
        if (selecting sub-session key) then
                select sub-session key;
                body.subkey := sub-session key;
        endif
        if (using sequence numbers) then
                select initial sequence number;
                body.seq-number := initial sequence;
        endif

Top      Up      ToC       Page 105 
A.9.  KRB_AP_REQ generation
        obtain ticket and session_key from cache;

        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_AP_REQ */

        if (desired(MUTUAL_AUTHENTICATION)) then
                set packet.ap-options.MUTUAL-REQUIRED;
        else
                reset packet.ap-options.MUTUAL-REQUIRED;
        endif
        if (using session key for ticket) then
                set packet.ap-options.USE-SESSION-KEY;
        else
                reset packet.ap-options.USE-SESSION-KEY;
        endif
        packet.ticket := ticket; /* ticket */
        generate authenticator;
        encode authenticator into OCTET STRING;
        encrypt OCTET STRING into packet.authenticator
                             using session_key;

A.10.  KRB_AP_REQ verification
        receive packet;
        if (packet.pvno != 5) then
                either process using other protocol spec
                or error_out(KRB_AP_ERR_BADVERSION);
        endif
        if (packet.msg-type != KRB_AP_REQ) then
                error_out(KRB_AP_ERR_MSG_TYPE);
        endif
        if (packet.ticket.tkt_vno != 5) then
                either process using other protocol spec
                or error_out(KRB_AP_ERR_BADVERSION);
        endif
        if (packet.ap_options.USE-SESSION-KEY is set) then
                retrieve session key from ticket-granting ticket for
                 packet.ticket.{sname,srealm,enc-part.etype};
        else
           retrieve service key for
           packet.ticket.{sname,srealm,enc-part.etype,enc-part.skvno};
        endif
        if (no_key_available) then
                if (cannot_find_specified_skvno) then
                        error_out(KRB_AP_ERR_BADKEYVER);
                else
                        error_out(KRB_AP_ERR_NOKEY);
                endif

Top      Up      ToC       Page 106 
        endif
        decrypt packet.ticket.enc-part into decr_ticket
                                       using retrieved key;
        if (decryption_error()) then
                error_out(KRB_AP_ERR_BAD_INTEGRITY);
        endif
        decrypt packet.authenticator into decr_authenticator
                using decr_ticket.key;
        if (decryption_error()) then
                error_out(KRB_AP_ERR_BAD_INTEGRITY);
        endif
        if (decr_authenticator.{cname,crealm} !=
            decr_ticket.{cname,crealm}) then
                error_out(KRB_AP_ERR_BADMATCH);
        endif
        if (decr_ticket.caddr is present) then
                if (sender_address(packet) is not in decr_ticket.caddr)
                        then error_out(KRB_AP_ERR_BADADDR);
                endif
        elseif (application requires addresses) then
                error_out(KRB_AP_ERR_BADADDR);
        endif
        if (not in_clock_skew(decr_authenticator.ctime,
                              decr_authenticator.cusec)) then
                error_out(KRB_AP_ERR_SKEW);
        endif
        if (repeated(decr_authenticator.{ctime,cusec,cname,crealm}))
                then error_out(KRB_AP_ERR_REPEAT);
        endif
        save_identifier(decr_authenticator.{ctime,cusec,cname,crealm});
        get system_time;
        if ((decr_ticket.starttime-system_time > CLOCK_SKEW) or
            (decr_ticket.flags.INVALID is set)) then
                /* it hasn't yet become valid */
                error_out(KRB_AP_ERR_TKT_NYV);
        endif
        if (system_time-decr_ticket.endtime > CLOCK_SKEW) then
                error_out(KRB_AP_ERR_TKT_EXPIRED);
        endif
        /* caller must check decr_ticket.flags for any pertinent */
        /* details */
        return(OK, decr_ticket, packet.ap_options.MUTUAL-REQUIRED);

A.11.  KRB_AP_REP generation
        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_AP_REP */
        body.ctime := packet.ctime;
        body.cusec := packet.cusec;

Top      Up      ToC       Page 107 
        if (selecting sub-session key) then
                select sub-session key;
                body.subkey := sub-session key;
        endif
        if (using sequence numbers) then
                select initial sequence number;
                body.seq-number := initial sequence;
        endif

        encode body into OCTET STRING;

        select encryption type;
        encrypt OCTET STRING into packet.enc-part;

A.12.  KRB_AP_REP verification
        receive packet;
        if (packet.pvno != 5) then
                either process using other protocol spec
                or error_out(KRB_AP_ERR_BADVERSION);
        endif
        if (packet.msg-type != KRB_AP_REP) then
                error_out(KRB_AP_ERR_MSG_TYPE);
        endif
        cleartext := decrypt(packet.enc-part)
                     using ticket's session key;
        if (decryption_error()) then
                error_out(KRB_AP_ERR_BAD_INTEGRITY);
        endif
        if (cleartext.ctime != authenticator.ctime) then
                error_out(KRB_AP_ERR_MUT_FAIL);
        endif
        if (cleartext.cusec != authenticator.cusec) then
                error_out(KRB_AP_ERR_MUT_FAIL);
        endif
        if (cleartext.subkey is present) then
                save cleartext.subkey for future use;
        endif
        if (cleartext.seq-number is present) then
                save cleartext.seq-number for future verifications;
        endif
        return(AUTHENTICATION_SUCCEEDED);

A.13.  KRB_SAFE generation
        collect user data in buffer;

        /* assemble packet: */
        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_SAFE */

Top      Up      ToC       Page 108 
        body.user-data := buffer; /* DATA */
        if (using timestamp) then
                get system_time;
                body.timestamp, body.usec := system_time;
        endif
        if (using sequence numbers) then
                body.seq-number := sequence number;
        endif
        body.s-address := sender host addresses;
        if (only one recipient) then
                body.r-address := recipient host address;
        endif
        checksum.cksumtype := checksum type;
        compute checksum over body;
        checksum.checksum := checksum value; /* checksum.checksum */
        packet.cksum := checksum;
        packet.safe-body := body;

A.14.  KRB_SAFE verification
        receive packet;
        if (packet.pvno != 5) then
                either process using other protocol spec
                or error_out(KRB_AP_ERR_BADVERSION);
        endif
        if (packet.msg-type != KRB_SAFE) then
                error_out(KRB_AP_ERR_MSG_TYPE);
        endif
        if (packet.checksum.cksumtype is not both collision-proof
                                             and keyed) then
                error_out(KRB_AP_ERR_INAPP_CKSUM);
        endif
        if (safe_priv_common_checks_ok(packet)) then
                set computed_checksum := checksum(packet.body);
                if (computed_checksum != packet.checksum) then
                        error_out(KRB_AP_ERR_MODIFIED);
                endif
                return (packet, PACKET_IS_GENUINE);
        else
                return common_checks_error;
        endif

A.15.  KRB_SAFE and KRB_PRIV common checks
        if (packet.s-address != O/S_sender(packet)) then
            /* O/S report of sender not who claims to have sent it */
            error_out(KRB_AP_ERR_BADADDR);
        endif
        if ((packet.r-address is present) and
            (packet.r-address != local_host_address)) then

Top      Up      ToC       Page 109 
                /* was not sent to proper place */
                error_out(KRB_AP_ERR_BADADDR);
        endif
        if (((packet.timestamp is present) and
             (not in_clock_skew(packet.timestamp,packet.usec))) or
            (packet.timestamp is not present and timestamp expected))
                then error_out(KRB_AP_ERR_SKEW);
        endif
        if (repeated(packet.timestamp,packet.usec,packet.s-address))
                then error_out(KRB_AP_ERR_REPEAT);
        endif
        if (((packet.seq-number is present) and
             ((not in_sequence(packet.seq-number)))) or
            (packet.seq-number is not present and sequence expected))
                then error_out(KRB_AP_ERR_BADORDER);
        endif
        if (packet.timestamp not present and
            packet.seq-number not present) then
                error_out(KRB_AP_ERR_MODIFIED);
        endif

        save_identifier(packet.{timestamp,usec,s-address},
                        sender_principal(packet));

        return PACKET_IS_OK;

A.16.  KRB_PRIV generation
        collect user data in buffer;

        /* assemble packet: */
        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_PRIV */

        packet.enc-part.etype := encryption type;

        body.user-data := buffer;
        if (using timestamp) then
                get system_time;
                body.timestamp, body.usec := system_time;
        endif
        if (using sequence numbers) then
                body.seq-number := sequence number;
        endif
        body.s-address := sender host addresses;
        if (only one recipient) then
                body.r-address := recipient host address;
        endif

Top      Up      ToC       Page 110 
        encode body into OCTET STRING;

        select encryption type;
        encrypt OCTET STRING into packet.enc-part.cipher;

A.17.  KRB_PRIV verification
        receive packet;
        if (packet.pvno != 5) then
                either process using other protocol spec
                or error_out(KRB_AP_ERR_BADVERSION);
        endif
        if (packet.msg-type != KRB_PRIV) then
                error_out(KRB_AP_ERR_MSG_TYPE);
        endif

        cleartext := decrypt(packet.enc-part) using negotiated key;
        if (decryption_error()) then
                error_out(KRB_AP_ERR_BAD_INTEGRITY);
        endif

        if (safe_priv_common_checks_ok(cleartext)) then
            return(cleartext.DATA, PACKET_IS_GENUINE_AND_UNMODIFIED);
        else
                return common_checks_error;
        endif

A.18.  KRB_CRED generation
        invoke KRB_TGS; /* obtain tickets to be provided to peer */

        /* assemble packet: */
        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_CRED */

        for (tickets[n] in tickets to be forwarded) do
                packet.tickets[n] = tickets[n].ticket;
        done

        packet.enc-part.etype := encryption type;

        for (ticket[n] in tickets to be forwarded) do
                body.ticket-info[n].key = tickets[n].session;
                body.ticket-info[n].prealm = tickets[n].crealm;
                body.ticket-info[n].pname = tickets[n].cname;
                body.ticket-info[n].flags = tickets[n].flags;
                body.ticket-info[n].authtime = tickets[n].authtime;
                body.ticket-info[n].starttime = tickets[n].starttime;
                body.ticket-info[n].endtime = tickets[n].endtime;
                body.ticket-info[n].renew-till = tickets[n].renew-till;

Top      Up      ToC       Page 111 
                body.ticket-info[n].srealm = tickets[n].srealm;
                body.ticket-info[n].sname = tickets[n].sname;
                body.ticket-info[n].caddr = tickets[n].caddr;
        done

        get system_time;
        body.timestamp, body.usec := system_time;

        if (using nonce) then
                body.nonce := nonce;
        endif

        if (using s-address) then
                body.s-address := sender host addresses;
        endif
        if (limited recipients) then
                body.r-address := recipient host address;
        endif

        encode body into OCTET STRING;

        select encryption type;
        encrypt OCTET STRING into packet.enc-part.cipher
        using negotiated encryption key;

A.19.  KRB_CRED verification
        receive packet;
        if (packet.pvno != 5) then
                either process using other protocol spec
                or error_out(KRB_AP_ERR_BADVERSION);
        endif
        if (packet.msg-type != KRB_CRED) then
                error_out(KRB_AP_ERR_MSG_TYPE);
        endif

        cleartext := decrypt(packet.enc-part) using negotiated key;
        if (decryption_error()) then
                error_out(KRB_AP_ERR_BAD_INTEGRITY);
        endif
        if ((packet.r-address is present or required) and
           (packet.s-address != O/S_sender(packet)) then
            /* O/S report of sender not who claims to have sent it */
            error_out(KRB_AP_ERR_BADADDR);
        endif
        if ((packet.r-address is present) and
            (packet.r-address != local_host_address)) then
                /* was not sent to proper place */
                error_out(KRB_AP_ERR_BADADDR);

Top      Up      ToC       Page 112 
        endif
        if (not in_clock_skew(packet.timestamp,packet.usec)) then
                error_out(KRB_AP_ERR_SKEW);
        endif
        if (repeated(packet.timestamp,packet.usec,packet.s-address))
                then error_out(KRB_AP_ERR_REPEAT);
        endif
        if (packet.nonce is required or present) and
           (packet.nonce != expected-nonce) then
                error_out(KRB_AP_ERR_MODIFIED);
        endif

        for (ticket[n] in tickets that were forwarded) do
                save_for_later(ticket[n],key[n],principal[n],
                               server[n],times[n],flags[n]);
        return

A.20.  KRB_ERROR generation

        /* assemble packet: */
        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_ERROR */

        get system_time;
        packet.stime, packet.susec := system_time;
        packet.realm, packet.sname := server name;

        if (client time available) then
                packet.ctime, packet.cusec := client_time;
        endif
        packet.error-code := error code;
        if (client name available) then
                packet.cname, packet.crealm := client name;
        endif
        if (error text available) then
                packet.e-text := error text;
        endif
        if (error data available) then
                packet.e-data := error data;
        endif