Macintosh Development |
revised by Miro Jurisic
This is basically how Kerberos works: in the Kerberos system every user and every service has an identity often referred to as a Kerberos principal. For user friendliness, KClient refers to the user's identity as a network-id (???no). Kerberos principals are stored in a database along with a 64-bit key which is derived from the principal's password.
Kerberos administration is divided into realms. Usually each institution administers its own realm. In some cases, large or distributed institutions may administer multiple realms or several institutions may share one realm; however, these are the exceptions.
A Kerberos realm is created by setting up a Kerberos server. A Kerberos server has three components:
Kerberos provides authentication functionality through encrypted tokens called "tickets". A typical Kerberos interaction involves three players, the application client, the application server, and the Kerberos server. The client gets tickets from the Kerberos server. Clients need a secret key, sometimes derived from the user's password, sometimes randomly generated for use in a particular session, in order to decode the ticket and make use of it. A client generates an authenticator derived from a ticket that it received from the Kerberos server. The client sends the authenticator to an application server. Authenticators have protections schemes that prevent replay and tampering.
When an application server receives an authenticator from the client, the application server decodes the authenticator using the server's private key. This process provides the server with proof that user X at the client end has posession of user X's password, and most likely is user X. This proof is obtained without user X ever having sent her password to the server.
Only user X knows her password, but the Kerberos database has user X's private key that was generated from user X's password and is virtually equivalent to user X's password as far as security goes. Since the Kerberos server contains all the keys to the kingdom, it's kept in a really secure place.
KClient always obtains a ticket-granting ticket when the user enters their network-id (???) and password. Once the ticket is obtained, other tickets can be obtained using the ticket-granting ticket for up to eight hours (or less, depending on how the Kerberos administrator has configured your realm).
Tickets are stored in memory in the ticket cache. When you reboot your Macintosh, all tickets are destroyed and you need to login again to access authenticated services.
Of course, this is a gross simplification of Kerberos. I can see its designers squirming in their chairs. However, for most people, this explanation suffices. For a more in-depth technical discussion of Kerberos, see MIT FTP site ftp://athena-dist.mit.edu/pub/kerberos/.
Most client applications need to use only three calls in order to add Kerberos authentication support. The calls are:
Servers use the following calls to verify a client authenticator and create a mutual authenticator to send back to the client.
Client authenticators have a life time associated with them. Servers that only authenticate once in the beginning of a session need to check this time periodically and deny service once the session time runs out.
Usually the server's key is obtained from the Kerberos administrator in a key file called "srvtab". The servers key is normally randomly generated by Kerberos. KClient provides another means of creating a srvtab file. You can use KServerAddKey to create a srvtab file for a principal that you know the password for. For quick and dirty operations this is handy because you can use your personal network-id as the principal for the server.
CAUTION: any machine that stores a key file on its disk should be kept in a secure are because anyone who has access to the disk has access to the key and therefore has access to anything that this principal has access to.
Once a session between client and server is established by a call to KClientGetTicketForService (clients) or KServerVerifyTicket (servers), a data stream can encrypted using the Data Encryption Standard (DES) by calling:
KClientEncrypt -to encrypt data
To reverse the process, the receiving end of an encrypted stream calls:
KClientDecrypt - to decrypt data
The way this works is that when you call KClientGetTicketForService or KServerVerifyTicket, a session key is stored in the KClient session record. When you encrypt or decrypt data this session key (shared between client and server) is used as the key for encryption.
The user interface provided by KClient is acceptable for most applications. The default behavior is for KClient to display a window asking for network-id and password anytime an application calls a routine that requires a ticket if a ticket granting ticket is not already in the ticket cache.
Sometimes it may be desirable to bypass the default user interface behavior. This usually requires that you take over management of the Kerberos session. For instance, you might want to always want to prompt the user for a password at the beginning of your session. You can do this by either logging the user out (KClientLogout) which will cause the user to be prompted the next time you call KClientGetTicketForService. Another way to do this is to call KClientLogin which will cause the user to be prompted for a password immediately.
Another example is that you might want to bypass the login window entirely. You can do this by testing the login status (KClientStatus) before calling KClientGetTicketForService. If the user is not logged in you can login without KClient prompting the user. If you prompt the user for a password, call KClientSetUserName to set the network-id, and KClientPasswordLogin to login. You could also convert the user's password to a key, store it and use it later to login by calling KClientKeyLogin.
Bypassing KClient's user interface is not recommended unless it is really necessary.
The default text in the login window can be modified:
A client application can find out the username of the user currently logged into this system by calling KClientGetUserName. If a server application calls KClientGetUserName, it returns the server's principal.
KClientGetSessionUserName always returns the client's network-id regardless of whether this is a client application or a server application.
An application can obtain the 64-bit session key that was generated for a particular client/server session. While there are no KClient routines that accept a session key as input, the session key can be used directly with DES routines.
You can convert errors returned by KClient into text messages appropriate for display:
If you require specific features, you can obtain the version number of KClient. Version number is returned in the format of ??? 'vers' resource:
<don't need all of this crap>
/* KClient.h -- Application interface for KClient © Copyright 1994,95 by Project Mandarin Inc. Initial coding 8/94 Peter Bosanko. Added new routines 8/95 PCB Moved some constants from krbdriver.h ======================================================================== DES and Kerberos portions of this file are... ======================================================================== Copyright (C) 1989 by the Massachusetts Institute of Technology Export of this software from the United States of America is assumed to require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. */ #ifndef _KCLIENT_ #define _KCLIENT_ #ifndef _TYPES_ #include <Types.h> #endif /* Error codes */ enum { cKrbCorruptedFile = -1024, /* couldn't find a needed resource */ cKrbNoKillIO, /* can't killIO because all calls sync */ cKrbBadSelector, /* csCode passed doesn't select a recognized function */ cKrbCantClose, /* we must always remain open */ cKrbMapDoesntExist, /* tried to access a map that doesn't exist (index too large, or criteria doesn't match anything) */ cKrbSessDoesntExist, /* tried to access a session that doesn't exist */ cKrbCredsDontExist, /* tried to access credentials that don't exist */ cKrbTCPunavailable, /* couldn't open MacTCP driver */ cKrbUserCancelled, /* user cancelled a log in operation */ cKrbConfigurationErr, /* Kerberos Preference file is not configured properly */ cKrbServerRejected, /* A server rejected our ticket */ cKrbServerImposter, /* Server appears to be a phoney */ cKrbServerRespIncomplete, /* Server response is not complete */ cKrbNotLoggedIn, /* Returned by cKrbGetUserName if user is not logged in */ cKrbOldDriver, /* old version of the driver */ cKrbDriverInUse, /* driver is not reentrant */ cKrbAppInBkgnd, /* driver won't put up password dialog when in background */ cKrbInvalidSession, /* invalid structure passed to KClient/KServer routine */ cKrbOptionNotDefined, /* returned from GetOption */ cKrbKerberosErrBlock = -20000 /* start of block of 256 kerberos error numbers */ }; #define LARGEST_DRIVER_ERROR cKrbOptionNotDefined typedef char KClientErrString[64]; enum { KClientLoggedIn, KClientNotLoggedIn }; /* Different kerberos name formats (for KServerGetUserName) */ enum { KClientLocalName, /* Don't specify realm */ KClientCommonName, /* Only specify realm if it isn't local */ KClientFullName /* Always specify realm */ }; /* Options */ enum { kclientOptionSaveName = 1, kclientOptionSynchTime, kclientOptionShowMenu, kclientOptionInstalled_1_6 }; struct KClientKey { unsigned char keyBytes[8]; }; typedef struct KClientKey KClientKey; struct KClientSessionInfo { char sessionBytes[256]; }; typedef struct KClientSessionInfo KClientSessionInfo; typedef KClientSessionInfo *KClientSessionPtr; /* Defines for obsolete function names */ #define KClientInitSession KClientNewSession #define KClientVerifySendAuth KClientVerifyReplyTicket /************************************/ /* Some includes from des.h & krb.h */ /************************************/ #if defined(powerc) || defined(__powerc) #pragma options align=mac68k #endif #ifndef DES_DEFS typedef unsigned char des_cblock[8]; /* crypto-block size */ /* Key schedule */ typedef struct des_ks_struct { des_cblock _; } des_key_schedule[16]; #endif /* DES_DEFS */ #ifndef KRB_DEFS #define C_Block des_cblock #define Key_schedule des_key_schedule /* The maximum sizes for aname, realm, sname, and instance +1 */ #define ANAME_SZ 40 #define REALM_SZ 40 #define SNAME_SZ 40 #define INST_SZ 40 /* Definition of text structure used to pass text around */ #define MAX_KTXT_LEN 1250 struct ktext { long length; /* Length of the text */ unsigned char dat[MAX_KTXT_LEN]; /* The data itself */ unsigned long mbz; /* zero to catch runaway strings */ }; typedef struct ktext *KTEXT; typedef struct ktext KTEXT_ST; struct credentials { char service[ANAME_SZ]; /* Service name */ char instance[INST_SZ]; /* Instance */ char realm[REALM_SZ]; /* Auth domain */ C_Block session; /* Session key */ long lifetime; /* Lifetime */ long kvno; /* Key version number */ KTEXT_ST ticket_st; /* The ticket itself */ long issue_date; /* The issue time */ char pname[ANAME_SZ]; /* Principal's name */ char pinst[INST_SZ]; /* Principal's instance */ }; typedef struct credentials CREDENTIALS; /* Structure definition for rd_private_msg and rd_safe_msg */ struct msg_dat { unsigned char *app_data; /* pointer to appl data */ unsigned long app_length; /* length of appl data */ unsigned long hash; /* hash to lookup replay */ long swap; /* swap bytes? */ long time_sec; /* msg timestamp seconds */ unsigned char time_5ms; /* msg timestamp 5ms units */ }; typedef struct msg_dat MSG_DAT; typedef unsigned long u_long; typedef unsigned short u_short; #define KRB_PASSWORD_SERVICE "changepw.kerberos" #endif /* KRB_DEFS */ #if defined(powerc) || defined(__powerc) #pragma options align=reset #endif #ifdef __cplusplus extern "C" { #endif
OSErr KClientNewSession(KClientSessionInfo *session, unsigned long lAddr,unsigned short lPort,unsigned long fAddr,unsigned short fPort);
OSErr KClientDisposeSession(KClientSessionInfo *session);
OSErr KClientGetTicketForService(KClientSessionInfo *session, char *service,void *buf,unsigned long *buflen);
OSErr KClientLogin( KClientSessionInfo *session, KClientKey *privateKey );
OSErr KClientSetPrompt( KClientSessionInfo *session, char *prompt );
OSErr KClientPasswordLogin( KClientSessionInfo *session, char *password, KClientKey *privateKey );
OSErr KClientPasswordToKey( char *password, KClientKey *privateKey );
OSErr KClientKeyLogin( KClientSessionInfo *session, KClientKey *privateKey );
OSErr KClientLogout( void );
short KClientStatus( void );
OSErr KClientVersion( short *majorVersion, short *minorVersion, char *versionString );
OSErr KClientGetUserName(char *user);
OSErr KClientGetSessionUserName(KClientSessionInfo *session, char *user, short nameType);
OSErr KClientSetUserName(char *user);
OSErr KClientCacheInitialTicket(KClientSessionInfo *session, char *service);
OSErr KClientGetSessionKey(KClientSessionInfo *session, KClientKey *sessionKey);
OSErr KClientMakeSendAuth(KClientSessionInfo *session, char *service,void *buf,unsigned long *buflen,long checksum, char *applicationVersion);
OSErr KClientVerifyReplyTicket(KClientSessionInfo *session, void *buf,unsigned long *buflen );
OSErr KClientEncrypt(KClientSessionInfo *session, void *buf,unsigned long buflen,void *encryptBuf,unsigned long *encryptLength);
OSErr KClientDecrypt(KClientSessionInfo *session, void *buf,unsigned long buflen,unsigned long *decryptOffset,unsigned long *decryptLength);
void KClientErrorText(OSErr err, char *text); /* KServer calls */
OSErr KServerNewSession( KClientSessionInfo *session, char *service, unsigned long lAddr,unsigned short lPort,unsigned long fAddr,unsigned short fPort);
OSErr KServerVerifyTicket( KClientSessionInfo *session, void *buf, char *keyFileName );
OSErr KServerGetReplyTicket( KClientSessionInfo *session, void *buf, unsigned long *buflen );
OSErr KServerGetKey( KClientSessionInfo *session, KClientKey *privateKey, char *service, long version, char *filename );
OSErr KServerAddKey( KClientSessionInfo *session, KClientKey *privateKey, char *service, long version, char *filename );
OSErr KServerGetSessionTimeRemaining( KClientSessionInfo *session, long *seconds );
/* Configuration routines */ OSErr KClientGetLocalRealm( char *realm ); OSErr KClientSetLocalRealm( char *realm ); OSErr KClientGetRealm( char *host, char *realm ); OSErr KClientAddRealmMap( char *host, char *realm ); OSErr KClientDeleteRealmMap( char *host ); OSErr KClientGetNthRealmMap( long n, char *host, char *realm ); OSErr KClientGetNthServer( long n, char *host, char *realm, Boolean admin ); OSErr KClientAddServerMap( char *host, char *realm, Boolean admin ); OSErr KClientDeleteServerMap( char *host, char *realm ); OSErr KClientGetNthServerMap( long n, char *host, char *realm, Boolean *admin ); OSErr KClientGetNthServerPort( long n, short *port ); OSErr KClientSetNthServerPort( long n, short port ); OSErr KClientGetNumSessions( long *n ); OSErr KClientGetNthSession( long n, char *name, char *instance, char *realm ); OSErr KClientDeleteSession( char *name, char *instance, char *realm ); OSErr KClientGetCredentials( char *name, char *instance, char *realm, CREDENTIALS *cred ); OSErr KClientAddCredentials( char *name, char *instance, char *realm, CREDENTIALS *cred ); OSErr KClientDeleteCredentials( char *name, char *instance, char *realm, char *sname, char *sinstance, char *srealm ); OSErr KClientGetNumCredentials( long *n, char *name, char *instance, char *realm ); OSErr KClientGetNthCredential( long n, char *name, char *instance, char *realm, char *sname, char *sinstance, char *srealm ); OSErr KClientAddSpecial( char *service, char *name ); OSErr KClientDeleteSpecial( char *service ); OSErr KClientGetNumSpecials( long *n ); OSErr KClientGetNthSpecial( long n, char *name, char *service ); OSErr KClientSetOption( short option, void *value ); OSErr KClientGetOption( short option, void *value ); #ifdef __cplusplus } #endif #endif