An icon image (description text to the right)

Kerberos Login and Logout Plugin API

This documentation applies to Mac OS X 10.2 and later.

Purpose of login and logout plugins

Login and logout plugins are called whenever a user logs in or logs out. They can be used, for example, to communicate with other authentication systems which require Kerberos tickets, or to automatically mount network volumes which require Kerberos authentication.

Login and logout plugins are invoked by the Kerberos Login API whenever any of the login and logout functions in the Kerberos Login API is called.


Configuration of login and logout plugins

In order to activate a login and logout plugin, the libdefaults section in the edu.mit.Kerberos file has to include a login_logout_notification line:


[libdefaults]
    ...
    login_logout_notification = "PluginName"

On that line, PluginName, should be a unique string describing the plugin. The string must not be longer than 127 characters, and must end in ".loginLogout".

The plugin file itself has to be located in the directory /Library/Kerberos Plug-Ins/ (note space and hyphen) - you may have to create this directory if it doesn't already exist.


Format of login and logout plugins

A login and logout plugin imust be a Mach-O bundle with a name ending in ".loginLogout".

The plugin must export three functions, which are detailed in the next section.

The plugin can contain any other code, data, or resources, but be mindful that it can be executed by arbitrary applications, and therefore should be careful not to affect the application's resources or resource handling.


Functions exported by a login/logout plugin

All three of these functions must be exported by every login/logout plugin. All functions use C linkage and standard Mach-O calling conventions.

The functions and related type declarations are specified in the /System/Library/Frameworks/Kerberos.framework/Headers/KLLoginLogoutNotification.h header file.

KerberosLoginNotification_InitializePlugin


OSErr KerberosLoginNotification_InitializePlugin (
    KLN_APIVersion          inAPIVersion);

This function is called by the Kerberos Login API when the plugin is loaded. The function must return noErr only if the plugin understands the API version passed in inAPIVersion; otherwise, it should return paramErr.

If this function returns anything but noErr, the other functions will not be called.

For example, a plugin which only understands the version 1 of the API (which is the current version), would do this:


KLStatus KerberosLoginNotification_InitializePlugin (
    KLPT_APIVersion      inAPIVersion)
{
    if (inAPIVersion == kKLN_APIVersion1) {
        return noErr;
    } else {
        return paramErr;
    }
}

KerberosLoginNotification_Login


KLStatus KerberosLoginNotification_Login (
	KLN_LoginType	inLoginType,
	const char*     inCredentialsCache);

This function is called when a user is logging in. When the plugin is called, the ticket-granting tickets for the user have already been acquired and stored in a credentials cache.

On input, inLoginType is either kKLN_DialogLogin or kKLN_PasswordLogin. If it is kKLN_DialogLogin, the plugin is running in a context where it is safe to display user interface. If it is kKLN_PasswordLogin, then the plugin is running in a context where it is impossible to display user interface (for example, a faceless background application), and must not attempt to do so.

On input, inCredentialsCache points to a C string which is the name of the credentials cache in which the ticket granting tickets for the user being logged in have been stored. The plugin can open this credentials cache with the Credentials Cache API function cc_context_open_ccache() and use the tickets to acquire additional Kerberos tickets.

If the plugin wants to abort the login process, it must return an error other than noErr. The Kerberos Login library will then abort the login process, destroy any tickets that may have been acquired, and display an error dialog to the user, using the plugin's errors to convert the error returned to a string.

If the plugin returns noErr, the Kerberos Login library will proceed with login.

For example, the following code displays a confirmation dialog to the user when the plugin can display a dialog, and posts a notification if it can't display a dialog. This plugin demonstrates the use of the CredentialsCache API to get information about the user being logged in:


