RFC 1510
The Kerberos Network Authentication Service (V5)
RFC 1510: The Kerberos Network Authentication Service (V5) ๐
part 1 of 24 ๐
Title Block ๐
okay so this is RFC 1510 ๐ฆ, and it came out in September 1993. The authors are J. Kohl (Digital Equipment Corporation) and C. Neuman (ISI) ๐ฉโ๐ป. It's an Internet standards track protocol ๐ฅ. The title block doesn't list any RFCs it obsoletes or updates, so no drama there โจ.
Status of This Memo โจ
so this RFC is an Internet standards track protocol, and it's basically asking the Internet community to chat about it and drop suggestions ๐ฌ. If you want the official standardization state and status, check the current edition of the "Internet Official Protocol Standards" ๐. Distribution of this memo is unlimited ๐.
Abstract ๐ธ
this doc gives an overview and spec of Version 5 of the Kerberos network authentication system ๐. Version 4 is described elsewhere [1,2], and it's still actually in production use at MIT's Project Athena and other Internet sites ๐ซ. iconic longevity ๐.
Overview ๐ง
first, the legal sparkle ๐ : Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira, and Zephyr are trademarks of the Massachusetts Institute of Technology (MIT). You can't use them commercially without MIT's prior written permission โ (don't be that person).
this RFC explains the concepts and model Kerberos is built on, and it specifies Version 5 of the protocol ๐ฆ.
the motivations, goals, assumptions, and reasoning behind most design choices only get a quick mention here ๐. For Version 4 they're fully explained in the Kerberos section of the Athena Technical Plan [1]. The protocols are still under review and aren't being submitted as an Internet standard right now, but comments are very much encouraged ๐ซถ.
if you want to join the discussion mailing list for Kerberos, kerberos@MIT.EDU, ask to be added by writing to kerberos-request@MIT.EDU ๐ฌ. The list is also mirrored onto Usenet as comp.protocols.kerberos ๐ธ. For further info, including documents and code availability, email info-kerberos@MIT.EDU ๐.
Background ๐
the Kerberos model is partly based on Needham and Schroeder's trusted third-party authentication protocol [3], plus some modifications Denning and Sacco suggested [4] ๐ง . The original design and implementation of Kerberos Versions 1 through 4 was the work of two former Project Athena staff members: Steve Miller of Digital Equipment Corporation, and Clifford Neuman (now at the Information Sciences Institute of the University of Southern California) ๐. Jerome Saltzer (Technical Director of Project Athena) and Jeffrey Schiller (MIT Campus Network Manager) were part of it too, and lots of other Athena people contributed to the work on Kerberos ๐ซถ. Version 4 is publicly available and has seen wide use across the Internet ๐.
Version 5 (the one this document is about) evolved from Version 4 because of new requirements and a desire for features Version 4 didn't have ๐ฆ. The differences between Kerberos Versions 4 and 5 are covered in [5] ๐.
Table of Contents ๐
Table of Contents
1. Introduction ....................................... 5
1.1. Cross-Realm Operation ............................ 7
1.2. Environmental assumptions ........................ 8
1.3. Glossary of terms ................................ 9
2. Ticket flag uses and requests ...................... 12
2.1. Initial and pre-authenticated tickets ............ 12
2.2. Invalid tickets .................................. 12
2.3. Renewable tickets ................................ 12
2.4. Postdated tickets ................................ 13
2.5. Proxiable and proxy tickets ...................... 14
2.6. Forwardable tickets .............................. 15
2.7. Other KDC options ................................ 15
3. Message Exchanges .................................. 16
3.1. The Authentication Service Exchange .............. 16
3.1.1. Generation of KRB_AS_REQ message ............... 17
3.1.2. Receipt of KRB_AS_REQ message .................. 17
3.1.3. Generation of KRB_AS_REP message ............... 17
3.1.4. Generation of KRB_ERROR message ................ 19
3.1.5. Receipt of KRB_AS_REP message .................. 19
3.1.6. Receipt of KRB_ERROR message ................... 20
3.2. The Client/Server Authentication Exchange ........ 20
3.2.1. The KRB_AP_REQ message ......................... 20
3.2.2. Generation of a KRB_AP_REQ message ............. 20
3.2.3. Receipt of KRB_AP_REQ message .................. 21
3.2.4. Generation of a KRB_AP_REP message ............. 23
3.2.5. Receipt of KRB_AP_REP message .................. 23
3.2.6. Using the encryption key ....................... 24
3.3. The Ticket-Granting Service (TGS) Exchange ....... 24
3.3.1. Generation of KRB_TGS_REQ message .............. 25
3.3.2. Receipt of KRB_TGS_REQ message ................. 26
3.3.3. Generation of KRB_TGS_REP message .............. 27
3.3.3.1. Encoding the transited field ................. 29
3.3.4. Receipt of KRB_TGS_REP message ................. 31
3.4. The KRB_SAFE Exchange ............................ 31
3.4.1. Generation of a KRB_SAFE message ............... 31
3.4.2. Receipt of KRB_SAFE message .................... 32
3.5. The KRB_PRIV Exchange ............................ 33
3.5.1. Generation of a KRB_PRIV message ............... 33
3.5.2. Receipt of KRB_PRIV message .................... 33
3.6. The KRB_CRED Exchange ............................ 34
3.6.1. Generation of a KRB_CRED message ............... 34
3.6.2. Receipt of KRB_CRED message .................... 34
4. The Kerberos Database .............................. 35
4.1. Database contents ................................ 35
4.2. Additional fields ................................ 36
4.3. Frequently Changing Fields ....................... 37
4.4. Site Constants ................................... 37
5. Message Specifications ............................. 38
5.1. ASN.1 Distinguished Encoding Representation ...... 38
5.2. ASN.1 Base Definitions ........................... 38
5.3. Tickets and Authenticators ....................... 42
5.3.1. Tickets ........................................ 42
5.3.2. Authenticators ................................. 47
5.4. Specifications for the AS and TGS exchanges ...... 49
5.4.1. KRB_KDC_REQ definition ......................... 49
5.4.2. KRB_KDC_REP definition ......................... 56
5.5. Client/Server (CS) message specifications ........ 58
5.5.1. KRB_AP_REQ definition .......................... 58
5.5.2. KRB_AP_REP definition .......................... 60
5.5.3. Error message reply ............................ 61
5.6. KRB_SAFE message specification ................... 61
5.6.1. KRB_SAFE definition ............................ 61
5.7. KRB_PRIV message specification ................... 62
5.7.1. KRB_PRIV definition ............................ 62
5.8. KRB_CRED message specification ................... 63
5.8.1. KRB_CRED definition ............................ 63
5.9. Error message specification ...................... 65
5.9.1. KRB_ERROR definition ........................... 66
6. Encryption and Checksum Specifications ............. 67
6.1. Encryption Specifications ........................ 68
6.2. Encryption Keys .................................. 71
6.3. Encryption Systems ............................... 71
6.3.1. The NULL Encryption System (null) .............. 71
6.3.2. DES in CBC mode with a CRC-32 checksum (descbc-crc)71
6.3.3. DES in CBC mode with an MD4 checksum (descbc-md4) 72
6.3.4. DES in CBC mode with an MD5 checksum (descbc-md5) 72
6.4. Checksums ........................................ 74
6.4.1. The CRC-32 Checksum (crc32) .................... 74
6.4.2. The RSA MD4 Checksum (rsa-md4) ................. 75
6.4.3. RSA MD4 Cryptographic Checksum Using DES
(rsa-md4-des) ......................................... 75
6.4.4. The RSA MD5 Checksum (rsa-md5) ................. 76
6.4.5. RSA MD5 Cryptographic Checksum Using DES
(rsa-md5-des) ......................................... 76
6.4.6. DES cipher-block chained checksum (des-mac)
6.4.7. RSA MD4 Cryptographic Checksum Using DES
alternative (rsa-md4-des-k) ........................... 77
6.4.8. DES cipher-block chained checksum alternative
(des-mac-k) ........................................... 77
7. Naming Constraints ................................. 78
7.1. Realm Names ...................................... 77
7.2. Principal Names .................................. 79
7.2.1. Name of server principals ...................... 80
8. Constants and other defined values ................. 80
8.1. Host address types ............................... 80
8.2. KDC messages ..................................... 81
8.2.1. IP transport ................................... 81
8.2.2. OSI transport .................................. 82
8.2.3. Name of the TGS ................................ 82
8.3. Protocol constants and associated values ......... 82
9. Interoperability requirements ...................... 86
9.1. Specification 1 .................................. 86
9.2. Recommended KDC values ........................... 88
10. Acknowledgments ................................... 88
11. References ........................................ 89
12. Security Considerations ........................... 90
13. Authors' Addresses ................................ 90
A. Pseudo-code for protocol processing ................ 91
A.1. KRB_AS_REQ generation ............................ 91
A.2. KRB_AS_REQ verification and KRB_AS_REP generation 92
A.3. KRB_AS_REP verification .......................... 95
A.4. KRB_AS_REP and KRB_TGS_REP common checks ......... 96
A.5. KRB_TGS_REQ generation ........................... 97
A.6. KRB_TGS_REQ verification and KRB_TGS_REP generation 98
A.7. KRB_TGS_REP verification ......................... 104
A.8. Authenticator generation ......................... 104
A.9. KRB_AP_REQ generation ............................ 105
A.10. KRB_AP_REQ verification ......................... 105
A.11. KRB_AP_REP generation ........................... 106
A.12. KRB_AP_REP verification ......................... 107
A.13. KRB_SAFE generation ............................. 107
A.14. KRB_SAFE verification ........................... 108
A.15. KRB_SAFE and KRB_PRIV common checks ............. 108
A.16. KRB_PRIV generation ............................. 109
A.17. KRB_PRIV verification ........................... 110
A.18. KRB_CRED generation ............................. 110
A.19. KRB_CRED verification ........................... 111
A.20. KRB_ERROR generation ............................ 112
1. Introduction โจ
okay bestie, so Kerberos is the thing that lets us verify who people (and servers) actually are ๐ on an open, unprotected network. And it's iconic because it does this without trusting the host's own OS to vouch for anyone ๐ , without trusting host addresses, and without needing every machine on the network to be physically locked down ๐. It even assumes packets can be read, modified, and inserted whenever an attacker wants ๐ญ. Kerberos is literally built for a messy internet.
Quick note: lots of apps only use Kerberos when they first open a stream-based connection, and they assume no "hijackers" are going to take over that connection ๐. That kind of setup quietly trusts the host addresses involved, so it's not the full Kerberos experience.
Kerberos does all this as a trusted third-party authentication service using conventional cryptography, aka a shared secret key ๐๏ธ. Quick vocab moment: "secret" and "private" get mixed up all the time in the literature, but here a secret takes two (or more) people to share it. So a shared DES key counts as a secret key ๐. Something is only private when nobody except its owner knows it, which is why in public key cryptosystems you get a public key and a private key ๐.
Now the vibe check on how authentication goes down ๐. A client sends a request to the authentication server (AS) asking for "credentials" for a given server. The AS sends those credentials back encrypted in the client's key ๐. The credentials have two parts: 1) a "ticket" for the server, and 2) a temporary encryption key (usually called a "session key") โจ. The client then sends the ticket to the server. The ticket holds the client's identity and a copy of the session key, all encrypted in the server's key ๐ง. Now the session key is shared by the client and server, and it's used to authenticate the client. It can optionally authenticate the server too ๐. It can also encrypt further chatter between them, or be used to swap a separate sub-session key for encrypting even more messages ๐.
Behind the scenes, the implementation is one or more authentication servers running on physically secure hosts ๐ฐ. These servers keep a database of principals (users and servers) and their secret keys. Code libraries handle the encryption and run the Kerberos protocol ๐. To add authentication to its transactions, a typical network app just adds one or two calls to the Kerberos library, and boom, the necessary messages get sent for authentication ๐.
The Kerberos protocol is made of several sub-protocols (or "exchanges") ๐ฆ. There are two ways a client can ask a Kerberos server for credentials:
- First approach: the client sends a cleartext request for a ticket for the desired server to the AS. The reply is encrypted in the client's secret key. Usually this request is for a ticket-granting ticket (TGT), which can later be used with the ticket-granting server (TGS) ๐๏ธ.
- Second approach: the client sends a request to the TGS. It sends the TGT the same way it would contact any other application server that needs Kerberos credentials. The reply is encrypted in the session key from the TGT ๐ .
Once you've got credentials, you can use them to verify the identities of principals in a transaction, to make sure messages between them haven't been messed with, or to keep messages private ๐ซถ. The application gets to choose whatever protection it needs.
Here's how identity verification works ๐. The client sends the ticket to the server. The ticket goes over the wire "in the clear" (parts of it are encrypted, but that encryption doesn't stop replay ๐ฌ). Since an attacker could grab it and reuse it, extra info gets sent to prove the message came from the principal the ticket was issued to. That info is called the authenticator, it's encrypted in the session key, and it includes a timestamp โฐ. The timestamp proves the message was made recently and isn't a replay. Encrypting the authenticator in the session key proves it came from someone who has the session key ๐. Since only the requesting principal and the server know the session key (it's never sent over the network in the clear), this guarantees the client's identity ๐ฅฐ.
Integrity works with the session key too ๐ฅ. This approach catches both replay attacks and message stream modification attacks. It works by generating and sending a collision-proof checksum (elsewhere called a hash or digest function) of the client's message, keyed with the session key. Privacy and integrity can also be locked down by encrypting the data with the session key from the ticket and credentials ๐ธ.
The authentication exchanges above only need read-only access to the Kerberos database ๐. But sometimes the database has to change, like when adding new principals or changing a principal's key ๐ ๏ธ. That's done through a protocol between a client and a third Kerberos server, the Kerberos Administration Server (KADM). This document does not describe the administration protocol ๐. There's also a protocol for keeping multiple copies of the Kerberos database in sync. That one counts as an implementation detail, and it can vary to support different database technologies ๐ง.
1.1. Cross-Realm Operation
Kerberos is designed to work across organizational boundaries ๐. A client in one organization can be authenticated to a server in another, which is so main-character energy ๐. Each organization that wants to run a Kerberos server sets up its own "realm". The name of the realm where a client is registered is part of the client's name, and the end-service can use it to decide whether to honor a request ๐.
By setting up "inter-realm" keys, the admins of two realms can let a client who's authenticated locally use that authentication remotely ๐ฆ. (Sure, with the right permission a client could register a separately-named principal in a remote realm and do normal exchanges with that realm's services. But even with just a few clients, that gets super cumbersome, so the more automatic methods described here are necessary ๐ฉ.) Exchanging inter-realm keys (a separate key can be used for each direction) registers each realm's ticket-granting service as a principal in the other realm ๐. That means a client can get a ticket-granting ticket for the remote realm's TGS from its local realm ๐๏ธ.
When that TGT gets used, the remote TGS uses the inter-realm key to decrypt it. (That key usually differs from the TGS's own normal key.) This way the remote TGS knows for sure the TGT was issued by the client's own TGS ๐ซถ. Tickets the remote TGS issues will tell the end-service that the client was authenticated from another realm โจ.
A realm "communicates" with another realm if the two share an inter-realm key, or if the local realm shares one with an intermediate realm that communicates with the remote realm ๐. An authentication path is the sequence of intermediate realms transited when going from one realm to another ๐บ๏ธ.
Realms are usually organized hierarchically ๐ณ. Each realm shares a key with its parent and a different key with each child. If two realms don't directly share an inter-realm key, the hierarchy makes it easy to build an authentication path ๐ช. If there's no hierarchy, you might have to consult some database to construct an authentication path between realms ๐ตโ๐ซ.
Realms are typically hierarchical, but intermediate realms can be bypassed to do cross-realm authentication through alternate authentication paths (these might be set up to make communication between two realms more efficient โก). It matters a lot for the end-service to know which realms were transited when deciding how much faith to put in the authentication ๐ง. So a field in each ticket holds the names of the realms that were involved in authenticating the client ๐.
1.2. Environmental assumptions
Kerberos has a few assumptions about the environment it needs in order to work properly ๐งธ:
- Denial of service attacks aren't solved by Kerberos. There are spots in these protocols where an intruder can stop an application from taking part in the proper authentication steps ๐ซ. Spotting and fixing these attacks (some can look like totally normal failure modes for the system ๐) is usually best left to the human administrators and users ๐ซ .
- Principals have to keep their secret keys secret ๐คซ. If an intruder somehow steals a principal's key, they can masquerade as that principal, or impersonate any server to the legit principal ๐.
- Password guessing attacks aren't solved by Kerberos ๐ค. If a user picks a poor password, an attacker can successfully run an offline dictionary attack. They do this by repeatedly trying dictionary entries to decrypt messages they've captured that were encrypted under a key derived from the user's password ๐.
- Every host on the network needs a clock that's "loosely synchronized" with the other hosts' clocks โฑ๏ธ. This synchronization is used to cut down on the bookkeeping application servers need for replay detection. The degree of "looseness" can be configured on a per-server basis ๐๏ธ. If clocks are synchronized over the network, the clock synchronization protocol itself has to be secured from network attackers ๐ก๏ธ.
- Principal identifiers aren't recycled on a short-term basis ๐ โโ๏ธ. A typical access control setup uses access control lists (ACLs) to grant permissions to particular principals ๐. If a stale ACL entry sticks around for a deleted principal, and that principal identifier gets reused, the new principal inherits the rights in the stale entry ๐ฑ. Not reusing principal identifiers removes the danger of accidental access ๐.
1.3. Glossary of terms ๐โจ
Okay bestie, here's the vocab list you'll see all over this doc ๐
- Authentication: verifying the claimed identity of a principal ๐ชช
- Authentication header: a record with a Ticket and an Authenticator, presented to a server as part of the authentication process ๐ซ
- Authentication path: a sequence of intermediate realms transited in the authentication process when communicating from one realm to another ๐ฃ๏ธ
- Authenticator: a record with info that can be shown to have been recently generated using the session key known only by the client and server ๐
- Authorization: the process of determining whether a client may use a service, which objects the client is allowed to access, and the type of access allowed for each ๐ช
- Capability: a token that grants the bearer permission to access an object or service. In Kerberos, this might be a ticket whose use is restricted by the contents of the authorization data field, but which lists no network addresses, together with the session key necessary to use the ticket ๐๏ธ
- Ciphertext: the output of an encryption function. Encryption transforms plaintext into ciphertext ๐
- Client: a process that makes use of a network service on behalf of a user. Note that in some cases a Server may itself be a client of some other server (e.g., a print server may be a client of a file server) ๐จ๏ธ
- Credentials: a ticket plus the secret session key necessary to successfully use that ticket in an authentication exchange ๐๏ธ
- KDC: Key Distribution Center, a network service that supplies tickets and temporary session keys; or an instance of that service or the host on which it runs. The KDC services both initial ticket and ticket-granting ticket requests. The initial ticket portion is sometimes referred to as the Authentication Server (or service). The ticket-granting ticket portion is sometimes referred to as the ticket-granting server (or service) ๐ฐ
- Kerberos: aside from the 3-headed dog guarding Hades ๐, the name given to Project Athena's authentication service, the protocol used by that service, or the code used to implement the authentication service ๐โจ
- Plaintext: the input to an encryption function or the output of a decryption function. Decryption transforms ciphertext into plaintext ๐
- Principal: a uniquely named client or server instance that participates in a network communication ๐ค
- Principal identifier: the name used to uniquely identify each different principal ๐ท๏ธ
- Seal: to encipher a record containing several fields in such a way that the fields cannot be individually replaced without either knowledge of the encryption key or leaving evidence of tampering ๐ง
- Secret key: an encryption key shared by a principal and the KDC, distributed outside the bounds of the system, with a long lifetime. In the case of a human user's principal, the secret key is derived from a password ๐คซ
- Server: a particular principal which provides a resource to network clients ๐ฅ๏ธ
- Service: a resource provided to network clients; often provided by more than one server (for example, remote file service) ๐๏ธ
- Session key: a temporary encryption key used between two principals, with a lifetime limited to the duration of a single login "session" โณ
- Sub-session key: a temporary encryption key used between two principals, selected and exchanged by the principals using the session key, and with a lifetime limited to the duration of a single association ๐งฉ
- Ticket: a record that helps a client authenticate itself to a server; it contains the client's identity, a session key, a timestamp, and other information, all sealed using the server's secret key. It only serves to authenticate a client when presented along with a fresh Authenticator ๐
2. Ticket flag uses and requests ๐ฉ
Every Kerberos ticket carries a set of flags that show off its various attributes ๐ Most flags can be requested by a client when the ticket is obtained, while some get turned on and off automatically by a Kerberos server whenever it's needed. The next sections spell out what each flag means and give examples of why you'd want to use it ๐ฆ
2.1. Initial and pre-authenticated tickets
The INITIAL flag tells you a ticket was issued using the AS protocol, and not based on a ticket-granting ticket ๐ธ Application servers that want to require knowledge of a client's secret key (e.g., a passwordchanging program) can insist that this flag be set in any tickets they accept, so they can be sure the client's key was recently presented to the application client ๐ฅ
The PRE-AUTHENT and HW-AUTHENT flags give you additional info about the initial authentication ๐ง This is true whether the current ticket was issued directly (in which case INITIAL will also be set) or issued based on a ticket-granting ticket (in which case the INITIAL flag is clear, but PRE-AUTHENT and HW-AUTHENT are carried forward from the ticket-granting ticket) ๐
2.2. Invalid tickets
The INVALID flag means a ticket is invalid, and it's giving "do not use" ๐
โโ๏ธ Application servers must reject tickets with this flag set. A postdated ticket will usually be issued in this form ๐ง Invalid tickets must be validated by the KDC before use, by presenting them to the KDC in a TGS request with the VALIDATE option specified. The KDC will only validate tickets after their starttime has passed. The validation is required so that postdated tickets which have been stolen before their starttime can be rendered permanently invalid (through a hot-list mechanism) ๐จ
2.3. Renewable tickets
Okay, so apps may want to hold tickets that stay valid for long periods ๐ฐ๏ธ But that exposes their credentials to potential theft for just as long, and stolen credentials would stay valid until the ticket's expiration time ๐ญ Simply using short-lived tickets and grabbing new ones periodically would mean the client needs long-term access to its secret key, which is an even bigger risk ๐ So renewable tickets to the rescue, they help limit the consequences of theft ๐ก๏ธ
Renewable tickets have two "expiration times" ๐ The first is when the current instance of the ticket expires, and the second is the latest permissible value for an individual expiration time. An application client must periodically (i.e., before it expires) present a renewable ticket to the KDC, with the RENEW option set in the KDC request. The KDC will issue a new ticket with a new session key and a later expiration time. All other fields of the ticket are left unmodified by the renewal process. When the latest permissible expiration time arrives, the ticket expires permanently. At each renewal, the KDC may consult a hot-list to determine if the ticket had been reported stolen since its last renewal; it will refuse to renew such stolen tickets, so the usable lifetime of stolen tickets is reduced ๐
The RENEWABLE flag in a ticket is normally only interpreted by the ticket-granting service (discussed below in section 3.3) ๐ง It can usually be ignored by application servers. However, some particularly careful application servers may wish to disallow renewable tickets ๐
If a renewable ticket is not renewed by its expiration time, the KDC will not renew the ticket ๐ฅ The RENEWABLE flag is reset by default, but a client may request it be set by setting the RENEWABLE option in the KRB_AS_REQ message. If it is set, then the renew-till field in the ticket contains the time after which the ticket may not be renewed โฐ
2.4. Postdated tickets
Sometimes apps need tickets for use much later ๐ For example, a batch submission system would need tickets to be valid at the time the batch job is serviced. But it's dangerous to hold valid tickets in a batch queue, since they'd be online longer and more prone to theft ๐ฌ Postdated tickets solve this: you get them from the KDC at job submission time, but they stay "dormant" until they're activated and validated by a further request to the KDC. If a ticket theft were reported in the interim, the KDC would refuse to validate the ticket, and the thief would be foiled ๐ต๏ธโโ๏ธ๐ซ
The MAY-POSTDATE flag in a ticket is normally only interpreted by the ticket-granting service, and application servers can ignore it ๐ This flag must be set in a ticket-granting ticket in order to issue a postdated ticket based on the presented ticket. It is reset by default; a client may request it by setting the ALLOW-POSTDATE option in the KRB_AS_REQ message. This flag does not let a client obtain a postdated ticket-granting ticket. Postdated ticket-granting tickets can only be obtained by requesting the postdating in the KRB_AS_REQ message ๐
โโ๏ธ The life (endtime-starttime) of a postdated ticket will be the remaining life of the ticket-granting ticket at the time of the request, unless the RENEWABLE option is also set, in which case it can be the full life (endtime-starttime) of the ticket-granting ticket ๐ง The KDC may limit how far in the future a ticket may be postdated ๐ฎ
The POSTDATED flag indicates that a ticket has been postdated ๐ The application server can check the authtime field in the ticket to see when the original authentication happened. Some services may choose to reject postdated tickets, or they may only accept them within a certain period after the original authentication ๐ซถ When the KDC issues a POSTDATED ticket, it will also be marked as INVALID, so the application client must present the ticket to the KDC to be validated before use ๐
2.5. Proxiable and proxy tickets โจ
okay so sometimes a principal needs a service to do something for them ๐ง The service has to be able to take on the client's identity, but only for one specific purpose ๐ A principal makes that happen by handing over a proxy ๐
The PROXIABLE flag in a ticket is mostly the ticket-granting service's business. Application servers can just ignore it ๐ When it's set, it tells the ticket-granting server that it is OK to issue a new ticket (but not a ticket-granting ticket ๐ซ) with a different network address based on this ticket. This flag is set by default ๐
This flag lets a client pass a proxy to a server so the server can do a remote request on its behalf ๐ซถ For example, a print service client can give the print server a proxy so it can reach the client's files on a particular file server and finish a print job ๐
Kerberos tickets are usually only valid from the network addresses specifically included in the ticket, which makes stolen credentials way less useful ๐ (It is permissible to request or issue tickets with no network addresses specified, but we do not recommend it ๐ .) So a client that wants to grant a proxy has to request a new ticket that is valid for the network address of the service getting the proxy.
The PROXY flag is set in a ticket by the TGS when it issues a proxy ticket. Application servers may check this flag and require additional authentication from the agent presenting the proxy, so there's an audit trail ๐
2.6. Forwardable tickets ๐ฆ
Authentication forwarding is an instance of the proxy case where the service gets complete use of the client's identity ๐ It's giving full main character energy. An example is when a user logs in to a remote system and wants authentication to work from that system as if the login were local ๐ป
The FORWARDABLE flag in a ticket is normally only interpreted by the ticket-granting service. Application servers can ignore it. It has an interpretation similar to PROXIABLE, except that ticket-granting tickets may also be issued with different network addresses. This flag is reset by default, but users can ask for it to be set by using the FORWARDABLE option in the AS request when they request their initial ticket-granting ticket ๐ธ
This flag allows authentication forwarding without making the user enter a password again ๐ If the flag is not set, then authentication forwarding is not permitted. The same end result can still happen if the user goes through the AS exchange with the requested network addresses and supplies a password, so it's not a dead end ๐ซ
The FORWARDED flag is set by the TGS when a client presents a ticket with the FORWARDABLE flag set and requests it be set by specifying the FORWARDED KDC option and supplying a set of addresses for the new ticket. It's also set in all tickets issued based on tickets that have the FORWARDED flag set. Application servers may wish to process FORWARDED tickets differently than non-FORWARDED tickets ๐ง
2.7. Other KDC options ๐
There are two more options a client can set in its request to the KDC. The RENEWABLE-OK option means the client will accept a renewable ticket if a ticket with the requested life can't be provided any other way. If that happens, the KDC may issue a renewable ticket with a renew-till equal to the requested endtime. The renew-till value may still be adjusted by site-determined limits or by limits imposed by the individual principal or server ๐
The ENC-TKT-IN-SKEY option is honored only by the ticket-granting service. It means the to-be-issued ticket for the end server is encrypted in the session key from the additional ticket-granting ticket sent with the request. See section 3.3.3 for the specific details ๐
3. Message Exchanges ๐ฌ
These sections are all about the interactions between network clients and servers, and the messages involved in those exchanges. Let's get into it ๐
3.1. The Authentication Service Exchange ๐
Summary
Message direction Message type Section
1. Client to Kerberos KRB_AS_REQ 5.4.1
2. Kerberos to client KRB_AS_REP or 5.4.2
KRB_ERROR 5.9.1
The Authentication Service (AS) Exchange between the client and the Kerberos Authentication Server is usually started by a client when it wants authentication credentials for a given server but doesn't have any credentials yet ๐ง The client's secret key is used for encryption and decryption. This exchange is typically used at the start of a login session to get credentials for a Ticket-Granting Server. Those credentials can then be used to get credentials for other servers (see section 3.3) without needing the client's secret key again ๐ This exchange is also used to request credentials for services that must not be mediated through the Ticket-Granting Service, but instead need a principal's secret key, such as the password-changing service. (The password-changing request must not be honored unless the requester can provide the old password, which is the user's current secret key. Otherwise, someone could walk up to an unattended session and change another user's password ๐ โโ๏ธ) This exchange does not, by itself, provide any assurance of the identity of the user. (To authenticate a user logging on to a local system, the credentials obtained in the AS exchange may first be used in a TGS exchange to get credentials for a local server. Those credentials must then be verified by the local server by successfully completing the Client/Server exchange ๐)
The exchange has two messages: KRB_AS_REQ from the client to Kerberos, and KRB_AS_REP or KRB_ERROR in reply. The formats for these messages are described in sections 5.4.1, 5.4.2, and 5.9.1 ๐
In the request, the client sends (in cleartext) its own identity and the identity of the server it's requesting credentials for. The response, KRB_AS_REP, contains a ticket for the client to present to the server, plus a session key that the client and the server will share ๐ค The session key and additional information are encrypted in the client's secret key. The KRB_AS_REP message contains information that can be used to detect replays and to match it to the message it replies to. Various errors can happen, and they show up as an error response (KRB_ERROR) instead of KRB_AS_REP. The error message is not encrypted. The KRB_ERROR message also contains information that can be used to match it to the message it replies to. Since KRB_ERROR isn't encrypted, it can't be used to detect replays or fabrications of those messages ๐ฌ
In the normal case, the authentication server doesn't know whether the client is actually the principal named in the request. It just sends a reply without knowing or caring whether they're the same ๐คทโโ๏ธ This is fine because nobody except the principal whose identity was given in the request can use the reply. Its critical information is encrypted in that principal's key ๐ The initial request supports an optional field that can be used to pass extra information that might be needed for the initial exchange. This field may be used for preauthentication if you want, but the mechanism isn't currently specified ๐ซถ
3.1.1. Generation of KRB_AS_REQ message ๐ ๏ธ
The client can specify a bunch of options in the initial request. Among them: whether preauthentication is to be performed; whether the requested ticket is to be renewable, proxiable, or forwardable; whether it should be postdated or allow postdating of derivative tickets; and whether a renewable ticket will be accepted in place of a non-renewable ticket if the requested ticket expiration date can't be met by a non-renewable ticket (because of configuration constraints; see section 4). See section A.1 for pseudocode ๐
The client prepares the KRB_AS_REQ message and sends it to the KDC โ๏ธ
3.1.2. Receipt of KRB_AS_REQ message ๐ฌ
If all goes well, processing the KRB_AS_REQ message results in the creation of a ticket for the client to present to the server. The format for the ticket is described in section 5.3.1. The contents of the ticket are determined as follows.
3.1.3. Generation of KRB_AS_REP message โจ
okay so the authentication server looks up the client and server principals named in the KRB_AS_REQ in its database and pulls out their keys ๐. if pre-authentication is required, the server checks it, and if the check fails, it returns an error with the code KDC_ERR_PREAUTH_FAILED ๐
โโ๏ธ. if the server can't handle the requested encryption type, it returns KDC_ERR_ETYPE_NOSUPP. otherwise it generates a "random" session key ๐ฒ. "random" here means, among other things, that it should be impossible to guess the next session key from knowing past session keys. a pseudo-random number generator can only get there if it's built on cryptographic principles. a truly random number generator, like one based on measurements of random physical phenomena, would be even better ๐
.
if the requested start time is absent or points to the past, the ticket's start time is set to the authentication server's current time โฐ. if it points to the future but the POSTDATED option wasn't specified, the server returns KDC_ERR_CANNOT_POSTDATE ๐ซ. otherwise the requested start time gets checked against the local realm's policy, because the administrator might ban certain types or ranges of postdated tickets ๐. if it passes, the ticket's start time is set as requested and the INVALID flag is set on the new ticket ๐ฆ. a postdated ticket has to be validated before use by presenting it to the KDC after the start time has been reached.
the expiration time of the ticket gets set to the minimum of these:
- The expiration time (endtime) requested in the
KRB_AS_REQmessage. - The ticket's start time plus the maximum allowable lifetime associated with the client principal (the authentication server's database includes a maximum ticket lifetime field in each principal's record; see section 4).
- The ticket's start time plus the maximum allowable lifetime associated with the server principal.
- The ticket's start time plus the maximum lifetime set by the policy of the local realm.
if the requested expiration time minus the start time (as worked out above) is less than a site-determined minimum lifetime, the server returns KDC_ERR_NEVER_VALID ๐. if the requested expiration time goes past what was worked out above and the RENEWABLE-OK option was requested, then the RENEWABLE flag gets set on the new ticket and the renew-till value is set as if RENEWABLE had been requested (the field and option names are fully described in section 5.4.1) ๐. when RENEWABLE was requested, or RENEWABLE-OK was set and a renewable ticket is being issued, the renew-till field gets set to the minimum of:
- Its requested value.
- The start time of the ticket plus the minimum of the two maximum renewable lifetimes associated with the principals' database entries.
- The start time of the ticket plus the maximum renewable lifetime set by the policy of the local realm.
the flags field of the new ticket will have these options set if they were requested and the local realm's policy allows it: FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE ๐ธ. if the new ticket is postdated (the start time is in the future), its INVALID flag gets set too.
if all of the above succeeds, the server formats a KRB_AS_REP message (see section 5.4.2) ๐ฆ. it copies the addresses from the request into the caddr of the response, puts any required pre-authentication data into the padata of the response, encrypts the ciphertext part with the client's key using the requested encryption method, and sends it to the client ๐. see section A.2 for pseudocode.
3.1.4. Generation of KRB_ERROR message
several errors can happen, and in each case the Authentication Server responds by sending back an error message, KRB_ERROR, with the error-code and e-text fields filled in with the right values ๐. the contents and details of the error message are described in Section 5.9.1.
3.1.5. Receipt of KRB_AS_REP message
if the reply message type is KRB_AS_REP, the client checks that the cname and crealm fields in the cleartext part of the reply match what it asked for ๐. if any padata fields are present, they may be used to derive the right secret key for decrypting the message ๐๏ธ. the client decrypts the encrypted part of the response with its secret key and checks that the nonce inside matches the nonce it sent in its request, which catches replays ๐. it also checks that the sname and srealm in the response match the ones in the request, and that the host address field is correct too ๐ง. then it stores the ticket, session key, start and expiration times, and other info for later use ๐. the key-expiration field from the encrypted part of the response may be checked to warn the user that their key is about to expire (the client program could then suggest remedial action, such as a password change) โณ. see section A.3 for pseudocode.
bestie, this part is SO important: decrypting theKRB_AS_REPproperly is NOT enough to prove who the user is ๐ญ. the user and an attacker could team up to make aKRB_AS_REP-format message that decrypts fine but didn't come from the real KDC. if the host wants to verify the user's identity, it has to make the user present application credentials that can be checked against a securely-stored secret key ๐. only once those credentials check out can the user's identity be assured ๐.
3.1.6. Receipt of KRB_ERROR message
if the reply message type is KRB_ERROR, the client treats it as an error and does whatever application-specific steps are needed to recover ๐ ๏ธ.
3.2. The Client/Server Authentication Exchange ๐
Summary โจ
Message direction Message type Section
Client to Application server KRB_AP_REQ 5.5.1
[optional] Application server to client KRB_AP_REP or 5.5.2
KRB_ERROR 5.9.1
the client/server authentication (CS) exchange is how network applications authenticate the client to the server and the server back to the client ๐ค. the client must already have credentials for the server, which it got through the AS or TGS exchange ๐๏ธ.
3.2.1. The KRB_AP_REQ message
the KRB_AP_REQ carries the authentication info that should be part of the very first message in an authenticated transaction ๐ช. it holds a ticket, an authenticator, and some extra bookkeeping info (see section 5.5.1 for the exact format). a ticket on its own can't authenticate a client, because tickets cross the network in cleartext (tickets have both an encrypted and an unencrypted portion, so "cleartext" here refers to the whole unit, which anyone can copy from one message and replay in another with zero cryptographic skill ๐ณ). that's why the authenticator exists: it stops invalid replay of tickets by proving to the server that the client knows the ticket's session key, and so is entitled to use it ๐๐
. the KRB_AP_REQ message is also called the "authentication header" elsewhere ๐ง.
3.2.2. Generation of a KRB_AP_REQ message
when a client wants to start authenticating to a server, it gets a ticket and session key for the service it wants (from a credentials cache, the AS exchange, or the TGS exchange) ๐ซ. the client can reuse any ticket it holds until that ticket expires โป๏ธ. then it builds a new Authenticator out of the system time, its name, and, optionally, an application-specific checksum, an initial sequence number for use in KRB_SAFE or KRB_PRIV messages, and/or a session subkey for negotiating a session key unique to this particular session ๐. authenticators may not be reused, and they'll be rejected if replayed to a server (note that this can make applications on unreliable transports tricky to code correctly, if the transport might deliver duplicate messages; in those cases, a new authenticator must be generated for each retry ๐). if a sequence number is included, it should be chosen at random, so that even after lots of messages have been exchanged it's unlikely to collide with other sequence numbers in use ๐ฒ.
the client can ask for mutual authentication, or for a session-key-based ticket, by setting the matching flag(s) in the ap-options field of the message ๐ฉ.
the Authenticator gets encrypted with the session key and combined with the ticket to form the KRB_AP_REQ message, which is then sent to the end server along with any extra application-specific info ๐ฌ. see section A.9 for pseudocode.
3.2.3. Receipt of KRB_AP_REQ message โจ
okay bestie, so the server's whole auth decision is based on its current time of day (clocks must be loosely synchronized ๐ฐ๏ธ), the authenticator, and the ticket ๐๏ธ. Lots of things can go wrong, and when one does, the server is expected to reply to the client with a KRB_ERROR message ๐จ. That message can get wrapped inside the application protocol too if its "raw" form isn't acceptable to that protocol. The format of error messages is in section 5.9.1 ๐
.
Here's how the server checks the auth info, step by step ๐:
If the message type isn't KRB_AP_REQ, the server returns the KRB_AP_ERR_MSG_TYPE error ๐
โโ๏ธ. If the key version the Ticket in the KRB_AP_REQ points to isn't one the server can use (like an old key the server no longer has a copy of), it returns KRB_AP_ERR_BADKEYVER ๐.
Now the USE-SESSION-KEY flag in the ap-options field is lowkey a plot twist ๐ญ. When it's set, it tells the server the ticket was encrypted with the session key from the server's ticket-granting ticket instead of the server's secret key. This is used for user-to-user authentication, as described in [6] ๐ค.
Servers can be registered in multiple realms, and each realm can have different keys for them. So the srealm field in the unencrypted part of the ticket tells the server which secret key to use to decrypt it ๐๏ธ. If the server doesn't have the right key to decipher the ticket, it returns KRB_AP_ERR_NOKEY ๐ญ.
The ticket gets decrypted using the version of the server's key the ticket specifies. If the decryption routines spot a modification in the ticket, the server returns KRB_AP_ERR_BAD_INTEGRITY. Each encryption system has to include safeguards that catch modified ciphertext (see section 6 for more on that ๐ก๏ธ). The chances are good that different keys were used to encrypt and decrypt, too ๐.
Next up, the authenticator gets decrypted using the session key pulled out of the decrypted ticket. If it turns out to have been modified, that's another KRB_AP_ERR_BAD_INTEGRITY ๐. The client's name and realm from the ticket get compared against the same fields in the authenticator. If they don't match, the server returns KRB_AP_ERR_BADMATCH. They might not match if the wrong session key was used to encrypt the authenticator, which is very much a "not meโฆ" moment ๐.
Then the addresses in the ticket (if there are any) get searched for one that matches the client's operating-system-reported address ๐. If nothing matches, or the server insists on ticket addresses but the ticket doesn't have any, you get KRB_AP_ERR_BADADDR ๐ซ.
If the server's time and the client's time in the authenticator are off by more than the allowable clock skew (say, 5 minutes โฑ๏ธ), the server returns KRB_AP_ERR_SKEW. Ugh, so messy.
If the server name, plus the client name, plus the time and microsecond fields from the authenticator match any tuple seen recently, the server returns KRB_AP_ERR_REPEAT ๐. The rejection only applies to authenticators from the same principal going to the same server. Other client principals talking to the same server principal should not have their authenticators rejected just because their time and microsecond fields happen to match somebody else's. Main character energy means nobody gets caught in the crossfire ๐.
The server must remember any authenticator presented within the allowable clock skew, so a replay attempt is guaranteed to fail ๐. And if a server loses track of any authenticator presented within that window, it must reject all requests until the clock skew interval has passed. This makes sure that any lost or replayed authenticators fall outside the allowable clock skew and can't be successfully replayed anymore ๐ง . (If this isn't done, an attacker could record the ticket and authenticator sent over the network to a server, then disable the client's host, pose as the disabled host, and replay the ticket and authenticator to subvert the authentication. Yikes ๐ฑ.)
If the authenticator includes a sequence number, the server saves it for later when it processes KRB_SAFE and/or KRB_PRIV messages ๐. If there's a subkey, the server either saves it for later use or uses it to help generate its own choice of subkey to send back in a KRB_AP_REP message ๐๏ธโจ.
The server also figures out the age of the ticket: local (server) time minus the start time inside the Ticket โณ. If the start time is later than the current time by more than the allowable clock skew, or the INVALID flag is set in the ticket, the server returns KRB_AP_ERR_TKT_NYV ๐
โโ๏ธ. Otherwise, if the current time is later than the end time by more than the allowable clock skew, it returns KRB_AP_ERR_TKT_EXPIRED ๐ฅ.
If all of these checks pass, the server is assured that the client has the credentials of the principal named in the ticket. That means the client has been authenticated to the server ๐๐. See section A.10 for pseudocode ๐ง.
3.2.4. Generation of a KRB_AP_REP message ๐ฆ
Usually, a client's request includes both the auth info and its initial request in the same message, so the server doesn't need to explicitly reply to the KRB_AP_REQ ๐. But if mutual authentication is happening (that's where the server authenticates to the client too, not just the client to the server ๐ค), the KRB_AP_REQ message will have MUTUAL-REQUIRED set in its ap-options field, and a KRB_AP_REP message is required in response ๐. Like the error message, this one can get encapsulated in the application protocol if its "raw" form isn't acceptable to that protocol.
The timestamp and microsecond field in the reply must be the client's timestamp and microsecond field (as provided in the authenticator) ๐ฐ๏ธ. Here's a fun little note ๐ค:
[Note: In the Kerberos version 4 protocol, the timestamp in the reply was the client's timestamp plus one. This is not necessary in version 5 because version 5 messages are formatted in such a way that it is not possible to create the reply by judicious message surgery (even in encrypted form) without knowledge of the appropriate encryption keys.]
If a sequence number is included, it should be randomly chosen, as described above for the authenticator ๐ฒ. A subkey may be included if the server wants to negotiate a different subkey ๐๏ธ. The KRB_AP_REP message is encrypted in the session key extracted from the ticket ๐. See section A.11 for pseudocode ๐ง.
3.2.5. Receipt of KRB_AP_REP message ๐
If a KRB_AP_REP message comes back, the client uses the session key from the credentials it got for the server to decrypt it. (Quick tea โ: for encrypting the KRB_AP_REP message, the sub-session key isn't used, even if it's present in the Authenticator ๐
โโ๏ธ.) Then the client checks that the timestamp and microsecond fields match the ones in the Authenticator it sent to the server โฐ. If they match, the client is assured the server is genuine ๐ซถ. The sequence number and subkey (if present) are kept for later use ๐. See section A.12 for pseudocode ๐ง.
3.2.6. Using the encryption key ๐
After the KRB_AP_REQ/KRB_AP_REP exchange has happened, the client and server share an encryption key that the application can use ๐ฅณ. The "true session key" for KRB_PRIV, KRB_SAFE, or other application-specific uses can be picked by the application, based on the subkeys in the KRB_AP_REP message and the authenticator. (Implementations may want to provide routines that choose subkeys based on session keys and random numbers, and that orchestrate a negotiated key to be returned in the KRB_AP_REP message ๐งฉ.)
Sometimes using this session key is implicit in the protocol. Other times, the method of use has to be chosen from several alternatives ๐ค. The protocol negotiations for how to use the key (like picking an encryption or checksum type) are left to the application programmer. The Kerberos protocol doesn't constrain the implementation options, so you get total creative freedom ๐ โจ.
With both the one-way and mutual authentication exchanges, the peers should take care not to send each other sensitive information without proper assurances ๐ซ๐จ. Applications that need privacy or integrity should use the KRB_AP_REP or KRB_ERROR responses from the server to the client to assure both client and server of their peer's identity ๐ชช. If an application protocol needs privacy for its messages, it can use the KRB_PRIV message (section 3.5) ๐. The KRB_SAFE message (section 3.4) can be used to assure integrity ๐ก๏ธ.
3.3. The Ticket-Granting Service (TGS) Exchange ๐
Summary
Message direction Message type Section
1. Client to Kerberos KRB_TGS_REQ 5.4.1
2. Kerberos to client KRB_TGS_REP or 5.4.2
KRB_ERROR 5.9.1
The TGS exchange is when a client talks to the Kerberos Ticket-Granting Server ๐๏ธ. A client starts it when it wants auth credentials for a given server (which might be registered in a remote realm), when it wants to renew or validate an existing ticket, or when it wants to get a proxy ticket ๐ง. In the first case, the client must already have a ticket for the Ticket-Granting Service from the AS exchange. (Usually the ticket-granting ticket shows up when a client first authenticates to the system, like when a user logs in ๐.)
The TGS message format is almost identical to the AS exchange's. The big difference is that encryption and decryption in the TGS exchange don't happen under the client's key. Instead, the session key from the ticket-granting ticket or renewable ticket is used, or the sub-session key from an Authenticator ๐๏ธ. Like all application servers, the TGS won't accept expired tickets, so once a renewable or ticket-granting ticket expires, the client has to use a separate exchange to get valid tickets ๐โก๏ธ๐ซ.
The TGS exchange has two messages: a request (KRB_TGS_REQ) from the client to the Kerberos Ticket-Granting Server, and a reply (KRB_TGS_REP or KRB_ERROR) ๐. The KRB_TGS_REQ message includes info that authenticates the client, plus a request for credentials ๐โโ๏ธ. The auth info is the authentication header (KRB_AP_REQ), which includes the client's previously obtained ticket-granting, renewable, or invalid ticket ๐ซ. For the ticket-granting ticket and proxy cases, the request may include one or more of these: a list of network addresses, a collection of typed authorization data to be sealed in the ticket for authorization use by the application server, or additional tickets (we'll get to how those work later ๐).
The TGS reply (KRB_TGS_REP) carries the requested credentials, encrypted in the session key from the ticket-granting ticket or renewable ticket. Or, if one is present, it's encrypted in the subsession key from the Authenticator (which is part of the authentication header) ๐. The KRB_ERROR message has an error code and text explaining what went wrong ๐จ. It isn't encrypted ๐ฌ. The KRB_TGS_REP message has info that can be used to detect replays and to match it up with the message it replies to ๐. The KRB_ERROR message also has info for matching it to the message it replies to. But since it isn't encrypted, it can't be used to detect replays or fabrications of such messages ๐โ ๏ธ.
3.3.1. Generation of KRB_TGS_REQ message โจ
Okay bestie, before the client can even ask the ticket-granting service for anything, it has to figure out which realm the application server is registered in ๐
[Note: This can be accomplished in several ways. It might be known beforehand (since the realm is part of the principal identifier), or it might be stored in a nameserver. Presently, however, this information is obtained from a configuration file. If the realm to be used is obtained from a nameserver, there is a danger of being spoofed if the nameservice providing the realm name is not authenticated. This might result in the use of a realm which has been compromised, and would result in an attacker's ability to compromise the authentication of the application server to the client.] ๐
If the client doesn't already have a ticket-granting ticket for that realm, it must get one ๐๏ธ The first move is to ask the local Kerberos server for a TGT for the destination realm, and it does this by sending a KRB_TGS_REQ message recursively. The server may hand back a TGT for the desired realm, and then we're good to go ๐ Or it may give back a TGT for a realm that's "closer" to the one we want (further along the standard hierarchical path). In that case this step must be repeated, this time with a Kerberos server in the realm named in the TGT we just got ๐ช If neither of those happens, the request must be retried with a Kerberos server for a realm higher up in the hierarchy. And that request will need its own TGT for the higher realm, which you get by recursively applying these same directions. It's giving russian doll energy, honestly ๐ช
Once the client has a TGT for the right realm, it works out which Kerberos servers serve that realm and contacts one ๐ The list might come from a configuration file or a network service. As long as the secret keys exchanged by realms stay secret, a false Kerberos server can only cause denial of service. Nothing scarier than that, which is a relief ๐
Just like in the AS exchange, the client may specify a bunch of options in the KRB_TGS_REQ message. It prepares the message with an authentication header tucked into the padata field, and it includes the same fields that KRB_AS_REQ uses, plus a few optional extras: the enc-authorization-data field for application server use, and additional tickets required by some options ๐
Now the sub-session key part, which is lowkey the most important bit ๐ When preparing the authentication header, the client can pick a sub-session key that the Kerberos server's response will be encrypted under. If the client does pick one, care must be taken to make sure the sub-session key is actually random. One approach would be to generate a random number and XOR it with the session key from the ticket-granting ticket. (Don't skip the randomness, bestie, it's literally the whole point ๐
โโ๏ธ) If the sub-session key isn't specified, the session key from the ticket-granting ticket gets used instead. And if enc-authorization-data is present, it must be encrypted in the sub-session key if one is present in the authenticator portion of the authentication header. If there isn't one, it gets encrypted in the session key from the ticket-granting ticket instead ๐
Once it's all prepared, the message gets sent to a Kerberos server for the destination realm ๐ฌ See section A.5 for pseudocode ๐
3.3.2. Receipt of KRB_TGS_REQ message โจ
The KRB_TGS_REQ message gets processed in a way that's similar to KRB_AS_REQ, but ngl there are way more checks to do ๐ญ First up, the Kerberos server has to figure out which server the accompanying ticket is for, and then it picks the right key to decrypt it with. For a normal KRB_TGS_REQ message, the ticket is for the ticket granting service, so the TGS's key gets used. If the TGT was issued by another realm, then the appropriate inter-realm key must be used ๐๏ธ If the accompanying ticket isn't a ticket granting ticket for the current realm, but it IS for an application server in the current realm, and the request has the RENEW, VALIDATE, or PROXY options set, and the server being asked for is the one named in the accompanying ticket, then the KDC decrypts the ticket in the authentication header using the key of the server it was issued for. If no ticket can be found in the padata field at all, the KDC_ERR_PADATA_TYPE_NOSUPP error is returned ๐ซ
Once the accompanying ticket has been decrypted, the user-supplied checksum in the Authenticator must be verified against the contents of the request. If the checksums don't match, the message gets rejected with the error code KRB_AP_ERR_MODIFIED ๐
โโ๏ธ The message also gets rejected if the checksum isn't keyed or isn't collision-proof, and that one comes back as KRB_AP_ERR_INAPP_CKSUM. If the checksum type isn't supported, the KDC_ERR_SUMTYPE_NOSUPP error is returned. And if the authorization-data are present, they get decrypted using the sub-session key from the Authenticator ๐
If any of the decryptions show failed integrity checks, the KRB_AP_ERR_BAD_INTEGRITY error is returned ๐
3.3.3. Generation of KRB_TGS_REP message โจ
The KRB_TGS_REP message shares its format with KRB_AS_REP (KRB_KDC_REP), the only difference being that its type field is set to KRB_TGS_REP ๐ฆ The detailed spec lives in section 5.4.2 ๐
The response will include a ticket for the requested server. The Kerberos database gets queried to pull up the record for that server, including the key the ticket will be encrypted with ๐ If the request is for a ticket granting ticket for a remote realm, and no key is shared with that requested realm, then the Kerberos server picks the realm "closest" to the requested one that it DOES share a key with, and uses that realm instead. This is the only case where the response from the KDC will be for a different server than the one the client asked for ๐
By default, the address field, the client's name and realm, the list of transited realms, the time of initial authentication, the expiration time, and the authorization data of the newly-issued ticket all get copied over from the ticket-granting ticket (TGT) or renewable ticket ๐ If the transited field needs to be updated but the transited type isn't supported, the KDC_ERR_TRTYPE_NOSUPP error is returned ๐
If the request specifies an endtime, then the endtime of the new ticket gets set to the minimum of three things: (a) that request, (b) the endtime from the TGT, and (c) the starttime of the TGT plus the minimum of the maximum life for the application server and the maximum life for the local realm. (The maximum life for the requesting principal was already applied when the TGT got issued, so we don't redo that one ๐) If the new ticket is going to be a renewal, then the endtime above gets replaced by the minimum of (a) the value of the renew_till field of the ticket and (b) the starttime for the new ticket plus the life (endtimestarttime) of the old ticket โณ
If the FORWARDED option has been requested, the resulting ticket will contain the addresses specified by the client ๐งณ But this option only gets honored if the FORWARDABLE flag is set in the TGT. The PROXY option is kind of similar, since the resulting ticket will also contain the addresses specified by the client. It only gets honored if the PROXIABLE flag in the TGT is set. And the PROXY option will NOT be honored on requests for additional ticket-granting tickets ๐ท
If the requested start time is absent or points to a time in the past, then the ticket's start time is set to the authentication server's current time โฐ If it points to a time in the future, but the POSTDATED option hasn't been specified or the MAY-POSTDATE flag isn't set in the TGT, then the error KDC_ERR_CANNOT_POSTDATE is returned ๐ค Otherwise, if the ticket-granting ticket has the MAYPOSTDATE flag set, the resulting ticket gets postdated, and the requested starttime gets checked against the policy of the local realm. If that's acceptable, the ticket's start time is set as requested and the INVALID flag is set ๐ง The postdated ticket must be validated before use, which means presenting it to the KDC after the starttime has been reached. But no matter what, the starttime, endtime, or renew_till time of a newly-issued postdated ticket can never extend beyond the renew_till time of the ticket-granting ticket ๐
If the ENC-TKT-IN-SKEY option has been specified and an additional ticket was included in the request, the KDC decrypts that additional ticket using the key for the server it was issued to, and it verifies that the ticket really is a ticket-granting ticket ๐ If the name of the requested server is missing from the request, the name of the client in the additional ticket gets used. Otherwise, the requested server's name gets compared to the client's name in the additional ticket, and if they're different, the request gets rejected ๐ซ If the request succeeds, the session key from the additional ticket is used to encrypt the new ticket that gets issued, instead of the key of the server the new ticket is for. This makes user-to-user authentication [6] easy to implement, since it uses ticket-granting ticket session keys in place of secret server keys in situations where those secret keys could be easily compromised ๐ซถ
If the name of the server in the ticket presented to the KDC as part of the authentication header is NOT the ticket-granting server itself, and that server is registered in the realm of the KDC, then the following checks apply. If the RENEW option is requested, the KDC verifies that the RENEWABLE flag is set in the ticket and that the renew_till time is still in the future. If the VALIDATE option is requested, the KDC checks that the starttime has passed and that the INVALID flag is set. If the PROXY option is requested, the KDC checks that the PROXIABLE flag is set in the ticket. If the tests succeed, the KDC issues the appropriate new ticket ๐
Whenever a request is made to the ticket-granting server, the presented ticket(s) gets checked against a hot-list of tickets that have been canceled ๐จ This hot-list might be implemented by storing a range of issue dates for "suspect tickets." If a presented ticket had an authtime in that range, it gets rejected. That's how a stolen ticket-granting ticket or renewable ticket is kept from being used to get extra tickets (renewals or otherwise) once the theft has been reported ๐ต๏ธโโ๏ธ Any normal ticket that was obtained before it got reported stolen will still be valid, since those don't need any interaction with the KDC. They just last until their normal expiration time โณ
The ciphertext part of the response in the KRB_TGS_REP message is encrypted in the sub-session key from the Authenticator if one is present. If not, it's encrypted in the session key from the ticket-granting ticket. It is NOT encrypted using the client's secret key ๐ Also, the client's key's expiration date and the key version number fields get left out, since those values are stored with the client's database record, and that record isn't needed to satisfy a request based on a ticket-granting ticket ๐งพ See section A.6 for pseudocode ๐
3.3.3.1. Encoding the transited field ๐ง
okay so bestie, when the TGT the client shows the KDC is for the ticket-granting service but was issued from a different realm ๐, the KDC looks up the inter-realm key it shares with that realm and uses it to decrypt the ticket. If the ticket is valid, the KDC honors the request, subject to the constraints outlined above in the section describing the AS exchange. The realm part of the client's identity gets taken from the ticket-granting ticket. The name of the realm that issued the TGT gets added to the transited field of the ticket being issued. This is done by reading the transited field from the TGT (which is treated as an unordered set of realm names ๐ซถ), adding the new realm to the set, and then building and writing out its encoded (shorthand) form. That might mean rearranging the existing encoding โจ.
Note that the ticket-granting service does not add the name of its own realm. Its job is to add the name of the previous realm. This stops a malicious Kerberos server from intentionally leaving out its own name (it could, however, omit other realms' names) ๐.
The names of neither the local realm nor the principal's realm go in the transited field. They show up elsewhere in the ticket, and both are known to have taken part in authenticating the principal. Since the endpoints aren't included, both local and single-hop inter-realm authentication end up with an empty transited field ๐ธ.
Because the name of each realm transited gets added, this field could get really long ๐ญ, so its contents are encoded. The initially supported encoding is optimized for the normal case of inter-realm communication: a hierarchical arrangement of realms using either domain or X.500 style realm names. This encoding, called DOMAIN-X500-COMPRESS, is described now.
Realm names in the transited field are separated by ",". The ",", "\", trailing "."s, and leading spaces " " are special characters. If they're part of a realm name, they must be quoted in the transited field by preceding them with a "\".
A realm name ending with a "." is interpreted as being prepended to the previous realm. For example, the traversal of EDU, MIT.EDU, ATHENA.MIT.EDU, WASHINGTON.EDU, and CS.WASHINGTON.EDU can be encoded as:
"EDU,MIT.,ATHENA.,WASHINGTON.EDU,CS.".
Note that if ATHENA.MIT.EDU or CS.WASHINGTON.EDU were endpoints, they would not be included in this field, and we would have:
"EDU,MIT.,WASHINGTON.EDU"
A realm name beginning with a "/" is interpreted as being appended to the previous realm. (For the purpose of appending, the realm preceding the first listed realm is considered to be the null realm "".) If it's going to stand by itself, it should be preceded by a space " ". For example, the traversal of /COM/HP/APOLLO, /COM/HP, /COM, and /COM/DEC can be encoded as:
"/COM,/HP,/APOLLO, /COM/DEC"
Like the example above, if /COM/HP/APOLLO and /COM/DEC are endpoints, they would not be included in this field, and we would have:
"/COM,/HP"
A null subfield preceding or following a "," indicates that all realms between the previous realm and the next realm have been traversed. (For the purpose of interpreting null subfields, the client's realm is considered to precede those in the transited field, and the server's realm is considered to follow them.) So "," means that all realms along the path between the client and the server have been traversed. ",EDU, /COM," means that all realms from the client's realm up to EDU (in a domain style hierarchy) have been traversed, and that everything from /COM down to the server's realm in an X.500 style has also been traversed. This could happen if the EDU realm in one hierarchy shares an inter-realm key directly with the /COM realm in another hierarchy ๐ฆ.
3.3.4. Receipt of KRB_TGS_REP message
When the client receives a KRB_TGS_REP, it's processed the same way as the KRB_AS_REP processing described above. The main difference is that the ciphertext part of the response must be decrypted using the session key from the ticket-granting ticket, not the client's secret key. See section A.7 for pseudocode ๐ .
3.4. The KRB_SAFE Exchange
The KRB_SAFE message may be used by clients who need to detect modifications to the messages they exchange. It does this by including a keyed collisionproof checksum of the user data and some control information. The checksum is keyed with an encryption key (usually the last key negotiated via subkeys, or the session key if no negotiation has occurred) ๐.
3.4.1. Generation of a KRB_SAFE message
When an application wants to send a KRB_SAFE message, it collects its data and the appropriate control information, then computes a checksum over them. The checksum algorithm should be some sort of keyed one-way hash function (such as the RSA-MD5-DES checksum algorithm specified in section 6.4.5, or the DES MAC), generated using the sub-session key if present, or the session key. Different algorithms may be selected by changing the checksum type in the message. Unkeyed or non-collision-proof checksums are not suitable for this use ๐ซ.
The control information for the KRB_SAFE message includes both a timestamp and a sequence number. The designer of an application using the KRB_SAFE message must choose at least one of the two mechanisms. This choice should be based on the needs of the application protocol โจ.
Sequence numbers are useful when all messages sent will be received by one's peer. Connection state is presently required to maintain the session key, so maintaining the next sequence number shouldn't be an extra problem ๐.
If the application protocol is expected to tolerate lost messages without them being resent, the timestamp is the appropriate replay detection mechanism. Timestamps are also the appropriate mechanism for multi-cast protocols where all of one's peers share a common sub-session key, but some messages will be sent to a subset of one's peers ๐ง.
After computing the checksum, the client transmits the information and checksum to the recipient in the message format specified in section 5.6.1 ๐ฆ.
3.4.2. Receipt of KRB_SAFE message
When an application receives a KRB_SAFE message, it verifies it as follows. If any error occurs, an error code is reported for use by the application ๐ต๏ธโโ๏ธ.
The message is first checked by verifying that the protocol version and type fields match the current version and KRB_SAFE, respectively. A mismatch generates a KRB_AP_ERR_BADVERSION or KRB_AP_ERR_MSG_TYPE error. The application verifies that the checksum used is a collisionproof keyed checksum. If it isn't, a KRB_AP_ERR_INAPP_CKSUM error is generated. The recipient verifies that the operating system's report of the sender's address matches the sender's address in the message, and (if a recipient address is specified or the recipient requires an address) that one of the recipient's addresses appears as the recipient's address in the message. A failed match in either case generates a KRB_AP_ERR_BADADDR error. Then the timestamp and usec and/or the sequence number fields are checked. If timestamp and usec are expected and not present, or they are present but not current, the KRB_AP_ERR_SKEW error is generated. If the server name, along with the client name, time, and microsecond fields from the Authenticator match any recently-seen such tuples, the KRB_AP_ERR_REPEAT error is generated. If an incorrect sequence number is included, or a sequence number is expected but not present, the KRB_AP_ERR_BADORDER error is generated. If neither a timestamp and usec nor a sequence number is present, a KRB_AP_ERR_MODIFIED error is generated ๐ค.
Finally, the checksum is computed over the data and control information. If it doesn't match the received checksum, a KRB_AP_ERR_MODIFIED error is generated.
If all the checks succeed, the application is assured that the message was generated by its peer and was not modified in transit ๐.
3.5. The KRB_PRIV Exchange
The KRB_PRIV message may be used by clients who need confidentiality and the ability to detect modifications to exchanged messages. It does this by encrypting the messages and adding control information ๐.
3.5.1. Generation of a KRB_PRIV message
When an application wants to send a KRB_PRIV message, it collects its data and the appropriate control information (specified in section 5.7.1), then encrypts them under an encryption key (usually the last key negotiated via subkeys, or the session key if no negotiation has occurred). As part of the control information, the client must choose to use either a timestamp or a sequence number (or both). See the discussion in section 3.4.1 for guidelines on which to use ๐. After the user data and control information are encrypted, the client transmits the ciphertext and some "envelope" information to the recipient ๐.
3.5.2. Receipt of KRB_PRIV message
When an application receives a KRB_PRIV message, it verifies it as follows. If any error occurs, an error code is reported for use by the application ๐ซ .
The message is first checked by verifying that the protocol version and type fields match the current version and KRB_PRIV, respectively. A mismatch generates a KRB_AP_ERR_BADVERSION or KRB_AP_ERR_MSG_TYPE error. The application then decrypts the ciphertext and processes the resultant plaintext. If decryption shows the data to have been modified, a KRB_AP_ERR_BAD_INTEGRITY error is generated. The recipient verifies that the operating system's report of the sender's address matches the sender's address in the message, and (if a recipient address is specified or the recipient requires an address) that one of the recipient's addresses appears as the recipient's address in the message. A failed match in either case generates a KRB_AP_ERR_BADADDR error. Then the timestamp and usec and/or the sequence number fields are checked. If timestamp and usec are expected and not present, or they are present but not current, the KRB_AP_ERR_SKEW error is generated. If the server name, along with the client name, time, and microsecond fields from the Authenticator match any recently-seen such tuples, the KRB_AP_ERR_REPEAT error is generated. If an incorrect sequence number is included, or a sequence number is expected but not present, the KRB_AP_ERR_BADORDER error is generated. If neither a timestamp and usec nor a sequence number is present, a KRB_AP_ERR_MODIFIED error is generated ๐จ.
If all the checks succeed, the application can assume the message was generated by its peer and was transmitted securely, with no intruders able to see the unencrypted contents ๐.
3.6. The KRB_CRED Exchange
The KRB_CRED message may be used by clients who need to send Kerberos credentials from one host to another. It does this by sending the tickets together with encrypted data containing the session keys and other information associated with the tickets ๐.
3.6.1. Generation of a KRB_CRED message
okay so when an app wants to send a KRB_CRED message ๐, it first gets credentials for the remote host using the KRB_TGS exchange โจ. Then it builds the KRB_CRED message with the ticket or tickets it got back. The session key needed to use each ticket goes in the key field of the matching KrbCredInfo sequence, inside the encrypted part of the message ๐.
Other info about each ticket, from the KRB_TGS exchange, also goes in its matching KrbCredInfo sequence in that encrypted part. Then the current time ๐ฐ๏ธ goes in too, plus the nonce, s-address, and raddress fields, but only if the app specifically needs them. The whole encrypted part gets encrypted under a key that was already agreed on in the KRB_AP exchange ๐. That's usually the last key negotiated via subkeys, or the session key if no negotiation has occurred.
3.6.2. Receipt of KRB_CRED message
When an app gets a KRB_CRED message, it checks it ๐ง. If anything goes wrong, an error code gets reported so the app can deal with it. First, it checks that the protocol version and type fields match the current version and KRB_CRED, respectively. If they don't match, you get a KRB_AP_ERR_BADVERSION or KRB_AP_ERR_MSG_TYPE error ๐. Then the app decrypts the ciphertext and processes the plaintext. If decryption shows the data was modified, you get a KRB_AP_ERR_BAD_INTEGRITY error ๐.
Next up, if present or required, the recipient checks two things ๐. First, that the operating system's report of the sender's address matches the sender's address in the message. Second, that one of the recipient's addresses shows up as the recipient's address in the message. A failed match for either one gives a KRB_AP_ERR_BADADDR error. Then the timestamp and usec fields get checked, plus the nonce field if it's required. If the timestamp and usec are missing, or they're there but not current, you get a KRB_AP_ERR_SKEW error โฐ.
If all the checks pass โ
, the app stores each new ticket in its ticket cache. It stores the session key and the other info from the matching KrbCredInfo sequence in the encrypted part right alongside it ๐.
4. The Kerberos Database ๐๏ธ
The Kerberos server MUST have access to a database holding the principal identifiers and secret keys of the principals to be authenticated. (The Kerberos server doesn't have to live on the same machine as its database ๐ฅ๏ธ. It's doable to store the principal database in something like a network name service, as long as the entries are protected from being disclosed to or modified by unauthorized parties. But we recommend against that strategy, since it can make system management and threat analysis super complicated ๐ตโ๐ซ.)
4.1. Database contents
A database entry SHOULD contain at least the following fields ๐:
Field Value
name Principal's identifier
key Principal's secret key
p_kvno Principal's key version
max_life Maximum lifetime for Tickets
max_renewable_life Maximum total lifetime for renewable
Tickets
The name field is an encoding of the principal's identifier ๐ชช. The key field holds an encryption key, which is the principal's secret key ๐๏ธ. (The key can be encrypted before storage under a Kerberos "master key" to protect it in case the database is compromised but the master key is not. In that case, an extra field MUST be added to indicate the master key version used, see below.) The p_kvno field is the key version number of the principal's secret key. The max_life field holds the maximum allowable lifetime (endtime - starttime) for any Ticket issued for this principal. The max_renewable_life field holds the maximum allowable total lifetime for any renewable Ticket issued for this principal. (See section 3.1 for a description of how these lifetimes are used in determining the lifetime of a given Ticket.)
A server MAY provide KDC service to several realms, as long as the database representation gives a way to tell apart principal records whose identifiers differ only in the realm name ๐.
When an application server's key changes, if the change is routine (i.e., not the result of disclosure of the old key), the old key SHOULD be kept by the server until all tickets issued with that key have expired ๐ฐ๏ธ. Because of this, several keys can be active for one principal at the same time. Ciphertext encrypted in a principal's key is always tagged with the version of the key that was used, so the recipient can find the right key for decryption ๐ท๏ธ.
When more than one key is active for a particular principal, that principal has more than one record in the Kerberos database ๐. The keys and key version numbers differ between the records, while the rest of the fields MAY or may not be the same. Whenever Kerberos issues a ticket, or responds to a request for initial authentication, the most recent key (the one the Kerberos server knows about) is used for encryption โจ. That's the key with the highest key version number ๐.
4.2. Additional fields
Project Athena's KDC implementation uses additional fields in its database ๐:
Field Value
K_kvno Kerberos' key version
expiration Expiration date for entry
attributes Bit field of attributes
mod_date Timestamp of last modification
mod_name Modifying principal's identifier
The K_kvno field indicates the key version of the Kerberos master key that the principal's secret key is encrypted under ๐.
After an entry's expiration date has passed, the KDC returns an error to any client trying to get tickets as or for the principal ๐ซ. (A database MAY want two expiration dates: one for the principal, and one for the principal's current key. That lets password aging work independently of the principal's expiration date. However, since responses have limited space, the KDC MUST combine the key expiration and principal expiration date into a single value called key_exp, which is used as a hint to the user to take administrative action.)
The attributes field is a bitfield used to govern operations involving the principal ๐งฉ. This field MAY be useful alongside user registration procedures for site-specific policy implementations. (Project Athena currently uses it for their user registration process, which is controlled by the system-wide database service, Moira [7].) It can also identify the "string to key" conversion algorithm used for a principal's key. (See the discussion of the padata field in section 5.4.2 for details on why this can be useful.) Other bits indicate that certain ticket options SHOULD NOT be allowed in tickets encrypted under a principal's key, with one bit each ๐ง:
- Disallow issuing postdated tickets
- Disallow issuing forwardable tickets
- Disallow issuing tickets based on TGT authentication
- Disallow issuing renewable tickets
- Disallow issuing proxiable tickets
- Disallow issuing tickets for which the principal is the server
The mod_date field contains the time of last modification of the entry โฑ๏ธ, and the mod_name field contains the name of the principal which last modified the entry ๐โโ๏ธ.
4.3. Frequently Changing Fields
Some KDC implementations MAY wish to keep track of the last time a particular principal made a request ๐. The information they keep can include the time of the last request, the time of the last request for a ticket-granting ticket, the time of the last use of a ticket-granting ticket, or other times ๐๏ธ. This information can then be returned to the user in the last-req field (see section 5.2).
Other frequently changing information that can be kept is the latest expiration time for any tickets issued using each key โณ. This field would be used to show how long old keys MUST remain valid so outstanding tickets can keep working ๐๏ธ.
4.4. Site Constants
The KDC implementation SHOULD have the following configurable constants or options, so an administrator can make and enforce policy decisions ๐ ๏ธ:
- The minimum supported lifetime. This is used to determine whether the
KDC_ERR_NEVER_VALIDerror SHOULD be returned. This constant SHOULD reflect reasonable expectations of round-trip time to the KDC, encryption/decryption time, and processing time by the client and target server. It SHOULD also allow for a minimum "useful" lifetime โฒ๏ธ. - The maximum allowable total (renewable) lifetime of a ticket (
renew_till - starttime). - The maximum allowable lifetime of a ticket (
endtime - starttime). - Whether to allow the issue of tickets with empty address fields. This includes the ability to specify that such tickets MAY only be issued if the request specifies some
authorization_data๐ . - Whether proxiable, forwardable, renewable or post-datable tickets are to be issued ๐ซ.
5. Message Specifications
These sections are where the exact contents and encoding of the protocol messages and objects live ๐, so details matter here.
The following sections describe the exact contents and encoding of protocol messages and objects ๐. The ASN.1 base definitions come first, in the opening subsection. The remaining subsections specify the protocol objects (tickets and authenticators) and messages ๐งพ. Specification of encryption and checksum techniques, and the fields related to them, appear in section 6 ๐.
5.1. ASN.1 Distinguished Encoding Representation
All uses of ASN.1 in Kerberos SHALL use the Distinguished Encoding Representation of the data elements, as described in the X.509 specification, section 8.7 [8] ๐ธ.
5.2. ASN.1 Base Definitions
okay bestie, here come the ASN.1 base definitions ๐ง we use these all over the rest of this section. quick heads up: ASN.1 names can't have underscores (_), so the hyphen (-) fills in for them ๐ซถ
Realm ::= GeneralString
PrincipalName ::= SEQUENCE {
name-type[0] INTEGER,
name-string[1] SEQUENCE OF GeneralString
}
Realms are encoded as GeneralStrings ๐ A realm shall not contain a character with the code 0 (the ASCII NUL). Most realms have a few components separated by periods (.), kinda like Internet Domain Names, or by slashes (/), like X.500 names ๐ The acceptable forms for realm names are in section 7. A PrincipalName is a typed sequence of components with these sub-fields:
name-typetells you what kind of name comes next ๐ฆ The predefined values are in section 7.2. It should be treated as a hint. Ignoring it, no two names can be the same (at least one of the components, or the realm, must be different). This constraint may be eliminated in the future.name-stringis a sequence of components that form a name, and each component is encoded as a GeneralString ๐ธ A PrincipalName and a Realm together form a principal identifier. Most PrincipalNames only have a few components, usually one or two.
KerberosTime ::= GeneralizedTime
-- Specifying UTC time zone (Z)
Timestamps in Kerberos are encoded as GeneralizedTimes โฐ An encoding shall specify the UTC time zone (Z), shall not include any fractional portions of the seconds, and further shall not include any separators. Example: the only valid format for UTC time 6 minutes, 27 seconds after 9 pm on 6 November 1985 is 19851106210627Z ๐
HostAddress ::= SEQUENCE {
addr-type[0] INTEGER,
address[1] OCTET STRING
}
HostAddresses ::= SEQUENCE OF SEQUENCE {
addr-type[0] INTEGER,
address[1] OCTET STRING
}
Host address encodings have two fields ๐
addr-typesays what type of address follows. The predefined values are in section 8.1.addressencodes a single address of typeaddr-type.
The two forms are a little different. HostAddress contains exactly one address, while HostAddresses contains a sequence of possibly many addresses ๐
AuthorizationData ::= SEQUENCE OF SEQUENCE {
ad-type[0] INTEGER,
ad-data[1] OCTET STRING
}
ad-datacontains authorization data, to be interpreted according to the value of the matchingad-typefield.ad-typespecifies the format of thead-datasubfield. All negative values are reserved for local use. Non-negative values are reserved for registered use ๐ฅ
APOptions ::= BIT STRING {
reserved(0),
use-session-key(1),
mutual-required(2)
}
TicketFlags ::= BIT STRING {
reserved(0),
forwardable(1),
forwarded(2),
proxiable(3),
proxy(4),
may-postdate(5),
postdated(6),
invalid(7),
renewable(8),
initial(9),
pre-authent(10),
hw-authent(11)
}
KDCOptions ::= BIT STRING {
reserved(0),
forwardable(1),
forwarded(2),
proxiable(3),
proxy(4),
allow-postdate(5),
postdated(6),
unused7(7),
renewable(8),
unused9(9),
unused10(10),
unused11(11),
renewable-ok(27),
enc-tkt-in-skey(28),
renew(30),
validate(31)
}
these three bit strings are giving main character energy ๐ they're just lists of named bit positions, so the flags you'll see later all come from here โจ
LastReq ::= SEQUENCE OF SEQUENCE {
lr-type[0] INTEGER,
lr-value[1] KerberosTime
}
lr-typetells you how to read thelr-valuefield ๐ฆ Negative values mean the info only applies to the responding server. Non-negative values apply to all servers in the realm.
If lr-type is zero (0), then lr-value conveys no information. If the absolute value of lr-type is one (1), then lr-value is the time of last initial request for a TGT. If it's two (2), lr-value is the time of last initial request. If it's three (3), lr-value is the time of issue for the newest ticket-granting ticket used. If it's four (4), lr-value is the time of the last renewal. If it's five (5), lr-value is the time of last request of any type ๐ญ
lr-valueis the time of the last request. You have to read it according to the matchinglr-type๐
See section 6 for the definitions of Checksum, ChecksumType, EncryptedData, EncryptionKey, EncryptionType, and KeyType ๐
5.3. Tickets and Authenticators
this section is about the format and encryption parameters for tickets and authenticators ๐ฆ When a ticket or authenticator shows up in a protocol message, it's treated as an opaque object, so nobody pokes inside it ๐
5.3.1. Tickets
a ticket is a record that helps a client authenticate to a service ๐ซถ A Ticket has the following information:
Ticket ::= [APPLICATION 1] SEQUENCE {
tkt-vno[0] INTEGER,
realm[1] Realm,
sname[2] PrincipalName,
enc-part[3] EncryptedData
}
-- Encrypted part of ticket
EncTicketPart ::= [APPLICATION 3] SEQUENCE {
flags[0] TicketFlags,
key[1] EncryptionKey,
crealm[2] Realm,
cname[3] PrincipalName,
transited[4] TransitedEncoding,
authtime[5] KerberosTime,
starttime[6] KerberosTime OPTIONAL,
endtime[7] KerberosTime,
renew-till[8] KerberosTime OPTIONAL,
caddr[9] HostAddresses OPTIONAL,
authorization-data[10] AuthorizationData OPTIONAL
}
-- encoded Transited field
TransitedEncoding ::= SEQUENCE {
tr-type[0] INTEGER, -- must be registered
contents[1] OCTET STRING
}
the EncTicketPart encoding is encrypted with the key shared by Kerberos and the end server (the server's secret key) ๐ Check section 6 for the format of the ciphertext ๐
tkt-vnois the version number for the ticket format. This document describes version number 5 โจrealmis the realm that issued the ticket ๐ธ It also identifies the realm part of the server's principal identifier. A Kerberos server can only issue tickets for servers in its own realm, so the two will always be identical ๐snameis the name part of the server's identity ๐งenc-partholds the encrypted encoding of theEncTicketPartsequence ๐ฅflagsshows which options were used or requested when the ticket was issued ๐ It's a bit-field: a selected option has its bit set (1), and unselected options and reserved fields are reset (0). Bit 0 is the most significant bit. The encoding of the bits is in section 5.2. The flags are described in more detail above in section 2. Here's what they mean ๐
Bit(s) Name Description
0 RESERVED Reserved for future expansion of this
field.
1 FORWARDABLE The FORWARDABLE flag is normally only
interpreted by the TGS, and can be
ignored by end servers. When set,
this flag tells the ticket-granting
server that it is OK to issue a new
ticket- granting ticket with a
different network address based on
the presented ticket.
2 FORWARDED When set, this flag indicates that
the ticket has either been forwarded
or was issued based on authentication
involving a forwarded ticket-granting
ticket.
3 PROXIABLE The PROXIABLE flag is normally only
interpreted by the TGS, and can be
ignored by end servers. The PROXIABLE
flag has an interpretation identical
to that of the FORWARDABLE flag,
except that the PROXIABLE flag tells
the ticket-granting server that only
non- ticket-granting tickets may be
issued with different network
addresses.
4 PROXY When set, this flag indicates that a
ticket is a proxy.
5 MAY-POSTDATE The MAY-POSTDATE flag is normally
only interpreted by the TGS, and can
be ignored by end servers. This flag
tells the ticket-granting server that
a post- dated ticket may be issued
based on this ticket-granting ticket.
6 POSTDATED This flag indicates that this ticket
has been postdated. The end-service
can check the authtime field to see
when the original authentication
occurred.
7 INVALID This flag indicates that a ticket is
invalid, and it must be validated by
the KDC before use. Application
servers must reject tickets which
have this flag set.
8 RENEWABLE The RENEWABLE flag is normally only
interpreted by the TGS, and can
usually be ignored by end servers
(some particularly careful servers
may wish to disallow renewable
tickets). A renewable ticket can be
used to obtain a replacement ticket
that expires at a later date.
9 INITIAL This flag indicates that this ticket
was issued using the AS protocol, and
not issued based on a ticket-granting
ticket.
10 PRE-AUTHENT This flag indicates that during
initial authentication, the client
was authenticated by the KDC before a
ticket was issued. The strength of
the preauthentication method is not
indicated, but is acceptable to the
KDC.
11 HW-AUTHENT This flag indicates that the protocol
employed for initial authentication
required the use of hardware expected
to be possessed solely by the named
client. The hardware authentication
method is selected by the KDC and the
strength of the method is not
indicated.
12-31 RESERVED Reserved for future use.
okay so the flags are done, and the rest of the ticket fields are next ๐โจ
key๐: this one lives in the ticket and the KDC response, and it's how the session key gets passed from Kerberos to the application server and the client. Its encoding is described in section 6.2.crealm๐ : the name of the realm where the client is registered and where the initial authentication happened.cname๐ชช: the name part of the client's principal identifier, so basically the "who" ๐transited๐บ๏ธ: lists the names of the Kerberos realms that took part in authenticating the user this ticket was issued to. It does NOT tell you the order the realms were transited, and see section 3.3.3.1 for how this field encodes the traversed realms.authtimeโฐ: the time of initial authentication for the named principal, which is also the time of issue for the original ticket this one is based on. It's in the ticket to give the end service extra info and to give the KDC what it needs for ahot listservice. A super paranoid end service could refuse tickets whose initial authentication happened "too far" in the past, and honestly iconic behaviour ๐ญ
This field also comes back in the KDC's response. In a KRB_AS_REP (the response to initial authentication), it's the current time on the Kerberos server. It is NOT recommended that this time value be used to adjust the workstation's clock, since the workstation can't reliably tell that a KRB_AS_REP actually came from the proper KDC in a timely manner. ๐ซโฐ
starttime๐ : the time after which the ticket is valid. Together withendtime, it sets the life of the ticket. If it's absent from the ticket, its value should be treated as that ofauthtime.endtime๐: the time after which the ticket won't be honored, aka its expiration time. Individual services can put their own limits on a ticket's life and may reject tickets that haven't expired yet, so really this is just an upper bound on the expiration time ngl ๐renew-till๐: this one only shows up in tickets with the RENEWABLE flag set in the flags field. It's the maximumendtimethat can be included in a renewal, so think of it as the absolute expiration time for the ticket, renewals and all ๐caddr๐: this holds zero (if omitted) or more (if present) host addresses, which are the addresses the ticket can be used from. If there are no addresses, the ticket can be used from any location. Whether the KDC issues, or an end server accepts, zero-address tickets is a policy decision left to the Kerberos and end-service administrators, and they may refuse to issue or accept them. The suggested and default policy is that these tickets are only issued or accepted when extra info that restricts the ticket's use is included in theauthorization_datafield. Such a ticket is a capability ๐ฆ
Network addresses go in the ticket to make it harder for an attacker to use stolen credentials. The session key never gets sent over the network in cleartext, so you can't just steal creds by listening to the wire ๐ An attacker has to get hold of the session key too (maybe through OS security breaches or a careless user's unattended session) before stolen tickets are any use to them.
It's important to note that the network address a connection came from can't be reliably determined. Even if it could, an attacker who compromised the client's workstation could use the credentials from there. Including network addresses only makes it more difficult, not impossible, for an attacker to walk off with stolen credentials and then use them from a "safe" location. ๐
authorization-data๐๏ธ: this field passes authorization data from the principal the ticket was issued on behalf of over to the application service. If no authorization data is included, the field is left out. The data is specific to the end service, and it's expected to contain the names of service-specific objects and the rights to those objects. The format is described in section 5.2. Kerberos doesn't care what the subfields contain, but it does carry type info (ad-type) ๐
Here's where it gets fun: a principal can use authorization-data to issue a proxy that's valid for one specific purpose. Say a client wants to print a file, so it grabs a file server proxy to pass to the print server. By putting the file's name in authorization-data, the file server knows the print server can only use the client's rights for that particular file ๐จ๏ธโจ
It's also interesting that if you specify authorization-data on a proxy and leave the host addresses blank, the resulting ticket and session key can be treated as a capability. See [9] for some suggested uses of this field ๐ง
The authorization-data field is optional, so it doesn't have to be included in a ticket ๐ธ
5.3.2. Authenticators
An authenticator is a record sent with a ticket to a server ๐ It's how the client proves it knows the encryption key in the ticket, helps the server catch replays, and helps pick a "true session key" to use for that particular session. The encoding is encrypted in the ticket's session key shared by the client and the server:
Authenticator ::= [APPLICATION 2] SEQUENCE {
authenticator-vno[0] INTEGER,
crealm[1] Realm,
cname[2] PrincipalName,
cksum[3] Checksum OPTIONAL,
cusec[4] INTEGER,
ctime[5] KerberosTime,
subkey[6] EncryptionKey OPTIONAL,
seq-number[7] INTEGER OPTIONAL,
authorization-data[8] AuthorizationData OPTIONAL
}
Okay so here's the breakdown of what's in the authenticator ๐โจ
authenticator-vno: this is the version number for the format of the authenticator. This document specifies version 5. ๐crealmandcname: these are the same as what we already covered for the ticket in section 5.3.1. ๐cksum: this is a checksum of the the application data that rides along with the KRB_AP_REQ. ๐งพcusec: this is the microsecond part of the client's timestamp. Before encryption, its value ranges from 0 to 999999. It usually shows up right alongsidectime, and the two of them work together to give you a reasonably accurate timestamp โฑ๏ธ๐ctime: this is the current time on the client's host. ๐ฐ๏ธsubkey: this is the client's pick for an encryption key that's used to protect this specific application session. Unless an application says otherwise, if you leave this field out, the session key from the ticket gets used instead. ๐๏ธseq-number: this optional field carries the initial sequence number for KRB_PRIV or KRB_SAFE messages, for when sequence numbers are used to detect replays (it can also be used by application specific messages too ๐). When it's in the authenticator, it sets the initial sequence number for messages from the client to the server. When it's in the AP-REP message, the initial sequence number is for messages going from the server to the client. When it's used in KRB_PRIV or KRB_SAFE messages, it goes up by one after each message is sent. ๐ข
Now the serious bit about seq-number ๐ For sequence numbers to properly support replay detection, they should be non-repeating, even across connection boundaries. The initial sequence number should be random and uniformly distributed across the full space of possible sequence numbers, so an attacker can't guess it, and so it and the successive sequence numbers don't repeat other sequences. Main character energy for randomness, no cap ๐
authorization-data: this field is the same as what's described for the ticket in section 5.3.1. It's optional and only shows up when you want extra restrictions placed on using a ticket, on top of the ones already inside the ticket itself. ๐
5.4. Specifications for the AS and TGS exchanges โจ
This section lays out the format of the messages that get exchanged between the client and the Kerberos server. If you want the format of the possible error messages, that's over in section 5.9.1 ๐ฆ
5.4.1. KRB_KDC_REQ definition
Okay bestie, so the KRB_KDC_REQ message doesn't have a type of its own ๐ Instead, its type is either KRB_AS_REQ or KRB_TGS_REQ, depending on whether the request is for an initial ticket or an additional ticket. Either way, the message gets sent from the client to the Authentication Server to ask for credentials for a service ๐
The message fields are:
AS-REQ ::= [APPLICATION 10] KDC-REQ
TGS-REQ ::= [APPLICATION 12] KDC-REQ
KDC-REQ ::= SEQUENCE {
pvno[1] INTEGER,
msg-type[2] INTEGER,
padata[3] SEQUENCE OF PA-DATA OPTIONAL,
req-body[4] KDC-REQ-BODY
}
PA-DATA ::= SEQUENCE {
padata-type[1] INTEGER,
padata-value[2] OCTET STRING,
-- might be encoded AP-REQ
}
KDC-REQ-BODY ::= SEQUENCE {
kdc-options[0] KDCOptions,
cname[1] PrincipalName OPTIONAL,
-- Used only in AS-REQ
realm[2] Realm, -- Server's realm
-- Also client's in AS-REQ
sname[3] PrincipalName OPTIONAL,
from[4] KerberosTime OPTIONAL,
till[5] KerberosTime,
rtime[6] KerberosTime OPTIONAL,
nonce[7] INTEGER,
etype[8] SEQUENCE OF INTEGER, -- EncryptionType,
-- in preference order
addresses[9] HostAddresses OPTIONAL,
enc-authorization-data[10] EncryptedData OPTIONAL,
-- Encrypted AuthorizationData encoding
additional-tickets[11] SEQUENCE OF Ticket OPTIONAL
}
The fields in this message are:
pvno: this is included in each message and specifies the protocol version number. This document specifies protocol version 5. ๐ธmsg-type: this indicates the type of a protocol message. It's almost always the same as the application identifier associated with a message. It's included to make the identifier more readily accessible to the application. For the KDC-REQ message, this type will be KRB_AS_REQ or KRB_TGS_REQ. ๐ท๏ธpadata: the padata (pre-authentication data) field holds a of authentication information that may be needed before credentials can be issued or decrypted. For requests for additional tickets (KRB_TGS_REQ), this field will include an element with padata-type of PA-TGS-REQ, and its data is an authentication header (a ticket-granting ticket and an authenticator). The checksum in the authenticator (which must be collisionproof) is computed over the KDC-REQ-BODY encoding. In most requests for initial authentication (KRB_AS_REQ) and most replies (KDC-REP), the padata field is left out. ๐ง
The padata field can also carry info for certain extensions to the Kerberos protocol. For example, it might be used to verify a client's identity before any response comes back. That happens with a padata field whose padata-type is PA-ENC-TIMESTAMP, and whose padata-value is defined like this:
padata-type ::= PA-ENC-TIMESTAMP
padata-value ::= EncryptedData -- PA-ENC-TS-ENC
PA-ENC-TS-ENC ::= SEQUENCE {
patimestamp[0] KerberosTime, -- client's time
pausec[1] INTEGER OPTIONAL
}
So patimestamp holds the client's time, and pausec holds the microseconds, which can be left out if a client won't make more than one request per second โฐ. The ciphertext (the padata-value) is the PA-ENC-TS-ENC sequence, encrypted with the client's secret key ๐
The padata field can also hold info that helps the KDC or the client pick the key needed to generate or decrypt the response. This form of padata is useful for supporting certain "smartcards" with Kerberos ๐ณ The details of those extensions are beyond the scope of this specification. See [10] for additional uses of this field.
padata-type: this tells you how the padata-value element should be interpreted. Negative values of padata-type are reserved for unregistered use, and non-negative values are used for a registered interpretation of the element type. ๐งฉreq-body: this is a placeholder that marks off the extent of the remaining fields. If a checksum is going to be calculated over the request, it's calculated over an encoding of the KDC-REQ-BODY sequence, which is enclosed within the req-body field. ๐ฆkdc-options: this field shows up in the KRB_AS_REQ and KRB_TGS_REQ requests to the KDC. It indicates the flags the client wants set on the tickets, plus other info that modifies how the KDC behaves. When it makes sense, an option's name may match the flag that option sets. Even though in most cases the bit in the options field matches the bit in the flags field, that's not guaranteed, so you can't just copy the options field over to the flags field ๐ โโ๏ธ There are various checks that have to happen before an option gets honored anyway. ๐ซถ
The kdc_options field is a bit-field. Selected options are shown by the bit being set (1), and unselected options and reserved fields are reset (0). The encoding of the bits is specified in section 5.2. The options are described in more detail above in section 2. Here's what the meanings look like:
Bit(s) Name Description
0 RESERVED Reserved for future expansion of this
field.
1 FORWARDABLE The FORWARDABLE option indicates that
the ticket to be issued is to have its
forwardable flag set. It may only be
set on the initial request, or in a
subsequent request if the ticket-
granting ticket on which it is based
is also forwardable.
2 FORWARDED The FORWARDED option is only specified
in a request to the ticket-granting
server and will only be honored if the
ticket-granting ticket in the request
has its FORWARDABLE bit set. This
option indicates that this is a
request for forwarding. The
address(es) of the host from which the
resulting ticket is to be valid are
included in the addresses field of the
request.
3 PROXIABLE The PROXIABLE option indicates that
the ticket to be issued is to have its
proxiable flag set. It may only be set
on the initial request, or in a
subsequent request if the ticket-
granting ticket on which it is based
is also proxiable.
Okay the bit table is giving everything ๐ Bit 0 is just reserved, bit 1 is FORWARDABLE, bit 2 is FORWARDED, and bit 3 is PROXIABLE. Keep up, we're getting to the rest ๐ง
4 PROXY The PROXY option indicates that this
is a request for a proxy. This option
will only be honored if the ticket-
granting ticket in the request has its
PROXIABLE bit set. The address(es) of
the host from which the resulting
ticket is to be valid are included in
the addresses field of the request.
5 ALLOW-POSTDATE The ALLOW-POSTDATE option indicates
that the ticket to be issued is to
have its MAY-POSTDATE flag set. It
may only be set on the initial
request, or in a subsequent request if
the ticket-granting ticket on which it
is based also has its MAY-POSTDATE
flag set.
6 POSTDATED The POSTDATED option indicates that
this is a request for a postdated
ticket. This option will only be
honored if the ticket-granting ticket
on which it is based has its MAY-
POSTDATE flag set. The resulting
ticket will also have its INVALID flag
set, and that flag may be reset by a
subsequent request to the KDC after
the starttime in the ticket has been
reached.
7 UNUSED This option is presently unused.
8 RENEWABLE The RENEWABLE option indicates that
the ticket to be issued is to have its
RENEWABLE flag set. It may only be
set on the initial request, or when
the ticket-granting ticket on which
the request is based is also
renewable. If this option is
requested, then the rtime field in the
request contains the desired absolute
expiration time for the ticket.
9-26 RESERVED Reserved for future use.
27 RENEWABLE-OK The RENEWABLE-OK option indicates that
a renewable ticket will be acceptable
if a ticket with the requested life
cannot otherwise be provided. If a
ticket with the requested life cannot
be provided, then a renewable ticket
may be issued with a renew-till equal
to the the requested endtime. The
value of the renew-till field may
still be limited by local limits, or
limits selected by the individual
principal or server.
28 ENC-TKT-IN-SKEY This option is used only by the
ticket-granting service. The ENC-
TKT-IN-SKEY option indicates that the
ticket for the end server is to be
encrypted in the session key from the
additional ticket-granting ticket
provided.
29 RESERVED Reserved for future use.
30 RENEW This option is used only by the
ticket-granting service. The RENEW
option indicates that the present
request is for a renewal. The ticket
provided is encrypted in the secret
key for the server on which it is
valid. This option will only be
honored if the ticket to be renewed
has its RENEWABLE flag set and if the
time in its renew till field has not
passed. The ticket to be renewed is
passed in the padata field as part of
the authentication header.
31 VALIDATE This option is used only by the
ticket-granting service. The VALIDATE
option indicates that the request is
to validate a postdated ticket. It
will only be honored if the ticket
presented is postdated, presently has
its INVALID flag set, and would be
otherwise usable at this time. A
ticket cannot be validated before its
starttime. The ticket presented for
validation is encrypted in the key of
the server for which it is valid and
is passed in the padata field as part
of the authentication header.
okay so now we get to the field-by-field bits ๐ โจ these are the ones that tell the KDC what you actually want, bestie ๐
cnameandsname๐ฆ these work exactly like they do for the ticket in section 5.3.1.snamecan only be left out when theENC-TKT-IN-SKEYoption is set. If it's missing, the server name gets pulled from the client name inside the ticket you passed inadditional-tickets. iykyk ๐ซถenc-authorization-data๐ this one only shows up in the TGS_REQ form. If it's there, it's the authorization data you want, encrypted under the sub-session key (if one is in the Authenticator) or otherwise under the session key from the ticket-granting ticket. Both of those keys come from thepadatafield in the KRB_AP_REQ. main character energy, no cap ๐realm๐ธ this is the realm part of the server's principal identifier. In the AS exchange, it's also the realm part of the client's principal identifier. โจfromโฐ this shows up in KRB_AS_REQ and KRB_TGS_REQ ticket requests when you want the ticket postdated. It's the start time you'd like the ticket to have. ๐งtill๐ this is the expiration date the client is asking for in a ticket request. ๐rtime๐ this is the requested renew-till time, sent from the client to the KDC in a ticket request. It's optional, so you can skip it. ๐nonce๐ฒ this one is part of both the KDC request and response. It's meant to hold a random number the client generates. If that same number shows up in the KDC's encrypted response, it proves the response is fresh and nobody replayed it. Nonces must never be re-used, periodt ๐ Ideally you'd make it random, but if you know the correct time, the time can be good enough.
ngl this part is a whole vibe check โ ๏ธ if you use the time as your nonce, your workstation time has to keep going forward, always (monotonically increasing). If the clock ever gets reset backwards, there's a small but finite chance a nonce gets reused. not me panicking over a clock ๐ญ
etype๐ฅ this is the encryption algorithm you want used in the response. ๐addresses๐ก this is included in the initial request for tickets, and it's optional in requests for additional tickets from the ticket-granting server. It lists the addresses the requested ticket is going to be valid from. Normally that's your client's host. If you're asking for a proxy, this field holds other addresses instead. The KDC usually copies these into thecaddrfield of the ticket it hands back. ๐ทadditional-tickets๐ you can optionally include these in a request to the ticket-granting server. If theENC-TKT-IN-SKEYoption is set, the session key from the additional ticket gets used instead of the server's key to encrypt the new ticket. If you ask for more than one option that needs additional tickets, they get used in the order set by the ordering of the options bits (see kdc-options, above). ๐งฉ
The application code is going to be either ten (10) or twelve (12), depending on whether you're asking for an initial ticket (AS-REQ) or an additional ticket (TGS-REQ). ๐ฆ
The optional fields (addresses, authorization-data and additional-tickets) only get included if they're actually needed to do what the kdc-options field asks for. Less is more, bestie ๐
๐
Also, in KRB_TGS_REQ, the protocol version number shows up twice and two different message types show up too ๐คฏ the KRB_TGS_REQ message has these fields, and so does the authentication header (KRB_AP_REQ) that gets passed in the padata field. double the paperwork, but honestly iconic ๐ซถโจ
5.4.2. KRB_KDC_REP definition ๐
okay so the KRB_KDC_REP message is the KDC's reply, and it shows up for both the initial (AS) request and the later (TGS) request ๐ there's no message type literally called KRB_KDC_REP tho. it's always either KRB_AS_REP or KRB_TGS_REP โจ and which key gets used to encrypt the ciphertext depends on which one you're getting:
- KRB_AS_REP: the ciphertext is encrypted in the client's secret key, and the client's key version number goes in the key version number for the encrypted data ๐
- KRB_TGS_REP: the ciphertext is encrypted in the sub-session key from the Authenticator, or if that's missing, the session key from the ticket-granting ticket used in the request ๐ฆ in this case there's no version number in the EncryptedData sequence
the KRB_KDC_REP message contains these fields ๐
AS-REP ::= [APPLICATION 11] KDC-REP
TGS-REP ::= [APPLICATION 13] KDC-REP
KDC-REP ::= SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
padata[2] SEQUENCE OF PA-DATA OPTIONAL,
crealm[3] Realm,
cname[4] PrincipalName,
ticket[5] Ticket,
enc-part[6] EncryptedData
}
EncASRepPart ::= [APPLICATION 25[25]] EncKDCRepPart
EncTGSRepPart ::= [APPLICATION 26] EncKDCRepPart
EncKDCRepPart ::= SEQUENCE {
key[0] EncryptionKey,
last-req[1] LastReq,
nonce[2] INTEGER,
key-expiration[3] KerberosTime OPTIONAL,
flags[4] TicketFlags,
authtime[5] KerberosTime,
starttime[6] KerberosTime OPTIONAL,
endtime[7] KerberosTime,
renew-till[8] KerberosTime OPTIONAL,
srealm[9] Realm,
sname[10] PrincipalName,
caddr[11] HostAddresses OPTIONAL
}
NOTE: In EncASRepPart, the application code in the encrypted part of a message provides an additional check that the message was decrypted properly. ๐
here's the field breakdown, bestie ๐ง
pvno and msg-type: these are explained above in section 5.4.1. msg-type is either KRB_AS_REP or KRB_TGS_REP. ๐ธ
padata: this one gets the full treatment in section 5.4.1. one possible use is encoding an alternate "mix-in" string for a string-to-key algorithm (like the one in section 6.3.2) ๐ this is so clutch for transitions when a realm name has to change, like when a company gets acquired. in that case, every existing password-derived entry in the KDC database gets flagged as needing a special mix-in string until the next password change ๐ญ
crealm, cname, srealm and sname: these are the same as what's described for the ticket in section 5.3.1 ๐
ticket: the newly-issued ticket, from section 5.3.1 ๐
enc-part: this is just a placeholder for the ciphertext and related info that makes up the encrypted part of a message. the description of the encrypted part comes right after each appearance of this field, and it's encoded as described in section 6.1 ๐ฆ
key: same deal as the ticket in section 5.3.1 ๐
last-req: the KDC returns this one, and it tells you the time(s) of a principal's last request โจ depending on what info is around, that could be the last time a ticket-granting ticket was requested, or the last time a request based on a ticket-granting ticket worked. it might cover all servers in a realm, or just one particular server. some implementations may show this to the user to help spot unauthorized use of their identity ๐ it's giving "last login time" vibes from timesharing systems ๐
nonce: described above in section 5.4.1 ๐
key-expiration: part of the KDC's response, and it says when the client's secret key is due to expire ๐๏ธ that could be because of password aging or an account expiration. this usually gets left out of the TGS reply, since that response is encrypted in a session key and there's no client info that needs pulling from the KDC database ๐ if the expiration is coming up soon, it's up to the application client (usually the login program) to do something about it, like notifying the user โญ
flags, authtime, starttime, endtime, renew-till and caddr: these are copies of the ones in the encrypted part of the attached ticket (see section 5.3.1). they're here so the client can check that they match the request it actually sent, and to help with proper ticket caching ๐ง if the message is KRB_TGS_REP, caddr only gets filled in if the request was for a proxy or forwarded ticket, or if the user is swapping in a subset of the addresses from the ticket-granting ticket ๐ซถ if the client-requested addresses aren't there or aren't used, the addresses in the ticket will match the ones in the ticket-granting ticket ๐
5.5. Client/Server (CS) message specifications ๐ฆ
this section lays out the format of the messages used to authenticate the client to the application server ๐ฅ
5.5.1. KRB_AP_REQ definition ๐
the KRB_AP_REQ message holds the Kerberos protocol version number, the message type KRB_AP_REQ, an options field for any options in use, and the ticket and authenticator themselves โจ people often call the KRB_AP_REQ message the "authentication header" ๐
AP-REQ ::= [APPLICATION 14] SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
ap-options[2] APOptions,
ticket[3] Ticket,
authenticator[4] EncryptedData
}
APOptions ::= BIT STRING {
reserved(0),
use-session-key(1),
mutual-required(2)
}
pvno and msg-type: explained above in section 5.4.1. msg-type is KRB_AP_REQ ๐ธ
ap-options: this field shows up in the application request (KRB_AP_REQ) and changes how the request gets processed ๐ง it's a bit-field. a selected option has its bit set to 1, and unselected options and reserved fields are reset to 0 ๐ the bits are encoded as described in section 5.2. here's what the options mean:
Bit(s) Name Description
0 RESERVED Reserved for future expansion of
this field.
1 USE-SESSION-KEYThe USE-SESSION-KEY option indicates
that the ticket the client is
presenting to a server is encrypted in
the session key from the server's
ticket-granting ticket. When this
option is not specified, the ticket is
encrypted in the server's secret key.
2 MUTUAL-REQUIREDThe MUTUAL-REQUIRED option tells the
server that the client requires mutual
authentication, and that it has to
respond with a KRB_AP_REP message.
3-31 RESERVED Reserved for future use.
ticket: this is the ticket that authenticates the client to the server ๐
authenticator: this holds the authenticator, which includes the client's choice of a subkey ๐ฆ its encoding is described in section 5.3.2 โจ
5.5.2. KRB_AP_REP definition ๐
the KRB_AP_REP message has the Kerberos protocol version number, the message type, and an encrypted timestamp ๐ it gets sent back in response to an application request (KRB_AP_REQ) where the mutual authentication option was selected in the ap-options field ๐ซถ
AP-REP ::= [APPLICATION 15] SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
enc-part[2] EncryptedData
}
EncAPRepPart ::= [APPLICATION 27] SEQUENCE {
ctime[0] KerberosTime,
cusec[1] INTEGER,
subkey[2] EncryptionKey OPTIONAL,
seq-number[3] INTEGER OPTIONAL
}
NOTE: in EncAPRepPart, the application code in the encrypted part of a message provides an additional check that the message was decrypted properly. ๐
the encoded EncAPRepPart is encrypted in the shared session key of the ticket ๐ the optional subkey field can be used in an application-arranged negotiation to pick a per-association session key ๐
pvno and msg-type: explained above in section 5.4.1. msg-type is KRB_AP_REP ๐ธ
enc-part: described above in section 5.4.2 ๐
ctime: the current time on the client's host โฐ
cusec: the microsecond part of the client's timestamp ๐ง
subkey: an encryption key used to protect this specific application session ๐ see section 3.2.6 for the specifics on how this field negotiates a key. unless an application specifies otherwise, if this field is left out, the sub-session key from the authenticator gets used, or if that's also left out, the session key from the ticket ๐
5.5.3. Error message reply ๐ฅบ
if something goes wrong while the server processes the application request, a KRB_ERROR message gets sent back ๐ฌ see section 5.9.1 for its format โจ the cname and crealm fields can be left out if the server can't work out their right values from the corresponding KRB_AP_REQ message ๐คทโโ๏ธ and if the authenticator could be deciphered, the ctime and cusec fields will hold the values from it ๐
5.6. KRB_SAFE message specification ๐ก๏ธ
this section lays out the format of a message either side of an application (client or server) can use to send a tamper-proof message to its peer ๐ฅ it assumes a session key was already exchanged, for example through the KRB_AP_REQ/KRB_AP_REP messages ๐ซถ
5.6.1. KRB_SAFE definition
โจ Okay bestie, the KRB_SAFE message is how you send user data together with a collision-proof checksum keyed with the session key. So the data stays readable, but nobody can mess with it without you noticing ๐๐ Here are the fields:
KRB-SAFE ::= [APPLICATION 20] SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
safe-body[2] KRB-SAFE-BODY,
cksum[3] Checksum
}
KRB-SAFE-BODY ::= SEQUENCE {
user-data[0] OCTET STRING,
timestamp[1] KerberosTime OPTIONAL,
usec[2] INTEGER OPTIONAL,
seq-number[3] INTEGER OPTIONAL,
s-address[4] HostAddress,
r-address[5] HostAddress OPTIONAL
}
pvnoandmsg-type: these are described above in section 5.4.1 ๐ For this message,msg-typeisKRB_SAFE.safe-body: this is a placeholder for the body of the KRB-SAFE message ๐ซถ You encode the body separately, then compute the checksum over it for thecksumfield.cksum: this holds the checksum of the application data ๐ Checksum details live in section 6.4. The checksum is computed over the encoding of the KRB-SAFE-BODY sequence, so the body is the star of the show โจuser-data: this is the application-specific data being passed from sender to recipient ๐ It shows up in both KRB_SAFE and KRB_PRIV messages.timestamp: this is the sender's current time ๐ฐ๏ธ By checking it, the recipient can tell whether the message was recently made, which helps catch replays ๐ซ It's part of both KRB_SAFE and KRB_PRIV.usec: this is the microsecond part of the timestamp โฑ๏ธ It's part of the KRB_SAFE and KRB_PRIV headers.seq-number: this one is described above in section 5.3.2 ๐ฆs-address: this specifies the address the sender is using ๐r-address: this specifies the address the recipient is using ๐ฏ It may be left out for some uses (such as broadcast protocols), but the recipient may arbitrarily reject such messages. Together withs-address, it can help spot messages that were delivered to the wrong recipient, or that were tampered with, which is giving detective energy ๐ต๏ธโโ๏ธ๐
5.7. KRB_PRIV message specification
๐ธ This section covers a message either side of an application (client or server) can use to send something to its peer that is both secure and private ๐ It assumes a session key has already been exchanged, for example through the KRB_AP_REQ/KRB_AP_REP messages.
5.7.1. KRB_PRIV definition
๐ The KRB_PRIV message contains user data encrypted in the Session Key. Here are the fields:
KRB-PRIV ::= [APPLICATION 21] SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
enc-part[3] EncryptedData
}
EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE {
user-data[0] OCTET STRING,
timestamp[1] KerberosTime OPTIONAL,
usec[2] INTEGER OPTIONAL,
seq-number[3] INTEGER OPTIONAL,
s-address[4] HostAddress, -- sender's addr
r-address[5] HostAddress OPTIONAL
-- recip's addr
}
๐จ NOTE: In EncKrbPrivPart, the application code inside the encrypted part gives an extra check that the message was decrypted properly. Iconic double-check behaviour ๐
pvnoandmsg-type: these are described above in section 5.4.1 ๐ For this message,msg-typeisKRB_PRIV.enc-part: this holds an encoding of the EncKrbPrivPart sequence, encrypted under the session key ๐ If the encryption method in use supports it, an initialization vector may be passed to the encryption procedure so proper cipher chaining happens ๐ง That IV might come from the last block of the ciphertext in the previous KRB_PRIV message, but the application gets to decide whether to use one ๐ If it's left out, the encryption algorithm's default IV is used. This encrypted encoding goes in theenc-partfield of the KRB-PRIV message, and section 6 has the ciphertext format ๐ฆuser-data,timestamp,usec,s-addressandr-address: these are described above in section 5.6.1 ๐seq-number: this one is described above in section 5.3.2 โจ
5.8. KRB_CRED message specification
๐ This section covers a message used to send Kerberos credentials from one principal to another ๐ง It's here to encourage a common mechanism that applications can use when forwarding tickets or providing proxies to subordinate servers. It presumes a session key has already been exchanged, perhaps by using the KRB_AP_REQ/KRB_AP_REP messages.
5.8.1. KRB_CRED definition
๐ The KRB_CRED message holds a sequence of tickets to be sent, plus the info needed to use them, including the session key from each. That info is encrypted under an encryption key that was previously exchanged. Here are the fields:
KRB-CRED ::= [APPLICATION 22] SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER, -- KRB_CRED
tickets[2] SEQUENCE OF Ticket,
enc-part[3] EncryptedData
}
EncKrbCredPart ::= [APPLICATION 29] SEQUENCE {
ticket-info[0] SEQUENCE OF KrbCredInfo,
nonce[1] INTEGER OPTIONAL,
timestamp[2] KerberosTime OPTIONAL,
usec[3] INTEGER OPTIONAL,
s-address[4] HostAddress OPTIONAL,
r-address[5] HostAddress OPTIONAL
}
KrbCredInfo ::= SEQUENCE {
key[0] EncryptionKey,
prealm[1] Realm OPTIONAL,
pname[2] PrincipalName OPTIONAL,
flags[3] TicketFlags OPTIONAL,
authtime[4] KerberosTime OPTIONAL,
starttime[5] KerberosTime OPTIONAL,
endtime[6] KerberosTime OPTIONAL
renew-till[7] KerberosTime OPTIONAL,
srealm[8] Realm OPTIONAL,
sname[9] PrincipalName OPTIONAL,
caddr[10] HostAddresses OPTIONAL
}
pvnoandmsg-type: these are described above in section 5.4.1 ๐ซ For this message,msg-typeisKRB_CRED.tickets: these are the tickets obtained from the KDC specifically for the intended recipient ๐ซ Each successive ticket is paired with the matching KrbCredInfo sequence from theenc-partof the KRB-CRED message.enc-part: this holds an encoding of the EncKrbCredPart sequence, encrypted under the session key shared between the sender and the intended recipient ๐ This encrypted encoding goes in theenc-partfield of the KRB-CRED message. Section 6 has the ciphertext format.nonce: if it's practical, an application can require a nonce generated by the recipient of the message ๐ฒ If that same value comes back in the message, it's evidence the message is fresh and wasn't replayed by an attacker. A nonce must never be re-used. It should be generated randomly by the recipient and given to the sender of the message in an application-specific way ๐ โโ๏ธtimestampandusec: these specify the time the KRB-CRED message was generated โฐ The time is used to give assurance that the message is fresh.s-addressandr-address: these are described above in section 5.6.1 ๐ธ They're used, optionally, to give extra assurance of the integrity of the KRB-CRED message.key: this field exists in the corresponding ticket passed by the KRB-CRED message ๐๏ธ It's used to pass the session key from sender to intended recipient. Its encoding is described in section 6.2.
The following fields are optional ๐ท If they're present, they can be associated with the credentials in the remote ticket file. If they're left out, it's assumed the recipient already knows their values.
prealmandpname: the name and realm of the delegated principal identity ๐งโ๐คโ๐งflags,authtime,starttime,endtime,renew-till,srealm,sname, andcaddr: these contain the values of the corresponding fields from the ticket in theticketfield ๐ Their descriptions are identical to the ones in the KDC-REP message.
5.9. Error message specification
๐จ This section specifies the format for the KRB_ERROR message ๐ The fields are meant to return as much information as possible about an error. It's not expected that all the information the fields need will be available for every type of error ๐ If the right info isn't available when the message gets composed, the matching field is left out of the message.
โ ๏ธ Heads up: since the KRB_ERROR message isn't protected by any encryption, it's totally possible for an intruder to make up or modify one ๐ฑ That means the client should not use any fields in this message for security-critical purposes, like setting a system clock or generating a fresh authenticator. It can still be useful for telling a user why something failed ๐
5.9.1. KRB_ERROR definition
okay so the KRB_ERROR message is basically the server's "ope, that didn't work" text ๐ and it's got these fields:
KRB-ERROR ::= [APPLICATION 30] SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
ctime[2] KerberosTime OPTIONAL,
cusec[3] INTEGER OPTIONAL,
stime[4] KerberosTime,
susec[5] INTEGER,
error-code[6] INTEGER,
crealm[7] Realm OPTIONAL,
cname[8] PrincipalName OPTIONAL,
realm[9] Realm, -- Correct realm
sname[10] PrincipalName, -- Correct name
e-text[11] GeneralString OPTIONAL,
e-data[12] OCTET STRING OPTIONAL
}
Let's go field by field, bestie ๐ซถ
pvno and msg-type ๐ These fields are described above in section 5.4.1. msg-type is KRB_ERROR.
ctime โฐ This field is described above in section 5.4.1.
cusec ๐ฆ This field is described above in section 5.5.2.
stime โญ This field contains the current time on the server. It is of type KerberosTime.
susec ๐ธ This field contains the microsecond part of the server's timestamp. Its value ranges from 0 to 999. It appears along with stime. The two fields are used in conjunction to specify a reasonably accurate timestamp. so stime plus susec is the combo ๐ง
error-code ๐ This field contains the error code returned by Kerberos or the server when a request fails. To interpret the value of this field see the list of error codes in section 8. Implementations are encouraged to provide for national language support in the display of error messages. the people deserve to read their errors in their own language, periodt ๐
crealm, cname, srealm and sname ๐ These fields are described above in section 5.3.1.
e-text ๐ฌ This field contains additional text to help explain the error code associated with the failed request (for example, it might include a principal name which was unknown). basically the "wait, who even is this?" hint ๐
e-data ๐ฅ This field contains additional data about the error for use by the application to help it recover from or handle the error. If the errorcode is KDC_ERR_PREAUTH_REQUIRED, then the e-data field will contain an encoding of a sequence of padata fields, each corresponding to an acceptable pre-authentication method and optionally containing data for the method:
METHOD-DATA ::= SEQUENCE of PA-DATA
If the error-code is KRB_AP_ERR_METHOD, then the e-data field will contain an encoding of the following sequence:
METHOD-DATA ::= SEQUENCE {
method-type[0] INTEGER,
method-data[1] OCTET STRING OPTIONAL
}
method-type will indicate the required alternate method; method-data will contain any required additional information. โจ
6. Encryption and Checksum Specifications โจ
okay this section is where the crypto gets serious ๐ The Kerberos protocols described in this document are designed to use stream encryption ciphers, which can be simulated using commonly available block encryption ciphers, such as the Data Encryption Standard [11], in conjunction with block chaining and checksum methods [12]. Encryption is used to prove the identities of the network entities participating in message exchanges. The Key Distribution Center for each realm is trusted by all principals registered in that realm to store a secret key in confidence. Proof of knowledge of this secret key is used to verify the authenticity of a principal. the vibe is "prove you know the secret, and you're real" ๐
The KDC uses the principal's secret key (in the AS exchange) or a shared session key (in the TGS exchange) to encrypt responses to ticket requests; the ability to obtain the secret key or session key implies the knowledge of the appropriate keys and the identity of the KDC. The ability of a principal to decrypt the KDC response and present a Ticket and a properly formed Authenticator (generated with the session key from the KDC response) to a service verifies the identity of the principal; likewise the ability of the service to extract the session key from the Ticket and prove its knowledge thereof in a response verifies the identity of the service. so it goes both ways, the client proves itself and the service proves itself ๐ค
The Kerberos protocols generally assume that the encryption used is secure from cryptanalysis; however, in some cases, the order of fields in the encrypted portions of messages are arranged to minimize the effects of poorly chosen keys. It is still important to choose good keys. If keys are derived from user-typed passwords, those passwords need to be well chosen to make brute force attacks more difficult. Poorly chosen keys still make easy targets for intruders. โ ๏ธ bestie, pick a strong password, the internet is watching ๐
The following sections specify the encryption and checksum mechanisms currently defined for Kerberos. The encodings, chaining, and padding requirements for each are described. For encryption methods, it is often desirable to place random information (often referred to as a confounder) at the start of the message. The requirements for a confounder are specified with each encryption mechanism. ๐ฒ
Some encryption systems use a block-chaining method to improve the the security characteristics of the ciphertext. However, these chaining methods often don't provide an integrity check upon decryption. Such systems (such as DES in CBC mode) must be augmented with a checksum of the plaintext which can be verified at decryption and used to detect any tampering or damage. Such checksums should be good at detecting burst errors in the input. If any damage is detected, the decryption routine is expected to return an error indicating the failure of an integrity check. Each encryption type is expected to provide and verify an appropriate checksum. The specification of each encryption method sets out its checksum requirements. the checksum is the bouncer that catches the tampering ๐จ
Finally, where a key is to be derived from a user's password, an algorithm for converting the password to a key of the appropriate type is included. It is desirable for the string to key function to be one-way, and for the mapping to be different in different realms. This is important because users who are registered in more than one realm will often use the same password in each, and it is desirable that an attacker compromising the Kerberos server in one realm not obtain or derive the user's key in another. ugh, reusing passwords across realms is a whole vibe nobody wants ๐
For a discussion of the integrity characteristics of the candidate encryption and checksum methods considered for Kerberos, the the reader is referred to [13]. ๐
6.1. Encryption Specifications
The following ASN.1 definition describes all encrypted messages. The enc-part field which appears in the unencrypted part of messages in section 5 is a sequence consisting of an encryption type, an optional key version number, and the ciphertext.
EncryptedData ::= SEQUENCE {
etype[0] INTEGER, -- EncryptionType
kvno[1] INTEGER OPTIONAL,
cipher[2] OCTET STRING -- ciphertext
}
etype ๐ง This field identifies which encryption algorithm was used to encipher the cipher. Detailed specifications for selected encryption types appear later in this section.
kvno ๐ This field contains the version number of the key under which data is encrypted. It is only present in messages encrypted under long lasting keys, such as principals' secret keys.
cipher ๐ This field contains the enciphered text, encoded as an OCTET STRING.
The cipher field is generated by applying the specified encryption algorithm to data composed of the message and algorithm-specific inputs. Encryption mechanisms defined for use with Kerberos must take sufficient measures to guarantee the integrity of the plaintext, and we recommend they also take measures to protect against precomputed dictionary attacks. If the encryption algorithm is not itself capable of doing so, the protections can often be enhanced by adding a checksum and a confounder. ๐ก๏ธ
The suggested format for the data to be encrypted includes a confounder, a checksum, the encoded plaintext, and any necessary padding. The msg-seq field contains the part of the protocol message described in section 5 which is to be encrypted. The confounder, checksum, and padding are all untagged and untyped, and their length is exactly sufficient to hold the appropriate item. The type and length is implicit and specified by the particular encryption type being used (etype). The format for the data to be encrypted is described in the following diagram:
+-----------+----------+-------------+-----+
|confounder | check | msg-seq | pad |
+-----------+----------+-------------+-----+
The format cannot be described in ASN.1, but for those who prefer an ASN.1-like notation:
CipherText ::= ENCRYPTED SEQUENCE {
confounder[0] UNTAGGED OCTET STRING(conf_length) OPTIONAL,
check[1] UNTAGGED OCTET STRING(checksum_length) OPTIONAL,
msg-seq[2] MsgSequence,
pad UNTAGGED OCTET STRING(pad_length) OPTIONAL
}
In the above specification, UNTAGGED OCTET STRING(length) is the notation for an octet string with its tag and length removed. It is not a valid ASN.1 type. The tag bits and length must be removed from the confounder since the purpose of the confounder is so that the message starts with random data, but the tag and its length are fixed. For other fields, the length and tag would be redundant if they were included because they are specified by the encryption type. ๐งฉ
One generates a random confounder of the appropriate length, placing it in confounder; zeroes out check; calculates the appropriate checksum over confounder, check, and msg-seq, placing the result in check; adds the necessary padding; then encrypts using the specified encryption type and the appropriate key. ๐ณ
Unless otherwise specified, a definition of an encryption algorithm that specifies a checksum, a length for the confounder field, or an octet boundary for padding uses this ciphertext format (The ordering of the fields in the CipherText is important. Additionally, messages encoded in this format must include a length as part of the msg-seq field. This allows the recipient to verify that the message has not been truncated. Without a length, an attacker could use a chosen plaintext attack to generate a message which could be truncated, while leaving the checksum intact. Note that if the msg-seq is an encoding of an ASN.1 SEQUENCE or OCTET STRING, then the length is part of that encoding.). Those fields which are not specified will be omitted. ๐ the length is the anti-truncation shield, so don't skip it ๐
In the interest of allowing all implementations using a particular encryption type to communicate with all others using that type, the specification of an encryption type defines any checksum that is needed as part of the encryption process. If an alternative checksum is to be used, a new encryption type must be defined. ๐ค
Some cryptosystems require additional information beyond the key and the data to be encrypted. For example, DES, when used in cipher-block-chaining mode, requires an initialization vector. If required, the description for each encryption type must specify the source of such additional information. ๐ฆ
6.2. Encryption Keys ๐
Okay bestie, here's how an encryption key gets packed up for the trip! ๐ The sequence below shows the encoding:
EncryptionKey ::= SEQUENCE {
keytype[0] INTEGER,
keyvalue[1] OCTET STRING
}
keytype tells you what kind of key is sitting in the keyvalue field. It almost always lines up with the encryption algorithm that made the EncryptedData, but more than one algorithm can share the same key type, so it's many to one ๐ฆ. That happens when an algorithm swaps in a different checksum for its integrity check, or uses a different chaining mechanism.
keyvalue is the key itself, encoded as an octet string. โจ
All negative values for the encryption key type are reserved for local use. All non-negative values are reserved for officially assigned type fields and interpretations. Iykyk, the ranges are split on purpose ๐
6.3. Encryption Systems ๐ง
6.3.1. The NULL Encryption System (null) ๐ซง
If no encryption is in use, the encryption system is called the NULL encryption system. There's no checksum, confounder, or padding in it at all. The ciphertext is literally just the plaintext, no cap ๐ The NULL Key is used by the null encryption system. It's zero octets long, and its keytype is zero (0).
6.3.2. DES in CBC mode with a CRC-32 checksum (des-cbc-crc) ๐
This mode encrypts information under the Data Encryption Standard [11], using the cipher block chaining mode [12]. A CRC-32 checksum (described in ISO 3309 [14]) gets applied to the confounder and message sequence (msg-seq) and then goes into the cksum field. DES blocks are 8 bytes. So the data you're encrypting (the confounder, the checksum, and the message, all stuck together) must be padded out to an 8 byte boundary before encryption. The actual encryption details are identical to those for the des-cbc-md5 mode.
Now the tea โ Since the CRC-32 checksum isn't collision-proof, an attacker could use a probabilistic chosen-plaintext attack to generate a valid message, even if a confounder is used [13]. The use of collision-proof checksums is recommended for environments where these attacks are a real threat. The use of CRC-32 as the checksum for a ticket or authenticator is no longer mandated as an interoperability requirement for Kerberos Version 5 Specification 1 (See section 9.1 for specific details). It's giving "we've moved on" ๐
6.3.3. DES in CBC mode with an MD4 checksum (des-cbc-md4) ๐ธ
This mode also encrypts information under the Data Encryption Standard [11], using the cipher block chaining mode [12]. An MD4 checksum (described in [15]) is applied to the confounder and message sequence (msg-seq) and placed in the cksum field. DES blocks are 8 bytes. That means the data to be encrypted (the confounder, checksum, and message combined) must be padded to an 8 byte boundary before encryption. The details of encrypting this data are identical to those for the descbc-md5 mode.
6.3.4. DES in CBC mode with an MD5 checksum (des-cbc-md5) ๐
Same squad, different checksum! ๐ This mode encrypts information under the Data Encryption Standard [11], using the cipher block chaining mode [12]. An MD5 checksum (described in [16]) gets applied to the confounder and message sequence (msg-seq) and placed in the cksum field. DES blocks are 8 bytes, so the data to be encrypted (the confounder, checksum, and message together) must be padded to an 8 byte boundary before encryption.
Plaintext and DES ciphertext are encoded as 8-octet blocks, and those blocks get concatenated to make the 64-bit inputs for the DES algorithms. The first octet supplies the 8 most significant bits (the octet's MSbit becomes the DES input block's MSbit, and so on). The second octet supplies the next 8 bits, and this keeps going until the eighth octet, which supplies the 8 least significant bits ๐ง
Encryption under DES using cipher block chaining needs an extra input called an initialization vector. Unless otherwise specified, zero should be used as the initialization vector. Kerberos' use of DES requires an 8-octet confounder.
The DES specifications identify some "weak" and "semiweak" keys. Those keys shall not be used for encrypting messages for use in Kerberos. There's also a sneaky extra rule: because of the way keys get derived for encrypting checksums, keys shall not be used if they yield "weak" or "semi-weak" keys when eXclusive-ORed with the constant F0F0F0F0F0F0F0F0. Not today, sus keys ๐ โโ๏ธ
A DES key is 8 octets of data, and its keytype is one (1). It's made of 56 bits of key plus 8 parity bits, one per octet. The key is encoded as a series of 8 octets written in MSB-first order, and the bits inside the key are also encoded in MSB order. For example, if the encryption key is (B1,B2,...,B7,P1,B8,...,B14,P2,B15,...,B49,P7,B50,...,B56,P8), where B1 through B56 are the key bits in MSB order and P1 through P8 are the parity bits, then the first octet of the key is B1,B2,...,B7,P1 (with B1 as the MSbit). [See the FIPS 81 introduction for reference.] ๐
To generate a DES key from a text string (password), the string normally needs the realm and each component of the principal's name appended. (In some cases, you might need a different "mix-in" string for compatibility reasons. See the discussion of padata in section 5.4.2.) Then you pad it with ASCII nulls to an 8 byte boundary. That string gets fan-folded and eXclusive-ORed with itself to form an 8 byte DES key. The parity is corrected on that key, and then it's used to generate a DES CBC checksum on the initial string (with the realm and name appended). Next, the parity is corrected on the CBC checksum. If the result matches a "weak" or "semiweak" key from the DES specification, it gets eXclusive-ORed with the constant 00000000000000F0. Finally, the result is returned as the key. Here's the pseudocode, bestie ๐ซถ
string_to_key(string,realm,name) {
odd = 1;
s = string + realm;
for(each component in name) {
s = s + component;
}
tempkey = NULL;
pad(s); /* with nulls to 8 byte boundary */
for(8byteblock in s) {
if(odd == 0) {
odd = 1;
reverse(8byteblock)
}
else odd = 0;
tempkey = tempkey XOR 8byteblock;
}
fixparity(tempkey);
key = DES-CBC-check(s,tempkey);
fixparity(key);
if(is_weak_key_key(key))
key = key XOR 0xF0;
return(key);
}
6.4. Checksums ๐
Here's the ASN.1 definition used for a checksum, periodt:
Checksum ::= SEQUENCE {
cksumtype[0] INTEGER,
checksum[1] OCTET STRING
}
cksumtype tells you which algorithm generated the accompanying checksum. checksum is the checksum itself, encoded as an octet string.
Detailed specs for selected checksum types show up 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.
Kerberos checksums can be sorted by two properties: whether they're collision-proof, and whether they're keyed. For a collision-proof checksum, it's infeasible to find two plaintexts that produce the same checksum value. A keyed checksum needs a key to perturb or initialize the algorithm. To stop an active attacker from modifying the message stream, unkeyed checksums should only be used when the checksum and message will be encrypted afterwards (for example, the checksums defined as part of the encryption algorithms covered earlier in this section). Collision-proof checksums can be made tamper-proof too, as long as the checksum value gets encrypted before it's put in a message. In that case, the combination of the checksum and the encryption algorithm must be considered a separate checksum algorithm. (For example, RSA-MD5 encrypted using DES is a new checksum algorithm of type RSA-MD5-DES.) For most keyed checksums, plus the encrypted forms of collision-proof 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 result is four (4) octets long. CRC-32 is neither keyed nor collision-proof, so its use 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. If these attacks are a significant threat, collision-proof checksums are recommended.
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 an input message of arbitrary length and produces a 128-bit (16 octet) checksum as output. RSA-MD4 is believed to be collision-proof. ๐ง
6.4.3. RSA MD4 Cryptographic Checksum Using DES (rsa-md4des) ๐
This checksum is the keyed, collision-proof glow-up of the one above ๐ It prepends an 8 octet confounder before the text, applies the RSA MD4 checksum algorithm, and then encrypts the confounder and the checksum with DES in cipher-block-chaining (CBC) mode. It uses a variant of the key, made by eXclusive-ORing the key with the constant F0F0F0F0F0F0F0F0. (A key variant limits a key to one particular function, which keeps generating a checksum separate from other encryption done with the session key. The constant F0F0F0F0F0F0F0F0 was picked because it keeps key parity intact. DES's properties ruled out using the complement. The same constant is used for a similar purpose in the Message Integrity Check of the Privacy Enhanced Mail standard.) The initialization vector should be zero. The result is 24 octets long, and 8 of those octets 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.
Here's the format for the checksum, in diagram form:
+--+--+--+--+--+--+--+--
| des-cbc(confounder
+--+--+--+--+--+--+--+--
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
rsa-md4(confounder+msg),key=var(key),iv=0) |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The format can't be described in ASN.1, but here's an ASN.1-like version for those who prefer it:
rsa-md4-des-checksum ::= ENCRYPTED UNTAGGED SEQUENCE {
confounder[0] UNTAGGED OCTET STRING(8),
check[1] UNTAGGED OCTET STRING(16)
}
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 an input message of arbitrary length and produces a 128-bit (16 octet) checksum as output. RSA-MD5 is believed to be collision-proof. Main character energy ๐๐
6.4.5. RSA MD5 Cryptographic Checksum Using DES (rsa-md5des) โจ
okay bestie, so the RSA-MD5-DES checksum is a keyed collisionproof checksum ๐ and it's giving secure. you stick an 8 octet confounder on the front of the text, run it through the RSA MD5 checksum algorithm, then encrypt the confounder and the checksum with DES in cipher-block-chaining (CBC) mode, using a variant of the key. that variant is made by XORing the key with the constant F0F0F0F0F0F0F0F0. the initialization vector SHOULD be zero. the result is 24 octets long, and 8 of those are redundant ๐ซถ this checksum is tamper-proof and believed to be collision-proof ๐
heads up ๐ the DES specs identify some "weak keys", and those keys SHALL NOT be used for encrypting RSA-MD5 checksums for use in Kerberos.
the format for the checksum looks like this:
+--+--+--+--+--+--+--+--
| des-cbc(confounder
+--+--+--+--+--+--+--+--
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
rsa-md5(confounder+msg),key=var(key),iv=0) |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
the format can't be written in ASN.1, but if you like an ASN.1-ish vibe, here it is:
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 a whole vibe ๐ธ you prepend an 8 octet confounder to the plaintext, run DES CBC-mode encryption on the result using the key and an initialization vector of zero, and keep the last block of that ciphertext. then you prepend the same confounder and encrypt the pair with DES in cipher-block-chaining (CBC) mode, using a variant of the key. the variant is the key XORed with the constant F0F0F0F0F0F0F0F0. the initialization vector SHOULD be zero. the result is 128 bits (16 octets) long, and 64 of those bits are redundant. this checksum is tamper-proof and collision-proof ๐
the format for the checksum looks like this:
+--+--+--+--+--+--+--+--
| des-cbc(confounder
+--+--+--+--+--+--+--+--
+-----+-----+-----+-----+-----+-----+-----+-----+
des-mac(conf+msg,iv=0,key),key=var(key),iv=0) |
+-----+-----+-----+-----+-----+-----+-----+-----+
the format can't be written in ASN.1, but for the ASN.1 fans:
des-mac-checksum ::= ENCRYPTED UNTAGGED SEQUENCE {
confounder[0] UNTAGGED OCTET STRING(8),
check[1] UNTAGGED OCTET STRING(8)
}
the DES specs identify some "weak" and "semiweak" keys ๐ญ those keys SHALL NOT be used for generating DES-MAC checksums for use in Kerberos, and NOR SHALL a key be used whose variant 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 is a keyed collision-proof checksum ๐ง you run the RSA MD4 checksum algorithm, then encrypt the result with DES in cipher-block-chaining (CBC) mode, using a DES key as both the key and the initialization vector. the result is 16 octets long. this checksum is tamper-proof and believed to be collision-proof. ngl, this one is the old way of encoding the RSA-MD4-DES checksum, so it's no longer RECOMMENDED ๐
6.4.8. DES cipher-block chained checksum alternative (desmac-k) ๐ฆ
the DES-MAC-K checksum is computed by running DES CBC-mode encryption on the plaintext and using the last block of the ciphertext as the checksum value. it's keyed with an encryption key and an initialization vector. any uses that don't specify an extra initialization vector will just use the key as both key and initialization vector ๐ the result is 64 bits (8 octets) long. this checksum is tamper-proof and collision-proof. this one is also the old method for encoding the DES-MAC checksum, so it's no longer RECOMMENDED.
the DES specs 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
okay so realm names are encoded as GeneralStrings, and a realm can technically pick whatever name it wants ๐ but interoperability across realm boundaries needs everyone to agree on how realm names get assigned and what info they imply.
to keep this consistent, each realm MUST conform to these conventions itself, and it MUST require that any realms it shares inter-realm keys with also conform to the conventions, and MUST require the same from its neighbors.
there are currently four styles of realm names: domain, X500, other, and reserved. here's an example of each:
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're made of components separated by periods (.), and they contain neither colons (:) nor slashes (/) ๐
X.500 names contain an equal (=) and can't have a colon (:) before the equal. the realm names for X.500 names are string representations of the names, with components separated by slashes. leading and trailing slashes are not included โจ
names in the other category MUST begin with a prefix that has no equal (=) or period (.). that prefix MUST be followed by a colon (:) and the rest of the name. all prefixes MUST be assigned before they MAY be used. right now, none are assigned ๐
the reserved category has strings that don't fall into the first three categories. every name in this category is reserved. it's unlikely any names will get assigned here unless there's a really strong reason not to use the "other" category ๐
these rules guarantee there won't be conflicts between the different name styles. there are also some extra constraints for realm names in the domain and X.500 categories: a realm name in the domain or X.500 format MUST either be used by the organization that owns the Internet domain name or X.500 name (the one it was assigned to), or, if no such name is registered, the authority to use a realm name MAY be derived from the authority of the parent realm. for example, if there's no domain name for E40.MIT.EDU, then the administrator of the MIT.EDU realm can authorize creating a realm with that name ๐
this works because the organization the parent is assigned to is presumably the one allowed to assign names to its children in the X.500 and domain name systems too ๐ซถ if the parent assigns a realm name without also registering it in the domain name or X.500 hierarchy, it's the parent's job to make sure that a name identical to the child's realm name doesn't show up in the future, unless it's assigned to the same entity as the realm name ๐ญ
7.2. Principal Names
just like with realm names, we need conventions so everyone agrees on what info a principal name implies. the name-type field in a principal name tells you what kind of info the name implies. the name-type SHOULD be treated as a hint ๐ ignoring the name type, no two names can be the same (at least one of the components, or the realm, must be different). this constraint MAY be eliminated in the future. these 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 nothing beyond being unique 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's guaranteed never to be reassigned, the name type UID SHOULD be used. (fair warning: it's generally a bad idea to reassign names of any type, since stale entries might stick around in access control lists ๐)
if the first component of a name identifies a service, and the remaining components identify an instance of that service in a server-specified way, then the name type SRV-INST SHOULD be used. the Kerberos ticket-granting ticket is a perfect example: its first component is krbtgt, and its second component identifies the realm the ticket is valid for ๐
if the instance is a single component after the service name and it identifies the host the server is running on, then the name type SRV-HST SHOULD be used. this is typically used for Internet services like telnet and the Berkeley R commands ๐ง if the separate components of the host name show up as successive components after the service name, then the name type SRV-XHST SHOULD be used. this might identify servers on hosts with X.500 names, where the slash (/) could otherwise be ambiguous ๐ฆ
the name type UNKNOWN SHOULD be used when the form of the name isn't 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, though, 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. check out section 8.2.3 for the form those names take โจ
7.2.1. Name of server principals
the principal identifier for a server on a host is usually made of two parts ๐ (1) the realm of the KDC the server is registered with, 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 host name is in a form like X.500 that uses slash (/) separators. the first component of the two- or multi-component name identifies the service, and the later components identify the host. where the host name isn't case sensitive (like Internet domain names), the host name MUST be lower case ๐ for services like telnet and the Berkeley R commands, which run with system privileges, the first component is the string "host" instead of a service-specific identifier ๐ซถ
8. Constants and other defined values
8.1. Host address types ๐ธ
okay so all the negative values for the host address type are reserved for local use ๐ and every non-negative value is saved for officially assigned type fields and interpretations โจ
the type values for the addresses below match the defined address family constants in the Berkeley Standard Distributions of Unix. you can find those in <sys/socket.h> with the 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, and 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
okay this one is so important bestie ๐ญ when a client is contacting a Kerberos server (KDC) for a KRB_KDC_REQ request over IP transport, the client SHALL send a UDP datagram that contains only an encoding of the request to port 88 (decimal) at the KDC's IP address. the KDC will then reply with a datagram that contains only an encoding of the reply message, which is either a KRB_ERROR or a KRB_KDC_REP, sent to the sending port at the sender's IP address ๐
8.2.2. OSI transport
during authentication of an OSI client to an 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. 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. Kerberos MAY use the application code 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, and (2) a two-part name of type NT-SRVINST, where the first part is "krbtgt" and the second part is the name of the realm that 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) and ("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) and ("krbtgt", "MIT.EDU") (name) ๐
8.3. Protocol constants and associated values ๐
the following tables list the constants used in the protocol and define what they mean. let's go ๐
---------------+-----------+----------+----------------+---------------
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
--------------+-------+-----------------------------------------
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
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
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 are described in section 5.9.1 ๐ฅบ
9. Interoperability requirements โจ
okay so Kerberos v5 is literally packed with options ๐ like multiple encryption and checksum types, different encodings for the transited field, optional pre-auth stuff, what to do with tickets that have no addresses, mutual auth, user to user auth, proxies, forwarding, postdating, renewing tickets, the format of realm names, and how authorization data gets handled ๐ซถ. Since everyone's doing their own thing, realms need a minimal configuration that all implementations MUST support so they can actually talk to each other ๐ฆ. This minimal config is allowed to change as tech does ๐. Like, if one of the required encryption or checksum algorithms turns out to be insecure, it'll get replaced ๐.
9.1. Specification 1
this section defines the first spec of these options ๐. Implementations that are configured this way can be said to support Kerberos Version 5 Specification 1 (5.1) ๐.
Encryption and checksum methods
the following 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, andDES-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 KDC's realm and the requested realm ๐บ๏ธ.
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 it is 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 it is 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) ๐๏ธ. 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 ๐ค. However, individual realms MAY decide, as a matter of policy, to reject such requests on a per-principal or realm-wide basis ๐
โโ๏ธ.
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
here's 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 ๐
okay the early versions of this doc, which described version 4 of the protocol, were written by Jennifer Steiner (formerly at Project Athena) ๐ท. Those drafts gave us an excellent starting point for this version 5 spec ๐. Tons of 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 spec into its current form ๐ซถ.
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).
[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 ๐
okay this one is short but SO important ๐: 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
A. Pseudo-code for protocol processing โจ
this appendix is the pseudo-code for how clients and servers build and read the messages ๐งโ๐ป๐.
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
A.2. KRB_AS_REQ verification and KRB_AS_REP generation โจ
Okay bestie, this is where the KDC gets the AS request and does all the checking ๐๐ Let's look at the full pseudo-code.
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;
/* 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);
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;
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 ๐
Now the client side gets to check the reply ๐ Here's the pseudo-code for that.
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
print(warning message);
endif
save_for_later(ticket,session,client,server,times,flags);
A.4. KRB_AS_REP and KRB_TGS_REP common checks ๐ง
These checks are shared by both the AS and TGS replies, so they get their own spot ๐ Here's the full set.
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;
endif
A.5. KRB_TGS_REQ generation ๐ฆ
Last one for this part: how a TGS request gets built, step by step ๐ธ
/* 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
/* 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 โจ
okay bestie, this is where the KDC checks a KRB_TGS_REQ and builds the KRB_TGS_REP ๐ the comments are the good part ๐
/* 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
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;
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
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);
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
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,
req.padata.authenticator.subkey;
else resp.enc-part := encrypt OCTET STRING
using use_etype, tgt.key;
send(resp);
A.7. KRB_TGS_REP verification ๐
now the other side ๐ฆ the client decodes the response and checks it, and if something's off it tosses the response and zeroes that session key right away ๐ฅ
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 โจ
okay so this is where the authenticator gets built, bestie ๐ the client fills in its name and realm, then adds the optional bits like a checksum, timestamp, sub-session key, and sequence number. literally so pretty ๐
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
A.9. KRB_AP_REQ generation ๐ฆ
this one is the client's request to a service, and it's giving organized ๐ grab the ticket and session key from the cache first, then set the version and message type. the MUTUAL-REQUIRED and USE-SESSION-KEY flags get set or reset depending on what the client wants, then the authenticator gets generated, encoded, and encrypted with the session key ๐
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 ๐
this is the service checking the request, and it's a whole gauntlet ๐ญ version numbers, message type, the ticket version, which key to use, decryption, name matching, addresses, clock skew, replays, and then whether the ticket is valid right now. every check has its own error, so nothing slips through ๐ซถ
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
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 ๐ธ
the service's reply, which is basically "yep, I got you" ๐ it copies the client's timestamp and microseconds into the body, optionally adds a sub-session key and sequence number, then encodes and encrypts the whole thing.
packet.pvno := protocol version; /* 5 */
packet.msg-type := message type; /* KRB_AP_REP */
body.ctime := packet.ctime;
body.cusec := packet.cusec;
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 ๐
now the client checks the reply. it decrypts with the ticket's session key, makes sure the timestamps match what it sent, then saves the sub-session key and sequence number if they're there. if it all lines up, mutual auth succeeds โจ
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 ๐ฅ
KRB_SAFE is for sending user data with integrity protection, so it's a checksum situation ๐ง the sender collects the data, optionally adds a timestamp and sequence number, lists its own addresses, and adds a recipient address if there's only one recipient. then it computes the checksum over the body and attaches it.
collect user data in buffer;
/* assemble packet: */
packet.pvno := protocol version; /* 5 */
packet.msg-type := message type; /* KRB_SAFE */
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 ๐ง
the receiver does the usual version and message type checks, then makes sure the checksum type is both collision-proof and keyed. after that it runs the common checks, recomputes the checksum, and compares. if the numbers don't match, it's giving modified ๐จ
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 ๐
these are the shared checks that both KRB_SAFE and KRB_PRIV run, and they're thorough bestie ๐ the sender address has to match, the recipient address has to match if it's there, the timestamp needs to be inside clock skew, repeats get caught, the sequence number needs to be in order, and a message with neither a timestamp nor a sequence number counts as modified. the identifier then gets saved so it can't be replayed ๐ฆ
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
/* 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 ๐ง
KRB_PRIV is the encrypted version of the KRB_SAFE idea, so it's the private one ๐คซ it sets the encryption type, builds the same kind of body with the user data, optional timestamp and sequence number, and addresses, then encodes it and encrypts it into the packet.
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
encode body into OCTET STRING;
select encryption type;
encrypt OCTET STRING into packet.enc-part.cipher;
A.17. KRB_PRIV verification ๐
last one in this batch, and it's the receiver's final check ๐ it validates the version and message type, decrypts with the negotiated key, then runs the common checks. if everything passes, the user data comes back marked as genuine and unmodified โจ
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 ๐
okay bestie, this is how you pack up tickets to send to a peer ๐ first you go get the tickets you want to forward, then you stack them into the packet and wrap the details up in an encrypted envelope ๐โจ
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;
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 ๐
this is the receiver's checklist, and it's giving very thorough ๐ค check the version, check the message type, decrypt, then make sure the addresses, the timestamp, the repeats, and the nonce all line up before saving the tickets for later ๐ฆ
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);
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 ๐จ
and last but absolutely not least, the error packet ๐ it stamps the server name and the time, then fills in whatever extra info it actually has, like the client's time, name, text, or data. so cute, so organized, no notes ๐ซถ
/* 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
๐ฆ where this page comes from
- ietf: rfc1510 1993-09
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms