If you are in an environment where you are integrating Mac OS X with a 3rd party KDCs, you already know about builtin:krb5authnoverify addition to your /etc/authorization. But did you know that you can use the builtin:krb5authenticate option to provide better security by assuring that your KDC is not being spoofed? Are you safe from the "Zanarotti attack"? Read on to find out how to get it set up and running.
Getting Mac OS X set up to use a 3rd party KDC is relatively straightforward, and is covered in some detail at http://docs.info.apple.com/article.html?artnum=107154. However, let's dig in a bit deeper and see what those changes to your /etc/authorization are actually doing, and how to provide TGT verification.
First, let's review how to set Mac OS X up for kerberos authentication with a 3rd party KDC.
1. Craft a valid /Library/Preference/edu.mit.Kerberos file. This is the same file that is normally found in /etc/krb5.conf file on other *nixs in your network, so grab a copy and install it on the client. It specifies your kerberos realm, where the KDC is, and other client related options. You can now do a "kinit" in terminal and should be able to authenticate as a kerberos user. This helps to verify that you got the edu.mit.Kerberos file correct.
2. Set up the login window to use your 3rd party KDC. In order for login window to use your 3rd party KDC, you'll need to modify /etc/authorization. /etc/authorization is the file that the Security framework uses to figure out who can access what system resources. For instance, when you click on the lock in the Accounts pane of System Preferences, the /etc/authorization file specifies that "system.preferences.accounts" right requires admin authentication. /etc/authorization could be an article on its own, but for now, let's focus on the section for login window. This is the system.login.console section:
<key>system.login.console</key>
<dict>
<key>class</key>
<string>evaluate-mechanisms</string>
<key>comment</key>
<string>Login mechanism based rule. Not for general use, yet.</string>
<key>mechanisms</key>
<array>
<string>builtin:smartcard-sniffer,privileged</string>
<string>loginwindow:login</string>
<string>builtin:reset-password,privileged</string>
<string>builtin:auto-login,privileged</string>
<string>builtin:authenticate,privileged</string>
<string>HomeDirMechanism:login,privileged</string>
<string>HomeDirMechanism:status</string>
<string>MCXMechanism:login</string>
<string>loginwindow:success</string>
<string>loginwindow:done</string>
</array>
</dict>
The login window uses this section to figure out how to authenticate the user, and uses Security Agent Plugins to do this. These live in /System/Library/CoreServices/SecurityAgentPlugins, and are specified in the mechanisms section. Each mechanism is specified by the name of the plugin, a colon, and the name of the mechanism that is passed to the plugin. This allows the same plugin to provide multiple mechanisms. There is also the "privileged" option. If this is specified, the plugin is run in a privileged context where the effective UID is 0 (root). If privileged is not specified, the plugin is in non-privileged mode, and can access the GUI.
The mechanisms are checked in order, and a fallure or error in any mechanism would cause the authenication to fail. Let's go down them in order:
builtin:smartcard-sniffer,privileged
provides smart card support at login window
loginwindow:login
Presents the actual login window GUI. Doesn't authenicate, but rather just puts up the GUI.
builtin:reset-password,privileged
Checks to see if the users password needs to be reset.
builtin:auto-login,privileged
support for auto-login via the /etc/kcpassword file
builtin:authenticate,privileged
checks the user's credentials via Directory Services
HomeDirMechanism:login,privileged
HomeDirMechanism:status
Filevault support, and tools to fix filevault issues, such as password changing.
MCXMechanism:login
GUI for selecting workgroup if you are in multiple workgroups
loginwindow:success
loginwindow:done
cleanup and GUI progress updates
As you can see, the heavy lifting for authenication is done by the builtin:authenticate,privileged mechanism. This is the mechanism we need to change in order to authenicate off of a 3rd party KDC. You can change "builtin:authenticate,privileged" to "builtin:krb5authnoverify,privileged", and you can authenticate via the login window to a KDC, but you'll be vunerable an the Zanarotti attack.
The Zanarotti attack leverages the idea that if successful authentication is based on the receipt of the TGT, this can be exploted to break into system. The attacker sets up a machine on the local network that can craft TGT with a current user name and a password made up by the attacker. This machine then continually floods the network with the TGT. The attacker then goes to the machine they want to break into, and puts in a valid username and the password that was used to generate the rogue TGTs. The client system will try to authenicate via kerberos by requesting a TGT, and there is a good chance that the TGT that gets received back is from the rogue system. Since the fake TGT is encrypted with the attackers password, the login window can decrypt the TGT and allow login. The TGT is not good for getting actual service tickets, but the attacker doesn't care; they got access to the local files.
Note that this attack requires access to the network to flood the network with TGTs, as well as access to the Login Window to type in the credentials. Also note that the Zanarotti attack doesn't work with Open Directory, since the login windows does not predicate login with getting a TGT, but rather authenticates via the Password Server specified in the user's authentication authority. The Active Directory plugin does a host princincal verification like the one that is described below.
In order to prevent this type of attack, the host should actually try and use the TGT that it receives, and see if it can access services locally with it. In order to do this, a keytab must be installed on the local machine, and must contain a host principal, like this:
host/[email protected]
Now, when login window receives the TGT, and decrypts it with the user's password, it turns around and asks the KDC for a services ticket for the "host" service on the local machine. Since the service ticket is encrypted with the host principal on the KDC and can only be decrypted with the host principal on the client, the client can be sure that the TGT and the user's password are valid (since only the "real" KDC would be able to encrypt something with the host principal)
If you are installing keytabs on the local clients, there is an issue you should be aware of. They keytab principals for services on the local machine as based on DNS name (like host1.example.com from above). If your clients are assigned IP address via DHCP, the hostname of the client system will change. In order to prevent this, you either need to assign static IP address and the corresponding forward DNS records, or use the scutil to hardcode in the hostname:
sudo scutil –set HostName host1.example.com
You now need to get a keytab from your KDC that has a host principal for each client. Install it in /etc/krb5.keytab, and verify you can read it with:
sudo klist -k
Also verify that they keytab is valid by trying to authenticate as the host principal, but instead of using a password to authenicate, use the keytab:
kinit -k host/host1.example.com
If you don't get an error, verify that you have a TGT with the host principal using klist.
Now that you have verified that the keytab is correct, in /etc/authorization, change "builtin:authenticate,privileged" to "builtin:krb5authenticate,privileged". You should now be able to log in with authentication via your 3rd party KDC. If you look in the logs on the KDC, you should see a request for a TGT for the user principal, followed by a host principal request for the client.
Sweet! It is all working. Or is it? If you try and use that user as a admin user by adding them to the admin groups via dseditgroup, or if you try to unlock the screen saver or unlock from sleep, you cannot use these credentials. Why? Because the authentication dialog that comes up is not the login window, but a different authentication dialog, and does not use the system.login.console rights. In order to get this last piece to work, edit the "authenticate" rule /etc/authorization:
<key>authenticate</key>
<dict>
<key>class</key>
<string>evaluate-mechanisms</string>
<key>mechanisms</key>
<array>
<string>builtin:smartcard-sniffer,privileged</string>
<string>builtin:authenticate</string>
<string>builtin:authenticate,privileged</string>
</array>
</dict>
Note that it looks similar to the system.console.login, but has a smaller set of mechanisms. The difference in the mechanisms is the "builtin:authenticate" mechanism without the privilege option. This presents the dialog box that asks for the user's credentials. As with the system.console.login, replace "builtin:authenticate,privileged" with "builtin:krb5authenticate,privileged". Now when you authenticate when waking from sleep, unlocking the screen saver, or unlocking a system preference, you'll renew your TGT and know that both the user's and the TGT are valid.
It will also give you rights to drop the "Zanarotti attack" phrase at dinner parties when people talk about Kerberos authentication. Good times.
You can go one step further by using a kerberos password to authenticate sudo requests. The following is an article I wrote for our internal KB:
PAM kerberos module needed for sudo command
——————————————————
When a user with an NIS or LDAP account (and no password) needs to escalate to root, the preferred
method is to use the "sudo" command ("sudo -s" will give the user a root shell). If
the computer uses kerberos for authentication then the sudo command will not work.
This page describes how to kerberize the sudo command by adding a PAM module.
1. Get the PAM module.
* Source code for the module can be found at
http://www.math.gatech.edu/~villegas/pam_krb5/
2. Install the PAM module
— 1. sudo cp pam_krb5.so /usr/lib/pam
— 2. sudo chmod 444 /usr/lib/pam/pam_krb5.so
— 3. sudo chown root:wheel /usr/lib/pam/pam_krb5.so
3. Tell the computer to use the new PAM module
— 1. vi /etc/pam.d/sudo
— 2. add the following line below the other "auth sufficient" lines
auth sufficient pam_krb5.so
— 3. :wq!
4. Modify the /etc/sudoers file with visudo as appropriate.
—
To recap, one best practice to secure your Mac (or other *nix box) is to limit access
to the root account. If you turn off PermitRootLogin in the sshd_config, then no one
can login as root. You then need to add an account for each user that needs to access
the machine (or use NIS or LDAP), and add to the /etc/sudoers file those users that
need to have escalated privileges. You can use sudo to give all or limited escalated
privileges to different users, groups, or computers (via netgroups). See man sudoers .
If you are using LDAP or NIS with kerberos passwords, you should not use a
.k5login in the root home directory. If a person’s credentials are hijaked (which is
possible) you don’t want the hijacker to have unauthenticated root access to your
box. To reduce the chance of this happening, instead of the .k5login file, have the
users use the sudo command. On Red Hat, sudo is already kerberized and the user’s
kerberos password will work. On a Mac you need to follow the instructions above to kerberize
sudo. You also need to configure the machine for kerberos
(/Library/Preferences/edu.mit.kerberos needs to be configured). If the users need to
ssh into the machine, then the machine also needs a keytab