KLStatus KerberosLoginNotification_Login (
  KLN_LoginType   inLoginType,
  const char*     inCredentialsCache)
{
  switch (inLoginType) {
    case kKLN_DialogLogin:
      AlertStdAlertParamRec alertParam;
      alertParam.movable = false;
      alertParam.helpButton = false;
      alertParam.filterProc = nil;
      alertParam.defaultText = "\pLogin";
      alertParam.cancelText = "\pCancel";
      alertParam.otherText = nil;
      alertParam.defaultButton = kAlertStdAlertOKButton;
      alertParam.cancelButton = kAlertStdAlertCancelButton;
      alertParam.position = kWindowAlertPositionParentWindow;

      SInt16  item;
      OSErr err = StandardAlert (kAlertNoteAlert,
        "\pAre you sure you want to login?", "\pMake sure noone is looking over your shoulder.",
        &alertParam, &item);
      if ((err == noErr) && (item == kAlertStdAlertOKButton)) {
        return noErr;
      } else {
        return 22221;
      }
      
    case kKLN_PasswordLogin:
      static NMRec  notification;
      static Str255 principal;
      notification.qType = nmType;
      notification.nmStr = principal;
      notification.nmResp = (NMUPP) -1;
      
      cc_context_t  ccContext = nil;
      cc_ccache_t   ccache = nil;
      cc_string_t   ccPrincipal = nil;
      
      cc_int32 ccErr = cc_initialize (&ccContext, ccapi_version_3, nil, nil);
      if (ccErr == ccNoError) {
        ccErr = cc_context_open_ccache (ccContext, inCredentialsCache, &ccache);
      }
      
      if (ccErr == ccNoError) {
        ccErr = cc_ccache_get_principal (ccache, cc_credentials_v4, &ccPrincipal);
      }
      
      if (ccErr == ccNoError) {
        CopyCStringToPascal (ccPrincipal -> data, principal);
        NMInstall (¬ification);
      }
      
      if (ccPrincipal != nil) {
        cc_string_release (ccPrincipal);
      }
      
      if (ccache != nil) {
        cc_ccache_release (ccache);
      }
      
      if (ccContext != nil) {
        cc_context_release (ccContext);
      }
  }

  return noErr;
}

KerberosLoginNotification_Logout


void KerberosLoginNotification_Logout (
	const char*			inCredentialsCache);

This function is called by the Kerberos Login API whenever a user is logging out. The plugin can perform and logout-time cleanup necessary, such as unmounting server volumes. This function must not attempt to display user interface, because it can be called from contexts in which that is not permitted, such as faceless background applications.

On input, inCredentialsCache points to a C string which is the name of the credentials cache in which the tickets for the userlogging out have been stored. The plugin can open this credentials cache with the Credentials Cache API function cc_context_open_ccache() and use the tickets; the plugin doesn't need to destroy any tickets, as they will be destroyed soon after the plugin returns.

If the plugin performs some cleanup on logout, it should be prepared to handle the case where no cleanup is necessary. For example, if the plugin cancels login, as in the above example, then the Kerberos Login library destroys the cache containing the new ticket granting ticket. In the process of destroying that cache, plugin's logout procedure is called, but since the login procedure aborted, the cleanup might not be necessary.

The following code demonstrates posting a notification from the logout procedure, and using the CredentialsCache API to determine the principal of the user being logged out.


void KerberosLoginNotification_Logout (
  const char*     inCredentialsCache)
{
  static NMRec  notification;
  static Str255 principal;
  notification.qType = nmType;
  notification.nmStr = principal;
  notification.nmResp = (NMUPP) -1;
  
  cc_context_t  ccContext = nil;
  cc_ccache_t   ccache = nil;
  cc_string_t   ccPrincipal = nil;
  
  cc_int32 ccErr = cc_initialize (&ccContext, ccapi_version_4, nil, nil);
  if (ccErr == ccNoError) {
    ccErr = cc_context_open_ccache (ccContext, inCredentialsCache, &ccache);
  }
  
  if (ccErr == ccNoError) {
    ccErr = cc_ccache_get_principal (ccache, cc_credentials_v4, &ccPrincipal);
  }
  
  if (ccErr == ccNoError) {
    CopyCStringToPascal (ccPrincipal -> data, principal);
    NMInstall (¬ification);
  }
  
  if (ccPrincipal != nil) {
    cc_string_release (ccPrincipal);
  }
  
  if (ccache != nil) {
    cc_ccache_release (ccache);
  }
  
  if (ccContext != nil) {
    cc_context_release (ccContext);
  }
}

Error handling in login and logout plugins

KerberosLoginNotification_Login() is allowed to return private error codes. This error code will be propagated into the Kerberos Login API, which in turn calls KLGetErrorString() to translate the error into a message for the user. The private error codes must not conflict with any existing Mac OS error codes.

In order to allow the Kerberos Login API to translate the plugin's error codes into user messages, the plugin should register its errors with the KerberosErrors API. After that, errors returned by the plugin will be mapped to the strings in the plugin's error table.