MIT Information Systems

Macintosh Development


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


KClient Programmer Documentation

revised by Miro Jurisic


The basics of Kerberos

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:

Authentication service
Used to "login" to the network, or, in Kerberos terminology, to obtain a ticket for the ticket-granting service (called a ticket-granting ticket).
Ticket granting service
Used to obtain tickets for other services
Administration service
Handles adding or modifying kerberos principal records, changing passwords etc.

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/.

Adding Kerberos support to an application

Client support

Most client applications need to use only three calls in order to add Kerberos authentication support. The calls are:

Server support

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.

Message privacy

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.

User Interface

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:

Session management

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.

Miscellaneous

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


Questions or comments? Send mail to macdev@mit.edu
Last updated on $Date: 1999/07/02 20:59:09 $
Last modified by $Author: lxs $