From 5434b4b675b53ed41436bccc9ef70d76d199a081 Mon Sep 17 00:00:00 2001 From: Alexandra Ellwood Date: Wed, 7 May 2008 19:30:12 +0000 Subject: [PATCH] Move KIM implementation to the krb5 repository Moved sources and headers. ticket: new status: open git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20314 dc483132-0cff-0310-8789-dd5450dbe970 --- src/include/kim/kim.h | 176 + src/include/kim/kim_ccache.h | 554 +++ src/include/kim/kim_credential.h | 519 +++ src/include/kim/kim_error.h | 120 + src/include/kim/kim_identity.h | 307 ++ src/include/kim/kim_options.h | 637 +++ src/include/kim/kim_preferences.h | 485 ++ src/include/kim/kim_selection_hints.h | 537 +++ src/include/kim/kim_string.h | 84 + src/include/kim/kim_types.h | 162 + src/kim/agent/mac/KerberosAgent-Info.plist | 26 + src/kim/agent/mac/KerberosAgentPrefix.pch | 20 + src/kim/agent/mac/main.m | 6 + src/kim/agent/mac/resources/Add.tiff | Bin 0 -> 728 bytes src/kim/agent/mac/resources/Add_Pressed.tiff | Bin 0 -> 688 bytes .../English.lproj/Authentication.xib | 3698 ++++++++++++++++ .../mac/resources/English.lproj/MainMenu.xib | 3882 +++++++++++++++++ src/kim/agent/mac/resources/Gear.tiff | Bin 0 -> 22360 bytes .../agent/mac/resources/KerberosAgent.icns | Bin 0 -> 40494 bytes src/kim/agent/mac/resources/Remove.tiff | Bin 0 -> 694 bytes .../agent/mac/resources/Remove_Pressed.tiff | Bin 0 -> 656 bytes src/kim/lib/kim.exports | 149 + src/kim/lib/kim_ccache.c | 1051 +++++ src/kim/lib/kim_ccache_private.h | 39 + src/kim/lib/kim_credential.c | 908 ++++ src/kim/lib/kim_error.c | 244 ++ src/kim/lib/kim_error_code.et | 72 + src/kim/lib/kim_error_private.h | 55 + src/kim/lib/kim_identity.c | 544 +++ src/kim/lib/kim_identity_private.h | 38 + src/kim/lib/kim_library.c | 214 + src/kim/lib/kim_library_private.h | 54 + src/kim/lib/kim_options.c | 694 +++ src/kim/lib/kim_options_private.h | 41 + src/kim/lib/kim_preferences.c | 1120 +++++ src/kim/lib/kim_preferences_private.h | 102 + src/kim/lib/kim_private.h | 45 + src/kim/lib/kim_selection_hints.c | 702 +++ src/kim/lib/kim_selection_hints_private.h | 55 + src/kim/lib/kim_string.c | 181 + src/kim/lib/kim_string_private.h | 60 + src/kim/lib/mac/kim_os_identity.c | 50 + src/kim/lib/mac/kim_os_library.c | 56 + src/kim/lib/mac/kim_os_preferences.c | 552 +++ src/kim/lib/mac/kim_os_private.h | 54 + src/kim/lib/mac/kim_os_selection_hints.c | 538 +++ src/kim/lib/mac/kim_os_string.c | 266 ++ src/kim/test/main.c | 82 + src/kim/test/test_kim_common.c | 159 + src/kim/test/test_kim_common.h | 78 + src/kim/test/test_kim_identity.c | 559 +++ src/kim/test/test_kim_identity.h | 50 + src/kim/test/test_kim_preferences.c | 298 ++ src/kim/test/test_kim_preferences.h | 42 + src/kim/test/test_kim_selection_hints.c | 469 ++ src/kim/test/test_kim_selection_hints.h | 46 + 56 files changed, 20880 insertions(+) create mode 100644 src/include/kim/kim.h create mode 100644 src/include/kim/kim_ccache.h create mode 100644 src/include/kim/kim_credential.h create mode 100644 src/include/kim/kim_error.h create mode 100644 src/include/kim/kim_identity.h create mode 100644 src/include/kim/kim_options.h create mode 100644 src/include/kim/kim_preferences.h create mode 100644 src/include/kim/kim_selection_hints.h create mode 100644 src/include/kim/kim_string.h create mode 100644 src/include/kim/kim_types.h create mode 100644 src/kim/agent/mac/KerberosAgent-Info.plist create mode 100644 src/kim/agent/mac/KerberosAgentPrefix.pch create mode 100644 src/kim/agent/mac/main.m create mode 100644 src/kim/agent/mac/resources/Add.tiff create mode 100644 src/kim/agent/mac/resources/Add_Pressed.tiff create mode 100644 src/kim/agent/mac/resources/English.lproj/Authentication.xib create mode 100644 src/kim/agent/mac/resources/English.lproj/MainMenu.xib create mode 100644 src/kim/agent/mac/resources/Gear.tiff create mode 100644 src/kim/agent/mac/resources/KerberosAgent.icns create mode 100644 src/kim/agent/mac/resources/Remove.tiff create mode 100644 src/kim/agent/mac/resources/Remove_Pressed.tiff create mode 100644 src/kim/lib/kim.exports create mode 100644 src/kim/lib/kim_ccache.c create mode 100644 src/kim/lib/kim_ccache_private.h create mode 100644 src/kim/lib/kim_credential.c create mode 100644 src/kim/lib/kim_error.c create mode 100644 src/kim/lib/kim_error_code.et create mode 100644 src/kim/lib/kim_error_private.h create mode 100644 src/kim/lib/kim_identity.c create mode 100644 src/kim/lib/kim_identity_private.h create mode 100644 src/kim/lib/kim_library.c create mode 100644 src/kim/lib/kim_library_private.h create mode 100644 src/kim/lib/kim_options.c create mode 100644 src/kim/lib/kim_options_private.h create mode 100644 src/kim/lib/kim_preferences.c create mode 100644 src/kim/lib/kim_preferences_private.h create mode 100644 src/kim/lib/kim_private.h create mode 100644 src/kim/lib/kim_selection_hints.c create mode 100644 src/kim/lib/kim_selection_hints_private.h create mode 100644 src/kim/lib/kim_string.c create mode 100644 src/kim/lib/kim_string_private.h create mode 100644 src/kim/lib/mac/kim_os_identity.c create mode 100644 src/kim/lib/mac/kim_os_library.c create mode 100644 src/kim/lib/mac/kim_os_preferences.c create mode 100644 src/kim/lib/mac/kim_os_private.h create mode 100644 src/kim/lib/mac/kim_os_selection_hints.c create mode 100644 src/kim/lib/mac/kim_os_string.c create mode 100644 src/kim/test/main.c create mode 100644 src/kim/test/test_kim_common.c create mode 100644 src/kim/test/test_kim_common.h create mode 100644 src/kim/test/test_kim_identity.c create mode 100644 src/kim/test/test_kim_identity.h create mode 100644 src/kim/test/test_kim_preferences.c create mode 100644 src/kim/test/test_kim_preferences.h create mode 100644 src/kim/test/test_kim_selection_hints.c create mode 100644 src/kim/test/test_kim_selection_hints.h diff --git a/src/include/kim/kim.h b/src/include/kim/kim.h new file mode 100644 index 000000000..893891def --- /dev/null +++ b/src/include/kim/kim.h @@ -0,0 +1,176 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_H +#define KIM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + * \mainpage Kerberos Identity Management (KIM) API Documentation + * + * \section introduction Introduction + * + * The Kerberos Identity Management API is a high level API for managing the selection + * and management of Kerberos credentials. It is intended for use by applications, + * credential management applications (eg: kinit, kpasswd, etc) and internally by the + * Kerberos libraries. Under some circumstances client applications may also benefit + * from the Kerberos Identity Management API. + * + * + * \section conventions API Conventions + * + * Although KIM currently only provides a C API, it attempts to make that API as + * object-oriented as possible. KIM functions are grouped by object and all of the + * object types are opaque, including errors. The reason for this is two-fold. First, + * the KIM API is rather large. Grouping functions by object allows the API to be + * broken up into smaller, more manageable chunks. Second, providing an object-like C + * API will make it easier to port to object oriented languages. + * + * Because C lacks classes and other object oriented syntax, KIM functions adhere to + * the following naming conventions to make functions easier to identify: + * + * \li Functions beginning with \b kim_object_create are constructors for an object of + * type kim_object. On success these functions return a newly allocated object which + * must later be freed by the caller. + * + * \li Functions of the form \b kim_object_copy are copy constructors. They instantiate + * a new object of kim_object from an object of the same type. + * + * \li Functions of the form \b kim_object_free are destructors for objects of type + * kim_object. + * + * \li Functions beginning with \b kim_object_get and \b kim_object_set + * examine and modify properties of objects of type kim_object. + * + * \li All KIM APIs except destructors and error management APIs return a + * KIM Error object (kim_error_t). + * + * + * \section terminology Terminology + * + * Kerberos organizes its authentication tokens by client identity (the name of the user) + * and service identity (the name of a service). The following terms are used throughout + * this documentation: + * + * \li credential - A token which authenticates a client identity to a + * service identity. + * + * \li ccache - Short for "credentials cache". A set of credentials for a single + * client identity. + * + * \li cache collection - The set of all credential caches. + * + * \li default ccache - A credentials cache that the Kerberos libraries will use + * if no ccache is specified by the caller. Use of the default + * ccache is now discouraged. Instead applications should use + * selection hints to choose an appropriate client identity. + * + * \section selection_api Client Identity Selection APIs + * + * KIM provides high level APIs for applications to select which client identity to + * use. Use of these APIs is intended to replace the traditional "default ccache" + * mechanism previously used by Kerberos. + * + * KIM Selection Hints (kim_selection_hints_t) controls options for selecting + * a client identity: + * - \subpage kim_selection_hints_overview + * - \subpage kim_selection_hints_reference + * + * KIM Identity (kim_identity_t) provides an immutable Kerberos identity object + * - \subpage kim_identity_overview + * - \subpage kim_identity_reference + * + * + * \section management_api Credential Management APIs + * + * KIM also provides APIs for acquiring new credentials over the network + * by contacting a KDC and for viewing and modifying the existing credentials + * in the cache collection + * + * Whether or not you use the credential or ccache APIs depends on + * whether you want KIM to store any newly acquired credentials in the + * cache collection. KIM ccache APIs always create a ccache in the cache + * collection containing newly acquired credentials whereas the KIM + * credential APIs just return a credential object. In general most + * callers want to store newly acquired credentials and should use the + * KIM ccache APIs when acquiring credentials. + * + * KIM CCache (kim_ccache_t) manipulates credential caches in the cache collection: + * - \subpage kim_ccache_overview + * - \subpage kim_ccache_reference + * + * KIM Credential (kim_credential_t) manipulates credentials: + * - \subpage kim_credential_overview + * - \subpage kim_credential_reference + * + * KIM Options (kim_options_t) control options for credential acquisition: + * - \subpage kim_options_overview + * - \subpage kim_options_reference + * + * KIM Realms List (kim_favorite_identities_t) views and edits the current user's favorite realms list: + * - \subpage kim_favorite_identities_overview + * - \subpage kim_favorite_identities_reference + * + * KIM Preferences (kim_preferences_t) views and edits the current user's preferences: + * - \subpage kim_preferences_overview + * - \subpage kim_preferences_reference + * + * + * \section utility_apis Miscellaneous APIs + * + * The high and low level APIs depend on the following basic utility classes + * to manage generic types. + * + * KIM String (kim_string_t) provides memory management for an immutable string: + * - \subpage kim_string_overview + * - \subpage kim_string_reference + * + * KIM Error (kim_error_t) provides a machine and user-readable error message: + * - \subpage kim_error_overview + * - \subpage kim_error_reference + * + * + * \section types Types and Constants + * + * \li \subpage kim_types_reference + */ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_H */ diff --git a/src/include/kim/kim_ccache.h b/src/include/kim/kim_ccache.h new file mode 100644 index 000000000..ad3ee1626 --- /dev/null +++ b/src/include/kim/kim_ccache.h @@ -0,0 +1,554 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_CCACHE_H +#define KIM_CCACHE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! + * \page kim_ccache_overview KIM CCache Overview + * + * \section kim_ccache_introduction Introduction + * + * Kerberos credentials are stored in "ccaches" (short for "credentials caches"). + * The set of all ccaches which the KIM can use is called the "cache collection". + * Each ccache has a name and type which uniquely identify it in the cache + * collection and a client identity. The ccache's client identity is the + * identity whose credentials are stored in the ccache. This allows for easy + * lookup of all the credentials for a given identity. + * + * KIM attempts to preserve a one-to-one relationship between client identities + * and ccaches. If the KIM is used to manipulate the cache collection, there + * will be one ccache per identity. However, because low-level APIs allow callers + * to create multiple ccaches for the same client identity or a single ccache + * containing credentials for different client identities, KIM handles those + * situations. In general when searching KIM will find the first ccache matching + * the requested client identity. It will not find credentials for the requested + * client identity if they are in a ccache with a different client identity. + * + * The kim_ccache_t object is a reference to a ccache in the cache collection. + * If other applications make changes to the the ccache pointed to by a KIM ccache + * object, the object will immediately show those changes. KIM performs locking + * on the cache collection to prevent deadlocks and maintain a consistent behavior + * when multiple applications attempt to modify the cache collection. + * + * \note KIM ccache APIs are intended for applications and system + * tools which manage credentials for the user. They are not a substitute for + * krb5 and GSSAPI functions which obtain service credentials for the purpose + * of authenticating a client to an application server. + * + * \section kim_credential_cache_collection Acquiring a CCache from the Cache Collection + * + * KIM provides a simple iterator API for iterating over the ccaches + * in the cache collection. First, call #kim_ccache_iterator_create() to obtain + * an iterator for the cache collection. Then loop calling + * #kim_ccache_iterator_next() until either you find the ccache you are looking + * for or the API returns a NULL ccache, indicating that there are no more + * ccaches in the cache collection. When you are done with the iterator, call + * #kim_ccache_iterator_free(). + * + * \note #kim_ccache_iterator_next() returns ccache objects which + * must be freed with #kim_ccache_free() to avoid leaking memory. + * + * KIM also provides a convenient API #kim_ccache_create_from_client_identity() + * which returns the ccache for a specific client identity, if any exists. + * Typically callers of this API obtain the client identity using + * #kim_selection_hints_get_identity(). + * + * + * \section kim_ccache_acquire_default Acquiring Credentials from the Default CCache + * + * #kim_ccache_create_from_default() returns the default ccache. + * The default ccache is a legacy concept which was replaced by selection + * hints. Prior to the existence of selection hints, applications always + * looked at the default ccache for credentials. By setting the system default + * ccache, users could manually control which credentials each application used. + * As the number of ccaches and applications has grown, this mechanism has become + * unusable. You should avoid using this API whenever possible. + * + * + * \section kim_ccache_acquire_new Acquiring New Credentials in a CCache + * + * KIM provides the #kim_ccache_create_new() API for acquiring new + * credentials and storing them in a ccache. Credentials can either be + * obtained for a specific client identity or by specifying + * #KIM_IDENTITY_ANY to allow the user to choose. Typically + * callers of this API obtain the client identity using + * #kim_selection_hints_get_identity(). Depending on the kim_options + * specified, #kim_ccache_create_new() may present a GUI or command line + * prompt to obtain information from the user. + * + * #kim_ccache_create_new_if_needed() + * searches the cache collection for a ccache for the client identity + * and if no appropriate ccache is available, attempts to acquire + * new credentials and store them in a new ccache. Depending on the + * kim_options specified, #kim_ccache_create_new_if_needed() may + * present a GUI or command line prompt to obtain information from the + * user. This function exists for convenience and to avoid code duplication. + * It can be trivially implemented using + * #kim_ccache_create_from_client_identity() and #kim_ccache_create_new(). + * + * KIM provides the #kim_ccache_create_from_keytab() to create credentials + * using a keytab and store them in the cache collection. A keytab is an + * on-disk copy of a client identity's secret key. Typically sites use + * keytabs for client identities that identify a machine or service and + * protect the keytab with disk permissions. Because a keytab is + * sufficient to obtain credentials, keytabs will normally only be readable + * by root, Administrator or some other privileged account. + * Typically applications use credentials obtained from keytabs to obtain + * credentials for batch processes. These keytabs and credentials are usually + * for a special identity used for the batch process rather than a user + * identity. + * + * + * \section kim_ccache_validate Validating Credentials in a CCache + * + * A credential with a start time in the future (ie: after the issue date) + * is called a post-dated credential. Because the KDC administrator may + * wish to disable a identity, once the start time is reached, all post-dated + * credentials must be validated before they can be used. Otherwise an + * attacker using a compromised account could acquire lots of post-dated + * credentials to circumvent the acccount being disabled. + * + * KIM provides the #kim_ccache_validate() API to validate the TGT + * credential in a ccache. Note that this API replaces any existing + * credentials with the validated credential. + * + * + * \section kim_ccache_renew Renewing Credentials in a CCache + * + * A renewable credential can be used to obtain a new identical credential + * without resending secret information (such as a password) to the KDC. + * A credential may only be renewed during its renewal lifetime and while + * valid. + * + * KIM provides the #kim_ccache_renew() API to renew the TGT credential + * in a ccache. Note that this API replaces any existing credentials with the + * renewed credential. + * + * + * \section kim_ccache_verify Verifying Credentials in a CCache + * + * When a program acquires TGT credentials for the purpose of authenticating + * itself to the machine it is running on, it is insufficient for the machine + * to assume that the caller is authorized just because it got credentials. + * Instead, the credentials must be verified using a key the local machine. + * The reason this is necessary is because an attacker can trick the + * machine into obtaining credentials from any KDC, including malicious ones + * with the same realm name as the local machine's realm. This exploit is + * called the Zanarotti attack. + * + * In order to avoid the Zanarotti attack, the local machine must authenticate + * the process in the same way an application server would authenticate a client. + * Like an application server, the local machine must have its own identity in + * its realm and a keytab for that identity on its local disk. However, + * rather than forcing system daemons to use the network-oriented calls in the + * krb5 and GSS APIs, KIM provides the #kim_ccache_verify() API to + * verify credentials directly. + * + * The most common reason for using #kim_ccache_verify() is user login. + * If the local machine wants to use Kerberos to verify the username and password + * provided by the user, it must call #kim_ccache_verify() on the credentials + * it obtains to make sure they are really from a KDC it trusts. Another common + * case is a server which is only using Kerberos internally. For example an + * LDAP or web server might use a username and password obtained over the network + * to get Kerberos credentials. In order to make sure they aren't being tricked + * into talking to the wrong KDC, these servers must also call + * #kim_ccache_verify(). + * + * The Zanarotti attack is only a concern if the act of accessing the machine + * gives the process special access. Thus a managed cluster machine with + * Kerberos-authenticated networked home directories does not need to call + * #kim_ccache_verify(). Even though an attacker can log in as any user on + * the cluster machine, the attacker can't actually access any of the user's data + * or use any of their privileges because those are all authenticated via + * Kerberized application servers (and thus require actually having credentials + * for the real local realm). + * + * #kim_ccache_verify() provides an option to + * return success even if the machine's host key is not present. This option + * exists for sites which have a mix of different machines, some of which are + * vulnerable to the Zanarotti attack and some are not. If this option is used, + * it is the responsiblity of the machine's maintainer to obtain a keytab + * for their machine if it needs one. + * + * + * \section kim_ccache_properties Examining CCache Properties + * + * \li #kim_ccache_get_type() returns the type of the ccache. Types include + * "API" for CCAPI ccaches, "FILE" for file-based ccaches and "MEMORY" for + * single-process in-memory ccaches. + * + * \li #kim_ccache_get_name() returns the name of the ccache. A ccache's name + * identifies the ccache uniquely among ccaches of the same type. Note that + * two ccaches with different types may have the same name. + * + * \li #kim_ccache_get_display_name() returns a display string which uniquely + * identifies a ccache. A ccache display name is of the form ":" + * and can be displayed to the user or used as an argument to certain krb5 + * APIs, such as krb5_cc_resolve(). + * + * \li #kim_ccache_get_client_identity() + * returns the ccache's client identity. + * + * \li #kim_ccache_get_valid_credential() + * returns the first valid TGT in the ccache for its client identity. + * If there are no TGTs in the ccache, it returns the first + * valid non-TGT credential for the ccache's client identity. + * TGT credentials (ie: "ticket-granting tickets") are credentials for + * the krbtgt service: a service identity of the form "krbtgt/@". + * These credentials allow the entity named by the client identity to obtain + * additional credentials without resending shared secrets (such as a password) + * to the KDC. Kerberos uses TGTs to provide single sign-on authentication. + * + * \li #kim_ccache_get_start_time() + * returns when the credential's in a ccache will become valid. + * Credentials may be "post-dated" which means that their lifetime starts sometime + * in the future. Note that when a post-dated credential's start time is reached, + * the credential must be validated. See \ref kim_credential_validate for more information. + * + * \li #kim_ccache_get_expiration_time() + * returns when the credential's in a ccache will expire. + * Credentials are time limited by the lifetime of the credential. While you can + * request a credential of any lifetime, the KDC limits the credential lifetime + * to a administrator-defined maximum. Typically credential lifetime range from 10 + * to 21 hours. + * + * \li #kim_ccache_get_renewal_expiration_time() + * returns when the credential's in a ccache will no longer be renewable. + * Valid credentials may be renewed up until their renewal expiration time. + * Renewing credentials acquires a fresh set of credentials with a full lifetime + * without resending secrets to the KDC (such as a password). If credentials are + * not renewable, this function will return an error. + * + * See \ref kim_ccache_reference and \ref kim_ccache_iterator_reference for + * information on specific APIs. + */ + + +/*! + * \defgroup kim_ccache_iterator_reference KIM CCache Iterator Reference Documentation + * @{ + */ + +/*! + * \param out_ccache_iterator on exit, a ccache iterator object for the cache collection. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get a ccache iterator to enumerate ccaches in the cache collection. + */ +kim_error_t kim_ccache_iterator_create (kim_ccache_iterator_t *out_ccache_iterator); + +/*! + * \param in_ccache_iterator a ccache iterator object. + * \param out_ccache on exit, the next ccache in the cache collection. If there are + * no more ccaches in the cache collection this argument will be + * set to NULL. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the next ccache in the cache collection. + */ +kim_error_t kim_ccache_iterator_next (kim_ccache_iterator_t in_ccache_iterator, + kim_ccache_t *out_ccache); + +/*! + * \param io_ccache_iterator a ccache iterator object to be freed. Set to NULL on exit. + * \brief Free memory associated with a ccache iterator. + */ +void kim_ccache_iterator_free (kim_ccache_iterator_t *io_ccache_iterator); + +/*!@}*/ + +/*! + * \defgroup kim_ccache_reference KIM CCache Reference Documentation + * @{ + */ + +/*! + * \param out_ccache on exit, a new cache object for a ccache containing a newly acquired + * initial credential. Must be freed with kim_ccache_free(). + * \param in_client_identity a client identity to obtain a credential for. Specify KIM_IDENTITY_ANY to + * allow the user to choose. + * \param in_options options to control credential acquisition. + * \note Depending on the kim_options specified, #kim_ccache_create_new() may + * present a GUI or command line prompt to obtain information from the user. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Acquire a new initial credential and store it in a ccache. + */ +kim_error_t kim_ccache_create_new (kim_ccache_t *out_ccache, + kim_identity_t in_client_identity, + kim_options_t in_options); + +/*! + * \param out_ccache on exit, a ccache object for a ccache containing a newly acquired + * initial credential. Must be freed with kim_ccache_free(). + * \param in_client_identity a client identity to obtain a credential for. + * \param in_options options to control credential acquisition (if a credential is acquired). + * \note Depending on the kim_options specified, #kim_ccache_create_new_if_needed() may + * present a GUI or command line prompt to obtain information from the user. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Find a ccache containing a valid initial credential in the cache collection, or if + * unavailable, acquire and store a new initial credential. + */ +kim_error_t kim_ccache_create_new_if_needed (kim_ccache_t *out_ccache, + kim_identity_t in_client_identity, + kim_options_t in_options); + +/*! + * \param out_ccache on exit, a ccache object for a ccache containing a TGT + * credential. Must be freed with kim_ccache_free(). + * \param in_client_identity a client identity to obtain a credential for. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Find a ccache for a client identity in the cache collection. + */ +kim_error_t kim_ccache_create_from_client_identity (kim_ccache_t *out_ccache, + kim_identity_t in_client_identity); + +/*! + * \param out_ccache on exit, a new ccache object containing an initial credential + * for the client identity \a in_identity obtained using in_keytab. + * Must be freed with kim_ccache_free(). + * \param in_identity a client identity to obtain a credential for. Specify NULL for + * the first client identity in the keytab. + * \param in_options options to control credential acquisition. + * \param in_keytab a path to a keytab. Specify NULL for the default keytab location. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Acquire a new initial credential from a keytab and store it in a ccache. + */ +kim_error_t kim_ccache_create_from_keytab (kim_ccache_t *out_ccache, + kim_identity_t in_identity, + kim_options_t in_options, + kim_string_t in_keytab); + +/*! + * \param out_ccache on exit, a ccache object for the default ccache. + * Must be freed with kim_ccache_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the default ccache. + */ +kim_error_t kim_ccache_create_from_default (kim_ccache_t *out_ccache); + +/*! + * \param out_ccache on exit, a ccache object for the ccache identified by + * \a in_type and \a in_name. Must be freed with kim_ccache_free(). + * \param in_type a ccache type string. + * \param in_name a ccache name string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \note This API is provided for backwards compatibilty with applications which are not + * KIM-aware and should be avoided whenever possible. + * \brief Get a ccache for a ccache type and name. + */ +kim_error_t kim_ccache_create_from_type_and_name (kim_ccache_t *out_ccache, + kim_string_t in_type, + kim_string_t in_name); + +/*! + * \param out_ccache on exit, a new ccache object which is a copy of in_krb5_ccache. + * Must be freed with kim_ccache_free(). + * \param in_krb5_context the krb5 context used to create \a in_krb5_ccache. + * \param in_krb5_ccache a krb5 ccache object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get a ccache for a krb5 ccache. + */ +kim_error_t kim_ccache_create_from_krb5_ccache (kim_ccache_t *out_ccache, + krb5_context in_krb5_context, + krb5_ccache in_krb5_ccache); + +/*! + * \param out_ccache on exit, the new ccache object which is a copy of in_ccache. + * Must be freed with kim_ccache_free(). + * \param in_ccache a ccache object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy a ccache. + */ +kim_error_t kim_ccache_copy (kim_ccache_t *out_ccache, + kim_ccache_t in_ccache); + +/*! + * \param in_ccache a ccache object. + * \param in_krb5_context a krb5 context which will be used to create out_krb5_ccache. + * \param out_krb5_ccache on exit, a new krb5 ccache object which is a copy of in_ccache. + * Must be freed with krb5_cc_close() or krb5_cc_destroy(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get a krb5 ccache for a ccache. + */ +kim_error_t kim_ccache_get_krb5_ccache (kim_ccache_t in_ccache, + krb5_context in_krb5_context, + krb5_ccache *out_krb5_ccache); + +/*! + * \param in_ccache a ccache object. + * \param out_name on exit, the name string of \a in_ccache. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the name of a ccache. + */ +kim_error_t kim_ccache_get_name (kim_ccache_t in_ccache, + kim_string_t *out_name); + +/*! + * \param in_ccache a ccache object. + * \param out_type on exit, the type string of \a in_ccache. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the type of a ccache. + */ +kim_error_t kim_ccache_get_type (kim_ccache_t in_ccache, + kim_string_t *out_type); + +/*! + * \param in_ccache a ccache object. + * \param out_display_name on exit, the type and name of \a in_ccache in a format appropriate for + * display to the user in command line programs. (ie: ":") + * Must be freed with kim_string_free(). + * Note: this string can also be passed to krb5_cc_resolve(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the type and name for a ccache in display format. + */ +kim_error_t kim_ccache_get_display_name (kim_ccache_t in_ccache, + kim_string_t *out_display_name); + +/*! + * \param in_ccache a ccache object. + * \param out_client_identity on exit, an identity object containing the client identity of + * \a in_ccache. Must be freed with kim_identity_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the client identity for a ccache. + */ +kim_error_t kim_ccache_get_client_identity (kim_ccache_t in_ccache, + kim_identity_t *out_client_identity); + +/*! + * \param in_ccache a ccache object. + * \param out_credential on exit, the first valid credential in \a in_ccache. + * Must be freed with kim_credential_free(). Set to NULL + * if you only want return value, not the actual credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the first valid credential in a ccache. + * \note This function prefers TGT credentials. If there are any non-valid TGTs + * in the ccache, it will always return an error. However, if there are no + * TGTs at all, it will return the first valid non-TGT credential. If you only want + * TGTs, use kim_credential_is_tgt() to verify that \a out_credential is a tgt. + */ +kim_error_t kim_ccache_get_valid_credential (kim_ccache_t in_ccache, + kim_credential_t *out_credential); + +/*! + * \param in_ccache a ccache object. + * \param out_start_time on exit, the time when the credentials in \a in_ccache + * become valid. May be in the past or future. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the time when the credentials in the ccache become valid. + */ +kim_error_t kim_ccache_get_start_time (kim_ccache_t in_ccache, + kim_time_t *out_start_time); + +/*! + * \param in_ccache a ccache object. + * \param out_expiration_time on exit, the time when the credentials in + * \a in_ccache will expire. May be in the past or future. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the time when the credentials in the ccache will expire. + */ +kim_error_t kim_ccache_get_expiration_time (kim_ccache_t in_ccache, + kim_time_t *out_expiration_time); + +/*! + * \param in_ccache a ccache object. + * \param out_renewal_expiration_time on exit, the time when the credentials in \a in_ccache + * will no longer be renewable. May be in the past or future. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the time when the credentials in the ccache will no longer be renewable. + */ +kim_error_t kim_ccache_get_renewal_expiration_time (kim_ccache_t in_ccache, + kim_time_t *out_renewal_expiration_time); + +/*! + * \param io_ccache a ccache object which will be set to the default ccache. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \note This API is provided for backwards compatibilty with applications which are not + * KIM-aware and should be avoided whenever possible. + * \brief Set a ccache to the default ccache. + */ +kim_error_t kim_ccache_set_default (kim_ccache_t io_ccache); + +/*! + * \param in_ccache a ccache object containing the TGT credential to be verified. + * \param in_service_identity a service identity to look for in the keytab. Specify + * KIM_IDENTITY_ANY to use the default service identity + * (usually host/@). + * \param in_keytab a path to a keytab. Specify NULL for the default keytab location. + * \param in_fail_if_no_service_key whether or not the absence of a key for \a in_service_identity + * in the host's keytab will cause a failure. + * \note specifying FALSE for \a in_fail_if_no_service_key may expose the calling program to + * the Zanarotti attack if the host has no keytab installed. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Verify the TGT in a ccache. + */ +kim_error_t kim_ccache_verify (kim_ccache_t in_ccache, + kim_identity_t in_service_identity, + kim_string_t in_keytab, + kim_boolean_t in_fail_if_no_service_key); + +/*! + * \param in_ccache a ccache object containing a TGT to be renewed. + * \param in_options initial credential options to be used if a new credential is obtained. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Renew the TGT in a ccache. + */ +kim_error_t kim_ccache_renew (kim_ccache_t in_ccache, + kim_options_t in_options); + +/*! + * \param in_ccache a ccache object containing a TGT to be validated. + * \param in_options initial credential options. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Validate the TGT in a ccache. + */ +kim_error_t kim_ccache_validate (kim_ccache_t in_ccache, + kim_options_t in_options); + +/*! + * \param io_ccache a ccache object to be destroyed. Set to NULL on exit. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Remove a ccache from the cache collection. + * \note Frees memory associated with the ccache. Do not call kim_ccache_free() + * after calling this function. + */ +kim_error_t kim_ccache_destroy (kim_ccache_t *io_ccache); + +/*! + * \param io_ccache a ccache object to be freed. Set to NULL on exit. + * \brief Free memory associated with a ccache. + */ +void kim_ccache_free (kim_ccache_t *io_ccache); + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_CCACHE_H */ diff --git a/src/include/kim/kim_credential.h b/src/include/kim/kim_credential.h new file mode 100644 index 000000000..03fe50c49 --- /dev/null +++ b/src/include/kim/kim_credential.h @@ -0,0 +1,519 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_CREDENTIAL_H +#define KIM_CREDENTIAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/*! + * \addtogroup kim_types_reference + * @{ + */ + +/*! + * Possible credential states. Credentials may be: + * \li valid - The credential can be used. + * \li expired - The credential's lifetime has been exceeded. + * \li not_yet_valid - The credential is post dated and the time when + * it becomes valid has not yet been reached. + * \li needs_validation - The credential is post-dated and although + * the time when it becomes valid has been reached + * it has not yet been validated. + * \li address_mismatch - The credential contains IP address(es) which do + * not match the host's local address(es). + */ +enum kim_credential_state_enum { + kim_credentials_state_valid = 0, + kim_credentials_state_expired = 1, + kim_credentials_state_not_yet_valid = 2, + kim_credentials_state_needs_validation = 3, + kim_credentials_state_address_mismatch = 4 +}; + +/*! + * The state of a credential. See #kim_credential_state_enum for + * possible values. + */ +typedef int kim_credential_state_t; + +/*! @} */ + +/*! + * \page kim_credential_overview KIM Credential Overview + * + * \section kim_credential_introduction Introduction + * + * A Kerberos credential (also called a "Kerberos ticket") is a time-limited + * token issued by a KDC which authenticates the entity named by the credential's + * client identity to the service named by the credential's service identity. + * + * The kim_credential_t object contains a single Kerberos credential. KIM credentials + * objects are always copies of credentials, not references to credentials + * stored in the cache collection. Modifying credential objects in the ccache + * collection will not change any existing KIM credential objects. + * + * KIM credential APIs are intended for applications and system + * tools which manage credentials for the user. They are not a substitute for + * krb5 and GSSAPI functions which obtain service credentials for the purpose + * of authenticating a client to an application server. + * + * \note Many of the APIs listed below have equivalent functions which + * operate on ccaches. In most cases applications will want to use the + * ccache versions of these APIs since they automatically store any + * newly created credentials. See \ref kim_ccache_overview for more + * information. + * + * + * \section kim_credential_acquire_new Acquiring New Credentials + * + * KIM provides the #kim_credential_create_new() API for acquiring new + * credentials. Credentials can either be obtained for a specific + * client identity or by specifying #KIM_IDENTITY_ANY to allow + * the user to choose. Typically callers of this API obtain the client + * identity using #kim_selection_hints_get_identity(). Depending on the + * kim_options specified, #kim_credential_create_new() may present a + * GUI or command line prompt to obtain information from the user. + * + * KIM provides the #kim_credential_create_from_keytab() to create credentials + * using a keytab. A keytab is an on-disk copy of a client identity's secret + * key. Typically sites use keytabs for client identities that identify a + * machine or service and protect the keytab with disk permissions. Because + * a keytab is sufficient to obtain credentials, keytabs will normally only + * be readable by root, Administrator or some other privileged account. + * Typically applications use credentials obtained from keytabs to obtain + * credentials for batch processes. These keytabs and credentials are usually + * for a special identity used for the batch process rather than a user + * identity. + * + * + * \section kim_credential_validate Validating Credentials + * + * A credential with a start time in the future (ie: after the issue date) + * is called a post-dated credential. Because the KDC administrator may + * wish to disable a identity, once the start time is reached, all post-dated + * credentials must be validated before they can be used. Otherwise an + * attacker using a compromised account could acquire lots of post-dated + * credentials to circumvent the acccount being disabled. + * + * KIM provides the #kim_credential_validate() API to validate a credential. + * Note that this API replaces the credential object with a new validated + * credential object. If you wish to store the new credential in the + * ccache collection you must either call #kim_credential_store() on the + * validated credential or use #kim_ccache_validate() instead. + * + * + * \section kim_credential_renew Renewing Credentials + * + * A renewable credential can be used to obtain a new identical credential + * without resending secret information (such as a password) to the KDC. + * A credential may only be renewed during its renewal lifetime and while + * valid. + * + * KIM provides the #kim_credential_renew() API to renew a credential. + * Note that this API replaces the credential object with a new renewed + * credential object. If you wish to store the new credential in the + * ccache collection you must either call #kim_credential_store() on the + * renewed credential or use #kim_ccache_renew() instead. + * + * + * \section kim_credential_storing Storing Credentials in the Cache Collection + * + * KIM credential objects may be stored in the ccache collection using + * #kim_credential_store(). This function runs any KIM authentication + * plugins on the credential and if the plugins return successfully, creates a + * new ccache for the credential's client identity in the cache collection + * and stores the credential in that ccache. Any existing ccaches and credentials + * for that client identity will be overwritten. #kim_credential_store() may + * optionally return a kim_ccache_t object for the new ccache if you need to perform + * further operations on the new ccache. + * + * Most of the time if you plan to store the credentials you are manipulating, you + * should use one of KIM ccache APIs. These functions perform the same operations + * except that they also call #kim_credential_store() any time the credential object + * changes. See \ref kim_ccache_overview for more information. + * + * + * \section kim_credential_iterator_t Iterating over the Credentials in a CCache + * + * KIM provides a simple iterator API for iterating over the credentials + * in a ccache. First, call #kim_credential_iterator_create() to obtain + * an iterator for a ccache. Then loop calling #kim_credential_iterator_next() + * until either you find the credential you are looking for or the API + * returns a NULL credential, indicating that there are no more + * credentials in the ccache. When you are done with the iterator, call + * #kim_credential_iterator_free(). + * + * \note #kim_credential_iterator_next() returns credential objects which + * must be freed with #kim_credential_free() to avoid leaking memory. + * + * + * \section kim_credential_verify Verifying Credentials + * + * When a program acquires TGT credentials for the purpose of authenticating + * itself to the machine it is running on, it is insufficient for the machine + * to assume that the caller is authorized just because it got credentials. + * Instead, the credentials must be verified using a key the local machine. + * The reason this is necessary is because an attacker can trick the + * machine into obtaining credentials from any KDC, including malicious ones + * with the same realm name as the local machine's realm. This exploit is + * called the Zanarotti attack. + * + * In order to avoid the Zanarotti attack, the local machine must authenticate + * the process in the same way an application server would authenticate a client. + * Like an application server, the local machine must have its own identity in + * its realm and a keytab for that identity on its local disk. However, + * rather than forcing system daemons to use the network-oriented calls in the + * krb5 and GSS APIs, KIM provides the #kim_credential_verify() API to + * verify credentials directly. + * + * The most common reason for using #kim_credential_verify() is user login. + * If the local machine wants to use Kerberos to verify the username and password + * provided by the user, it must call #kim_credential_verify() on the credentials + * it obtains to make sure they are really from a KDC it trusts. Another common + * case is a server which is only using Kerberos internally. For example an + * LDAP or web server might use a username and password obtained over the network + * to get Kerberos credentials. In order to make sure they aren't being tricked + * into talking to the wrong KDC, these servers must also call + * #kim_credential_verify(). + * + * The Zanarotti attack is only a concern if the act of accessing the machine + * gives the process special access. Thus a managed cluster machine with + * Kerberos-authenticated networked home directories does not need to call + * #kim_credential_verify(). Even though an attacker can log in as any user on + * the cluster machine, the attacker can't actually access any of the user's data + * or use any of their privileges because those are all authenticated via + * Kerberized application servers (and thus require actually having credentials + * for the real local realm). + * + * #kim_credential_verify() provides an option to + * return success even if the machine's host key is not present. This option + * exists for sites which have a mix of different machines, some of which are + * vulnerable to the Zanarotti attack and some are not. If this option is used, + * it is the responsiblity of the machine's maintainer to obtain a keytab + * for their machine if it needs one. + * + * + * \section kim_credential_properties Examining Credential Properties + * + * \li #kim_credential_get_client_identity() + * returns the credential's client identity. + * + * \li #kim_credential_get_service_identity() + * returns the credential's service identity. + * + * \li #kim_credential_is_tgt() + * returns whether the credential is a TGT (ie: "ticket-granting ticket"). TGTs are + * credentials for the krbtgt service: a service identity of the form "krbtgt/@". + * These credentials allow the entity named by the client identity to obtain + * additional service credentials without resending shared secrets (such as a password) + * to the KDC. Kerberos uses TGTs to provide single sign-on authentication. + * + * \li #kim_credential_is_valid() + * returns whether the credential is valid and if not why the credential is not valid. + * + * \li #kim_credential_get_start_time() + * returns when the credential will become valid. + * Credentials may be "post-dated" which means that their lifetime starts sometime + * in the future. Note that when a post-dated credential's start time is reached, + * the credential must be validated. See \ref kim_credential_validate for more information. + * + * \li #kim_credential_get_expiration_time() + * returns when the credential will expire. + * Credentials are time limited by the lifetime of the credential. While you can + * request a credential of any lifetime, the KDC limits the credential lifetime + * to a administrator-defined maximum. Typically credential lifetime range from 10 + * to 21 hours. + * + * \li #kim_credential_get_renewal_expiration_time() + * returns when the credential will no longer be renewable. + * Valid credentials may be renewed up until their renewal expiration time. + * Renewing credentials acquires a fresh set of credentials with a full lifetime + * without resending secrets to the KDC (such as a password). If credentials are + * not renewable, this function will return an error. + * + * + * See \ref kim_credential_reference and \ref kim_credential_iterator_reference for + * information on specific APIs. + */ + +/*! + * \defgroup kim_credential_iterator_reference KIM Credential Iterator Reference Documentation + * @{ + */ + +/*! + * \param out_credential_iterator on exit, a credential iterator object for \a in_ccache. + * Must be freed with kim_credential_iterator_free(). + * \param in_ccache a ccache object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get a credential iterator to enumerate credentials in a ccache. + */ + +kim_error_t kim_credential_iterator_create (kim_credential_iterator_t *out_credential_iterator, + kim_ccache_t in_ccache); + +/*! + * \param in_credential_iterator a credential iterator object. + * \param out_credential on exit, the next credential in the ccache iterated by + * \a in_credential_iterator. Must be freed with + * kim_credential_free(). If there are no more credentials + * this argument will be set to NULL. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the next credential in a ccache. + */ + +kim_error_t kim_credential_iterator_next (kim_credential_iterator_t in_credential_iterator, + kim_credential_t *out_credential); + +/*! + * \param io_credential_iterator a credential iterator object to be freed. Set to NULL on exit. + * \brief Free memory associated with a credential iterator. + */ +void kim_credential_iterator_free (kim_credential_iterator_t *io_credential_iterator); + +/*!@}*/ + +/*! + * \defgroup kim_credential_reference KIM Credential Reference Documentation + * @{ + */ + +/*! + * \param out_credential on exit, a new credential object containing a newly acquired + * initial credential. Must be freed with kim_credential_free(). + * \param in_client_identity a client identity to obtain a credential for. Specify NULL to + * allow the user to choose the identity + * \param in_options options to control credential acquisition. + * \note Depending on the kim_options specified, #kim_credential_create_new() may + * present a GUI or command line prompt to obtain information from the user. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Acquire a new initial credential. + * \sa kim_ccache_create_new + */ +kim_error_t kim_credential_create_new (kim_credential_t *out_credential, + kim_identity_t in_client_identity, + kim_options_t in_options); + +/*! + * \param out_credential on exit, a new credential object containing an initial credential + * for \a in_identity obtained using \a in_keytab. + * Must be freed with kim_credential_free(). + * \param in_identity a client identity to obtain a credential for. Specify NULL for + * the first identity in the keytab. + * \param in_options options to control credential acquisition. + * \param in_keytab a path to a keytab. Specify NULL for the default keytab location. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Acquire a new initial credential from a keytab. + * \sa kim_ccache_create_from_keytab + */ +kim_error_t kim_credential_create_from_keytab (kim_credential_t *out_credential, + kim_identity_t in_identity, + kim_options_t in_options, + kim_string_t in_keytab); + +/*! + * \param out_credential on exit, a new credential object which is a copy of \a in_krb5_creds. + * Must be freed with kim_credential_free(). + * \param in_krb5_context the krb5 context used to create \a in_krb5_creds. + * \param in_krb5_creds a krb5 credential object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy a credential from a krb5 credential object. + */ +kim_error_t kim_credential_create_from_krb5_creds (kim_credential_t *out_credential, + krb5_context in_krb5_context, + krb5_creds *in_krb5_creds); + +/*! + * \param out_credential on exit, a new credential object which is a copy of \a in_credential. + * Must be freed with kim_credential_free(). + * \param in_credential a credential object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy a credential object. + */ +kim_error_t kim_credential_copy (kim_credential_t *out_credential, + kim_credential_t in_credential); + +/*! + * \param in_credential a credential object. + * \param in_krb5_context a krb5 context which will be used to create \a out_krb5_creds. + * \param out_krb5_creds on exit, a new krb5 creds object which is a copy of \a in_credential. + * Must be freed with krb5_free_creds(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get a krb5 credentials object for a credential object. + */ +kim_error_t kim_credential_get_krb5_creds (kim_credential_t in_credential, + krb5_context in_krb5_context, + krb5_creds **out_krb5_creds); + +/*! + * \param in_credential a credential object. + * \param out_client_identity on exit, an identity object containing the client identity of + * \a in_credential. Must be freed with kim_identity_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the client identity of a credential object. + */ +kim_error_t kim_credential_get_client_identity (kim_credential_t in_credential, + kim_identity_t *out_client_identity); + +/*! + * \param in_credential a credential object. + * \param out_service_identity on exit, an identity object containing the service identity of + * \a in_credential. Must be freed with kim_identity_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the service identity of a credential object. + */ +kim_error_t kim_credential_get_service_identity (kim_credential_t in_credential, + kim_identity_t *out_service_identity); + +/*! + * \param in_credential a credential object. + * \param out_is_tgt on exit, whether or not the credential is a TGT. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Check if a credential is a ticket granting ticket. + */ +kim_error_t kim_credential_is_tgt (kim_credential_t in_credential, + kim_boolean_t *out_is_tgt); + +/*! + * \param in_credential a credential object. + * \param out_state on exit, the state of the credential. See #kim_credential_state_enum + * for the possible values of \a out_state. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Check the state of a credential (valid, expired, postdated, etc). + */ +kim_error_t kim_credential_get_state (kim_credential_t in_credential, + kim_credential_state_t *out_state); + +/*! + * \param in_credential a credential object. + * \param out_start_time on exit, the time when \a in_credential becomes valid. + * May be in the past or future. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the time when the credentials become valid. + * \sa kim_ccache_get_start_time + */ +kim_error_t kim_credential_get_start_time (kim_credential_t in_credential, + kim_time_t *out_start_time); + +/*! + * \param in_credential a credential object. + * \param out_expiration_time on exit, the time when \a in_credential will expire. + * May be in the past or future. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the time when the credentials will expire. + * \sa kim_ccache_get_expiration_time + */ +kim_error_t kim_credential_get_expiration_time (kim_credential_t in_credential, + kim_time_t *out_expiration_time); + +/*! + * \param in_credential a credential object. + * \param out_renewal_expiration_time on exit, the time when \a in_credential will no longer + * be renewable. May be in the past or future. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the time when the credentials will no longer be renewable. + * \sa kim_ccache_get_renewal_expiration_time + */ +kim_error_t kim_credential_get_renewal_expiration_time (kim_credential_t in_credential, + kim_time_t *out_renewal_expiration_time); + + +/*! + * \param in_credential a credential object. + * \param in_client_identity a client identity. + * \param out_ccache on exit, a ccache object containing \a in_credential with the client + * identity \a in_client_identity. Must be freed with kim_ccache_free(). + * Specify NULL if you don't want this return value. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Store a credential in a ccache in the cache collection. + */ +kim_error_t kim_credential_store (kim_credential_t in_credential, + kim_identity_t in_client_identity, + kim_ccache_t *out_ccache); + +/*! + * \param in_credential a TGT credential to be verified. + * \param in_service_identity a service identity to look for in the keytab. Specify + * KIM_IDENTITY_ANY to use the default service identity + * (usually host/@). + * \param in_keytab a path to a keytab. Specify NULL for the default keytab location. + * \param in_fail_if_no_service_key whether or not the absence of a key for \a in_service_identity + * in the host's keytab will cause a failure. + * \note specifying FALSE for \a in_fail_if_no_service_key may expose the calling program to + * the Zanarotti attack if the host has no keytab installed. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Verify a TGT credential. + * \sa kim_ccache_verify + */ +kim_error_t kim_credential_verify (kim_credential_t in_credential, + kim_identity_t in_service_identity, + kim_string_t in_keytab, + kim_boolean_t in_fail_if_no_service_key); + +/*! + * \param io_credential a TGT credential to be renewed. On exit, the old credential + * object will be freed and \a io_credential will be replaced + * with a new renewed credential. The new credential must be freed + * with kim_credential_free(). + * \param in_options initial credential options. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Renew a TGT credential. + * \sa kim_ccache_renew + */ +kim_error_t kim_credential_renew (kim_credential_t *io_credential, + kim_options_t in_options); + +/*! + * \param io_credential a credential object to be validated. On exit, the old credential + * object will be freed and \a io_credential will be replaced + * with a new validated credential. The new credential must be freed + * with kim_credential_free(). + * \param in_options initial credential options. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Validate a TGT credential. + * \sa kim_ccache_validate + */ +kim_error_t kim_credential_validate (kim_credential_t *io_credential, + kim_options_t in_options); + +/*! + * \param io_credential the credential object to be freed. Set to NULL on exit. + * \brief Free memory associated with a credential object. + */ +void kim_credential_free (kim_credential_t *io_credential); + +/*!@}*/ + + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_CREDENTIAL_H */ diff --git a/src/include/kim/kim_error.h b/src/include/kim/kim_error.h new file mode 100644 index 000000000..8a6a4d576 --- /dev/null +++ b/src/include/kim/kim_error.h @@ -0,0 +1,120 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_ERROR_H +#define KIM_ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/*! + * \ingroup kim_types_reference + * The kim_error_t returned when no error occurred. + * Does not need to be freed with kim_error_free(). + */ +#define KIM_NO_ERROR ((kim_error_t) NULL) + +/*! + * \ingroup kim_types_reference + * The kim_error_code_t for KIM_NO_ERROR. + */ +#define KIM_NO_ERROR_ECODE ((kim_error_code_t) 0) + +/*! + * \page kim_error_overview KIM Error Overview + * + * An error object. Error objects consist of a machine readable error code for + * for programmatic error handling and a string describing the error. All KIM APIs + * return kim_errors with the exception of memory deallocation functions and the + * kim_error_t APIs which return pieces of a kim_error_t object. + * + * Functions which return successfully will return #KIM_NO_ERROR (NULL). Because + * #KIM_NO_ERROR does not need to be freed, you may use if-ladders or goto-style + * error handling when calling the KIM APIs. In addition, kim_error_free() may + * be called on #KIM_NO_ERROR. + * + * \note Certain kim_error_t objects are preallocated by the libraries avoid + * exacerbating existing problems while trying to report an error. For example, + * the out of memory error object is preallocated. It is safe to call + * #kim_error_free() on these errors, although the function may not actually free + * the object. + * + * By providing an error object rather than a numeric code, the KIM APIs can + * tailor error strings to the circumstances of the error. So rather than returning + * error strings like "Client not found in Kerberos database", we can report + * "'user@REALM' not found in Kerberos database" while still providing the machine + * readable error KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN. + * + * See \ref kim_error_reference for information on specific APIs. + */ + +/*! + * \defgroup kim_error_reference KIM Error Reference Documentation + * @{ + */ + +/*! + * \param out_error on exit, a new error object which is a copy of \a in_error. + * Must be freed with kim_error_free(). + * \param in_error the error to copy. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy an error. + */ +kim_error_t kim_error_copy (kim_error_t *out_error, + kim_error_t in_error); + +/*! + * \param in_error an error object. + * \return On success, a machine-readable error code describing the error represented + * by \a in_error. On failure, #KIM_PARAMETER_ECODE. + * \brief Get a numerical error code for an error. + */ +kim_error_code_t kim_error_get_code (kim_error_t in_error); + +/*! + * \param in_error an error object. + * \return On success, a human-readable error string describing the error represented + * by \a in_error. On failure, NULL, indicating that the kim_error_t object was + * invalid. + * \brief Get a text description of an error. + */ +kim_string_t kim_error_get_display_string (kim_error_t in_error); + +/*! + * \param io_error the error object to be freed. Set to NULL on exit. + * \brief Free memory associated with an error. + */ +void kim_error_free (kim_error_t *io_error); + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_ERROR_H */ diff --git a/src/include/kim/kim_identity.h b/src/include/kim/kim_identity.h new file mode 100644 index 000000000..9ee7fc00e --- /dev/null +++ b/src/include/kim/kim_identity.h @@ -0,0 +1,307 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_IDENTITY_H +#define KIM_IDENTITY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/*! + * \ingroup kim_types_reference + * Constant to specify any Kerberos identity is acceptable. + */ +#define KIM_IDENTITY_ANY ((kim_identity_t) NULL) + +/*! + * \page kim_identity_overview KIM Identity Overview + * + * \section kim_identity_introduction Introduction + * + * Identities in Kerberos are named by "principals". These identies may be people (users) + * or services (a server running on a host). When Kerberos issues credentials which + * authenticate one identity to another, the identity being authenticated is called + * the "client identity" and the identity being authenticated to is called the + * "service identity". + * + * Kerberos identities are made up of one or more components, as well as the Kerberos realm + * the entity belongs to. For client identities the first component is usually the client + * username (eg: "jdoe"). For service identities the first component is the name of the + * service (eg: "imap"). + * + * Kerberos identities have both a binary (opaque) representation and also a string + * representation. The string representation consists of the components separated by '/' + * followed by an '@' and then the realm. For example, the identity "jdoe/admin@EXAMPLE.COM" + * represents John Doe's administrator identity at the realm EXAMPLE.COM. Note that + * identity components may contain both '/' and '@' characters. When building a + * identity from its string representation these syntactic characters must be escaped + * with '\'. + * + * + * \section kim_identity_create_display Creating and Displaying Identities + * + * KIM Identities can be generated from components, their escaped string representation + * or from a krb5_principal. Once you have a KIM identity object, you can also get + * the component, string or krb5_principal representations back out: + * + * \li #kim_identity_create_from_components() creates an identity object from a list of components. + * \li #kim_identity_get_number_of_components() returns the number of components in an identity object. + * \li #kim_identity_get_component_at_index() return a component of an identity object. + * \li #kim_identity_get_realm() returns the identity's realm. + * + * \li #kim_identity_create_from_string() generates an identity object from an escaped string representation. + * \li #kim_identity_get_string() returns the identity's escaped string representation. + * \li #kim_identity_get_display_string() returns a non-escaped string for display to the user. + * This string cannot be passed into #kim_identity_create_from_string(). + * + * \li #kim_identity_create_from_krb5_principal() generates an identity object from a krb5_principal object. + * \li #kim_identity_get_krb5_principal() returns a krb5_principal object for an identity object. + * + * \li #kim_identity_get_gss_name() returns a gss_name_t object for use with gss_acquire_cred(). + * + * \note If you need to know if two identity objects refer to the same entity, use #kim_identity_compare(). + * + * + * \section kim_identity_selection Choosing a Client Identity + * + * Unfortunately most of the time applications don't know what client identity to use. + * Users may have identities for multiple Kerberos realms, as well as multiple identities + * in a single realm (such as a user and administrator identity). + * + * To solve this problem, #kim_selection_hints_get_identity() takes information + * from the application in the form of a selection hints object and returns the best + * matching client identity, if one is available. See \ref kim_selection_hints_overview + * for more information. + * + * + * \section kim_identity_password Changing a Identity's Password + * + * Many Kerberos sites use passwords for user accounts. Because passwords may be + * stolen or compromised, they must be frequently changed. KIM provides APIs to + * change the identity's password directly, and also handles changing the identity's + * password when it has expired. + * + * #kim_identity_change_password() presents a user interface to obtain the old and + * new passwords from the user. #kim_identity_change_password_with_passwords() takes + * the old and new passwords as input, but may still present a user interface if it + * needs to obtain additional information to authenticate. + * + * \note Not all identities have a password. Some sites use certificates (pkinit) + * and in the future there may be other authentication mechanisms (eg: smart cards). + * + * See \ref kim_identity_reference for information on specific APIs. + */ + +/*! + * \defgroup kim_identity_reference KIM Identity Reference Documentation + * @{ + */ + +/*! + * \param out_identity on exit, a new identity object. Must be freed with kim_identity_free(). + * \param in_string a string representation of a Kerberos identity. + * Special characters such as '/' and '@' must be escaped with '\'. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Create a identity from a string. + */ +kim_error_t kim_identity_create_from_string (kim_identity_t *out_identity, + kim_string_t in_string); + +/*! + * \param out_identity on exit, a new identity object. Must be freed with kim_identity_free(). + * \param in_realm a string representation of a Kerberos realm. + * \param in_1st_component a string representing the first component of the identity. + * \param ... zero or more strings of type kim_string_t representing additional components + * of the identity followed by a terminating NULL. Components will be assembled in + * order (ie: the 4th argument to kim_identity_create_from_components() will be + * the 2nd component of the identity). + * \note The last argument must be a NULL or kim_identity_create_from_components() may crash. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Create a identity from a realm and component strings. + */ +kim_error_t kim_identity_create_from_components (kim_identity_t *out_identity, + kim_string_t in_realm, + kim_string_t in_1st_component, + ...); + +/*! + * \param out_identity on exit, a new identity object which is a copy of \a in_krb5_principal. + * Must be freed with kim_identity_free(). + * \param in_krb5_context the krb5 context used to create \a in_krb5_principal. + * \param in_krb5_principal a krb5 principal object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Create an identity object from a krb5_principal. + */ +kim_error_t kim_identity_create_from_krb5_principal (kim_identity_t *out_identity, + krb5_context in_krb5_context, + krb5_principal in_krb5_principal); + +/*! + * \param out_identity on exit, a new identity object which is a copy of \a in_identity. + * Must be freed with kim_identity_free(). + * \param in_identity an identity object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy an identity object. + */ +kim_error_t kim_identity_copy (kim_identity_t *out_identity, + kim_identity_t in_identity); + + +/*! + * \param in_identity an identity object. + * \param in_compare_to_identity an identity object. + * \param out_comparison on exit, a comparison of \a in_identity and + * \a in_compare_to_identity which determines whether + * or not the two identities are equivalent and their + * sort order (for display to the user) if they are not. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Compare identity objects for equivalency. + */ +kim_error_t kim_identity_compare (kim_identity_t in_identity, + kim_identity_t in_compare_to_identity, + kim_comparison_t *out_comparison); +/*! + * \param in_identity an identity object. + * \param out_string on exit, a string representation of \a in_identity. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the string representation of a identity. + * \note Special characters such as '@' and '/' will be escaped with '\'. + */ +kim_error_t kim_identity_get_string (kim_identity_t in_identity, + kim_string_t *out_string); + + +/*! + * \param in_identity an identity object. + * \param out_display_string on exit, a string representation of \a in_identity appropriate for + * display to the user. Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get a human-readable string representation of an identity. + * \note Special characters such as '/' and '@' are \em not escaped with '\'. As a result the + * string returned from this function cannot be used with kim_identity_create_from_string() + * because it does not uniquely specify a principal. + * The result of this function should \em only be used to display to the user. + */ +kim_error_t kim_identity_get_display_string (kim_identity_t in_identity, + kim_string_t *out_display_string); + +/*! + * \param in_identity an identity object. + * \param out_realm_string on exit, a string representation of \a in_identity's realm. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the realm string of an identity. + */ +kim_error_t kim_identity_get_realm (kim_identity_t in_identity, + kim_string_t *out_realm_string); + +/*! + * \param in_identity an identity object. + * \param out_number_of_components on exit the number of components in \a in_identity. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the number of components of an identity. + */ +kim_error_t kim_identity_get_number_of_components (kim_identity_t in_identity, + kim_count_t *out_number_of_components); + +/*! + * \param in_identity an identity object. + * \param in_index the index of the desired component. Component indexes start at 0. + * \param out_component_string on exit, a string representation of the component in \a in_identity + * specified by \a in_index. Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the Nth component of an identity. + */ +kim_error_t kim_identity_get_component_at_index (kim_identity_t in_identity, + kim_count_t in_index, + kim_string_t *out_component_string); + +/*! + * \param in_identity an identity object. + * \param in_krb5_context a krb5 context object. + * \param out_krb5_principal on exit, a krb5_principal representation of \a in_identity + * allocated with \a in_krb5_context. Must be freed with + * krb5_free_principal() using \a in_krb5_context. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the krb5_principal representation of an identity. + */ +kim_error_t kim_identity_get_krb5_principal (kim_identity_t in_identity, + krb5_context in_krb5_context, + krb5_principal *out_krb5_principal); + +/*! + * \param in_identity an identity object. + * \param out_gss_name on exit, a gss_name_t representation of \a in_identity. + * Must be freed with gss_release_name(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the gss_name_t representation of an identity. + */ +kim_error_t kim_identity_get_gss_name (kim_identity_t in_identity, + gss_name_t *out_gss_name); + +/*! + * \param in_identity an identity object whose password will be changed. + * \param in_options initial credential options to be used if a new credential is obtained. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Change the password for an identity. + * \note kim_identity_change_password() will acquire a temporary credential to change + * the password. It uses the \a in_options structure to obtain information about the desired + * prompter and current password. + */ +kim_error_t kim_identity_change_password (kim_identity_t in_identity, + kim_options_t in_options); + +/*! + * \param in_identity an identity object whose password will be changed. + * \param in_options initial credential options to be used if a new credential is obtained. + * \param in_new_password a string representation of the identity's new password. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Change the password for an identity to a caller-provided new password. + * \note kim_identity_change_password_with_passwords() will acquire a temporary credential + * to change the password. It uses the \a in_options structure to obtain information about + * the desired prompter and current password. + */ +kim_error_t kim_identity_change_password_to_password (kim_identity_t in_identity, + kim_options_t in_options, + kim_string_t in_new_password); + +/*! + * \param io_identity the identity object to be freed. Set to NULL on exit. + * \brief Free memory associated with an identity. + */ +void kim_identity_free (kim_identity_t *io_identity); + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_IDENTITY_H */ diff --git a/src/include/kim/kim_options.h b/src/include/kim/kim_options.h new file mode 100644 index 000000000..ebdb791e1 --- /dev/null +++ b/src/include/kim/kim_options.h @@ -0,0 +1,637 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_OPTIONS_H +#define KIM_OPTIONS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! + * \addtogroup kim_types_reference + * @{ + */ + +/*! + * Specifies the user's default options. + */ +#define KIM_OPTIONS_DEFAULT ((kim_options_t) NULL) + +/*! + * Specifies that credentials should be valid immediately. + */ +#define KIM_OPTIONS_START_IMMEDIATELY ((kim_time_t) 0) + +/*! + * The type of prompt which needs to be displayed. + * This value determines what type of user interface is displayed. + * See \ref kim_options_custom_prompt_callback for more information. + */ +typedef uint32_t kim_prompt_type_t; + +enum kim_prompt_type_enum { + kim_prompt_type_password = 0, + kim_prompt_type_challenge = 1 +}; + +/*! + * The prompt callback used to display a prompt to the user. + * See \ref kim_options_custom_prompt_callback for more information. + */ +typedef kim_error_code_t (*kim_prompt_callback_t) (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply); + +/*! + * The default prompt callback. + * See \ref kim_options_custom_prompt_callback for more information. + */ +kim_error_code_t kim_prompt_callback_default (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply); + +/*! + * The graphical prompt callback. + * See \ref kim_options_custom_prompt_callback for more information. + */ +kim_error_code_t kim_prompt_callback_gui (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply); + +/*! + * The command line prompt callback. + * See \ref kim_options_custom_prompt_callback for more information. + */ +kim_error_code_t kim_prompt_callback_cli (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply); + +/*! + * The prompt callback which always returns an error. + * Use to turn off prompting entirely. + * \note Using this callback may prevent the user from authenicating. + * See \ref kim_options_custom_prompt_callback for more information. + */ +kim_error_code_t kim_prompt_callback_none (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply); + +/*! @} */ + +/*! + * \page kim_options_overview KIM Options Overview + * + * \section kim_options_introduction Introduction + * + * Kerberos Identity Management Options (kim_options_t) allows you to control how + * the Kerberos library obtains credentials. When the options structure is initialized with + * #kim_options_create(), each option is filled in with a default value which can then be modified + * with the kim_options_set_*() APIs. If you only want to use the default values, you may pass + * #KIM_OPTIONS_DEFAULT into any KIM function that takes a kim_options_t. + * + * KIM options fall into two major categories: options for controlling how credentials are + * acquired and options for controlling what properties the newly acquired credentials will have: + * + * \section kim_options_credential_acquisition Options for Controlling Credential Acquisition + * + * In order to acquire credentials, Kerberos needs to obtain one or more secrets from the user. + * These secrets may be a certificate, password, SecurID pin, or information from a smart card. + * If obtaining the secret requires interaction with the user, the Kerberos libraries call a + * "prompter callback" to display a dialog or command line prompt to request information from + * the user. If you want to provide your own custom dialogs or command line prompts, + * the KIM APIs provide a mechanism for replacing the default prompt callbacks with your own. + * + * \subsection kim_options_custom_prompt_callback Providing a Custom Prompt Callback + * + * All secrets are obtained from the user through a #kim_prompt_callback_t. By default, + * options use #kim_prompt_callback_default, which presents an expanding dialog to request + * information from the user, or if no graphical access is available, a command line prompt. + * + * KIM also provides three other callbacks: #kim_prompt_callback_gui only presents + * a dialog and returns an error if there is no graphical access. #kim_prompt_callback_cli + * only presents a command line interface and returns an error if there is no controlling + * terminal available. #kim_prompt_callback_none always returns an error. + * + * Using #kim_options_set_prompt_callback(), you can change the prompt callback to one of + * the above callbacks or a callback you have defined yourself. Callbacks are called in a + * loop, one for each prompt. Because network traffic may occur between calls to the prompt + * callback, your prompt interface should support time passing between calls to the prompter. + * If you are defining a callback yourself, you should also set your own options data with + * #kim_options_set_data() for storing state between calls. Options data is a caller + * defined pointer value -- the Kerberos libaries make no use of it. + * + * \subsection kim_options_preset_prompts Prefetching Prompt Responses + * + * Sometimes you may have already collected some of the information needed to acquire + * Kerberos credentials. Rather than creating a prompt callback, you may also prefetch + * responses to the options directly with #kim_options_set_prompt_response(). Once you + * have associated your response with a given prompt type, the Kerberos libraries will + * use this response for the first prompt of that type rather than calling the prompt + * callback to obtain it. + * + * Note that even if you prefetch responses, the prompt callback may still be called if + * you did not provide all the information required for the identity. You may specify + * the #kim_prompt_callback_none prompt callback to prevent prompting from occuring entirely, + * however, doing so will tie your application to a particular Kerberos configuration. + * For example, if your application assumes that all identities only require a password, + * it will not be able to acquire credentials at sites using SecurID pins. + * + * + * \section kim_options_credential_properties Options for Controlling Credential Properties + * + * Kerberos credentials have a number of different properties which can be requested + * when credentials are acquired. These properties control when and for how long the + * credentials are valid and what you can do with them. + + * Note that setting these properties in the KIM options only changes what the Kerberos + * libraries \em request from the KDC. The KDC itself may choose not to honor your + * requested properties if they violate the site security policy. For example, most sites + * place an upper bound on how long credentials may be valid. If you request a credential + * lifetime longer than this upper bound, the KDC may return credentials with a shorter + * lifetime than you requested. + * + * \subsection kim_options_lifetimes Credential Lifetime + * + * Kerberos credentials have start time and a lifetime during which they are valid. + * Once the lifetime has passed, credentials "expire" and can no longer be used. + * + * The requested credential start time can be set with #kim_options_set_start_time() + * and examined with #kim_options_get_start_time(). The requested credential + * lifetime can be set with #kim_options_set_lifetime() and examined with + * #kim_options_get_lifetime(). + * + * \subsection kim_options_renewable Renewable Credentials + * + * Credentials with very long lifetimes are more convenient since the user does not + * have authenticate as often. Unfortunately they are also a higher security + * risk: if credentials are stolen they can be used until they expire. + * Credential renewal exists to compromise between these two conflicting goals. + * + * Renewable credentials are TGT credentials which can be used to obtain new + * TGT credentials without reauthenticating. By regularly renewing credentials + * the KDC has an opportunity to check to see if the client's credentials have been + * reported stolen and refuse to renew them. Renewable credentials have a "renewal + * lifetime" during which credentials can be renewed. This lifetime is relative + * to the original credential start time. If credentials are renewed shortly before + * the end of the renewal lifetime, their lifetime will be capped to the end of the + * renewal lifetime. + * + * Note that credentials must be valid to be renewed and therefore may not be + * an appropriate solution for all use cases. Sites which use renewable + * credentials often create helper processes running as the user which will + * automatically renew the user's credentials when they get close to expiration. + * + * Use #kim_options_set_renewable() to change whether or not the Kerberos libraries + * request renewable credentials and #kim_options_get_renewable() to find out the + * current setting. Use #kim_options_set_renewal_lifetime() to change the requested + * renewal lifetime and #kim_options_get_renewal_lifetime() to find out the current + * value. + * + * \subsection kim_options_addressless Addressless Credentials + * + * Traditionally Kerberos used the host's IP address as a mechanism to restrict + * the user's credentials to a specific host, thus making it harder to use stolen + * credentials. When authenticating to a remote service with credentials containing + * addresses, the remote service verifies that the client's IP address is one of the + * addresses listed in the credential. Unfortunately, modern network technologies + * such as NAT rewrite the IP address in transit, making it difficult to use + * credentials with addresses in them. As a result, most Kerberos sites now obtain + * addressless credentials. + * + * Use #kim_options_set_addressless() to change whether or not the Kerberos libraries + * request addressless credentials. Use #kim_options_get_addressless() to find out the + * current setting. + * + * \subsection kim_options_forwardable Forwardable Credentials + * + * Forwardable credentials are TGT credentials which can be forwarded to a service + * you have authenticated to. If the credentials contain IP addresses, the addresses + * are changed to reflect the service's IP address. Credential forwarding is most + * commonly used for Kerberos-authenticated remote login services. By forwarding + * TGT credentials through the remote login service, the user's credentials will + * appear on the remote host when the user logs in. + * + * The forwardable flag only applies to TGT credentials. + * + * Use #kim_options_set_forwardable() to change whether or not the Kerberos libraries + * request forwardable credentials. Use #kim_options_get_forwardable() to find out the + * current setting. + * + * \subsection kim_options_proxiable Proxiable Credentials + * + * Proxiable credentials are similar to forwardable credentials except that instead of + * forwarding the a TGT credential itself, a service credential is forwarded + * instead. Using proxiable credentials, a user can permit a service to perform + * a specific task as the user using one of the user's service credentials. + * + * Like forwardability, the proxiable flag only applies to TGT credentials. Unlike + * forwarded credentials, the IP address of proxiable credentials are not modified for + * the service when being proxied. This can be solved by also requesting addressless + * credentials. + * + * Use #kim_options_set_proxiable() to change whether or not the Kerberos libraries + * request proxiable credentials. Use #kim_options_get_proxiable() to find out the + * current setting. + * + * \subsection kim_options_service_name Service Name + * + * Normally users acquire TGT credentials (ie "ticket granting tickets") and then + * use those credentials to acquire service credentials. This allows Kerberos to + * provide single sign-on while still providing mutual authentication to services. + * However, sometimes you just want an initial credential for a service. KIM + * options allows you to set the service name with + * #kim_options_set_service_name() and query it with + * #kim_options_get_service_name(). + * + * See \ref kim_options_reference for information on specific APIs. + */ + +/*! + * \defgroup kim_options_reference KIM Options Reference Documentation + * @{ + */ + +/*! + * \param out_options on exit, a new options object. Must be freed with kim_options_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Create new options with default values. + */ +kim_error_t kim_options_create (kim_options_t *out_options); + +/*! + * \param out_options on exit, a new options object which is a copy of \a in_options. + * Must be freed with kim_options_free(). + * \param in_options a options object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy options. + */ +kim_error_t kim_options_copy (kim_options_t *out_options, + kim_options_t in_options); + +/*! + * \param io_options an options object to modify. + * \param in_prompt_callback a prompt callback function. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the prompt callback for obtaining information from the user. + * \par Default value + * #kim_prompt_callback_default + * \sa kim_options_get_prompt_callback() + */ +kim_error_t kim_options_set_prompt_callback (kim_options_t io_options, + kim_prompt_callback_t in_prompt_callback); + +/*! + * \param in_options an options object. + * \param out_prompt_callback on exit, the prompt callback specified by in_options. + * Does not need to be freed but may become invalid when + * \a in_options is freed. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the prompt callback for obtaining information from the user. + * \par Default value + * #kim_prompt_callback_default + * \sa kim_options_set_prompt_callback() + */ +kim_error_t kim_options_get_prompt_callback (kim_options_t in_options, + kim_prompt_callback_t *out_prompt_callback); + +/*! + * \param io_options an options object to modify. + * \param in_data a pointer to caller-specific data. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set caller-specific data for use in library callbacks. + * \note This option can be used by the caller to store a pointer to data needed when handling a + * callback. The KIM library does not use this options data in any way. + * \par Default value + * NULL (no data is set by default) + * \sa kim_options_get_data() + */ +kim_error_t kim_options_set_data (kim_options_t io_options, + const void *in_data); + +/*! + * \param in_options an options object. + * \param out_data on exit, the pointer to caller specific data specified by in_options. + * Does not need to be freed but may become invalid when \a in_options is freed. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get caller-specific data for use in library callbacks. + * \note This option can be used by the caller to store a pointer to data needed when handling a + * callback. The KIM library does not use this options data in any way. + * \par Default value + * NULL (no data is set by default) + * \sa kim_options_set_data() + */ +kim_error_t kim_options_get_data (kim_options_t in_options, + const void **out_data); + +/*! + * \param io_options an options object to modify. + * \param in_prompt_type a type of prompt. + * \param in_response a response to prompts of \a in_prompt_type. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set a response for a prompt for use when acquiring credentials. + * \note Each response only overrides the first prompt of a given prompt type. If multiple + * prompts of the same type are required, or if a prompt of a different type is requested, + * the prompt callback will be called to obtain user input. If you want to turn off prompting + * entirely, call #kim_options_set_prompt_callback() with #kim_prompt_callback_none. + * \par Default value + * NULL (no response is set by default) + * \sa kim_options_get_prompt_response() + */ +kim_error_t kim_options_set_prompt_response (kim_options_t io_options, + kim_prompt_type_t in_prompt_type, + void *in_response); + +/*! + * \param in_options an options object. + * \param in_prompt_type a type of prompt. + * \param out_response on exit, the response to prompts of type \a in_prompt_type specified + * by \a in_options. Does not need to be freed but may become invalid + * when \a in_options is freed. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the response for a prompt for use when acquiring credentials. + * \note Each response only overrides the first prompt of a given prompt type. If multiple + * prompts of the same type are required, or if a prompt of a different type is requested, + * the prompt callback will be called to obtain user input. If you want to turn off prompting + * entirely, call #kim_options_set_prompt_callback() with #kim_prompt_callback_none. + * \par Default value + * NULL (no response is set by default) + * \sa kim_options_set_prompt_response() + */ +kim_error_t kim_options_get_prompt_response (kim_options_t in_options, + kim_prompt_type_t in_prompt_type, + void **out_response); + +/*! + * \param io_options an options object to modify. + * \param in_start_time a start date (in seconds since January 1, 1970). Set to + * #KIM_OPTIONS_START_IMMEDIATELY for the acquired credential to be valid + * immediately. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the date when a credential should become valid. + * \note When using a start time in the future, once the start time has been reached the credential + * must be validated before it can be used. + * \par Default value + * 0, indicating "now". The credential will be valid immediately. + * \sa kim_options_get_start_time(), kim_credential_validate(), kim_ccache_validate(), kim_identity_validate() + */ +kim_error_t kim_options_set_start_time (kim_options_t io_options, + kim_time_t in_start_time); + +/*! + * \param in_options an options object. + * \param out_start_time on exit, the start date (in seconds since January 1, 1970) specified by + * \a in_options. #KIM_OPTIONS_START_IMMEDIATELY indicates the credential + * will be valid immediately. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the date when a credential should become valid. + * \note When using a start time in the future, once the start time has been reached the credential + * must be validated before it can be used. + * \par Default value + * 0, indicating "now". The credential will be valid immediately. + * \sa kim_options_set_start_time(), kim_credential_validate(), kim_ccache_validate(), kim_identity_validate() + */ +kim_error_t kim_options_get_start_time (kim_options_t in_options, + kim_time_t *out_start_time); + +/*! + * \param io_options an options object to modify. + * \param in_lifetime a lifetime duration (in seconds). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the duration during which a credential should be valid. + * \note KDCs have a maximum allowed lifetime per identity (usually 10 to 21 hours). + * As a result the credential will actually have a lifetime which is the minimum of + * \a in_lifetime and the KDC's maximum allowed lifetime. + * \sa kim_options_get_lifetime() + * \par Default value + * Read from the user's preferences and the Kerberos configuration. 10 hours if unspecified. + */ +kim_error_t kim_options_set_lifetime (kim_options_t io_options, + kim_lifetime_t in_lifetime); + +/*! + * \param in_options an options object. + * \param out_lifetime on exit, the lifetime duration (in seconds) specified in \a in_options. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the duration during which an acquired credential should be valid. + * \note KDCs have a maximum allowed lifetime per identity (usually 10 to 21 hours). + * As a result the credential will actually have a lifetime which is the minimum of + * \a in_lifetime and the KDC's maximum allowed lifetime. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. 10 hours if unspecified. + * \sa kim_options_set_lifetime() + */ +kim_error_t kim_options_get_lifetime (kim_options_t in_options, + kim_lifetime_t *out_lifetime); + +/*! +* \param io_options an options object to modify. + * \param in_renewable a boolean value indicating whether or not to request a renewable + * credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set whether or not to request a renewable credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_get_renewable() + */ +kim_error_t kim_options_set_renewable (kim_options_t io_options, + kim_boolean_t in_renewable); + +/*! +* \param in_options an options object. + * \param out_renewable on exit, a boolean value indicating whether or \a in_options will + * request a renewable credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get whether or not to request a renewable credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_set_renewable() + */ +kim_error_t kim_options_get_renewable (kim_options_t in_options, + kim_boolean_t *out_renewable); + +/*! + * \param io_options an options object to modify. + * \param in_renewal_lifetime a renewal lifetime duration (in seconds). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the duration during which a valid credential should be renewable. + * \note KDCs have a maximum allowed renewal lifetime per identity (usually 10 to 21 hours). + * As a result the credential will actually have a lifetime which is the minimum of + * \a in_lifetime and the KDC's maximum allowed lifetime. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. 7 days if unspecified. + * \sa kim_options_get_renewal_lifetime(), kim_identity_renew(), kim_credential_renew(), kim_ccache_renew() + */ +kim_error_t kim_options_set_renewal_lifetime (kim_options_t io_options, + kim_lifetime_t in_renewal_lifetime); + +/*! + * \param in_options an options object. + * \param out_renewal_lifetime on exit, the renewal lifetime duration (in seconds) specified + * in \a in_options. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the duration during which a valid credential should be renewable. + * \note KDCs have a maximum allowed lifetime per identity (usually 10 to 21 hours). + * As a result the credential will actually have a lifetime which is the minimum of + * \a in_lifetime and the KDC's maximum allowed lifetime. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. 7 days if unspecified. + * \sa kim_options_set_renewal_lifetime(), kim_identity_renew(), kim_credential_renew(), kim_ccache_renew() + */ +kim_error_t kim_options_get_renewal_lifetime (kim_options_t in_options, + kim_lifetime_t *out_renewal_lifetime); + +/*! + * \param io_options an options object to modify. + * \param in_forwardable a boolean value indicating whether or not to request a forwardable + * credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set whether or not to request a forwardable credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_get_forwardable() + */ +kim_error_t kim_options_set_forwardable (kim_options_t io_options, + kim_boolean_t in_forwardable); + +/*! + * \param in_options an options object. + * \param out_forwardable on exit, a boolean value indicating whether or \a in_options will + * request a forwardable credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get whether or not to request a forwardable credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_set_forwardable() + */ +kim_error_t kim_options_get_forwardable (kim_options_t in_options, + kim_boolean_t *out_forwardable); + +/*! + * \param io_options an options object to modify. + * \param in_proxiable a boolean value indicating whether or not to request a proxiable + * credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set whether or not to request a proxiable credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_get_proxiable() + */ +kim_error_t kim_options_set_proxiable (kim_options_t io_options, + kim_boolean_t in_proxiable); + +/*! + * \param in_options an options object. + * \param out_proxiable on exit, a boolean value indicating whether or \a in_options will + * request a proxiable credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get whether or not to request a proxiable credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_set_proxiable() + */ +kim_error_t kim_options_get_proxiable (kim_options_t in_options, + kim_boolean_t *out_proxiable); + +/*! + * \param io_options an options object to modify. + * \param in_addressless a boolean value indicating whether or not to request an addressless + * credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set whether or not to request an addressless credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_get_addressless() + */ +kim_error_t kim_options_set_addressless (kim_options_t io_options, + kim_boolean_t in_addressless); + +/*! + * \param in_options an options object. + * \param out_addressless on exit, a boolean value indicating whether or \a in_options will + * request an addressless credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get whether or not to request an addressless credential. + * \par Default value + * Read from the user's preferences and the Kerberos configuration. TRUE if unspecified. + * \sa kim_options_set_addressless() + */ +kim_error_t kim_options_get_addressless (kim_options_t in_options, + kim_boolean_t *out_addressless); + +/*! + * \param io_options an options object to modify. + * \param in_service_name a service name. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the service name to request a credential for. + * \par Default value + * NULL, indicating "krbtgt@", the ticket granting ticket (TGT) service. + * \sa kim_options_get_service_name() + */ +kim_error_t kim_options_set_service_name (kim_options_t io_options, + kim_string_t in_service_name); + +/*! + * \param in_options an options object. + * \param out_service_name on exit, the service name specified in \a in_options. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the service name to request a credential for. + * \par Default value + * NULL, indicating "krbtgt@", the ticket granting ticket (TGT) service. + * \sa kim_options_set_service_name() + */ +kim_error_t kim_options_get_service_name (kim_options_t in_options, + kim_string_t *out_service_name); + +/*! + * \param io_options the options object to be freed. Set to NULL on exit. + * \brief Free memory associated with an options object. + */ +void kim_options_free (kim_options_t *io_options); + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_OPTIONS_H */ diff --git a/src/include/kim/kim_preferences.h b/src/include/kim/kim_preferences.h new file mode 100644 index 000000000..17ca351ed --- /dev/null +++ b/src/include/kim/kim_preferences.h @@ -0,0 +1,485 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_PREFERENCES_H +#define KIM_PREFERENCES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! + * \page kim_favorite_identities_overview KIM Favorite Identities Overview + * + * \section kim_favorite_identities_introduction Introduction + * + * As Kerberos becomes more widespread, the number of possible Kerberos + * identities and realms a user might want to use will become very large. + * Sites may list hundreds of realms in their Kerberos configuration files. + * In addition, sites may wish to use DNS SRV records to avoid having to list + * all the realms they use in their Kerberos configuration. As a result, the + * list of realms in the Kerberos configuration may be exceedingly large and/or + * incomplete. Users may also use multiple identities from the same realm. + * + * On platforms which use a GUI to acquire credentials, the KIM would like + * to to display a list of identities for the user to select from. Depending on + * what is appropriate for the platform, identities may be displayed in a popup + * menu or other list. + * + * To solve this problem, the KIM maintains a list of favorite identities + * specifically for identity selection. This list is a set of unique identities + * in alphabetical order (as appropriate for the user's language localization). + * + * On most platforms the list of favorite identities has both an administrator + * preference and a user preference which overrides it. The administrator + * preference exists only to initialize the favorite identities for new user + * accounts. Once the user modifies the list their favorite identities may + * diverge from the site favorite identities preference. + * + * \note The location of user preferences and the semantics of + * preference synchronization is platform-specific. Where possible KIM will use + * platform-specific preference mechanisms. + * + * Most callers will not need to use the favorite identities APIs. However if you + * are implementing your own graphical prompt callback or a credential management + * application, you may to view and/or edit the user's favorite identities. + * + * \section kim_favorite_identities_edit Viewing and Editing the Favorite Identities + * + * First, you need to acquire the Favorite Identities stored in the user's + * preferences using #kim_preferences_create() and + * #kim_preferences_get_favorite_identities(). Or you can use + * #kim_favorite_identities_create() to get an empty identities list if you want to + * overwrite the user's identities list entirely. See \ref kim_preferences_overview + * for more information on modifying the user's preferences. + * + * Then use #kim_favorite_identities_get_number_of_identities() and + * #kim_favorite_identities_get_identity_at_index() to display the identities list. + * Use #kim_favorite_identities_add_identity() and #kim_favorite_identities_remove_identity() + * to change which identities are in the identities list. Identities are always stored in + * alphabetical order and duplicate identities are not permitted, so when you add or remove a + * identity you should redisplay the entire list. + * + * Once you are done editing the identities list, store changes in the user's preference file + * using #kim_preferences_set_favorite_identities() and #kim_preferences_synchronize(). + * + * See \ref kim_favorite_identities_reference for information on specific APIs. + */ + +/*! + * \defgroup kim_favorite_identities_reference KIM Favorite Identities Documentation + * @{ + */ + +/*! + * \param out_favorite_identities on exit, a new favorite identities object. + * Must be freed with kim_favorite_identities_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Create a new favorite identities list. + */ +kim_error_t kim_favorite_identities_create (kim_favorite_identities_t *out_favorite_identities); + +/*! + * \param out_favorite_identities on exit, a new favorite identities object which is + * a copy of in_favorite_identities. + * Must be freed with kim_favorite_identities_free(). + * \param in_favorite_identities a favorite identities object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy a favorite identities list. + */ +kim_error_t kim_favorite_identities_copy (kim_favorite_identities_t *out_favorite_identities, + kim_favorite_identities_t in_favorite_identities); + +/*! + * \param in_favorite_identities a favorite identities object. + * \param out_number_of_identities on exit, the number of identities in \a in_favorite_identities. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the number of identities in a favorite identities list. + */ +kim_error_t kim_favorite_identities_get_number_of_identities (kim_favorite_identities_t in_favorite_identities, + kim_count_t *out_number_of_identities); + +/*! + * \param in_favorite_identities a favorite identities object. + * \param in_index a index into the identities list (starting at 0). + * \param out_realm on exit, the identity at \a in_index in \a in_favorite_identities. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the Nth identity in a favorite identities list. + */ +kim_error_t kim_favorite_identities_get_identity_at_index (kim_favorite_identities_t in_favorite_identities, + kim_count_t in_index, + kim_identity_t *out_identity); +/*! + * \param io_favorite_identities a favorite identities object. + * \param in_identity an identity string to add to \a in_favorite_identities. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Add an identity to a favorite identities list. + */ +kim_error_t kim_favorite_identities_add_identity (kim_favorite_identities_t io_favorite_identities, + kim_identity_t in_identity); + +/*! + * \param io_favorite_identities a favorite identities object. + * \param in_identity an identity to remove from \a in_favorite_identities. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Remove an identity from a identities list. + */ +kim_error_t kim_favorite_identities_remove_identity (kim_favorite_identities_t io_favorite_identities, + kim_identity_t in_identity); + +/*! + * \param io_favorite_identities a favorite identities object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Empty a favorite identities list. + */ +kim_error_t kim_favorite_identities_remove_all_identities (kim_favorite_identities_t io_favorite_identities); + +/*! + * \param io_favorite_identities the favorite identities object to be freed. Set to NULL on exit. + * \brief Free memory associated with an identities list. + */ + +void kim_favorite_identities_free (kim_favorite_identities_t *io_favorite_identities); + +/*!@}*/ + +/*! + * \page kim_preferences_overview KIM Preferences Overview + * + * \section kim_preferences_introduction Introduction + * + * In addition to the site preferences stored in the Kerberos configuration, users may also + * want to have their own personal preferences for controlling credential acquisition. + * As a result, KIM provides user preferences for initial credential options and + * user interface behavior such as the default client identity and the favorite identities list. + * + * \section kim_preferences_edit Viewing and Editing the Preferences + * + * In order to view and edit the user's preferences, call #kim_preferences_create() to acquire a + * preferences object containing the user's preferences. You can examine preferences + * with the functions starting with "kim_preferences_get_" and change preferences with + * the functions starting with "kim_preferences_set_". Once you are done making changes, + * you can write changes back out to the user's preferences with #kim_preferences_synchronize(). + * + * \note The location of user preferences and the semantics of + * preference synchronization is platform-specific. Where possible KIM will use + * platform-specific preference mechanisms. + * + * \section kim_preferences_options Initial Credential Options Preferences + * + * KIM provides user preferences for initial credential options. These + * are the options #kim_options_create() will use when creating a new KIM + * options object. They are also the options specified by KIM_OPTIONS_DEFAULT. + * You can view and edit the initial credential options using + * #kim_preferences_get_options() and #kim_preferences_set_options(). + * + * \note Not all credential options in the kim_options_t object have corresponding + * user preferences. For example, the prompt callback function is not stored + * in the user preferences since it has no meaning outside of the current + * application. Some options which are not currently stored in the + * preferences may be stored there in the future. + * + * If you are implementing a user interface for credentials acquisition, + * you should be aware that KIM has a user preference to manage the initial + * credential options preferences. If the user successfully acquires credentials + * with non-default options and #kim_preferences_get_remember_options() is set + * to TRUE, you should store the options used to get credentials with + * #kim_preferences_set_options(). + * + * \section kim_preferences_client_identity Client Identity Preferences + * + * KIM also provides user preferences for the default client identity. + * This identity is used whenever KIM needs to display a graphical dialog for + * credential acquisition but does not know what client identity to use. + * You can view and edit the default client identity using + * #kim_preferences_get_client_identity() and + * #kim_preferences_set_client_identity(). + * + * If you are implementing a user interface for credentials acquisition, + * you should be aware that KIM has a user preference to manage + * the client identity preferences. If the user successfully acquires credentials + * with non-default options and #kim_preferences_get_remember_client_identity() is + * set to TRUE, you should store the client identity for which credentials were + * acquired using #kim_preferences_set_client_identity(). + * + * \section kim_preferences_favorite_identities Favorite Identities Preferences + * + * When presenting a graphical interface for credential acquisition, KIM + * may need to display a list of identities for the user to select from. + * This list is generated by the user's favorite identities preference. + * You can view and edit the favorite identities preference using + * #kim_preferences_get_favorite_identities() and + * #kim_preferences_set_favorite_identities(). Please see the + * \ref kim_favorite_identities_overview for more information. + * + * See \ref kim_preferences_reference for information on specific APIs. + */ + +/*! + * \defgroup kim_preferences_reference KIM Preferences Documentation + * @{ + */ + +/*! + * \param out_preferences on exit, a new preferences object. + * Must be freed with kim_preferences_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Create a new preferences object from the current user's preferences. + */ +kim_error_t kim_preferences_create (kim_preferences_t *out_preferences); + +/*! + * \param out_preferences on exit, a new preferences object which is a copy of in_preferences. + * Must be freed with kim_preferences_free(). + * \param in_preferences a preferences object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy a preferences object. + */ +kim_error_t kim_preferences_copy (kim_preferences_t *out_preferences, + kim_preferences_t in_preferences); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_options an options object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the user's preferred options. + * \sa kim_preferences_get_options() + */ +kim_error_t kim_preferences_set_options (kim_preferences_t io_preferences, + kim_options_t in_options); + +/*! + * \param in_preferences a preferences object. + * \param out_options on exit, the options specified in \a in_preferences. + * Must be freed with kim_options_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the user's preferred options. + * \sa kim_preferences_set_options() + */ +kim_error_t kim_preferences_get_options (kim_preferences_t in_preferences, + kim_options_t *out_options); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_remember_options a boolean value indicating whether or not to remember the last + * options used to acquire a credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set whether or not to remember the last options the user used to acquire a credential. + * \sa kim_preferences_get_remember_options() + */ +kim_error_t kim_preferences_set_remember_options (kim_preferences_t io_preferences, + kim_boolean_t in_remember_options); + +/*! + * \param in_preferences a preferences object. + * \param out_remember_options on exit, a boolean value indicating whether or \a in_preferences will + * remember the last options used to acquire a credential. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get whether or not to remember the last options the user used to acquire a credential. + * \sa kim_preferences_set_remember_options() + */ +kim_error_t kim_preferences_get_remember_options (kim_preferences_t in_preferences, + kim_boolean_t *out_remember_options); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_client_identity a client identity object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the user's preferred client identity. + * \sa kim_preferences_get_client_identity() + */ +kim_error_t kim_preferences_set_client_identity (kim_preferences_t io_preferences, + kim_identity_t in_client_identity); + +/*! + * \param in_preferences a preferences object. + * \param out_client_identity on exit, the client identity specified in \a in_preferences. + * Must be freed with kim_identity_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the user's preferred client identity. + * \sa kim_preferences_set_client_identity() + */ +kim_error_t kim_preferences_get_client_identity (kim_preferences_t in_preferences, + kim_identity_t *out_client_identity); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_remember_client_identity a boolean value indicating whether or not to remember the last + * client identity for which a credential was acquired. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set whether or not to remember the last client identity the user acquired a credential for. + * \sa kim_preferences_get_remember_client_identity() + */ +kim_error_t kim_preferences_set_remember_client_identity (kim_preferences_t io_preferences, + kim_boolean_t in_remember_client_identity); + +/*! + * \param in_preferences a preferences object. + * \param out_remember_client_identity on exit, a boolean value indicating whether or \a in_preferences will + * remember the last client identity for which a credential was acquired. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get whether or not to remember the last client identity the user acquired a credential for. + * \sa kim_preferences_set_remember_client_identity() + */ +kim_error_t kim_preferences_get_remember_client_identity (kim_preferences_t in_preferences, + kim_boolean_t *out_remember_client_identity); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_minimum_lifetime a minimum lifetime indicating how small a lifetime the + * GUI tools should allow the user to specify for credentials. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the minimum credential lifetime for GUI credential lifetime controls. + * \sa kim_preferences_get_minimum_lifetime() + */ +kim_error_t kim_preferences_set_minimum_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_minimum_lifetime); + +/*! + * \param in_preferences a preferences object. + * \param out_minimum_lifetime on exit, the minimum lifetime that GUI tools will + * allow the user to specify for credentials. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the minimum credential lifetime for GUI credential lifetime controls. + * \sa kim_preferences_set_minimum_lifetime() + */ +kim_error_t kim_preferences_get_minimum_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_minimum_lifetime); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_maximum_lifetime a maximum lifetime indicating how large a lifetime the + * GUI tools should allow the user to specify for credentials. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the maximum credential lifetime for GUI credential lifetime controls. + * \sa kim_preferences_get_maximum_lifetime() + */ +kim_error_t kim_preferences_set_maximum_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_maximum_lifetime); + +/*! + * \param in_preferences a preferences object. + * \param out_maximum_lifetime on exit, the maximum lifetime that GUI tools will + * allow the user to specify for credentials. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the maximum credential lifetime for GUI credential lifetime controls. + * \sa kim_preferences_set_maximum_lifetime() + */ +kim_error_t kim_preferences_get_maximum_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_maximum_lifetime); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_minimum_renewal_lifetime a minimum lifetime indicating how small a lifetime the + * GUI tools should allow the user to specify for + * credential renewal. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the minimum credential renewal lifetime for GUI credential lifetime controls. + * \sa kim_preferences_get_minimum_renewal_lifetime() + */ +kim_error_t kim_preferences_set_minimum_renewal_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_minimum_renewal_lifetime); + +/*! + * \param in_preferences a preferences object. + * \param out_minimum_renewal_lifetime on exit, the minimum lifetime that GUI tools will + * allow the user to specify for credential renewal. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the minimum credential renewal lifetime for GUI credential lifetime controls. + * \sa kim_preferences_set_minimum_renewal_lifetime() + */ +kim_error_t kim_preferences_get_minimum_renewal_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_minimum_renewal_lifetime); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_maximum_renewal_lifetime a maximum lifetime indicating how large a lifetime the + * GUI tools should allow the user to specify for + * credential renewal. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the maximum credential renewal lifetime for GUI credential lifetime controls. + * \sa kim_preferences_get_minimum_renewal_lifetime() + */ +kim_error_t kim_preferences_set_maximum_renewal_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_maximum_renewal_lifetime); + +/*! + * \param in_preferences a preferences object. + * \param out_maximum_renewal_lifetime on exit, the maximum lifetime that GUI tools will + * allow the user to specify for credential renewal. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the maximum credential renewal lifetime for GUI credential lifetime controls. + * \sa kim_preferences_set_minimum_renewal_lifetime() + */ +kim_error_t kim_preferences_get_maximum_renewal_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_maximum_renewal_lifetime); + +/*! + * \param io_preferences a preferences object to modify. + * \param in_favorite_identities a favorite identities object. + * See \ref kim_favorite_identities_overview for more information on KIM + * Favorite Identities. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the user's preferred list of identities. + * \sa kim_preferences_get_favorite_identities() + */ +kim_error_t kim_preferences_set_favorite_identities (kim_preferences_t io_preferences, + kim_favorite_identities_t in_favorite_identities); + +/*! + * \param in_preferences a preferences object. + * \param out_favorite_identities on exit, a copy of the favorite identities specified in \a in_preferences. + * See \ref kim_favorite_identities_overview for more information on KIM + * Favorite Identities. Must be freed with kim_favorite_identities_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the user's preferred list of identities. + * \sa kim_preferences_set_favorite_identities() + */ +kim_error_t kim_preferences_get_favorite_identities (kim_preferences_t in_preferences, + kim_favorite_identities_t *out_favorite_identities); + +/*! + * \param in_preferences a preferences object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Synchronize a preferences object with the user's preferences, writing pending changes + * and reading any changes applied by other processes. + */ +kim_error_t kim_preferences_synchronize (kim_preferences_t in_preferences); + +/*! + * \param io_preferences the preferences object to be freed. Set to NULL on exit. + * \brief Free memory associated with a preferences object. + */ +void kim_preferences_free (kim_preferences_t *io_preferences); + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_PREFERENCES_H */ diff --git a/src/include/kim/kim_selection_hints.h b/src/include/kim/kim_selection_hints.h new file mode 100644 index 000000000..42ec1f0cd --- /dev/null +++ b/src/include/kim/kim_selection_hints.h @@ -0,0 +1,537 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_SELECTION_HINTS_H +#define KIM_SELECTION_HINTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! + * \page kim_selection_hints_overview KIM Selection Hints Overview + * + * \section kim_selection_hints_introduction Introduction + * + * Most users belong to multiple organizations and thus need + * to authenticate to multiple Kerberos realms. Traditionally Kerberos sites + * solved this problem by setting up a cross-realm relationship, which allowed + * the user to use TGT credentials for their client identity in one realm + * to obtain credentials in another realm via cross-realm authentication. As a + * result users could acquire credentials for a single client identity and use + * them everywhere. + * + * Setting up cross-realm requires that realms share a secret, so sites must + * coordinate with one another to set up a cross-realm relationship. In + * addition, sites must set up authorization policies for users from other + * realms. As Kerberos becomes increasingly wide-spread, many realms will + * not have cross-realm relationships, and users will need to + * manually obtain credentials for their client identity at each realm + * (eg: "user@BANK.COM", "user@UNIVERSITY.EDU", etc). As a result, users + * will often have multiple credentials caches, one for each client identity. + * + * Unfortunately this presents a problem for applications which need to obtain + * service credentials. Which client identity should they use? + * Rather than having each application to manually search the cache collection, + * KIM provides a selection hints API for choosing the best client identity. + * This API is intended to simplify the process of choosing credentials + * and provide consistent behavior across all applications. + * + * Searching the cache collection for credentials may be expensive if there + * are a large number of caches. If credentials for the client identity + * are expired or not present, KIM may also wish to prompt the user for + * new credentials for the appropriate client identity. As a result, + * applications might want to remember which client identity worked in + * the past and always request credentials using that identity. + * + * + * \section kim_selection_hints_creating Creating KIM Selection Hints + * + * A KIM selection hints object consists of an application identifier and one or + * more pieces of information about the service the client application will be + * contacting. The application identifier is used by user preferences + * to control how applications share cache entries. It is important to be + * consistent about what application identifier you provide. Java-style + * identifiers are recommended to avoid collisions. + * + * \section kim_selection_hints_searching Selection Hint Search Behavior + * + * When using selection hints to search for an appropriate client identity, + * KIM uses a consistent hint search order. This allows applications to specify + * potentially contradictory information without preventing KIM from locating a + * single ccache. In addition the selection hint search order may change, + * especially if more hints are added. + * + * As a result, callers are encouraged to provide all relevant search hints, + * even if only a subset of those search hints are necessary to get reasonable + * behavior in the current implementation. Doing so will provide the most + * user-friendly selection experience. + * + * Currently the search order looks like this: + * + * \li Service Identity The client identity which has obtained a service credential for this service identity. + * \li Server A client identity which has obtained a service credential for this server. + * \li Service Realm A client identity which has obtained a service credential for this realm. + * \li Service A client identity which has obtained a service credential for this service. + * \li Client Realm A client identity in this realm. + * \li User A client identity whose first component is this user string. + * + * For example, if you specify a service identity and a credential for + * that identity already exists in the ccache collection, KIM may use that + * ccache, even if your user and client realm entries in the selection hints would + * lead it to choose a different ccache. If no credentials for the service identity + * exist then KIM will fall back on the user and realm hints. + * + * \note Due to performance and information exposure concerns, currently all + * searching is done by examining the cache collection. In the future the KIM + * may also make network requests as part of its search algorithm. For example + * it might check to see if the TGT credentials in each ccache can obtain + * credentials for the service identity specified by the selection hints. + * + * \section kim_selection_hints_selecting Selecting an Identity Using Selection Hints + * + * Once you have provided search criteria for selecting an identity, use + * #kim_selection_hints_get_identity() to obtain an identity object. + * You can then use #kim_identity_get_gss_name() to obtain a gss_name_t + * for use in gss_acquire_cred() or use + * #kim_ccache_create_from_client_identity() to obtain a ccache containing + * credentials for the identity. + * + * \note #kim_selection_hints_get_identity() obtains an identity based on + * the current state of the selection hints object. If you change the + * selection hints object you must call #kim_selection_hints_get_identity() + * again. + * + * \section kim_selection_hints_caching Selection Hint Caching Behavior + * + * In addition to using selection hints to search for an appropriate client + * identity, KIM can also use them to remember which client identity worked. + * KIM maintains a per-user cache mapping selection hints to identities so + * that applications do not have to maintain their own caches or present + * user interface for selecting which cache to use. + * + * When #kim_selection_hints_get_identity() is called KIM looks up in the + * cache and returns the identity which the selection hints map to. If + * there is not a preexisting cache entry for the selection hints then + * #kim_selection_hints_get_identity() will search for an identity and + * prompt the user if it cannot find an appropriate one. + * + * If the client identity returned by KIM authenticates and passes + * authorization checks, you should tell KIM to cache the identity by calling + * #kim_selection_hints_remember_identity(). This will create a cache entry + * for the mapping between your selection hints and the identity so that + * subsequent calls to #kim_selection_hints_get_identity() do not need to + * prompt the user. + * + * If the client identity returned by KIM fails to authenticate or fails + * authorization checks, you must call #kim_selection_hints_forget_identity() + * to remove any mapping that already exists. After this function is called, + * future calls to #kim_selection_hints_get_identity() will search for an + * identity again. You may also wish to call this function if the user + * changes your application preferences such that the identity might be + * invalidated. + * + * \note It is very important that you call #kim_selection_hints_forget_identity() + * if your application fails to successfully establish a connection with the + * server. Otherwise the user can get "stuck" using the same non-working + * identity if they chose the wrong one accidentally or if their identity + * information changes. Because only your application understands the + * authorization checksof the protocol it uses, KIM cannot tell whether or not + * the identity worked. + * + * If you wish to search and prompt for an identity without using + * the cached mappings, you can turn off the cached mapping lookups using + * #kim_selection_hints_set_remember_identity(). This is not recommended + * for most applications since it will result in a lot of unnecessary + * searching and prompting for identities. + * + * \note Because cache entries key off of selection hints, it is important + * to always specify the same hints when contacting a particular + * service. Otherwise KIM will not always find the cache entries. + * + * \section kim_selection_hints_prompt Selection Hint Prompting Behavior + * + * If valid credentials for identity in the selection hints cache are + * unavailable or if no identity could be found using searching or caching + * when #kim_selection_hints_get_identity() is called, KIM may present a + * GUI to ask the user to select an identity or acquire credentials for + * an identity. + * + * \note Because of the caching behavior described above the user will + * only be prompted to choose an identity when setting up the application + * or when their identity stops working. + * + * In order to let the user know why Kerberos needs their assistance, KIM + * displays the name of the application which requested the identity + * selection. Unfortunately, some platforms do not provide a runtime + * mechanism for determining the name of the calling process. If your + * application runs on one of these platforms (or is cross-platform) + * you should provide a localized version of its name with + * #kim_selection_hints_set_application_name(). You can check what name + * will be used with #kim_selection_hints_get_application_name(). + * + * In many cases a single application may select different identities for + * different purposes. For example an email application might use different + * identities to check mail for different accounts. If your application + * has this property you may need to provide the user with a localized + * string describing how the identity will be used. You can specify + * this string with #kim_selection_hints_get_explanation(). You can find + * out what string will be used with kim_selection_hints_set_explanation(). + * + * Since the user may choose to acquire credentials when selection an + * identity, KIM also provides #kim_selection_hints_set_options() to + * set what credential acquisition options are used. + * #kim_selection_hints_get_options() returns the options which will be used. + * + * If you need to disable user interaction, use + * #kim_selection_hints_set_allow_user_interaction(). Use + * #kim_selection_hints_get_allow_user_interaction() to find out whether or + * not user interaction is enabled. User interaction is enabled by default. + * + * See \ref kim_selection_hints_reference for information on specific APIs. + */ + +/*! + * \defgroup kim_selection_hints_reference KIM Selection Hints Reference Documentation + * @{ + */ + +/*! + * \param out_selection_hints on exit, a new selection hints object. + * Must be freed with kim_selection_hints_free(). + * \param in_application_identifier an application identifier string. Java-style identifiers are recommended + * to avoid cache entry collisions (eg: "com.example.MyApplication") + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Create a new selection hints object. + */ +kim_error_t kim_selection_hints_create (kim_selection_hints_t *out_selection_hints, + kim_string_t in_application_identifier); + +/*! + * \param out_selection_hints on exit, a new selection hints object which is a copy of in_selection_hints. + * Must be freed with kim_selection_hints_free(). + * \param in_selection_hints a selection hints object. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy a selection hints object. + */ +kim_error_t kim_selection_hints_copy (kim_selection_hints_t *out_selection_hints, + kim_selection_hints_t in_selection_hints); + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_service_identity a service identity. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the preferred service identity. + * \sa kim_selection_hints_get_service_identity_hint() + */ +kim_error_t kim_selection_hints_set_service_identity_hint (kim_selection_hints_t io_selection_hints, + kim_identity_t in_service_identity); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_service_identity on exit, the service identity specified in \a in_selection_hints. + * Must be freed with kim_identity_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the preferred service identity. + * \sa kim_selection_hints_set_service_identity_hint() + */ +kim_error_t kim_selection_hints_get_service_identity_hint (kim_selection_hints_t in_selection_hints, + kim_identity_t *out_service_identity); + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_client_realm a client realm string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the preferred client realm. + * \sa kim_selection_hints_get_client_realm_hint() + */ +kim_error_t kim_selection_hints_set_client_realm_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_client_realm); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_client_realm on exit, the client realm string specified in \a in_selection_hints. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the preferred client realm. + * \sa kim_selection_hints_set_client_realm_hint() + */ +kim_error_t kim_selection_hints_get_client_realm_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_client_realm); + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_user a user name string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the preferred user name. + * \sa kim_selection_hints_get_user_hint() + */ +kim_error_t kim_selection_hints_set_user_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_user); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_user on exit, the user name string specified in \a in_selection_hints. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the preferred user name. + * \sa kim_selection_hints_set_user_hint() + */ +kim_error_t kim_selection_hints_get_user_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_user); + + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_service_realm a service realm string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the preferred service realm. + * \sa kim_selection_hints_get_service_realm_hint() + */ +kim_error_t kim_selection_hints_set_service_realm_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_service_realm); + +/*! + * \param io_selection_hints a selection hints object. + * \param out_service_realm on exit, the service realm string specified in \a in_selection_hints. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the preferred service realm. + * \sa kim_selection_hints_set_service_realm_hint() + */ +kim_error_t kim_selection_hints_get_service_realm_hint (kim_selection_hints_t io_selection_hints, + kim_string_t *out_service_realm); + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_service a service name string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the preferred service name. + * \sa kim_selection_hints_get_service_hint() + */ +kim_error_t kim_selection_hints_set_service_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_service); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_service on exit, the service name string specified in \a in_selection_hints. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the preferred service name. + * \sa kim_selection_hints_set_service_hint() + */ +kim_error_t kim_selection_hints_get_service_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_service); + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_server a server host name string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the preferred server host name. + * \sa kim_selection_hints_get_server_hint() + */ +kim_error_t kim_selection_hints_set_server_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_server); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_server on exit, the server host name string specified in \a in_selection_hints. + * Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the preferred server host name. + * \sa kim_selection_hints_set_server_hint() + */ +kim_error_t kim_selection_hints_get_server_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_server); + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_application_name a localized string containing the full name of the application. + * \note If you do not call this function KIM will attempt to determine the application + * name at runtime. If that fails (the functionality is only available on some platforms) + * then KIM will use the application identity string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the application name for use in user interaction. + * \sa kim_selection_hints_get_application_name() + */ +kim_error_t kim_selection_hints_set_application_name (kim_selection_hints_t io_selection_hints, + kim_string_t in_application_name); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_application_name on exit, the localized full name of the application specified + * in \a in_selection_hints. Must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the application name for use in user interaction. + * \sa kim_selection_hints_set_application_name() + */ +kim_error_t kim_selection_hints_get_application_name (kim_selection_hints_t in_selection_hints, + kim_string_t *out_application_name); + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_explanation a localized string describing why the caller needs the identity. + * \note If the application only does one thing (the reason it needs an identity is obvious) + * then you may not need to call this function. You may still need to call + * #kim_selection_hints_set_application_name() + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the strings used to prompt the user to select the identity. + * \sa kim_selection_hints_get_explanation() + */ +kim_error_t kim_selection_hints_set_explanation (kim_selection_hints_t io_selection_hints, + kim_string_t in_explanation); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_explanation on exit, the localized string specified in \a in_selection_hints + * which describes why the caller needs the identity. May be NULL. + * If non-NULL, must be freed with kim_string_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the strings used to prompt the user to select the identity. + * \sa kim_selection_hints_set_explanation() + */ +kim_error_t kim_selection_hints_get_explanation (kim_selection_hints_t in_selection_hints, + kim_string_t *out_explanation); + + +/*! + * \param io_selection_hints a selection hints object to modify. + * \param in_options options to control credential acquisition. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Set the options which will be used if credentials need to be acquired. + * \sa kim_selection_hints_get_options() + */ +kim_error_t kim_selection_hints_set_options (kim_selection_hints_t io_selection_hints, + kim_options_t in_options); + +/*! + * \param in_selection_hints a selection hints object. + * \param out_options on exit, the options to control credential acquisition + * specified in \a in_selection_hints. May be KIM_OPTIONS_DEFAULT. + * If not, must be freed with kim_options_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Get the options which will be used if credentials need to be acquired. + * \sa kim_selection_hints_set_options() + */ +kim_error_t kim_selection_hints_get_options (kim_selection_hints_t in_selection_hints, + kim_options_t *out_options); + +/*! + * \param in_selection_hints a selection hints object to modify + * \param in_allow_user_interaction a boolean value specifying whether or not KIM should ask + * the user to select an identity for \a in_selection_hints. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \note This setting defaults to TRUE. + * \brief Set whether or not KIM may interact with the user to select an identity. + * \sa kim_selection_hints_get_allow_user_interaction + */ +kim_error_t kim_selection_hints_set_allow_user_interaction (kim_selection_hints_t in_selection_hints, + kim_boolean_t in_allow_user_interaction); + +/*! + * \param in_selection_hints a selection hints object to modify + * \param out_allow_user_interaction on exit, a boolean value specifying whether or not KIM + * should ask the user to select an identity for + * \a in_selection_hints. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \note This setting defaults to TRUE. + * \brief Get whether or not KIM may interact with the user to select an identity. + * \sa kim_selection_hints_set_allow_user_interaction + */ +kim_error_t kim_selection_hints_get_allow_user_interaction (kim_selection_hints_t in_selection_hints, + kim_boolean_t *out_allow_user_interaction); + +/*! + * \param in_selection_hints a selection hints object to modify + * \param in_remember_identity a boolean value specifying whether or not KIM should use a cached + * mapping between \a in_selection_hints and a Kerberos identity. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \note This setting defaults to TRUE. + * \brief Set whether or not KIM will use cached mappings for this selection hints object. + * \sa kim_selection_hints_get_remember_identity + */ +kim_error_t kim_selection_hints_set_remember_identity (kim_selection_hints_t in_selection_hints, + kim_boolean_t in_remember_identity); + +/*! + * \param in_selection_hints a selection hints object to modify + * \param out_remember_identity on exit, a boolean value specifying whether or not KIM will use a + * cached mapping between \a in_selection_hints and a Kerberos identity. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \note This setting defaults to TRUE. + * \brief Get whether or not KIM will use cache mappings for this selection hints object. + * \sa kim_selection_hints_set_remember_identity + */ +kim_error_t kim_selection_hints_get_remember_identity (kim_selection_hints_t in_selection_hints, + kim_boolean_t *out_remember_identity); + +/*! + * \param in_selection_hints the selection hints to add to the cache. + * \param out_identity the Kerberos identity \a in_selection_hints maps to. + * Must be freed with kim_identity_free(). + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \note \a out_identity is the identity mapped to by the current state of \a in_selection_hints. + * This function may prompt the user via a GUI to choose that identity. + * Subsequent modifications to \a in_selection_hints will not change \a out_identity. + * \brief Choose a client identity based on selection hints. + */ + +kim_error_t kim_selection_hints_get_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t *out_identity); + +/*! + * \param in_selection_hints the selection hints to add to the cache. + * \param in_identity the Kerberos identity \a in_selection_hints maps to. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Add an entry for the selection hints to the selection hints cache, + * replacing any existing entry. + */ + +kim_error_t kim_selection_hints_remember_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t in_identity); + +/*! + * \param in_selection_hints the selection hints to remove from the cache. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Remove an entry for the selection hints from the selection hints cache. + */ + +kim_error_t kim_selection_hints_forget_identity (kim_selection_hints_t in_selection_hints); + +/*! + * \param io_selection_hints the selection hints object to be freed. Set to NULL on exit. + * \brief Free memory associated with a selection hints object. + */ + +void kim_selection_hints_free (kim_selection_hints_t *io_selection_hints); + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_SELECTION_HINTS_H */ diff --git a/src/include/kim/kim_string.h b/src/include/kim/kim_string.h new file mode 100644 index 000000000..eb5277ef5 --- /dev/null +++ b/src/include/kim/kim_string.h @@ -0,0 +1,84 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_STRING_H +#define KIM_STRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! + * \page kim_string_overview KIM String Overview + * + * A UTF8 string. + * + * Memory management routines are provided for runtime consistency on + * operating systems with shared libraries and multiple runtimes. + * + * See \ref kim_string_reference for information on specific APIs. + */ + +/*! + * \defgroup kim_string_reference KIM String Reference Documentation + * @{ + */ + +/*! + * \param out_string on exit, a new string object which is a copy of \a in_string. + Must be freed with kim_string_free(). + * \param in_string the string to copy. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Copy a string. + */ +kim_error_t kim_string_copy (kim_string_t *out_string, + const kim_string_t in_string); + +/*! + * \param in_string a string. + * \param in_compare_to_string a string to be compared to \a in_string. + * \param out_comparison on exit, a comparison result indicating whether \a in_string + * is greater than, less than or equal to \a in_compare_to_string. + * \return On success, #KIM_NO_ERROR. On failure, an error object representing the failure. + * \brief Compare two strings. + */ +kim_error_t kim_string_compare (kim_string_t in_string, + kim_string_t in_compare_to_string, + kim_comparison_t *out_comparison); + +/*! + * \param io_string a string to be freed. Set to NULL on exit. + * \brief Free memory associated with a string. + */ +void kim_string_free (kim_string_t *io_string); + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_STRING_H */ diff --git a/src/include/kim/kim_types.h b/src/include/kim/kim_types.h new file mode 100644 index 000000000..7898e626e --- /dev/null +++ b/src/include/kim/kim_types.h @@ -0,0 +1,162 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_TYPES_H +#define KIM_TYPES_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \defgroup kim_types_reference KIM Types and Constants + * @{ + */ + +/*! + * The KIM String type. See \ref kim_string_overview for more information. + */ +typedef int32_t kim_error_code_t; + +/*! + * A time value represented in seconds since January 1, 1970. + */ +typedef int64_t kim_time_t; + +/*! + * A duration represented in seconds. + */ +typedef int64_t kim_lifetime_t; + +/*! + * An quantity, usually used to return the number of elements in an array. + */ +typedef uint64_t kim_count_t; + +/*! + * A boolean value. 0 means false, all other values mean true. + */ +typedef int kim_boolean_t; + +/*! + * A comparison between two sortable objects. + * \li Less than 0 means the first object is less than the second. + * \li 0 means the two objects are identical. + * \li Greater than 0 means the first object is greater than the second. + * \note Convenience macros are provided for interpreting kim_comparison_ts + * to improve code readability. + * See #kim_comparison_is_less_than(), #kim_comparison_is_equal() and + * #kim_comparison_is_greater_than() + */ +typedef int kim_comparison_t; + +/*! + * Convenience macro for interpreting #kim_comparison_t. + */ +#define kim_comparison_is_less_than(c) (c < 0) + +/*! + * Convenience macro for interpreting #kim_comparison_t. + */ +#define kim_comparison_is_equal_to(c) (c == 0) + +/*! + * Convenience macro for interpreting #kim_comparison_t. + */ +#define kim_comparison_is_greater_than(c) (c > 0) + +/*! + * The KIM String type. See \ref kim_string_overview for more information. + */ +typedef const char *kim_string_t; + +struct kim_error_opaque; +/*! + * A KIM Error object. See \ref kim_error_overview for more information. + */ +typedef struct kim_error_opaque *kim_error_t; + +struct kim_identity_opaque; +/*! + * A KIM Principal object. See \ref kim_identity_overview for more information. + */ +typedef struct kim_identity_opaque *kim_identity_t; + +struct kim_options_opaque; +/*! + * A KIM Options object. See \ref kim_options_overview for more information. + */ +typedef struct kim_options_opaque *kim_options_t; + +struct kim_selection_hints_opaque; +/*! + * A KIM Selection Hints object. See \ref kim_selection_hints_overview for more information. + */ +typedef struct kim_selection_hints_opaque *kim_selection_hints_t; + +struct kim_favorite_identities_opaque; +/*! + * A KIM Favorite Realms object. See \ref kim_favorite_identities_overview for more information. + */ +typedef struct kim_favorite_identities_opaque *kim_favorite_identities_t; + +struct kim_preferences_opaque; +/*! + * A KIM Preferences object. See \ref kim_preferences_overview for more information. + */ +typedef struct kim_preferences_opaque *kim_preferences_t; + +struct kim_ccache_iterator_opaque; +/*! + * A KIM CCache Iterator object. See \ref kim_credential_cache_collection for more information. + */ +typedef struct kim_ccache_iterator_opaque *kim_ccache_iterator_t; + +struct kim_ccache_opaque; +/*! + * A KIM CCache object. See \ref kim_ccache_overview for more information. + */ +typedef struct kim_ccache_opaque *kim_ccache_t; + +struct kim_credential_iterator_opaque; +/*! + * A KIM Credential Iterator object. See \ref kim_credential_iterator_t for more information. + */ +typedef struct kim_credential_iterator_opaque *kim_credential_iterator_t; + +struct kim_credential_opaque; +/*! + * A KIM Credential object. See \ref kim_credential_overview for more information. + */ +typedef struct kim_credential_opaque *kim_credential_t; + +/*!@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* KIM_TYPES_H */ diff --git a/src/kim/agent/mac/KerberosAgent-Info.plist b/src/kim/agent/mac/KerberosAgent-Info.plist new file mode 100644 index 000000000..2a00a313f --- /dev/null +++ b/src/kim/agent/mac/KerberosAgent-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + KerberosAgent + CFBundleIdentifier + edu.mit.KerberosAgent + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + KrbA + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/src/kim/agent/mac/KerberosAgentPrefix.pch b/src/kim/agent/mac/KerberosAgentPrefix.pch new file mode 100644 index 000000000..88c32a80e --- /dev/null +++ b/src/kim/agent/mac/KerberosAgentPrefix.pch @@ -0,0 +1,20 @@ +#ifdef __OBJC__ +#import + +#define CacheCollectionDidChangeNotification @"CacheCollectionDidChange" +#endif + +#define BIND_8_COMPAT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/src/kim/agent/mac/main.m b/src/kim/agent/mac/main.m new file mode 100644 index 000000000..fb0b9dfd3 --- /dev/null +++ b/src/kim/agent/mac/main.m @@ -0,0 +1,6 @@ +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/src/kim/agent/mac/resources/Add.tiff b/src/kim/agent/mac/resources/Add.tiff new file mode 100644 index 0000000000000000000000000000000000000000..b78b0c2a631a0fd47323d347943cf1f44180a630 GIT binary patch literal 728 zcmebEWzb?^U=nIzP;kr;V9;i5KCsXr!^KRLug&>Upu`eAU4iZ+4-->lO8?bNus^@a zQe=`_?W9xum$v^|^H+D8_cEcMQ?4cJOx}O~^Uf=u%IfD`Q2!$SrZR`KFYMEt`Yu(O$@V{e!^7dY_mg)QJ-=D0$tM+Ej|2x9puF2f~d-Ht# z?y6Y1`JT70y#Bgd#SCi-v|3oe|W$9gYj+70~17qUhm^wBhK!);f~GAJ?BmA z`3?qZuX{H4qUV2+xff$X#au7uBuXCrxF%KF&G1a7^zn&T3ZBgq;{2~LPe`U!$>WmF zMAZ|Ya;B<#8uiRnKQ(E}f@kxLPX5=KXLM()p7*6U2Mteu);VhIW9)O%`0V763(w{S zcmB7S7d+>wmEYwxA8pTle)HAd--PF<{e>x_4A173c>Z^pR}w4g7I-CB(evV$wW{7h zCTBIhFHgB@@azijTBl7{vZwk6T@@A1S}c(HH#Yp5Xm4udwW(`!Bd>OFFfi~jGB7YR zFfcGOhyw{mC|eB3W&*QWfa&D`BQsQ-6)4UIWitWUf($@;pk4+B4n|R^I8cO93?|N) z!YB@7GjTFXF#t^fsb@ODC=F*{V3dLC1V!kV1Is- zrN|_=+DWJQFKz#`=CAHF?`1+ir(8?cnY{n}=bcwRmDSI?p#DYtPbu4f?@RHoex>To z_6fhd%9Lwr*vjzb*Ro1iZ7tduy&*_5WXj&M!||J=a>aIi-F7}^Q^cBWr)#fW&D(u< z-PGvbW8drdB*Seki;D5*dz(kQ$@kw^SnuXjPhS80WmVOj`|i|c=b!wY#W8>KiBAp}dc>Yhxv=s-*OUu;ba|Wu&RFsvF66Nl zbl#}rD0o!R#N*ji!OZ_+Qw3{cCENmgGNq3dp2?MU+xVtX_JrVq@^=zt9=YNfGd0kqwQTNoRH(T|+jd^zJpP4Lj;Mvq*&;KS zXnFSYny1!&#%EqypPzi?!?O##Yne7($ezmUbWub&Yq3D)Us?A{BE71fm!_=M^}N`@ z!N9=B$iTqN0E}z~aUj76Ws3pXOkg$(0|V0xMrNouD^Q#b%4Pzx1sQ + + + 1050 + 9A581 + 629 + 949 + 343.00 + + YES + + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{279, 363}, {419, 465}} + 1886912512 + Select a Kerberos Identity + + NSWindow + + + View + + + + 256 + + YES + + + 268 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{17, 386}, {62, 62}} + + + YES + + 537001472 + 33554432 + + NSImage + KerberosAgent + + 0 + 0 + 0 + NO + + YES + + + + 266 + {{84, 386}, {318, 40}} + + + YES + + 67239424 + 272629760 + Please select a Kerberos Identity + + LucidaGrande + 1.300000e+01 + 1044 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {380, 300} + + + YES + + + 256 + {380, 17} + + + + + + + -2147483392 + {{-26, 0}, {16, 17}} + + + + + YES + + 2.739741e+02 + 1.499741e+02 + 1.000000e+03 + + 75628032 + 0 + Identity + + + 1.100000e+01 + 3100 + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 1411513920 + 272761856 + Text Cell + + + + + + 3 + YES + YES + + + + 1.003135e+02 + 1.000000e+02 + 1.500000e+02 + + 67239424 + 67108864 + Time Remaining + + + 6 + + headerColor + + 3 + MQA + + + + + + 1140981312 + 71435264 + + + + + + + 3 + YES + YES + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 1119879168 + 5 + 15 + 0 + YES + + + {{1, 17}, {380, 300}} + + + + + + 6 + + controlBackgroundColor + + + 4 + + + + -2147483392 + {{-30, 17}, {15, 285}} + + + + _doScroller: + 9.684210e-01 + + + + -2147483392 + {{1, -30}, {362, 15}} + + + 1 + + + 9.040768e-01 + + + + 2304 + + YES + + + {{1, 0}, {380, 17}} + + + + + + 4 + + + + {{17, 60}, {382, 318}} + + + + 562 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 289 + {{320, 12}, {87, 32}} + + + YES + + 67239424 + 134217728 + Select + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 289 + {{156, 12}, {82, 32}} + + + YES + + 67239424 + 134217728 + Cancel + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 289 + {{238, 12}, {82, 32}} + + + YES + + 67239424 + 134217728 + TmV34oCmA + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + {419, 465} + + + + {{0, 0}, {1920, 1178}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + 13 + 2 + {{378, 247}, {484, 199}} + 1886912512 + Authenticate to Kerberos + + NSWindow + + + View + + + + 256 + + YES + + + 274 + + YES + + + 256 + + YES + + + 289 + {{392, 12}, {82, 32}} + + + + YES + + 67239424 + 134217728 + Done + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 268 + + YES + + YES + + + + + + + + + {{17, 122}, {62, 62}} + + + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 266 + {{84, 167}, {387, 17}} + + + + YES + + 67239424 + 272629760 + Conclusion + + + + + + + + + 274 + {{84, 60}, {387, 99}} + + + + YES + + 67239424 + 272760832 + Congratulations! You have acquired Kerberos tickets for lxs@ATHENA.MIT.EDU. + + + + 3 + MSAwLjk3MDAwMDAzAA + + + + + + + 289 + {{305, 12}, {91, 32}} + + + + YES + + 67239424 + 134217728 + Go Back + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + {488, 201} + + + + + + {{-4, 0}, {488, 201}} + + + + + YES + + 1 + + + 256 + + YES + + + 268 + + YES + + YES + + + + + + + + + {{20, 119}, {62, 62}} + + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 266 + {{87, 119}, {387, 51}} + + + YES + + 67239424 + 272629760 + Please enter your Kerberos identity + + + + + + + + + 266 + {{90, 89}, {378, 22}} + + + YES + + -1804468671 + 272630784 + + + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{17, 94}, {68, 17}} + + + YES + + 67239424 + 71303168 + Name: + + + + + + + + + 266 + {{90, 57}, {381, 26}} + + + YES + + 343014976 + 272630784 + + + + + + YES + + + 5 + YES + + + + 274 + {15, 0} + + + YES + + YES + + + 1.200000e+01 + 1.000000e+01 + 1.000000e+03 + + 75628032 + 0 + + + + 1.200000e+01 + 16 + + + 3 + MC4zMzMzMzI5OQA + + + + + 338820672 + 1024 + + + YES + + + + 3 + YES + + + + 3.000000e+00 + 2.000000e+00 + + + 1.900000e+01 + tableViewAction: + -767524864 + + + + 1 + 15 + 0 + YES + + + + + + 268 + {{17, 61}, {68, 17}} + + + YES + + 67239424 + 71303168 + Realm: + + + + + + + + + 289 + {{376, 12}, {98, 32}} + + + YES + + 67239424 + 134217728 + Continue + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 292 + {{18, 15}, {51, 27}} + + YES + + 67239424 + 134217728 + + + + 1.000000e+01 + 16 + + + -2033434369 + 2 + + + Gear + + + + 400 + 75 + + + + {488, 201} + + + Select Identity + + + + + 2 + + + 256 + + YES + + + 268 + + YES + + YES + + + + + + + + + {{17, 122}, {62, 62}} + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 290 + {{87, 60}, {381, 22}} + + YES + + -1804468671 + 272630784 + + + + YES + + + + + + + 289 + {{376, 12}, {98, 32}} + + YES + + 67239424 + 134217728 + + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 289 + {{283, 12}, {93, 32}} + + YES + + 67239424 + 134217728 + + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 266 + {{84, 167}, {387, 17}} + + YES + + 67239424 + 272629760 + Please enter your Kerberos password + + + + + + + + + 274 + {{84, 90}, {387, 69}} + + YES + + 67239424 + 272629760 + Mail wants to connect to the account lxs@mit.edu + + + + + + + + + 292 + {{18, 16}, {51, 26}} + + YES + + 67239424 + 134217728 + + + + -2033958657 + 2 + + + + 400 + 75 + + + + {488, 201} + + Authentication Information + + + + + 3 + + Result + + + + + + + 6 + YES + YES + + + {484, 199} + + + + {{0, 0}, {1920, 1178}} + {239.32, 129} + {3.40282e+38, 131} + + + + + YES + servicePrincipalString + shortTimeRemainingString + + KerberosCredential + YES + + YES + YES + YES + YES + YES + + + + YES + credentialsArray + principalString + shortTimeRemainingString + + KerberosCache + + YES + YES + YES + YES + YES + + + KerberosCacheCollection + + + + YES + cachesArray + + KerberosCacheCollection + + + + Menu + + YES + + + VGlja2V0IE9wdGlvbnPigKY + + 1048576 + 2147483647 + + + NSMenuCheckmark + + + + NSMenuMixedState + + + + + Q2hhbmdlIFBhc3N3b3Jk4oCmA + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + About Kerberos... + + 1048576 + 2147483647 + + + + + + + + Authentication + + YES + + + KerberosAgent + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + About KerberosAgent + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + UHJlZmVyZW5jZXPigKY + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + + Services + + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide KerberosAgent + h + 1048576 + 2147483647 + + + + + + Hide Others + + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit KerberosAgent + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + + Window + + + YES + + + Close + w + 1048576 + 2147483647 + + + + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + + Help + + + YES + + + KerberosAgent Help + ? + 1048576 + 2147483647 + + + + + + + + _NSMainMenu + + + + + YES + + + selectNextTabViewItem: + + + + 35 + + + + selectNextTabViewItem: + + + + 65 + + + + selectPreviousTabViewItem: + + + + 66 + + + + selectPreviousTabViewItem: + + + + 68 + + + + performClose: + + + + 300013 + + + + value: arrangedObjects.principalString + + + + + + + value + arrangedObjects.principalString + + YES + + YES + NSAllowsEditingMultipleValuesSelection + NSAlwaysPresentsApplicationModalAlerts + NSConditionallySetsEditable + NSConditionallySetsEnabled + NSContinuouslyUpdatesValue + NSCreatesSortDescriptor + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + NSRaisesForNotApplicableKeys + NSValidatesImmediately + + + YES + + + + + + + + + + + + + + + 2 + + + 300026 + + + + value: arrangedObjects.shortTimeRemainingString + + + + + + + + arrangedObjects.shortTimeRemainingString + + YES + + YES + + + + + + + + + + + + + + + YES + + + + + + + + + + + + + + + 2 + + + 300027 + + + + menu + + + + 300036 + + + + popupMenu + + + + 300037 + + + + nextKeyView + + + + 300038 + + + + + + + + 300039 + + + + + + + + 300040 + + + + + + + + 300041 + + + + content + + + + 300042 + + + + + + + + 300043 + + + + + + + + 300044 + + + + hide: + + + + 300099 + + + + hideOtherApplications: + + + + 300100 + + + + unhide: + + + + 300101 + + + + terminate: + + + + 300102 + + + + performClose: + + + + 300104 + + + + performMiniaturize: + + + + 300105 + + + + performZoom: + + + + 300106 + + + + arrangeInFront: + + + + 300107 + + + + undo: + + + + 300108 + + + + redo: + + + + 300109 + + + + cut: + + + + 300110 + + + + copy: + + + + 300111 + + + + paste: + + + + 300112 + + + + pasteAsPlainText: + + + + 300113 + + + + delete: + + + + 300114 + + + + selectAll: + + + + 300115 + + + + showHelp: + + + + 300116 + + + + orderFrontStandardAboutPanel: + + + + 300117 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + 5 + + + YES + + + + + + + 6 + + + YES + + + + + + + + + + + 7 + + + YES + + + + + + 8 + + + YES + + + + + + 9 + + + YES + + + + + + + + + 10 + + + YES + + + + + + + 11 + + + YES + + + + + + 18 + + + YES + + + + + + 15 + + + YES + + + + + + 16 + + + YES + + + + + + 17 + + + YES + + + + + + 19 + + + YES + + + + Window1 + + + 20 + + + YES + + + + + + 21 + + + YES + + + + + + + + 22 + + + YES + + + + + + 23 + + + YES + + + + + + + + + + + + 43 + + + YES + + + + + + 45 + + + YES + + + + + + 47 + + + YES + + + + + + 48 + + + YES + + + + + + 49 + + + YES + + + + + + 50 + + + YES + + + + + + 24 + + + YES + + + + + + 25 + + + YES + + + + + + + + + + + + + 26 + + + YES + + + + + + 27 + + + YES + + + + + + 28 + + + YES + + + + + + 29 + + + YES + + + + + + 31 + + + YES + + + + + + 32 + + + YES + + + + + + 33 + + + YES + + + + + + 59 + + + YES + + + + + + 60 + + + YES + + + + + + + + + + 61 + + + YES + + + + + + 62 + + + YES + + + + + + 63 + + + YES + + + + + + 64 + + + YES + + + + + + 67 + + + YES + + + + + + 100007 + + + + + 100008 + + + + + 100015 + + + + + 100016 + + + + + 100017 + + + + + 100043 + + + + + 100045 + + + + + 100047 + + + + + 100048 + + + + + 100049 + + + + + 100050 + + + + + 100026 + + + + + 100027 + + + + + 100028 + + + + + 100029 + + + + + 100031 + + + + + 100032 + + + + + 100033 + + + + + 100061 + + + + + 100062 + + + + + 100063 + + + + + 100064 + + + + + 100067 + + + + + 100009 + + + + + 200009 + + + + + 300009 + + + + + -3 + + + Application + + + 300010 + + + YES + + + + + + 300011 + + + + + 300018 + + + + + 300019 + + + YES + + + + + 300021 + + + CredentialsController + + + 300022 + + + CachesController + + + 300023 + + + + + + 300024 + + + CacheCollectionController + + + 300028 + + + YES + + + + + + 300029 + + + + + 300030 + + + YES + + + + + + + GearMenu + + + 300031 + + + + + 300032 + + + + + 300033 + + + + + 300034 + + + + + 300045 + + + YES + + + + + + + MainMenu + + + 300047 + + + YES + + + + + + 300048 + + + YES + + + + + + 300049 + + + YES + + + + + + 300050 + + + YES + + + + + + 300051 + + + YES + + + + + + + + + + 300052 + + + + + 300053 + + + + + 300055 + + + + + 300057 + + + + + 300058 + + + + + 300059 + + + YES + + + + + + + + + + + + + + + + 300060 + + + + + 300061 + + + + + 300062 + + + + + 300063 + + + + + 300064 + + + + + 300065 + + + + + 300066 + + + + + 300067 + + + + + 300068 + + + YES + + + + + + 300069 + + + + + 300070 + + + + + 300071 + + + + + 300072 + + + YES + + + + + + 300073 + + + + + 300074 + + + YES + + + + + + + + + + + + + + 300076 + + + + + 300078 + + + + + 300079 + + + + + 300080 + + + + + 300081 + + + + + 300082 + + + + + 300083 + + + + + 300084 + + + + + 300085 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + 10.IBPluginDependency + 10.ImportedFromIB2 + 100009.IBShouldRemoveOnLegacySave + 11.IBPluginDependency + 11.ImportedFromIB2 + 15.IBPluginDependency + 15.ImportedFromIB2 + 16.IBPluginDependency + 16.ImportedFromIB2 + 17.IBPluginDependency + 17.ImportedFromIB2 + 18.IBPluginDependency + 18.ImportedFromIB2 + 19.IBPluginDependency + 19.IBWindowTemplateEditedContentRect + 19.ImportedFromIB2 + 19.NSWindowTemplate.visibleAtLaunch + 19.editorWindowContentRectSynchronizationRect + 19.windowTemplate.hasMaxSize + 19.windowTemplate.hasMinSize + 19.windowTemplate.maxSize + 19.windowTemplate.minSize + 20.IBPluginDependency + 20.ImportedFromIB2 + 200009.IBShouldRemoveOnLegacySave + 21.IBPluginDependency + 21.ImportedFromIB2 + 22.IBPluginDependency + 22.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 24.IBPluginDependency + 24.ImportedFromIB2 + 25.IBPluginDependency + 25.ImportedFromIB2 + 26.IBPluginDependency + 26.ImportedFromIB2 + 27.IBPluginDependency + 27.ImportedFromIB2 + 28.IBPluginDependency + 28.ImportedFromIB2 + 29.IBPluginDependency + 29.ImportedFromIB2 + 300009.IBShouldRemoveOnLegacySave + 300010.CustomClassName + 300010.IBPluginDependency + 300010.ImportedFromIB2 + 300018.IBPluginDependency + 300019.IBPluginDependency + 300021.IBPluginDependency + 300021.ImportedFromIB2 + 300022.IBPluginDependency + 300022.ImportedFromIB2 + 300023.IBPluginDependency + 300023.ImportedFromIB2 + 300024.IBPluginDependency + 300024.ImportedFromIB2 + 300028.CustomClassName + 300028.IBPluginDependency + 300028.ImportedFromIB2 + 300030.IBPluginDependency + 300030.ImportedFromIB2 + 300031.IBPluginDependency + 300031.ImportedFromIB2 + 300032.IBPluginDependency + 300032.ImportedFromIB2 + 300033.IBPluginDependency + 300033.ImportedFromIB2 + 300034.IBPluginDependency + 300034.ImportedFromIB2 + 300045.IBPluginDependency + 300045.ImportedFromIB2 + 300045.editorWindowContentRectSynchronizationRect + 300047.IBPluginDependency + 300047.ImportedFromIB2 + 300048.IBPluginDependency + 300048.ImportedFromIB2 + 300049.IBPluginDependency + 300049.ImportedFromIB2 + 300050.IBPluginDependency + 300050.ImportedFromIB2 + 300051.IBPluginDependency + 300051.ImportedFromIB2 + 300051.editorWindowContentRectSynchronizationRect + 300052.IBPluginDependency + 300052.ImportedFromIB2 + 300053.IBPluginDependency + 300053.ImportedFromIB2 + 300055.IBPluginDependency + 300055.ImportedFromIB2 + 300057.IBPluginDependency + 300057.ImportedFromIB2 + 300058.IBPluginDependency + 300058.ImportedFromIB2 + 300059.IBPluginDependency + 300059.ImportedFromIB2 + 300059.editorWindowContentRectSynchronizationRect + 300060.IBPluginDependency + 300060.ImportedFromIB2 + 300061.IBPluginDependency + 300061.ImportedFromIB2 + 300062.IBPluginDependency + 300062.ImportedFromIB2 + 300063.IBPluginDependency + 300063.ImportedFromIB2 + 300064.IBPluginDependency + 300064.ImportedFromIB2 + 300065.IBPluginDependency + 300065.ImportedFromIB2 + 300066.IBPluginDependency + 300066.ImportedFromIB2 + 300067.IBPluginDependency + 300067.ImportedFromIB2 + 300068.IBPluginDependency + 300068.ImportedFromIB2 + 300069.IBPluginDependency + 300069.ImportedFromIB2 + 300070.IBPluginDependency + 300070.ImportedFromIB2 + 300071.IBPluginDependency + 300071.ImportedFromIB2 + 300071.editorWindowContentRectSynchronizationRect + 300072.IBPluginDependency + 300072.ImportedFromIB2 + 300072.editorWindowContentRectSynchronizationRect + 300073.IBPluginDependency + 300073.ImportedFromIB2 + 300074.IBPluginDependency + 300074.ImportedFromIB2 + 300074.editorWindowContentRectSynchronizationRect + 300076.IBPluginDependency + 300076.ImportedFromIB2 + 300078.IBPluginDependency + 300078.ImportedFromIB2 + 300079.IBPluginDependency + 300079.ImportedFromIB2 + 300080.IBPluginDependency + 300080.ImportedFromIB2 + 300081.IBPluginDependency + 300081.ImportedFromIB2 + 300082.IBPluginDependency + 300082.ImportedFromIB2 + 300083.IBPluginDependency + 300083.ImportedFromIB2 + 300084.IBPluginDependency + 300084.ImportedFromIB2 + 300085.IBPluginDependency + 300085.ImportedFromIB2 + 31.IBPluginDependency + 31.ImportedFromIB2 + 32.IBPluginDependency + 32.ImportedFromIB2 + 33.IBPluginDependency + 33.ImportedFromIB2 + 43.IBPluginDependency + 43.ImportedFromIB2 + 45.CustomClassName + 45.IBPluginDependency + 45.ImportedFromIB2 + 47.IBPluginDependency + 47.ImportedFromIB2 + 48.IBPluginDependency + 48.ImportedFromIB2 + 49.IBPluginDependency + 49.ImportedFromIB2 + 5.IBPluginDependency + 5.IBWindowTemplateEditedContentRect + 5.ImportedFromIB2 + 5.NSWindowTemplate.visibleAtLaunch + 5.editorWindowContentRectSynchronizationRect + 5.windowTemplate.hasMaxSize + 5.windowTemplate.hasMinSize + 5.windowTemplate.maxSize + 5.windowTemplate.minSize + 50.IBPluginDependency + 50.ImportedFromIB2 + 59.IBPluginDependency + 59.ImportedFromIB2 + 6.IBPluginDependency + 6.ImportedFromIB2 + 60.IBPluginDependency + 60.ImportedFromIB2 + 61.IBPluginDependency + 61.ImportedFromIB2 + 62.IBPluginDependency + 62.ImportedFromIB2 + 63.IBPluginDependency + 63.ImportedFromIB2 + 64.IBPluginDependency + 64.ImportedFromIB2 + 67.IBPluginDependency + 67.ImportedFromIB2 + 7.IBPluginDependency + 7.ImportedFromIB2 + 8.IBPluginDependency + 8.ImportedFromIB2 + 9.IBPluginDependency + 9.ImportedFromIB2 + + + YES + + + + + + + + + + + + + + + + + {{932, 664}, {484, 199}} + + + {{932, 664}, {484, 199}} + + + {3.40282e+38, 109} + {239.32, 107} + + + + + + + + + + + + + + + + + + + + + + + PopupButton + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + PopupButton + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + {{21, 1069}, {314, 20}} + + + + + + + + + + + {{203, 976}, {197, 93}} + + + + + + + + + + + + + {{33, 886}, {240, 183}} + + + + + + + + + + + + + + + + + + + + + + + + + {{273, 1003}, {64, 6}} + + + {{274, 1046}, {211, 23}} + + + + + {{159, 896}, {253, 173}} + + + + + + + + + + + + + + + + + + + + + + + + + + + NSSecureTextField + + + + + + + + + + {{503, 256}, {419, 465}} + + + {{503, 256}, {419, 465}} + + + {3.40282e+38, 3.40282e+38} + {213, 107} + + + + + + + + + + + + + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300117 + + + + YES + + PopupButton + NSButton + + YES + + YES + + + YES + + + + + NSMenu + + + IBUserSource + + + + + KerberosCacheCollection + NSObject + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBProjectSource + ../../../Common/Headers/KerberosCacheCollection.h + + + + FirstResponder + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBUserSource + + + + + + 0 + ../../../Projects/KerberosIdentityManagement.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEDBAALAAwAMQA1ADYAPAA9 +AEMAWwBcAF0AYQBlAGgACwB1AH4AjgCXAJgAmQCaAJsAnACdAKEAogCwALUAtgC3ALoAvQDDAMwAzQDa +ANsA4wDkAOcA8QDyAPMA+AD6AP8BAAEDAQcBCgEiASoBOQE9AVoBWwFjAW0BbgFxAXgBeQF8AYEBkwGa +AZsBoAGjAagBqQGsAbUBtgG5AcMBygHLAdAB0QHUAd0B4QHmAecB6gHsAe0B8gHzAfYCAgIDAgQCBwIR +AhICFgIXAhgCGwIkAiUCOAI5AjoCPAI/AkICSgJLAlMCVAJWAl4CXwJnAmgCagALAmsCbQJuAm8CcAJz +AoACgQKCAoQChgALAo0CkQKiAqYCrgK2Ar8CwALIAskCywLWAt8C4ALpAu4C7wL4Av8DAAMBAwoDEgMT +AxYDFwMfAyADIgMjAyQDKgM0AzUDOwNGA1ADWQNaA2IDZgNnA28DdgN3A38DgAOIA5IDlwOYA50DngOf +A6cDqAOwAAsDsQPAA8IACwPUAAsD1QPZAKwD4gPoA+wD7gP3A/gD+wP+BAEEAgQJBAoEEgQTBBsEHAQe +BCkCQQQqBCsENwQ6BD4EPwMiBEIEQwRGBE0ETgRTBF0EZgLfBG8EcwR9AQkEfgR/BIcEEgSOBJYEmASf +BKAEqASqAu4EsQS4BLkEwATBBMgEyQQpBNIE0wMiBNwE3QTkBOUE5gToAAsCbQTpBOoE6wUQBRcFGAUc +BSEFIgUmBTYFPAU9BT4FQgVDBUcFSAVLBUwFUQVWBVcFXgVlBWYFbQVuBXkFgAWBBYMFhAWHBYwFkwWY +BZkFmgWeBaUFqQWqBasFrAWwBbcFuAW5BboFvwXABcQFywXMBc0F0QXYBdkF2gXbBd8F5gXqBesF7AXt +BfIF+gX7BfwANQX/BgAGBAYLBgwGDQYOBhIGGQYaBhsGHwYmBicGKAYpBRcGLgYyBjkGOgY7BjwFIQZB +BbkGRgZLBlAGYwZoBmkGagZrBm0GbwZ0Bn4GhAaFBmkGhgaHBokGigaPBpAGlAabBpwGnQaeBqMGqAat +BrIGtga3BrgGuga8BsEGyAbJBsoGzwbWBtcG2AbZBt4G5QbmBucG7AbzBvQG9Qb6BrcG/QcBBwgHCQcK +BwsHFwcYBxkHGgc3BzgHOQc6BzsHPAc9Bz4HPwdAB0EHQgCvAIcCOQdFB0gHSwdTB1QHVQdxB+gH7Qfu +B/UAtgf/CAAIDggXCB4IHwggCCkIMgf/CDMIOAg6CD0IPghHCFAIUQhaB/8IWwhnCHAIeQf/CHoIfAiE +CI0IjgiXB/8ImAiaCJ4InwihCRgJkAoICgkKCgoLCgwKDQoOCg8KEAoRChIKEwoUChUKFgoXChgKGQoa +ChsKHAodCh4KHwogCiEKIgojCiQKJQomCicKKAopCioKKwosCi0KLgovCjAKMQoyCjMKNAo1CjYKNwo4 +CjkKOgo7CjwKPQBnCj4KPwpACkEKQgpDCkQKRQpGCkcKSApJCkoKSwpMCk0KTgpPClAKUQpSClMKVApV +ClYKVwpYClkKWgpbClwKXQpeCl8KYAphCmIKYwpkCmUKZgpnCmgKaQpqCmsKbAptCm4KbwpwCnEKcgpz +CnQKdQp2CncKeAp5CnoKgAqGCyALugu7C7wLvQu+C78LwAvBC8ILwwvEC8ULxgvHC8gLyQvKC8sA7gvM +C80LzgvPC9AL0QvSC9ML1AvVC9YL1wvYC9kL2gvbAVEL3AvdC94L3wvgC+EL4gvjC+QL5QvmC+cL6Avp +C+oL6wvsC+0L7gvvC/AL8QvyC/ML9Av1C/YAWAv3C/gL+Qv6C/sL/Av9C/4L/wwADAEMAgwDDAQMBQIM +DAYMBwwIDAkMCgwLDAwMDQwODA8MEAwRDBIMEwwUDBUMFgwXAi8MGAwZDBoMGwwcDB0MHgwfDCAMIQwi +DCMMJAPrDCUMJgwnDCgMKQwqDCsMLAwtDC4MLwwwDDEMMgwzDDQMNQw2DDcMOAw5DDoMOww8DD0MPgw/ +DEAMQQxCDEMMRAxFDEYMRwxIDEkMSgxLDE4MUQxUVSRudWxs3xASAA0ADgAPABAAEQASABMAFAAVABYA +FwAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwVk5TUm9vdFYk +Y2xhc3NdTlNPYmplY3RzS2V5c18QD05TQ2xhc3Nlc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eU9pZHNW +YWx1ZXNdTlNDb25uZWN0aW9uc1tOU05hbWVzS2V5c1tOU0ZyYW1ld29ya11OU0NsYXNzZXNLZXlzWk5T +T2lkc0tleXNdTlNOYW1lc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eUNvbm5lY3RvcnNdTlNGb250TWFu +YWdlcl8QEE5TVmlzaWJsZVdpbmRvd3NfEA9OU09iamVjdHNWYWx1ZXNfEBdOU0FjY2Vzc2liaWxpdHlP +aWRzS2V5c1lOU05leHRPaWRcTlNPaWRzVmFsdWVzgAKBAwOBAcWBAmaBAwKBARCBAfCABYECZYECZ4EB +8YEDAIAAgAaBAe+BAwESAASUVoECaNIADgAyADMANFtOU0NsYXNzTmFtZYAEgANdTlNBcHBsaWNhdGlv +btIANwA4ADkAOlgkY2xhc3Nlc1okY2xhc3NuYW1logA6ADteTlNDdXN0b21PYmplY3RYTlNPYmplY3Rf +EBBJQkNvY29hRnJhbWV3b3Jr0gAOAD4APwBAWk5TLm9iamVjdHOAGKIAQQBCgAeAftwARAAOAEUARgBH +AEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFpcTlNXaW5kb3dWaWV3XE5TU2NyZWVu +UmVjdF1OU1dpbmRvd1RpdGxlWU5TV1RGbGFnc11OU1dpbmRvd0NsYXNzXE5TV2luZG93UmVjdFlOU01h +eFNpemVfEA9OU1dpbmRvd0JhY2tpbmdfEBFOU1dpbmRvd1N0eWxlTWFza1lOU01pblNpemVbTlNWaWV3 +Q2xhc3OADYB9gHqACRJweAAAgAqACIB8EAIQD4B7gAxfEBh7ezI3OSwgMzYzfSwgezQxOSwgNDY1fX1f +EBpTZWxlY3QgYSBLZXJiZXJvcyBJZGVudGl0edIADgBeAF8AYFlOUy5zdHJpbmeAC1hOU1dpbmRvd9IA +NwA4AGIAY6MAYwBkADtfEA9OU011dGFibGVTdHJpbmdYTlNTdHJpbmfSAA4AXgBfAGeAC1RWaWV31wBp +AA4AagBrAGwAYABtAG4AbwBwAHEAcgBuAHRfEA9OU05leHRSZXNwb25kZXJaTlNTdWJ2aWV3c1hOU3ZG +bGFnc1tOU0ZyYW1lU2l6ZVtOU1N1cGVydmlld4AOgHmADxEBAIB3gA6AeNIADgA+AHYAd4BSpgB4AHkA +egB7AHwAfYAQgCGAMoBlgG2ActoAaQAOAH8AgACBAIIAawCDAGAAbQBPAIUAhgCHAIgAiQCKAIcAbgBP +V05TRnJhbWVaTlNFZGl0YWJsZVZOU0NlbGxbTlNEcmFnVHlwZXNZTlNFbmFibGVkgA2AIIAZCYAagBER +AQwJgA6ADdIADgA+AD8AkIAYpgCRAJIAkwCUAJUAloASgBOAFIAVgBaAF18QGUFwcGxlIFBERiBwYXN0 +ZWJvYXJkIHR5cGVfEBlBcHBsZSBQTkcgcGFzdGVib2FyZCB0eXBlXxAxTmVYVCBFbmNhcHN1bGF0ZWQg +UG9zdFNjcmlwdCB2MS4yIHBhc3RlYm9hcmQgdHlwZV8QFU5TRmlsZW5hbWVzUGJvYXJkVHlwZV8QHk5l +WFQgVElGRiB2NC4wIHBhc3RlYm9hcmQgdHlwZV8QGkFwcGxlIFBJQ1QgcGFzdGVib2FyZCB0eXBl0gA3 +ADgAngCfowCfAKAAO1xOU011dGFibGVTZXRVTlNTZXRfEBV7ezE3LCAzODZ9LCB7NjIsIDYyfX3YAKMA +DgCkAKUApgCnAKgAqQCqAKsArACtAKwArACuAK9bTlNDZWxsRmxhZ3NXTlNTdHlsZVpOU0NvbnRlbnRz +V05TQWxpZ25XTlNTY2FsZVxOU0NlbGxGbGFnczJaTlNBbmltYXRlcxIgAf4AgB8QAIAbEgIAAAAI0wAO +ADIAsQCyALMAtF5OU1Jlc291cmNlTmFtZYAegByAHVdOU0ltYWdlXUtlcmJlcm9zQWdlbnTSADcAOAC4 +ALmiALkAO18QEE5TQ3VzdG9tUmVzb3VyY2XSADcAOAC7ALyjALwAgQA7W05TSW1hZ2VDZWxs0gA3ADgA +vgC/pQC/AMAAwQDCADtbTlNJbWFnZVZpZXdZTlNDb250cm9sVk5TVmlld1tOU1Jlc3BvbmRlctgAaQAO +AH8AgQBrAIMAYABtAE8AxQDGAMcAyACHAG4AT4ANgDGAIoAjEQEKCYAOgA1fEBZ7ezg0LCAzODZ9LCB7 +MzE4LCA0MH192ACjAA4AzgClAM8A0ACoANEA0gDTANQA1QDWAHkA2ADZXxARTlNCYWNrZ3JvdW5kQ29s +b3JZTlNTdXBwb3J0XU5TQ29udHJvbFZpZXdbTlNUZXh0Q29sb3ISBAH+AIAwgCiAJIAlgCESEEAAAIAt +XxAhUGxlYXNlIHNlbGVjdCBhIEtlcmJlcm9zIElkZW50aXR51AAOANwA3QDeAN8A4ADhAOJWTlNTaXpl +Vk5TTmFtZVhOU2ZGbGFnc4AnI0AqAAAAAAAAgCYRBBRcTHVjaWRhR3JhbmRl0gA3ADgA5QDmogDmADtW +TlNGb2501QAOAOgA6QDqAOsA7ADtAO4A7wDwV05TQ29sb3JcTlNDb2xvclNwYWNlW05TQ29sb3JOYW1l +XU5TQ2F0YWxvZ05hbWWALIArEAaAKoApVlN5c3RlbVxjb250cm9sQ29sb3LTAA4A6QD0AOwA9gD3V05T +V2hpdGWALBADSzAuNjY2NjY2NjkA0gA3ADgA+QDoogDoADvVAA4A6ADpAOoA6wDsAPwA7gD9APCALIAv +gC6AKV8QEGNvbnRyb2xUZXh0Q29sb3LTAA4A6QD0AOwA9gECgCxCMADSADcAOAEEAQWkAQUBBgCBADtf +EA9OU1RleHRGaWVsZENlbGxcTlNBY3Rpb25DZWxs0gA3ADgBCAEJpQEJAMAAwQDCADtbTlNUZXh0Rmll +bGTfEA8AaQELAA4AfwEMAQ0BDgBqAQ8AawBgAG0BEAERARIATwEUARUBFgEXARgBGQEaARsBHABuAE8B +HwEgASBbTlNIU2Nyb2xsZXJYTlNzRmxhZ3NcTlNDb3JuZXJWaWV3XxAQTlNIZWFkZXJDbGlwVmlld1xO +U1Njcm9sbEFtdHNbTlNWU2Nyb2xsZXJdTlNOZXh0S2V5Vmlld11OU0NvbnRlbnRWaWV3gA2AX4BkgGMR +AjKAPIA5gDNPEBBBIAAAQSAAAEGYAABBmAAAEQESgA6ADYBbgDSANNIADgA+AHYBJIBSpQEgAR8BFAEZ +ARiANIBbgF+AOYA82wBpAA4AfwErAGoAawEsAGABLQBtAREAegEvATABMQEyATMBNABuATYAegE0WU5T +Y3ZGbGFnc1lOU0RvY1ZpZXdZTlNCR0NvbG9ygDKAWoBXEASANREJAIA2gA6AWIAygDbSAA4APgB2ATuA +UqEBNIA23xAUAGkBPgAOAT8BQADOAUEBDQFCAUMBRABrAGwAgwBgAUUBRgBtAUcBSAEgAKwBSgFLAUwB +TQCHARgBUAFRAVIAcQFTAIcAbgFWAFgBIAFYAVlfEB9OU0RyYWdnaW5nU291cmNlTWFza0Zvck5vbkxv +Y2FsWU5TVHZGbGFnc1xOU0hlYWRlclZpZXdfEBJOU0FsbG93c1R5cGVTZWxlY3RfEBdOU0ludGVyY2Vs +bFNwYWNpbmdXaWR0aF8QGU5TQ29sdW1uQXV0b3Jlc2l6aW5nU3R5bGVfEBhOU0ludGVyY2VsbFNwYWNp +bmdIZWlnaHRbTlNHcmlkQ29sb3JfEBxOU0RyYWdnaW5nU291cmNlTWFza0ZvckxvY2FsXk5TVGFibGVD +b2x1bW5zW05TUm93SGVpZ2h0gDSAVhJCwAAAgDiAUAmAPCNACAAAAAAAABAFI0AAAAAAAAAAgDcJgA6A +U4A0gD8jQDEAAAAAAABaezM4MCwgMzAwfdcAaQAOAGsAbABgAG0BXAEZAV4AcQFfAG4BGQE0W05TVGFi +bGVWaWV3gDmAO4A6gA6AOYA22wBpAA4AfwErAGoAawEsAGABLQBtAREAegEvAWYBMQFnATMBTABuATYA +egFMgDKAWoBigGGAOIAOgFiAMoA4WXszODAsIDE3fdIANwA4AW8BcKQBcADBAMIAO18QEU5TVGFibGVI +ZWFkZXJWaWV31gBpAA4AfwBrAGAAbQB6AXMBdAF1AG4AeoAygD6APRP/////gAABAIAOgDJfEBR7ey0y +NiwgMH0sIHsxNiwgMTd9fdIANwA4AXoBe6QBewDBAMIAO11fTlNDb3JuZXJWaWV30gAOAD4AdgF+gFKi +AX8BgIBAgEvaAYIADgGDAYQBhQGGAYcBiAGJAVwAhwGLAYwBjQGOAPYBjwGQAIcBNF5OU0lzUmVzaXpl +YWJsZVxOU0hlYWRlckNlbGxXTlNXaWR0aFpOU0RhdGFDZWxsXk5TUmVzaXppbmdNYXNrWk5TTWluV2lk +dGhaTlNNYXhXaWR0aFxOU0lzRWRpdGFibGUJgEqAQSNAcR+WAAAAAIBII0BivywAAAAAI0CPQAAAAAAA +CYA21wCjAA4AzgClAM8AqADRAZQBlQGWAZcBmACsAZkSBIH+AIBHgESAQoBDgEVYSWRlbnRpdHnUAA4A +3ADdAN4A3wGdAOEBn4AnI0AmAAAAAAAAgCYRDBzTAA4A6QD0AOwA9gGigCxLMC4zMzMzMzI5OQDVAA4A +6ADpAOoA6wDsAPwA7gGmAPCALIAvgEaAKV8QD2hlYWRlclRleHRDb2xvctIANwA4AaoBq6UBqwEFAQYA +gQA7XxARTlNUYWJsZUhlYWRlckNlbGzYAKMADgDOAKUAzwDQAKgA0QGtANMA1AGwAZgBNAGzANkSVCH+ +QIAwgCiASYBDgDYSEEIEAIAtWVRleHQgQ2VsbNIANwA4AbcBuKIBuAA7XU5TVGFibGVDb2x1bW7aAYIA +DgGDAYQBhQGGAYcBiAGJAVwAhwGLAbwBvQG+APYBvwHAAIcBNAmASoBMI0BZFBAAAAAAgFEjQFkAAAAA +AAAjQGLAAAAAAAAJgDbXAKMADgDOAKUAzwCoANEA0gGVAcUBxgGYAcgBmYBHgE6ATYBDEgQAAACARV5U +aW1lIFJlbWFpbmluZ9UADgDoAOkA6gDrAOwBTQDuAc4A8IAsgFCAT4ApW2hlYWRlckNvbG9y0wAOAOkA +9ADsAPYB04AsQjEA2ACjAA4AzgClAM8A0ACoANEB1QDTANQBsAGYATQB2wDZEkQB/kCAMIAogEmAQ4A2 +EgRCBACALdIANwA4Ad4B36MB3wHgADteTlNNdXRhYmxlQXJyYXlXTlNBcnJhedUADgDoAOkA6gDrAOwB +4wDuAeQA8IAsgFWAVIApWWdyaWRDb2xvctMADgDpAPQA7AD2AemALEQwLjUA0gA3ADgB6wFcpQFcAMAA +wQDCADtfEBV7ezEsIDE3fSwgezM4MCwgMzAwfX3VAA4A6ADpAOoA6wDsAO0A7gHwAPCALIArgFmAKV8Q +FmNvbnRyb2xCYWNrZ3JvdW5kQ29sb3LSADcAOAH0AfWkAfUAwQDCADtaTlNDbGlwVmlld9kAaQH3AA4A +fwBrAGAB+ABtAfkAegB6AfwB/QF1AG4B/wB6AgFYTlNUYXJnZXRYTlNBY3Rpb25ZTlNQZXJjZW50gDKA +MoBegFyADoBdgDIjP+79TiAAAABfEBZ7ey0zMCwgMTd9LCB7MTUsIDI4NX19XF9kb1Njcm9sbGVyOtIA +NwA4AgUCBqUCBgDAAMEAwgA7Wk5TU2Nyb2xsZXLaAGkB9wAOAH8BDABrAGAB+ABtAfkAegB6AfwCCwIM +AXUAbgH/AHoCEIAygDKAXoBgEAGADoBdgDIjP+zuMmAAAABfEBV7ezEsIC0zMH0sIHszNjIsIDE1fX3S +AA4APgB2AhSAUqEBTIA4XxATe3sxLCAwfSwgezM4MCwgMTd9fV8QFnt7MTcsIDYwfSwgezM4MiwgMzE4 +fX3SADcAOAIZAhqkAhoAwQDCADtcTlNTY3JvbGxWaWV32ABpAA4AfwCBAGsAgwBgAG0ATwIdAh4CHwIg +AIcAbgBPgA2AbIBmgGcRASEJgA6ADV8QFXt7MzIwLCAxMn0sIHs4NywgMzJ9fd0AowAOAiYCJwIoAikC +KgClAM8A0AIrAKgCLADSAi0CLgIvAgwA1gIxAjIA1gB7AjUCNgI3XxATTlNBbHRlcm5hdGVDb250ZW50 +c18QEk5TUGVyaW9kaWNJbnRlcnZhbF5OU0J1dHRvbkZsYWdzMl8QEE5TQWx0ZXJuYXRlSW1hZ2VfEA9O +U0tleUVxdWl2YWxlbnRfEA9OU1BlcmlvZGljRGVsYXldTlNCdXR0b25GbGFnc4BrgGkQGYAlgGqAaIAl +gGUQyBIIAAAAE/////+GgkD/VlNlbGVjdFDSAA4AXgBfAjmAC9IANwA4Aj0CPqQCPgEGAIEAO1xOU0J1 +dHRvbkNlbGzSADcAOAJAAkGlAkEAwADBAMIAO1hOU0J1dHRvbtgAaQAOAH8AgQBrAIMAYABtAE8CHQJF +AkYCIACHAG4AT4ANgGyAboBvCYAOgA1fEBV7ezE1NiwgMTJ9LCB7ODIsIDMyfX3dAKMADgImAicCKAIp +AioApQDPANACKwCoAiwA0gItAi4CLwIMANYCTwJQANYAfAI1AjYCN4BrgGmAJYBxgHCAJYBtVkNhbmNl +bNIADgBeAF8COYAL2ABpAA4AfwCBAGsAgwBgAG0ATwIdAlkCWgIgAIcAbgBPgA2AbIBzgHQJgA6ADV8Q +FXt7MjM4LCAxMn0sIHs4MiwgMzJ9fd0AowAOAiYCJwIoAikCKgClAM8A0AIrAKgCLADSAi0CLgIvAgwA +1gJjAmQA1gB9AjUCNgI3gGuAaYAlgHaAdYAlgHJkAE4AZQB3ICbSAA4AXgBfAjmAC1p7NDE5LCA0NjV9 +0gA3ADgCbADBowDBAMIAO18QFnt7MCwgMH0sIHsxOTIwLCAxMTc4fX1aezIxMywgMTI5fV8QGnszLjQw +MjgyZSszOCwgMy40MDI4MmUrMzh90gA3ADgCcQJyogJyADtfEBBOU1dpbmRvd1RlbXBsYXRl3QBEAA4A +RQJ0AEYARwBIAEkASgBLAEwATQBOAnUAUAJ3Ai4CeQBTAnoCewJ8AFcCfQJ+An9fEBNOU0ZyYW1lQXV0 +b3NhdmVOYW1lgIOAfYEBDYBpgICAgYB/gQEPEA2BAQ6Agl8QGHt7Mzc4LCAyNDd9LCB7NDg0LCAxOTl9 +fV8QGEF1dGhlbnRpY2F0ZSB0byBLZXJiZXJvc9IADgBeAF8AYIAL0gAOAF4AXwBngAvXAGkADgBqAGsA +bABgAG0ChwBvAokAcQKKAocCjICEgHmAhYEBC4CEgQEM0gAOAD4AdgKPgFKhApCAht4AaQAOApIBPwB/ +AOYAagBrAGACkwBtApQBEQKVAnUClwKYAO4CmQDWApsBHAKHAIcCdQCHAqACoV5OU1RhYlZpZXdJdGVt +c18QEU5TRHJhd3NCYWNrZ3JvdW5kXxAWTlNBbGxvd1RydW5jYXRlZExhYmVsc18QFU5TU2VsZWN0ZWRU +YWJWaWV3SXRlbYCDgQEKgKOAooAlgIeAhAmAgwmAiIEBB9IADgA+AHYCpIBSoQKggIjYAGkADgBqAGsA +bABgAG0BEQKQAG8CqQBxAqoChwKQAq2AhoB5gImAoYCEgIaAj9IADgA+AHYCsIBSpQKxAq0CswK0ArWA +ioCPgJKAlYCZ2QBpAA4AfwCBAGsAgwBgAG0BEQKgAh0CuQK6AiAAhwKHAqACkICIgGyAi4CMCYCEgIiA +hl8QFXt7MzkyLCAxMn0sIHs4MiwgMzJ9fd0AowAOAiYCJwIoAikCKgClAM8A0AIrAKgCLADSAi0CLgIv +AgwA1gLEAsUA1gKxAjUCNgI3gGuAaYAlgI6AjYAlgIpURG9uZdIADgBeAF8COYAL2wBpAA4AfwCAAIEA +ggBrAIMAYABtARECoACFAs4AhwLQAtEAigCHAocCoAKzgIiAIICRCYCTgJAJgISAiICS0gAOAD4APwLY +gBimAJEAkgCTAJQAlQCWgBKAE4AUgBWAFoAXXxAVe3sxNywgMTIyfSwgezYyLCA2Mn192QBpAA4AfwCB +AGsAgwBgAG0BEQKgAMUC4wLkAMgAhwKHAqACtICIgDGAlICWCYCEgIiAldgAowAOAKQApQCmAKcAqACp +AuoAqwCsAK0ArACsAK4ArxIAAf4AgB+AGwhfEBZ7ezg0LCAxNjd9LCB7Mzg3LCAxN3192QBpAA4AfwCB +AGsAgwBgAG0BEQKgAMUC8gLzARwAhwKHAqACtYCIgDGAmICaCYCEgIiAmdgAowAOAM4ApQDPANAAqADR +ANIA0wDUAvsA1gKzANgA2YAwgCiAl4AlgJKALVpDb25jbHVzaW9uXxAVe3s4NCwgNjB9LCB7Mzg3LCA5 +OX192QBpAA4AfwCBAGsAgwBgAG0BEQKgAh0DBAMFAiAAhwKHAqACsYCIgGyAnYCeCYCEgIiAitgAowAO +AM4ApQDPANAAqADRANIA0wMMAw0BmAK0AxAA2YAwgJyAm4BDgJUSEEIAAIAtXxBLQ29uZ3JhdHVsYXRp +b25zISBZb3UgaGF2ZSBhY3F1aXJlZCBLZXJiZXJvcyB0aWNrZXRzIGZvciBseHNAQVRIRU5BLk1JVC5F +RFUu0wAOAOkA9ADsAPYDFYAsTTEgMC45NzAwMDAwMwBfEBV7ezMwNSwgMTJ9LCB7OTEsIDMyfX3dAKMA +DgImAicCKAIpAioApQDPANACKwCoAiwA0gItAi4CLwIMANYDGwMcANYCtQI1AjYCN4BrgGmAJYCggJ+A +JYCZV0dvIEJhY2vSAA4AXgBfAjmAC1p7NDg4LCAyMDF9XxAVe3stNCwgMH0sIHs0ODgsIDIwMX190gAO +AD4AdgMmgFKjAycDKAKhgKSA5IEBB9YADgMrAMEDLADoAy0DLgMvAzACkADUAzNcTlNJZGVudGlmaWVy +WU5TVGFiVmlld1dOU0xhYmVsgOOApYCmgIaAKIDiUTHWAGkADgBqAGsAbAERACsAbwM4AHEDOQM6gACA +eYCngOGAqNIADgA+AHYDPYBSqAM6Az8DQANBA0IDQwNEA0WAqICrgLGAroC8gLOA04DY2gBpAA4AfwCA +AIEAggBrAIMAbQERAzAAhQNJAIcDSwNMAIoAhwMwAz+ApoAggKoJgKyAqQmApoCr0gAOAD4APwNSgBim +AJEAkgCTAJQAlQCWgBKAE4AUgBWAFoAXXxAVe3syMCwgMTE5fSwgezYyLCA2Mn192ABpAA4AfwCBAGsA +gwBtAREDMADFA10DXgDIAIcDMANBgKaAMYCtgK8JgKaArtgAowAOAKQApQCmAKcAqACpAuoAqwCsAK0A +rACsAK4Ar4AfgBsIXxAWe3s4NywgMTE5fSwgezM4NywgNTF9fdgAaQAOAH8AgQBrAIMAbQERAzAAxQNq +A2sAigCHAzADQICmgDGAuYC6CYCmgLHYAKMADgDOAKUAzwDQAKgA0QDSANMA1ANyANYDPwDYANmAMIAo +gLCAJYCrgC1fECNQbGVhc2UgZW50ZXIgeW91ciBLZXJiZXJvcyBpZGVudGl0edgAaQAOAH8AgQBrAIMA +bQERAzAAxQN6A3sAyACHAzADQ4CmgDGAsoC0CYCmgLNfEBV7ezkwLCA4OX0sIHszNzgsIDIyfX3YAGkA +DgB/AIEAawCDAG0BEQMwAMUDgwOEAIoAhwMwA0KApoAxgNCA0QmApoC82QCjAA4AzgClAM8A0ACoApMA +0QOJANMDiwIuANYDQAOPAIcDkRP/////lHH+QYAwgLWAaYAlgLESEEAEAAmAt9UADgDoAOkA6gDrAOwB +TQDuA5UA8IAsgFCAtoApXxATdGV4dEJhY2tncm91bmRDb2xvctUADgDoAOkA6gDrAOwA/ADuA5sA8IAs +gC+AuIApWXRleHRDb2xvcl8QFHt7MTcsIDk0fSwgezY4LCAxN3192ACjAA4AzgClAM8A0ACoANEA0gDT +ANQDogDWA0EDpQDZgDCAKIC7gCWArhIEQAAAgC1VTmFtZTrYAGkADgB/AIEAawCDAG0BEQMwA6oDqwOs +AMgAhwMwA6+ApoDPgL2AvwmApoC+XxAVe3s5MCwgNTd9LCB7MzgxLCAyNn193QOyAKMADgDOAKUAzwDQ +AKgCkwOzAVwDtADRA0IDtgO3A4sDuQDWA0IDjwCHAVEDvQCHANlaTlNEZWxlZ2F0ZV8QEk5TVmlzaWJs +ZUl0ZW1Db3VudF8QFU5TSGFzVmVydGljYWxTY3JvbGxlcoC8EhRx/kCAzoC1gMCAJYC8CYDBCYAt0gAO +AF4AXwI5gAvfEBYAaQOyAfcBPwE+AA4AzgFBAUIBQwFEA8MAawBsAIMAYAH4AUUBRgBtAUcBSAPEA6wD +rAPHAKwDyAE2AIcBUAIMAVIDrAEcA8wAhwPOA88BVgBYA8QD0gPTXE5TRGF0YVNvdXJjZYDCgL+AvxP/ +////0kCAAIDNgFgJgL+AwwmAxIDMgFOAwoDFI0AzAAAAAAAAV3sxNSwgMH3SAA4APgB2A9eAUqED2IDG +2gGCAA4BgwMrAYQBhQGGAYcBiAFcAIcBiwPcA90D3gPfAPYD4AGQA70JgEqAyIDHI0AoAAAAAAAAgMsj +QCQAAAAAAACAwdcAowAOAM4ApQDPAKgA0QGUAZUD5AO5A+YArAFNgEeAyoDAgMmAUNQADgDcAN0A3gDf +A94A4QPrgCeAJhAQ0wAOAOkA9ADsAPYBooAs2ACjAA4AzgDPANAAqAKTANED7wDTATYA1gO9A/QAhwDZ +EhQx/kCAMIBYgCWAwREEAAmALV8QEHRhYmxlVmlld0FjdGlvbjrSADcAOAP5A/qmA/oBXADAAMEAwgA7 +XxAQTlNDb21ib1RhYmxlVmlld9IANwA4A/wD/aUD/QEFAQYAgQA7Xk5TQ29tYm9Cb3hDZWxs0gA3ADgD +/wQApgQAAQkAwADBAMIAO1pOU0NvbWJvQm94XxAUe3sxNywgNjF9LCB7NjgsIDE3fX3YAKMADgDOAKUA +zwDQAKgA0QDSANMA1AQFANYDQwOlANmAMIAogNKAJYCzgC1WUmVhbG062ABpAA4AfwCBAGsAgwBtARED +MAIdBA0EDgIgAIcDMAKQgKaAbIDUgNUJgKaAhl8QFXt7Mzc2LCAxMn0sIHs5OCwgMzJ9fd0AowAOAiYC +JwIoAikCKgClAM8A0AIrAKgCLADSAi0CLgIvAgwA1gQXBBgA1gNEAjUCNgI3gGuAaYAlgNeA1oAlgNNY +Q29udGludWXSAA4AXgBfAjmAC9kAaQAOAH8EHwCBAGsAgwAyAG0DMAQhBCIEIwQkBCUAhwQnAzBfEBNO +U09yaWdpbmFsQ2xhc3NOYW1lgKaA4IDbgNqA3BEBJAmA2YCmW1BvcHVwQnV0dG9uXxAUe3sxOCwgMTV9 +LCB7NTEsIDI3fX3dAKMADgImBCwCJwIoAioApQDPANACKwCoAiwA0gItAi4ELwQwAFcCLgIuBDMDRQQ1 +AjYENl1OU05vcm1hbEltYWdlgGuAaYDeEEuAaYBpgN2A2BEBkBP/////hsxA/9QADgDcAN0A3gDfA+AA +4QPrgCeAJtMADgAyALEAsgCzBD2AHoAcgN9UR2VhctIANwA4BEAEQaIEQQA7Xk5TQ2xhc3NTd2FwcGVy +XxAPU2VsZWN0IElkZW50aXR50gA3ADgERARFogRFADtdTlNUYWJWaWV3SXRlbdYADgMrAMEDLADoAy0D +LgRIBEkCkADUBEyA44DlgOaAhoAogQEGUTLVAGkADgBqAGsAbAArAG8EUQBxBFKAAIB5gOeBAQXSAA4A +PgB2BFWAUqcEVgRXBFgEWQRaBFsEXIDogOyA8YD1gPmA/YEBAdkAaQAOAH8AgACBAIIAawCDAG0ESQCF +BGAAhwRiBGMAigCHBEmA5oAggOoJgOuA6QmA5tIADgA+AD8EaIAYpgCRAJIAkwCUAJUAloASgBOAFIAV +gBaAF9gAowAOAKQApQCmAKcAqACpAuoAqwCsAK0ArACsAK4Ar4AfgBsI2QBpAA4AfwQfAIEAawCDADIA +bQRJBCEEdgR3BHgEeQCHBHsESYDmgOCA74DugPARASIJgO2A5l8QEU5TU2VjdXJlVGV4dEZpZWxkXxAV +e3s4NywgNjB9LCB7MzgxLCAyMn192QCjAA4AzgClAM8A0ACoApMA0QOJANMDiwIuANYEVwOPAIcDkYAw +gLWAaYAlgOwJgLfXAGkADgB/AIEAawCDAG0ESQIdBIoEiwIgAIcESYDmgGyA8oDzCYDm3QCjAA4CJgIn +AigCKQIqAKUAzwDQAisAqAIsANICLQIuAi8CDADWBJIEGADWBFgCNQI2AjeAa4BpgCWA9IDWgCWA8dIA +DgBeAF8COYAL1wBpAA4AfwCBAGsAgwBtBEkCHQSbBJwCIACHBEmA5oBsgPaA9wmA5l8QFXt7MjgzLCAx +Mn0sIHs5MywgMzJ9fd0AowAOAiYCJwIoAikCKgClAM8A0AIrAKgCLADSAi0CLgIvAgwA1gSkAxwA1gRZ +AjUCNgI3gGuAaYAlgPiAn4AlgPXSAA4AXgBfAjmAC9cAaQAOAH8AgQBrAIMAbQRJAMUErQSuAMgAhwRJ +gOaAMYD6gPsJgObYAKMADgDOAKUAzwDQAKgA0QDSANMA1AS0ANYEWgDYANmAMIAogPyAJYD5gC1fECNQ +bGVhc2UgZW50ZXIgeW91ciBLZXJiZXJvcyBwYXNzd29yZNcAaQAOAH8AgQBrAIMAbQRJAMUEvAS9ARwA +hwRJgOaAMYD+gP8JgOZfEBV7ezg0LCA5MH0sIHszODcsIDY5fX3YAKMADgDOAKUAzwDQAKgA0QDSANMA +1ATEAZgEWwDYANmAMIAogQEAgEOA/YAtXxAwTWFpbCB3YW50cyB0byBjb25uZWN0IHRvIHRoZSBhY2Nv +dW50IGx4c0BtaXQuZWR12QBpAA4AfwQfAIEAawCDADIAbQRJBCEEzAQjBM4EJQCHBNAESYDmgOCBAQOA +2oEBBAmBAQKA5l8QFHt7MTgsIDE2fSwgezUxLCAyNn193QCjAA4CJgQsAicCKAIqAKUAzwDQAisAqAIs +ANICLQIuBC8EMABXAi4CLgQzBFwENQI2BNuAa4BpgN6AaYBpgN2BAQET/////4bEQP9fEBpBdXRoZW50 +aWNhdGlvbiBJbmZvcm1hdGlvbtYADgMrAMEDLADoAy0DLgTfAqACkADUBOOA44EBCICIgIaAKIEBCVEz +VlJlc3VsdNIANwA4BOcDLKQDLADBAMIAO1p7NDg0LCAxOTl9XXsyMzkuMzIsIDEyOX1fEBJ7My40MDI4 +MmUrMzgsIDEzMX3SAA4APgB2BO2AUq8QIgTuBO8E8ATxBPIE8wT0BPUE9gT3BPgE+QT6BPsE/AT9BP4E +/wUABQEFAgUDBQQFBQUGBQcFCAUJBQoFCwUMBQ0FDgUPgQERgQEUgQEWgQEhgQEvgQE0gQE6gQE/gQFB +gQFFgQFKgQFQgQFXgQFcgQFggQFlgQFngQFsgQFugQFwgQFxgQGCgQGEgQGJgQGKgQGLgQGSgQGWgQGb +gQGfgQGjgQGmgQGrgQHB1AAOBREFEgMtBRMCkAK1BRZdTlNEZXN0aW5hdGlvblhOU1NvdXJjZYEBE4CG +gJmBARJfEBpzZWxlY3RQcmV2aW91c1RhYlZpZXdJdGVtOtIANwA4BRkFGqMFGgUbADtfEBVOU05pYkNv +bnRyb2xDb25uZWN0b3JeTlNOaWJDb25uZWN0b3LUAA4FEQUSAy0FEwKQA0QFIIEBE4CGgNOBARVfEBZz +ZWxlY3ROZXh0VGFiVmlld0l0ZW060wAOBRIDLQUTBSQFJYEBE4EBF4EBINgADgUnBSgFKQUqBSsFLAUt +BS4FLwUwBTEFMgUzBTQFNVdOU1RpdGxlXxARTlNLZXlFcXVpdk1vZE1hc2taTlNLZXlFcXVpdl1OU01u +ZW1vbmljTG9jWU5TT25JbWFnZVxOU01peGVkSW1hZ2VWTlNNZW51gQEfgQEZEgAQAACBARoSf////4EB +G4EBHYEBGNQADgUnAN0FNwU4BTkFOgU7W05TTWVudUl0ZW1zgQEsgQHkgQHngQHlWE1pbmltaXplUW3T +AA4AMgCxALIAswVBgB6AHIEBHF8QD05TTWVudUNoZWNrbWFya9MADgAyALEAsgCzBUaAHoAcgQEeXxAQ +TlNNZW51TWl4ZWRTdGF0ZdIANwA4BUkFSqIFSgA7Wk5TTWVudUl0ZW1fEBNwZXJmb3JtTWluaWF0dXJp +emU61AAOBREFEgMtBU0FTgNFBVCBAS6BASKA2IEBLdQADgUnAN0FNwU4BVMCLgVVgQEsgQEjgGmBASRU +TWVuddIADgA+AHYFWYBSpAVaBVsFXAVdgQElgQEngQEpgQEq2AAOBScFKAUpBSoFKwUsBS0FLgVgBTAC +LgUyBTMFNAVOgQEfgQEmgGmBARuBAR2BASJvEA8AVABpAGMAawBlAHQAIABPAHAAdABpAG8AbgBzICbY +AA4FJwUoBSkFKgUrBSwFLQUuBWgFMAIuBTIFMwU0BU6BAR+BASiAaYEBG4EBHYEBIm8QEABDAGgAYQBu +AGcAZQAgAFAAYQBzAHMAdwBvAHIAZCAm2gAOBScFKAVvBSkFcAUqBSsFLAUtBS4CLgUwAIcCLgCHBTIF +MwU0BU5dTlNJc1NlcGFyYXRvclxOU0lzRGlzYWJsZWSBAR+AaQmAaQmBARuBAR2BASLYAA4FJwUoBSkF +KgUrBSwFLQUuBXsFMAIuBTIFMwU0BU6BAR+BASuAaYEBG4EBHYEBIl8QEUFib3V0IEtlcmJlcm9zLi4u +0gA3ADgFggUtogUtADtUbWVuddIANwA4BYUFhqMFhgUbADtfEBROU05pYk91dGxldENvbm5lY3RvctQA +DgURBRIDLQUTAB8FigWLgQETgAKBATCBATPXAA4FJwUpBSoFKwUsBS0FLgWOAi4FMgUzBTQFkoEBH4EB +MoBpgQEbgQEdgQEx1AAOBScA3QU3BTgFlQWWBZeBASyBAcqBAduBAcxfEBNBYm91dCBLZXJiZXJvc0Fn +ZW50XxAdb3JkZXJGcm9udFN0YW5kYXJkQWJvdXRQYW5lbDrTAA4FEgMtBRMFnAWdgQETgQE1gQE52AAO +BScFKAUpBSoFKwUsBS0FLgWgBTAFoQUyBTMFNAWkgQEfgQE3gQE4gQEbgQEdgQE20wAOBScFNwU4BacF +qIEBLIEB3YEB31RVbmRvUXpVdW5kbzrTAA4FEgMtBRMFrgWvgQETgQE7gQE+2AAOBScFKAUpBSoFKwUs +BS0FLgWyBTAFswUyBTMFNAU1gQEfgQE8gQE9gQEbgQEdgQEYVUNsb3NlUXddcGVyZm9ybUNsb3NlOtQA +DgURBRIDLQVNA0ADQgW+gQEugLGAvIEBQFtuZXh0S2V5Vmlld9MADgUSAy0FEwXCBcOBAROBAUKBAUTY +AA4FJwUoBSkFKgUrBSwFLQUuBcYFMAIuBTIFMwU0BaSBAR+BAUOAaYEBG4EBHYEBNlZEZWxldGVXZGVs +ZXRlOtMADgUSAy0FEwXPBdCBAROBAUaBAUnYAA4FJwUoBSkFKgUrBSwFLQUuBdMFMAXUBTIFMwU0BaSB +AR+BAUeBAUiBARuBAR2BATZaU2VsZWN0IEFsbFFhWnNlbGVjdEFsbDrTAA4FEgMtBRMF3QXegQETgQFL +gQFP2AAOBScFKAUpBSoFKwUsBS0FLgXhBTAF4gUyBTMFNAXlgQEfgQFNgQFOgQEbgQEdgQFM0wAOBScF +NwU4BegF6YEBLIEB64EB7F8QEktlcmJlcm9zQWdlbnQgSGVscFE/WXNob3dIZWxwOtQADgURBRIDLQUT +Be8F8AXxgQETgQFUgQFRgQFW2AAOBScFKAUpBSoFKwUsBS0FLgX0BfUF9gUyBTMFNAWSgQEfgQFSEgAY +AACBAVOBARuBAR2BATFbSGlkZSBPdGhlcnNRaNIADgAyADMF/oAEgQFVXxAWaGlkZU90aGVyQXBwbGlj +YXRpb25zOtMADgUSAy0FEwYCBgOBAROBAViBAVvYAA4FJwUoBSkFKgUrBSwFLQUuBgYFMAYHBTIFMwU0 +BaSBAR+BAVmBAVqBARuBAR2BATZUUmVkb1FaVXJlZG860wAOBRIDLQUTBhAGEYEBE4EBXYEBX9gADgUn +BSgFKQUqBSsFLAUtBS4GFAUwAi4FMgUzBTQFNYEBH4EBXoBpgQEbgQEdgQEYVFpvb21ccGVyZm9ybVpv +b2060wAOBRIDLQUTBh0GHoEBE4EBYYEBZNgADgUnBSgFKQUqBSsFLAUtBS4GIQUwBiIFMgUzBTQFpIEB +H4EBYoEBY4EBG4EBHYEBNlVQYXN0ZVF2VnBhc3RlOtQADgURBRIDLQUTApAEWQYtgQETgIaA9YEBZtMA +DgUSAy0FEwYwBjGBAROBAWiBAWvYAA4FJwUoBSkFKgUrBSwFLQUuBjQFMAY1BTIFMwU0BaSBAR+BAWmB +AWqBARuBAR2BATZTQ3V0UXhUY3V0OtQADgURBRIDLQUTApAEWAZAgQETgIaA8YEBbdQADgURBRIDLQUT +AEECRgZFgQETgAeAb4EBb9QADgURBRIDLQVNA0IDQAW+gQEugLyAsYEBQNQADgURBRIDLQVNBk0GTgZP +gQEugQF6gQFygQGB2gZRAA4GUgZTAIAGVAZVBlYGVwZYAIcGWgCHBlwAhwZeBl8AhwCHAIdfEBpOU0Zp +bHRlclJlc3RyaWN0c0luc2VydGlvbl8QFE5TUHJlc2VydmVzU2VsZWN0aW9uXxAPX05TTWFuYWdlZFBy +b3h5XxARTlNPYmplY3RDbGFzc05hbWVeTlNEZWNsYXJlZEtleXNfECJOU0NsZWFyc0ZpbHRlclByZWRp +Y2F0ZU9uSW5zZXJ0aW9uXxAYTlNTZWxlY3RzSW5zZXJ0ZWRPYmplY3RzXxAWTlNBdm9pZHNFbXB0eVNl +bGVjdGlvbgmBAXkJgQF3CYEBdoEBcwkJCdIADgA+AHYGZYBSogZmBmeBAXSBAXVfEBZzZXJ2aWNlUHJp +bmNpcGFsU3RyaW5nXxAYc2hvcnRUaW1lUmVtYWluaW5nU3RyaW5nXxASS2VyYmVyb3NDcmVkZW50aWFs +0QAOBmyBAXjSADcAOAZuBlOiBlMAO9IANwA4BnAGcaQGcQZyBnMAO18QEU5TQXJyYXlDb250cm9sbGVy +XxASTlNPYmplY3RDb250cm9sbGVyXE5TQ29udHJvbGxlctkGUQAOBlIGUwZUBlUGVgZXBlgAhwZaAIcG +eAZ5BnoAhwCHAIcJgQF5CYEBgIEBf4EBewkJCdIADgA+AHYGgIBSowaBBoIGg4EBfIEBfYEBfl8QEGNy +ZWRlbnRpYWxzQXJyYXlfEA9wcmluY2lwYWxTdHJpbmddS2VyYmVyb3NDYWNoZdEADgZsgQF4V2NvbnRl +bnTUAA4FEQUSAy0FTQVOBFwGjoEBLoEBIoEBAYEBg1lwb3B1cE1lbnXTAA4FEgMtBRMGkgaTgQETgQGF +gQGI2AAOBScFKAUpBSoFKwUsBS0FLgaWBfUGlwUyBTMFNAWkgQEfgQGGgQGHgQEbgQEdgQE2XxAVUGFz +dGUgYW5kIE1hdGNoIFN0eWxlUVZfEBFwYXN0ZUFzUGxhaW5UZXh0OtQADgURBRIDLQVNBU4DRQaOgQEu +gQEigNiBAYPUAA4FEQUSAy0FTQVOBFwFUIEBLoEBIoEBAYEBLdQADgURBRIDLQVNBqoGTQZPgQEugQGM +gQF6gQGB1AAOBlMGVAZVBq4GrwawBrGBAZGBAZCBAY+BAY3SAA4APgB2BrSAUqEGtYEBjltjYWNoZXNB +cnJheV8QF0tlcmJlcm9zQ2FjaGVDb2xsZWN0aW9u0QAOBmyBAXjSADcAOAa7BnKjBnIGcwA71AAOBREF +EgMtBRMF7wa/BsCBAROBAVSBAZOBAZXYAA4FJwUoBSkFKgUrBSwFLQUuBsMFMAIuBTIFMwU0BTWBAR+B +AZSAaYEBG4EBHYEBGF8QEkJyaW5nIEFsbCB0byBGcm9udF8QD2FycmFuZ2VJbkZyb250OtQADgURBRID +LQUTBe8GzQbOgQETgQFUgQGXgQGa2AAOBScFKAUpBSoFKwUsBS0FLgbRBTAG0gUyBTMFNAWSgQEfgQGY +gQGZgQEbgQEdgQExXxASUXVpdCBLZXJiZXJvc0FnZW50UXFadGVybWluYXRlOtQADgURBRIDLQUTBe8G +3AbdgQETgQFUgQGcgQGe2AAOBScFKAUpBSoFKwUsBS0FLgbgBTAF9gUyBTMFNAWSgQEfgQGdgQFTgQEb +gQEdgQExXxASSGlkZSBLZXJiZXJvc0FnZW50VWhpZGU61AAOBREFEgMtBRMF7wbqBuuBAROBAVSBAaCB +AaLYAA4FJwUoBSkFKgUrBSwFLQUuBu4FMAIuBTIFMwU0BZKBAR+BAaGAaYEBG4EBHYEBMVhTaG93IEFs +bFd1bmhpZGU61AAOBREFEgMtBU0G9waqBk+BAS6BAaSBAYyBAYHSAA4AMgAzBvyABIEBpdMADgUSAy0F +Ewb/BwCBAROBAaeBAarYAA4FJwUoBSkFKgUrBSwFLQUuBwMFMAcEBTIFMwU0BaSBAR+BAaiBAamBARuB +AR2BATZUQ29weVFjVWNvcHk62AAOBREHDAcNBRIDLQcOBw8HEAZNBxIHEwF/BxUHFgBXWU5TS2V5UGF0 +aFlOU0JpbmRpbmdZTlNPcHRpb25zXxAcTlNOaWJCaW5kaW5nQ29ubmVjdG9yVmVyc2lvboEBwIEBeoEB +roEBrYBAgQGsgQGvXxAmdmFsdWU6IGFycmFuZ2VkT2JqZWN0cy5wcmluY2lwYWxTdHJpbmdVdmFsdWVf +EB9hcnJhbmdlZE9iamVjdHMucHJpbmNpcGFsU3RyaW5n0wAOBxsAPgccBx0HKldOUy5rZXlzgQG/rAce +Bx8HIAchByIHIwckByUHJgcnBygHKYEBsIEBsYEBsoEBs4EBtIEBtYEBtoEBt4EBuIEBuYEBuoEBu6wH +KwcsBysHKwcvBy8HKwcsBy8HLwcrByyBAbyBAb2BAbyBAbyBAb6BAb6BAbyBAb2BAb6BAb6BAbyBAb1f +EBpOU0NvbmRpdGlvbmFsbHlTZXRzRW5hYmxlZF8QHE5TUmFpc2VzRm9yTm90QXBwbGljYWJsZUtleXNf +EBZOU1ZhbGlkYXRlc0ltbWVkaWF0ZWx5XxAmTlNBbHdheXNQcmVzZW50c0FwcGxpY2F0aW9uTW9kYWxB +bGVydHNfEBtOU011bHRpcGxlVmFsdWVzUGxhY2Vob2xkZXJfEBFOU051bGxQbGFjZWhvbGRlcl8QGk5T +Q29udGludW91c2x5VXBkYXRlc1ZhbHVlXxAXTlNDcmVhdGVzU29ydERlc2NyaXB0b3JfEBpOU05vdEFw +cGxpY2FibGVQbGFjZWhvbGRlcl8QGE5TTm9TZWxlY3Rpb25QbGFjZWhvbGRlcl8QJk5TQWxsb3dzRWRp +dGluZ011bHRpcGxlVmFsdWVzU2VsZWN0aW9uXxAbTlNDb25kaXRpb25hbGx5U2V0c0VkaXRhYmxlCAnS +ADcAOAdGB0eiB0cAO1xOU0RpY3Rpb25hcnnSADcAOAdJB0qjB0oFGwA7XxAVTlNOaWJCaW5kaW5nQ29u +bmVjdG9y2AAOBREHDAcNBRIDLQcOBw8HEAZNB04HEwGAB1EHUgBXgQHAgQF6gQHDgQGtgEuBAcKBAcRf +EC92YWx1ZTogYXJyYW5nZWRPYmplY3RzLnNob3J0VGltZVJlbWFpbmluZ1N0cmluZ18QKGFycmFuZ2Vk +T2JqZWN0cy5zaG9ydFRpbWVSZW1haW5pbmdTdHJpbmfTAA4HGwA+BxwHVwdkgQG/rAceBx8HIAchByIH +IwckByUHJgcnBygHKYEBsIEBsYEBsoEBs4EBtIEBtYEBtoEBt4EBuIEBuYEBuoEBu6wHKwcsBysHKwcv +By8HKwcrBy8HLwcrByyBAbyBAb2BAbyBAbyBAb6BAb6BAbyBAbyBAb6BAb6BAbyBAb3SAA4APgdyB3OB +Ae6vEHQHdAE0AX8EDgJ1AHkGTQRYBVsCswMFB38BgABPBCQFnAWkAIgCtAUkA0AGkgRWBK4HjAYCARQA +QQeQBhABjgTOAqEG3AM/BVoHmAa/BjAF5QNBBvcEXAbNA0QAeAeiAtAESQVOB6YAewXCBfAG6gerAqAE +vQMoBGIHsAK1BFcDXgOsAh8Htge3BV0DSwRaA3sAQgWuAzAC8wRbAuQFNQZOA2sDQgXdA0MAfAKQAloA +xwM6Bv8HzgG+BJwCsQXPAroFigaqA0UEWQVcAycH2gR4BIsH3QB9AUwBHwWSA4QAegXvAkYCrQYdgQHG +gDaAQIDVgIOAIYEBeoDxgQEngJKAnoEBzYBLgA2A3IEBNYEBNoAagJWBAReAsYEBhYDogPuBAdyBAViA +X4AHgQHJgQFdgEiBAQSBAQeBAZyAq4EBJYEBzoEBk4EBaIEBTICugQGkgQEBgQGXgNOAEIEB4YCTgOaB +ASKBAeCAZYEBQoEBUYEBoIEB0oCIgP+A5IDrgQHVgJmA7ICvgL+AZ4EB6IEB2YEBKoCsgPmAtIB+gQE7 +gKaAmoD9gJaBARiBAXKAuoC8gQFLgLOAbYCGgHSAI4CogQGngQHagFGA94CKgQFGgIyBATCBAYyA2ID1 +gQEpgKSBAeaA8IDzgQHRgHKAOIBbgQExgNGAMoEBVIBvgI+BAWHUAA4FJwDdBTcFOAfqB+sH7IEBLIEB +x4EB7YEByF5BdXRoZW50aWNhdGlvbtIADgA+AHYH8IBSpAeQB4wHoge2gQHJgQHcgQHhgQHo2gAOB/YF +JwUoBSkFKgUrBSwFLQH4BS4FkgWVBTACLgUyBTMFNAd0B/5ZTlNTdWJtZW51gQEfgQExgQHKgGmBARuB +AR2BAcaBActec3VibWVudUFjdGlvbjrSAA4APgB2CAKAUqsFigd/B5gH3QerB7cG3AXwBuoHzgbNgQEw +gQHNgQHOgQHRgQHSgQHZgQGcgQFRgQGggQHagQGX2gAOBScFKAVvBSkFcAUqBSsFLAUtBS4CLgUwAIcC +LgCHBTIFMwU0BZKBAR+AaQmAaQmBARuBAR2BATHYAA4FJwUoBSkFKgUrBSwFLQUuCBkFMAgaBTIFMwU0 +BZKBAR+BAc+BAdCBARuBAR2BATFsAFAAcgBlAGYAZQByAGUAbgBjAGUAcyAmUSzaAA4FJwUoBW8FKQVw +BSoFKwUsBS0FLgIuBTAAhwIuAIcFMgUzBTQFkoEBH4BpCYBpCYEBG4EBHYEBMdoADgf2BScFKAUpBSoF +KwUsBS0B+AUuB7AILAUwAi4FMgUzBTQFkggxgQEfgQHVgQHTgGmBARuBAR2BATGBAdRYU2VydmljZXPU +AA4FJwDdBTcFOAg1CDYIN4EBLIEB1oEB2IEB19IADgBeAF8IMoAL0gAOAD4Adgg8gFKgXxAPX05TU2Vy +dmljZXNNZW512gAOBScFKAVvBSkFcAUqBSsFLAUtBS4CLgUwAIcCLgCHBTIFMwU0BZKBAR+AaQmAaQmB +ARuBAR2BATHaAA4FJwUoBW8FKQVwBSoFKwUsBS0FLgIuBTAAhwIuAIcFMgUzBTQFkoEBH4BpCYBpCYEB +G4EBHYEBMVxfTlNBcHBsZU1lbnXaAA4H9gUnBSgFKQUqBSsFLAUtAfgFLgWkBacFMAIuBTIFMwU0B3QI +WYEBH4EBNoEB3YBpgQEbgQEdgQHGgQHeVEVkaXTSAA4APgB2CF2AUqkFnAYCB6YGMAb/Bh0GkgXCBc+B +ATWBAViBAeCBAWiBAaeBAWGBAYWBAUKBAUbaAA4FJwUoBW8FKQVwBSoFKwUsBS0FLgIuBTAAhwIuAIcF +MgUzBTQFpIEBH4BpCYBpCYEBG4EBHYEBNtoADgf2BScFKAUpBSoFKwUsBS0B+AUuBTUIcwUwAi4FMgUz +BTQHdAh4gQEfgQEYgQHigGmBARuBAR2BAcaBAeNWV2luZG930gAOAF4AXwh5gAvSAA4APgB2CH6AUqUF +rgUkBhAH2ga/gQE7gQEXgQFdgQHmgQGT2gAOBScFKAVvBSkFcAUqBSsFLAUtBS4CLgUwAIcCLgCHBTIF +MwU0BTWBAR+AaQmAaQmBARuBAR2BARheX05TV2luZG93c01lbnXaAA4H9gUnBSgFKQUqBSsFLAUtAfgF +LgXlCJEFMAIuBTIFMwU0B3QIloEBH4EBTIEB6YBpgQEbgQEdgQHGgQHqVEhlbHDSAA4AXgBfCJeAC9IA +DgA+AHYInIBSoQXdgQFLW19OU01haW5NZW510gA3ADgIoAHgogHgADvSAA4APgdyCKOBAe6vEHQAHwB6 +ATQDRABCAE8AHwRJBU4CoAK1BZIBNABBA0UFpAeMAHgCoAU1AzAFpARJBFoHdAWkAHoAHwd0BTUBfwRc +ApAFkgMwBU4FkgU1BaQHtgMwAB8ESQWSAzAATwd0Aq0DKAAfBaQATwWkBZIFkgWSAqEEWwKQBFYHqwKg +BEkDPwNCAHsHdAWSBU4DOgRJA0AAHwU1AycCtARJArMHogAfA0EDMAXlAzAATwJ1AH0AeQMwBaQFkgGA +BFkCoAWkArEFkgAfAzAESQVOApAFNQRXBFgFkgBPAHoAegeQA0MATwAfAHwCoAWkgAKAMoA2gNOAfoAN +gAKA5oEBIoCIgJmBATGANoAHgNiBATaBAdyAEICIgQEYgKaBATaA5oD5gQHGgQE2gDKAAoEBxoEBGIBA +gQEBgIaBATGApoEBIoEBMYEBGIEBNoEB6ICmgAKA5oEBMYCmgA2BAcaAj4DkgAKBATaADYEBNoEBMYEB +MYEBMYEBB4D9gIaA6IEB0oCIgOaAq4C8gGWBAcaBATGBASKAqIDmgLGAAoEBGICkgJWA5oCSgQHhgAKA +roCmgQFMgKaADYCDgHKAIYCmgQE2gQExgEuA9YCIgQE2gIqBATGAAoCmgOaBASKAhoEBGIDsgPGBATGA +DYAygDKBAcmAs4ANgAKAbYCIgQE20gAOAD4HcgkagQHurxB1B3QBNAF/BA4CdQB5Bk0EWAVbArMDBQd/ +AYAATwQkBZwFpACIArQFJAaSA0AEVgYCB4wErgEUAEEHkAYQAY4EzgKhBtwDPwVaB5gGvwYwBeUDQQb3 +Bs0HogRcAHgDRALQBU4ESQemBfAAewXCBuoHqwKgBL0DKARiB7ACtQRXAB8DXgOsAh8Htge3BV0DSwRa +A3sAQgWuAzAC8wRbBTUC5AZOBd0DQgNrA0MAfAKQAloAxwM6Bv8HzgG+BJwCsQXPAroFigaqA0UFXARZ +AycH2gR4BIsH3QB9AUwBHwWSA4QAegXvAq0CRgYdgQHGgDaAQIDVgIOAIYEBeoDxgQEngJKAnoEBzYBL +gA2A3IEBNYEBNoAagJWBAReBAYWAsYDogQFYgQHcgPuAX4AHgQHJgQFdgEiBAQSBAQeBAZyAq4EBJYEB +zoEBk4EBaIEBTICugQGkgQGXgQHhgQEBgBCA04CTgQEigOaBAeCBAVGAZYEBQoEBoIEB0oCIgP+A5IDr +gQHVgJmA7IACgK+Av4BngQHogQHZgQEqgKyA+YC0gH6BATuApoCagP2BARiAloEBcoEBS4C8gLqAs4Bt +gIaAdIAjgKiBAaeBAdqAUYD3gIqBAUaAjIEBMIEBjIDYgQEpgPWApIEB5oDwgPOBAdGAcoA4gFuBATGA +0YAygQFUgI+Ab4EBYdIADgA+B3IJkoEB7q8QdQmTCZQJlQmWCZcJmAmZCZoJmwmcCZ0JngmfCaAJoQmi +CaMJpAmlCaYJpwmoCakJqgmrCawJrQhzCa8JsAmxCbIJswm0CbUJtgm3CbgJuQm6CbsG/Am9Cb4JvwnA +CcEJwgnDCcQJxQnGCccJyAnJCcoJywnMCc0JzgnPCdAJ0QnSCdMJ1AnVCdYJ1wnYCdkJ2gnbCdwJ3Qne +Cd8J4AnhCeIJ4wnkCeUJ5gnnCegJ6QnqCesJ7AntCe4J7wnwCfEJ8gnzCfQJ9Qn2CfcJ+An5CfoJ+wn8 +Cf0J/gn/CgAKAQoCCgMKBAoFCgYKB4EB8oEB84EB9IEB9YEB9oEB94EB+IEB+YEB+oEB+4EB/IEB/YEB +/oEB/4ECAIECAYECAoECA4ECBIECBYECBoECB4ECCIECCYECCoECC4ECDIEB4oECDYECDoECD4ECEIEC +EYECEoECE4ECFIECFYECFoECF4ECGIECGYEBpYECGoECG4ECHIECHYECHoECH4ECIIECIYECIoECI4EC +JIECJYECJoECJ4ECKIECKYECKoECK4ECLIECLYECLoECL4ECMIECMYECMoECM4ECNIECNYECNoECN4EC +OIECOYECOoECO4ECPIECPYECPoECP4ECQIECQYECQoECQ4ECRIECRYECRoECR4ECSIECSYECSoECS4EC +TIECTYECToECT4ECUIECUYECUoECU4ECVIECVYECVoECV4ECWIECWYECWoECW4ECXIECXYECXoECX4EC +YIECYYECYoECY4ECZFhNYWluTWVudV8QJVRhYmxlIFZpZXcgKElkZW50aXR5LCBUaW1lIFJlbWFpbmlu +ZylfEBdUYWJsZSBDb2x1bW4gKElkZW50aXR5KV8QGEJ1dHRvbiBDZWxsIChDb250aW51ZSktMV5Db250 +ZW50IFZpZXctMV8QL1N0YXRpYyBUZXh0IChQbGVhc2Ugc2VsZWN0IGEgS2VyYmVyb3MgSWRlbnRpdHkp +XxAQQ2FjaGVzQ29udHJvbGxlcl8QFlB1c2ggQnV0dG9uIChDb250aW51ZSlvEBwATQBlAG4AdQAgAEkA +dABlAG0AIAAoAEMAaABhAG4AZwBlACAAUABhAHMAcwB3AG8AcgBkICYAKV8QGFN0YXRpYyBUZXh0IChD +b25jbHVzaW9uKV8QFUJ1dHRvbiBDZWxsIChHbyBCYWNrKVtTZXBhcmF0b3ItMl8QHVRhYmxlIENvbHVt +biAoVGltZSBSZW1haW5pbmcpXENvbnRlbnQgVmlld18QFEJ1dHRvbiBDZWxsIChHZWFyKS0xXxAQTWVu +dSBJdGVtIChVbmRvKVtNZW51IChFZGl0KV8QGkltYWdlIENlbGwgKEtlcmJlcm9zQWdlbnQpXxBZU3Rh +dGljIFRleHQgKENvbmdyYXR1bGF0aW9ucyEgWW91IGhhdmUgYWNxdWlyZWQgS2VyYmVyb3MgdGlja2V0 +cyBmb3IgbHhzQEFUSEVOQS5NSVQuRURVLilfEBRNZW51IEl0ZW0gKE1pbmltaXplKV8QIU1lbnUgSXRl +bSAoUGFzdGUgYW5kIE1hdGNoIFN0eWxlKVpUZXh0IEZpZWxkXxAcSW1hZ2UgVmlldyAoS2VyYmVyb3NB +Z2VudCktMl8QEE1lbnUgSXRlbSAoUmVkbylfEBBNZW51IEl0ZW0gKEVkaXQpXxA1VGV4dCBGaWVsZCBD +ZWxsIChQbGVhc2UgZW50ZXIgeW91ciBLZXJiZXJvcyBwYXNzd29yZClfEBNIb3Jpem9udGFsIFNjcm9s +bGVyXxAZTWVudSBJdGVtIChLZXJiZXJvc0FnZW50KV8QEE1lbnUgSXRlbSAoWm9vbSlfEBtUZXh0IEZp +ZWxkIENlbGwgKFRleHQgQ2VsbClfEBJCdXR0b24gQ2VsbCAoR2VhcilfEBZUYWIgVmlldyBJdGVtIChS +ZXN1bHQpXxAeTWVudSBJdGVtIChIaWRlIEtlcmJlcm9zQWdlbnQpXxAxU3RhdGljIFRleHQgKFBsZWFz +ZSBlbnRlciB5b3VyIEtlcmJlcm9zIGlkZW50aXR5KW8QGwBNAGUAbgB1ACAASQB0AGUAbQAgACgAVABp +AGMAawBlAHQAIABPAHAAdABpAG8AbgBzICYAKW8QGABNAGUAbgB1ACAASQB0AGUAbQAgACgAUAByAGUA +ZgBlAHIAZQBuAGMAZQBzICYAKV8QHk1lbnUgSXRlbSAoQnJpbmcgQWxsIHRvIEZyb250KV8QD01lbnUg +SXRlbSAoQ3V0KVtNZW51IChIZWxwKV8QE1N0YXRpYyBUZXh0IChOYW1lOilfEB5NZW51IEl0ZW0gKFF1 +aXQgS2VyYmVyb3NBZ2VudClfEBJNZW51IEl0ZW0gKFdpbmRvdylfEBNQb3B1cCBCdXR0b24gKEdlYXIp +XxAaSW1hZ2UgVmlldyAoS2VyYmVyb3NBZ2VudClfEBhQdXNoIEJ1dHRvbiAoQ29udGludWUpLTFfEBxJ +bWFnZSBDZWxsIChLZXJiZXJvc0FnZW50KS0xWEdlYXJNZW51VlZpZXctMVtTZXBhcmF0b3ItMV8QF01l +bnUgSXRlbSAoSGlkZSBPdGhlcnMpXxAUUHVzaCBCdXR0b24gKFNlbGVjdClfEBJNZW51IEl0ZW0gKERl +bGV0ZSlfEBRNZW51IEl0ZW0gKFNob3cgQWxsKV8QFE1lbnUgSXRlbSAoU2VydmljZXMpXxBCVGV4dCBG +aWVsZCBDZWxsIChNYWlsIHdhbnRzIHRvIGNvbm5lY3QgdG8gdGhlIGFjY291bnQgbHhzQG1pdC5lZHUp +XxAqVGFiIFZpZXcgSXRlbSAoQXV0aGVudGljYXRpb24gSW5mb3JtYXRpb24pXxAcSW1hZ2UgQ2VsbCAo +S2VyYmVyb3NBZ2VudCktMl8QD01lbnUgKFNlcnZpY2VzKV8QFVB1c2ggQnV0dG9uIChHbyBCYWNrKV8Q +EVNlY3VyZSBUZXh0IEZpZWxkXEZpbGUncyBPd25lcl8QNVRleHQgRmllbGQgQ2VsbCAoUGxlYXNlIGVu +dGVyIHlvdXIgS2VyYmVyb3MgaWRlbnRpdHkpXkNvbWJvIEJveCBDZWxsXxAUQnV0dG9uIENlbGwgKFNl +bGVjdClfEBBNZW51IEl0ZW0gKEhlbHApW1NlcGFyYXRvci0zXxAdTWVudSBJdGVtIChBYm91dCBLZXJi +ZXJvcy4uLilfEBxJbWFnZSBDZWxsIChLZXJiZXJvc0FnZW50KS0zXxAxU3RhdGljIFRleHQgKFBsZWFz +ZSBlbnRlciB5b3VyIEtlcmJlcm9zIHBhc3N3b3JkKV8QEVRleHQgRmllbGQgQ2VsbC0xV1dpbmRvdzFf +EBFNZW51IEl0ZW0gKENsb3NlKVZWaWV3LTJfEF1UZXh0IEZpZWxkIENlbGwgKENvbmdyYXR1bGF0aW9u +cyEgWW91IGhhdmUgYWNxdWlyZWQgS2VyYmVyb3MgdGlja2V0cyBmb3IgbHhzQEFUSEVOQS5NSVQuRURV +LilfED5TdGF0aWMgVGV4dCAoTWFpbCB3YW50cyB0byBjb25uZWN0IHRvIHRoZSBhY2NvdW50IGx4c0Bt +aXQuZWR1KV1NZW51IChXaW5kb3cpXxAcVGV4dCBGaWVsZCBDZWxsIChDb25jbHVzaW9uKV8QFUNyZWRl +bnRpYWxzQ29udHJvbGxlcl8QHk1lbnUgSXRlbSAoS2VyYmVyb3NBZ2VudCBIZWxwKVlDb21ibyBCb3hf +EBdUZXh0IEZpZWxkIENlbGwgKE5hbWU6KV8QFFN0YXRpYyBUZXh0IChSZWFsbTopXxAUUHVzaCBCdXR0 +b24gKENhbmNlbClfEEhObyBTaGFkb3cgVGFiIFZpZXcgKFNlbGVjdCBJZGVudGl0eSwgQXV0aGVudGlj +YXRpb24gSW5mb3JtYXRpb24sIFJlc3VsdClvEBIAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABOAGUA +dyAmAClfEDNUZXh0IEZpZWxkIENlbGwgKFBsZWFzZSBzZWxlY3QgYSBLZXJiZXJvcyBJZGVudGl0eSlf +EBxJbWFnZSBWaWV3IChLZXJiZXJvc0FnZW50KS0zXxAQTWVudSBJdGVtIChDb3B5KVtTZXBhcmF0b3It +NF8QHVRleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKS0xXxAXQnV0dG9uIENlbGwgKEdvIEJhY2spLTFf +EBJQdXNoIEJ1dHRvbiAoRG9uZSlfEBZNZW51IEl0ZW0gKFNlbGVjdCBBbGwpXxASQnV0dG9uIENlbGwg +KERvbmUpXxAfTWVudSBJdGVtIChBYm91dCBLZXJiZXJvc0FnZW50KV8QGUNhY2hlQ29sbGVjdGlvbkNv +bnRyb2xsZXJfEBVQb3B1cCBCdXR0b24gKEdlYXIpLTFZU2VwYXJhdG9yXxAXUHVzaCBCdXR0b24gKEdv +IEJhY2spLTFfEB9UYWIgVmlldyBJdGVtIChTZWxlY3QgSWRlbnRpdHkpW1NlcGFyYXRvci02XxAPVGV4 +dCBGaWVsZCBDZWxsXxAWQnV0dG9uIENlbGwgKENvbnRpbnVlKVtTZXBhcmF0b3ItNW8QEgBQAHUAcwBo +ACAAQgB1AHQAdABvAG4AIAAoAE4AZQB3ICYAKV8QEVRhYmxlIEhlYWRlciBWaWV3XxARVmVydGljYWwg +U2Nyb2xsZXJfEBRNZW51IChLZXJiZXJvc0FnZW50KV8QGFRleHQgRmllbGQgQ2VsbCAoUmVhbG06KVtT +Y3JvbGwgVmlld1tBcHBsaWNhdGlvbl8QHEltYWdlIFZpZXcgKEtlcmJlcm9zQWdlbnQpLTFfEBRCdXR0 +b24gQ2VsbCAoQ2FuY2VsKV8QEU1lbnUgSXRlbSAoUGFzdGUp0gAOAD4Hcgp8gQHuowRXBFwDRYDsgQEB +gNjSAA4APgdyCoKBAe6jBHsE0AQngO2BAQKA2dIADgA+B3IKiIEB7q8Qlwd0ATQBfwT0BQAEDgJ1AHkE +7gZNBFgFWwUIArMFCQMFB38BgABPBCQFnAWkBPwAiAK0BSQE/QUKA0AGkgRWBK4HjAYCARQAQQeQBQEG +EAGOBM4FDAKhBtwDPwVaB5gGvwYwBeUDQQUDBvcEXAbNA0QAeAeiBQ8C0ARJBU4HpgB7BcIF8ATyBuoH +qwKgBL0E+QMoBGIHsAK1BPEEVwT1AB8FDQNeBQUDrAIfBPMHtgT+B7cE+gVdA0sFBwRaBPgDewBCBa4D +MALzBFsC5AU1Bk4E8ANrA0IF3QT2A0MFBAT3AHwFCwKQAloAxwM6BQYG/wfOAb4E+wScArEFzwK6BYoG +qgNFBFkFXAMnB9oEeAT/BIsH3QB9AUwE7wUCAR8FDgWSA4QAegXvAkYCrQYdgQHGgDaAQIEBOoEBboDV +gIOAIYEBEYEBeoDxgQEngQGSgJKBAZaAnoEBzYBLgA2A3IEBNYEBNoEBYIAagJWBAReBAWWBAZuAsYEB +hYDogPuBAdyBAViAX4AHgQHJgQFwgQFdgEiBAQSBAaOBAQeBAZyAq4EBJYEBzoEBk4EBaIEBTICugQGC +gQGkgQEBgQGXgNOAEIEB4YEBwYCTgOaBASKBAeCAZYEBQoEBUYEBL4EBoIEB0oCIgP+BAVCA5IDrgQHV +gJmBASGA7IEBP4ACgQGmgK+BAYmAv4BngQE0gQHogQFngQHZgQFXgQEqgKyBAYuA+YEBSoC0gH6BATuA +poCagP2AloEBGIEBcoEBFoC6gLyBAUuBAUGAs4EBhIEBRYBtgQGfgIaAdIAjgKiBAYqBAaeBAdqAUYEB +XID3gIqBAUaAjIEBMIEBjIDYgPWBASmApIEB5oDwgQFsgPOBAdGAcoA4gQEUgQFxgFuBAauBATGA0YAy +gQFUgG+Aj4EBYdIADgA+B3ILIoEB7q8QlwsjCyQLJQsmCycLKAspCyoLKwssCy0LLgsvCzALMQsyCzML +NAs1CzYLNws4CzkLOgs7CzwLPQs+Cz8LQAtBC0ILQwtEC0ULRgtHC0gLSQtKC0sLTAtNC04LTwtQC1EL +UgtTC1QLVQtWC1cLWAtZC1oLWwtcC10LXgtfC2ALYQtiC2MLZAtlC2YLZwtoC2kLagtrC2wLbQtuC28L +cAtxC3ILcwt0C3ULdgt3C3gLeQt6C3sLfAt9C34LfwuAC4ELgguDC4QLhQuGC4cLiAuJC4oLiwuMC40L +jguPC5ALkQuSC5MLlAuVC5YLlwuYC5kLmgubC5wLnQueC58LoAuhC6ILowukC6ULpgunC6gLqQuqC6sL +rAutC64LrwuwC7ELsguzC7QLtQu2C7cLuAu5gQJpgQJqgQJrgQJsgQJtgQJugQJvgQJwgQJxgQJygQJz +gQJ0gQJ1gQJ2gQJ3gQJ4gQJ5gQJ6gQJ7gQJ8gQJ9gQJ+gQJ/gQKAgQKBgQKCgQKDgQKEgQKFgQKGgQKH +gQKIgQKJgQKKgQKLgQKMgQKNgQKOgQKPgQKQgQKRgQKSgQKTgQKUgQKVgQKWgQKXgQKYgQKZgQKagQKb +gQKcgQKdgQKegQKfgQKggQKhgQKigQKjgQKkgQKlgQKmgQKngQKogQKpgQKqgQKrgQKsgQKtgQKugQKv +gQKwgQKxgQKygQKzgQK0gQK1gQK2gQK3gQK4gQK5gQK6gQK7gQK8gQK9gQK+gQK/gQLAgQLBgQLCgQLD +gQLEgQLFgQLGgQLHgQLIgQLJgQLKgQLLgQLMgQLNgQLOgQLPgQLQgQLRgQLSgQLTgQLUgQLVgQLWgQLX +gQLYgQLZgQLagQLbgQLcgQLdgQLegQLfgQLggQLhgQLigQLjgQLkgQLlgQLmgQLngQLogQLpgQLqgQLr +gQLsgQLtgQLugQLvgQLwgQLxgQLygQLzgQL0gQL1gQL2gQL3gQL4gQL5gQL6gQL7gQL8gQL9gQL+gQL/ +EgAElA0QChALEgAElEgSAAST7RIAAYbBEBQQCBBEEgAEk/YQLxIABJQBEgAElEsQPxIABJRGEgABhuMS +AASUHBASEgAEk/0SAASUMxIABJQqEgAElFASAAGGpxBAEgAElBQQQhIABJRDEBwSAASULBArEgABhtES +AASUDxIABJQuEgADDUkSAASUERIABJQGEgAElBcSAAST8hIABJPrEgAElAsQOxIABJQjEBsSAASUAhIA +BJQlEgAElBoSAASUMhIABJQoEB0SAASUCBIABJP3EgAEk+oSAASUIhAhEAcSAASUEhIABJP7EgABht4Q +FxIABJP+EgAElDUSAASUMRIABJQfEgAElFUSAASUHRIABJQkEDwSAAGG0hIABJREEBYSAAGGyxIABJQn +EEMSAASUBBAtEgAElAcSAASUTxIAAYa7EgAElAUSAAGGvxIAAYavEgAElEwSAASUEBIABJROEgAElCAS +AASUTRIABJP/EgABhroSAASUDBAxEgAElFQSAAGGvBATEgAElBkSAAGG4BAyEgABht8SAASUExIABJP1 +EgAElEkSAAGGvRAfEgAElCkSAASUUhAgEgAElFESAASUUxIABJRFEBUSAAGGsRIAAYaoEBoSAASUCRIA +BJQ0EgAElB4SAAST8xIABJRKEgABhtAQPRIABJQvEgABht0SAASUJhIABJP4EgAEk/wQMBIABJQAEBgS +AASUFRIAAYbNEEESAAGGzxIABJQhEBESAAST6RAjEgAElAoSAAGGqRIABJP6EgAElBsSAAGGwBAJE/// +///////9EgABhrAQPhIABJQw0gAOAD4AdgxNgFKg0gAOAD4HcgxQgQHuoNIADgA+B3IMU4EB7qDSADcA +OAxVDFaiDFYAO15OU0lCT2JqZWN0RGF0YQAIABkAIgAnADEAOgA/AEQAUgBUAGYGcgZ4BsMGygbRBt8G +8QcNBxsHJwczB0EHTAdaB3YHhAeXB6kHwwfNB9oH3AffB+IH5QfoB+sH7gfwB/MH9gf5B/wH/ggACAMI +BggLCA4IFwgjCCUIJwg1CD4IRwhSCFcIZghvCIIIiwiWCJgInQifCKEI0gjfCOwI+gkECRIJHwkpCTsJ +TwlZCWUJZwlpCWsJbQlyCXQJdgl4CXoJfAl+CYAJmwm4CcEJywnNCdYJ3wnmCfgKAQoKCgwKEQouCkAK +SwpUCmAKbApuCnAKcgp1CncKeQp7CoQKhgqTCpUKlwqZCpsKnQqfCsgK0ArbCuIK7gr4CvoK/Ar+Cv8L +AQsDCwYLBwsJCwsLFAsWCyMLJQsnCykLKwstCy8LSwtnC5sLswvUC/EL+gwBDA4MFAwsDE0MWQxhDGwM +dAx8DIkMlAyZDJsMnQyfDKQMpQyyDMEMwwzFDMcMzwzdDOYM6wz+DQcNDg0aDSMNLg06DUQNSw1XDXgN +eg18DX4NgA2DDYQNhg2IDaENwg3WDeAN7g36Df8OAQ4DDgUOBw4JDg4OEA40DkUOTA5TDlwOXg5nDmkO +bA55DoIOhw6ODqMOqw64DsQO0g7UDtYO2A7aDtwO4w7wDv0PBQ8HDwkPFQ8eDyMPOA86DzwPPg9AD1MP +YA9iD2UPbg93D4kPlg+fD6oPtg/1EAEQChAXECoQNxBDEFEQXxBhEGMQZRBnEGoQbBBuEHAQgxCGEIgQ +ihCMEI4QkBCZEJsQphCoEKoQrBCuELAQ3RDnEPEQ+xD9EP8RAREDEQURCBEKEQwRDhEQERIRGxEdESAR +IhF1EZcRoRGuEcMR3RH5EhQSIBI/Ek4SWhJcEl4SYxJlEmcSaBJqEnMSdRJ+EoASgRKDEoUShxKJEpIS +nRK6EsYSyBLKEswSzhLQEtIS/xMBEwMTBRMHEwkTCxMNEw8TERMbEyQTLRNBE1oTXBNeE2ATaRNrE20T +hBONE5YTpBOtE68TtBO2E7gT4RPwE/0UBRQQFB8UKhQ1FEIUQxRFFEcUUBRSFFsUZBRlFGcUhBSJFIsU +jRSPFJEUkxScFK0UrxS4FLoUvRTKFMwU2BTtFO8U8RTzFPUVBxUQFRsVLxVQFVUVVxVZFVsVXRVfFWQV +ZhVwFXkVfhWMFbUVthW4FboVwxXFFc4V1xXYFdoV9xX5FfsV/RX/FgQWBhYVFioWLBYuFjAWMhY+FksW +TRZQFnEWdhZ4FnoWfBZ+FoAWhRaHFpAWlxamFq4WwxbFFscWyRbLFtUW4hbkFukW8hb9FxUXKhcsFy4X +MBcyF0sXVBddF2gXjReWF58XqRerF60XrxexF7MXtRe3F8AX2RfmF+8X+hgFGC4YMBgyGDQYNhg4GDoY +PBg+GEcYXxhoGGoYbRhvGIUYnhinGLAYvRjeGOAY4hjkGOYY6RjqGOwY7hkGGTsZURlmGXUZiBmaGawZ +uhm8Gb4ZwBnCGcQZxhnIGcoZzBnRGdoZ4RniGesZ7Rn2Gf8aDBoVGiAaKRpKGkwaThpQGlIaUxpVGlca +bxqkGqYaqBqqGqwarhqwGrIauRrCGsQa5RrnGuka6xrtGu4a8BryGwobPxtBG0MbRRtHG0kbSxtNG1Yb +XxthG2wbdRt8G5UboBu9G8YbyxveHBMcKRwrHC0cMBwyHDQcNhw4HDscPRxAHEIcXRx4HIEcgxyMHI4c +qxytHK8csRy0HLYcuRzCHMQcxxzJHQIdER0lHT4dVh1YHVsdXR1fHWEdYx1lHWYdaB1pHWsdbh13HXkd +fB1+HZ8doR2jHaUdpx2pHasdrR22Hbgdwx3FHccdyR3LHc0d8h30HfYd+B36Hfsd/R3/HgEeGR5OHlAe +Uh5UHlYeWB5aHlweYR5qHmwemR6bHp0enx6gHqIepB6lHqceqR6rHrQeth7DHsUexx7JHssezR7PHucf +DB8OHxAfEh8UHxUfFx8ZHxsfPB9BH0MfRR9GH18fhB+GH4gfih+MH40fjx+RH5MftB+2H7gfuh+8H74f +wB/LH+MgCCAKIAwgDiAQIBEgEyAVIBcgOCA6IDwgPiBAIEIgRyBJIJcgpCCmILQgzCEBIQMhBSEHIQkh +CyENIQ8hFyEgISIhLSFFIU4hUCFXIVkhWyFeIXchhCGOIZYhmCGaIZwhniGgIaIhpCG9Ib8hwSHDIcUh +xyHQIdIh4yHlIech6SHrIe0h7yHxIfMiHCIeIiAiIiIjIiUiJyIoIioiLCI1IjciRCJGIkgiSiJMIk4i +UCJoIokiiyKNIo8ikSKSIpQiliK3IrkiuyK8ItUi9iL4Ivoi/CL+Iv8jASMDIyQjJiMoIyojLCMuIzAj +ViN3I3kjeyN9I38jgCOCI4QjnCO9I78jwSPDI8UjxiPII8oj7yP4I/oj/CP+JAAkAiQHJAgkCiQfJCEk +IyQlJCckPSRSJFQkViRYJFokZCR7JJwkniSgJKIkpCSmJKskrSSzJNQk1iTYJNok3CTdJN8k4ST5JS4l +OSVOJWYlaCVtJW8lcSVzJXUldyV4JXoleyV9JYYliCXjJfAl8iX0JfYl/yYBJgMmBCYGJggmCSYLJg0m +DyYRJhMmHCYkJi0mLyYyJjQmXSZeJmAmYiZkJm0mbyZ4JnomlyaZJpsmnSafJqEmsia0JrYmuCbFJscm +6CbtJu8m8SbzJvUm+Cb5JvsnDicXJyQnNydAJ0snWidjJ3AneyeSJ7MntSe3J7knuye9J78nxifnJ+kn +6yftJ+8n8CfyJ/QoDChBKEMoRShHKEkoSyhNKE8oWChhKGMoiCieKKAooiikKKYoqCirKKworiiwKLwo +0ykIKRYpGCkaKRwpHikgKSIpJCkmKSkpMilDKUUpRylUKVYpWClaKV8paCltKXwpjimXKZwpqinDKcUp +xynJKcspzSnQKdIp5ynpKesp7SnwKfkp+yoKKgwqDioQKhIqFCoWKhkqPipAKkIqRCpFKkcqSSpKKkwq +VSpXKmQqZipoKmoqbCpuKnAqkSqTKpUqliq7Kr0qvyrBKsMqxSrIKskqyyrNKuEq+SseKyArIiskKyYr +KCspKysrSCtKK0wrTitQK1ErUyuIK4orjCuOK5ArkiuUK5YrnyuhK74rwCvCK8QrxivHK8kr4SwWLBgs +GiwcLB4sICwiLCQsLSwvLEwsTixQLFIsVCxVLFcseCx6LHwsfiyALIIshCyqLMcsySzLLM0szyzQLNIs +6i0LLQ0tDy0SLRQtFi0YLUstcC1yLXQtdy15LXwtfS2ALYItmS3OLdAt0i3ULdYt2C3aLd0t5i4DLhwu +Hi4hLiMuJS4nLiouLC4zLjwuRS5QLl4ucy58Ln4uxS7ILssuzi7RLtQu1y7aLt0u4C7jLuYu6S7sLu8u +8i71Lvgu+y7+LwEvBC8HLwovDS8QLxMvFi8ZLxwvHy8iLyUvKC8rLzwvSi9TL1YvWC9aL10vei+DL4ov +oi+xL8IvxS/HL8kvzC/lL/Iv9S/4L/swHDAkMDgwQzBRMFswaDBvMHIwdTB6MH0wgjCFMIgwizCcMKgw +qzCuMLEwtDC9ML8wzDDOMNAw0zDlMPIw9DD2MPkxDDEVMRoxJTE7MUwxTzFSMVQxVzFoMWsxbjFwMXMx +eDGBMYMxjDGPMZIxlTGYMbkxvDG/McExxDHHMcox6zIMMg8yEjIUMhcyGjIdMkAyaTJ3MoQyhzKJMooy +jDKNMpAykzKWMrcyujK9Mr8ywjLFMsgy3DLlMuoy7zL4Mv8zFjMnMyozLDMvMzIzTzNSM1UzVzNaM10z +YDNxM3QzdzN6M30zkzOzM8AzwzPGM8kz6jPtM/Az8zP2M/kz/DQJNAw0DzQSNBc0GTQfNCw0LzQyNDU0 +VjRZNFw0XzRiNGU0aDRuNHA0fjSPNJI0lDSWNJk0pTSyNLU0uDS7NNw03zTiNOQ05zTqNO009DT8NQk1 +DDUPNRI1MzU2NTk1PDU/NUI1RTVQNVI1XTVqNW01cDVzNZQ1lzWaNZ01oDWjNaY1szW2Nbk1vDXRNdM1 +3TXuNfE19DX3Nfo2GzYeNiE2JjYpNiw2LzYyNj42QDZJNks2TjZnNnQ2dzZ6Nn02njahNqQ2pzaqNq02 +sDa1Nrc2vTbKNs020DbTNvQ29zb6Nvw2/zcCNwU3CjcXNyQ3JzcqNy03TjdRN1Q3VzdaN103YDdmN2g3 +bzeAN4M3hTeHN4o3lzeaN503oDfBN8Q3xzfKN8030DfTN9c32TfeN+838jf0N/Y3+TgKOA04DzgROBQ4 +JTgoOCo4LDgvOEA4QzhGOEk4TDh1OJI4qTi7OM843jkDOR45Nzk4OTs5PDk/OUA5QzlGOUc5SDlJOVI5 +VDlZOVw5Xzl4OZM5qDmtObA5uTm+Occ50DnkOfk6BjorOiw6LzowOjM6Njo5Ojo6Ozo8OkU6RzpOOlE6 +VDpXOmo6fDqKOo86kjqaOqs6rjqxOrQ6tzrBOs460TrUOtc6+Dr7Ov47ATsEOwc7CjsiOyQ7ODtJO0w7 +TztRO1Q7ZTtoO2s7bjtxO4I7hTuIO4s7jjufO6I7pTuoO6s7tDu2O7k7vDvIO+I75zvqO/M7+jwLPA48 +ETwUPBc8ODw7PD48QDxDPEY8STxePHA8gTyEPIc8ijyNPK48sTy0PLc8ujy9PMA81TzXPOI88zz2PPk8 +/Dz/PSA9Iz0mPSk9LD0vPTI9Rz1NPV49YT1kPWc9aj2LPY49kT2TPZY9mT2cPaU9rT2+PcE9xD3HPco9 +0z3VPdg95T3oPes97j4PPhI+FT4YPhs+Hj4hPiY+KD4uPk8+WT5jPm0+jD6PPpI+lT6YPpo+nT6gPsk+ +zz7xPv4/Bj8JPyI/JT8oPys/Lj8xPzQ/Nz86Pz0/QD9DP0Y/Xz9iP2U/aD9rP24/cT90P3c/ej99P4A/ +gz+gP78/2EABQB9AM0BQQGpAh0CiQMtA6UDqQOtA9ED5QQZBD0EWQS5BT0FSQVVBWEFbQV1BYEFjQZVB +wEHNQdBB6UHsQe9B8kH1QfhB+0H+QgFCBEIHQgpCDUImQilCLEIvQjJCNUI4QjtCPkJBQkRCR0JKQlNC +VkNBQ0RDRkNIQ0pDTENOQ1FDU0NWQ1hDWkNdQ19DYUNjQ2ZDaUNrQ21DcENyQ3VDd0N5Q3xDf0OBQ4ND +hkOJQ4tDjkORQ5RDlkOZQ5xDn0OiQ6VDp0OqQ61DsEOyQ7RDt0O5Q7tDvkPBQ8NDxkPJQ8xDz0PRQ9ND +1UPXQ9pD3EPeQ+BD4kPkQ+dD6kPtQ+9D8UPzQ/VD+EP6Q/xD/kQARANEBkQIRApEDUQPRBFEE0QVRBdE +GUQcRB9EIUQjRCVEKEQqRC1EMEQyRDREN0Q5RDxEPkRARENERURHRElETERORFBEU0RVRFdEWkRrRG5E +cUR0RHdEhkSPRJFEmkSdRKBEo0SmRM9E2UTcRN9E4kTkROdE6kTtRPBE/0UIRQpFIUUkRSdFKkUtRTBF +M0U2RTlFPEU/RUJFa0VuRXBFcUVzRXRFd0V6RX1FnkWhRaRFp0WqRa1FsEXJRctF9EX3RflF+kX8Rf1G +AEYDRgZGL0YyRjVGOEY6Rj1GQEZDRkZGT0ZgRmNGZkZpRmxGdUZ3RoBGgkaDRpVGvkbBRsNGxEbGRsdG +ykbNRtBG+Ub8Rv5G/0cBRwJHBUcIRwtHGEdBR0RHR0dKR0xHT0dSR1VHWEddR2ZHaEd7R35HgUeER4dH +ikeNR5BHk0eWR79HwkfER8VHx0fIR8tHzkfRR/pH/UgASANIBUgISAtIDkgRSBhIIUgjSCxILkg5SDxI +P0hCSEVISEhxSHRIdkh3SHlIekh9SIBIg0iSSLtIvkjBSMRIxkjJSMxIz0jSSNdI4EjiSOtI7UjwSPNI +/0kISQ1JFkkZSgRKBkoISgpKDEoOShBKEkoUShdKGUobSh5KIEoiSiRKJ0oqSixKLkoxSjNKNko4SjpK +PUpASkJKREpHSkpKTEpPSlFKVEpWSllKXEpfSmJKZUpnSmlKa0puSnBKckp1SndKeUp7Sn5KgEqDSoZK +iUqMSo9KkUqTSpVKmEqaSpxKnkqgSqJKpUqoSqtKrUqvSrFKs0q2SrhKukq8Sr5KwUrDSsVKx0rKSsxK +zkrQStJK1ErWStlK3EreSuBK4krlSudK6krsSu5K8ErzSvVK+Er6SvxK/0sBSwNLBUsISwpLDEsOSxBL +EksVSx5LIUwOTBFME0wVTBdMGUwbTB5MIEwjTCVMJ0wqTCxMLkwwTDNMNkw4TDpMPUxATEJMRExHTEpM +TExOTFBMU0xWTFhMW0xeTGFMY0xmTGlMbExvTHJMdEx3THpMfUyATIJMhEyGTIlMi0yOTJFMk0yWTJlM +nEyeTKBMokykTKdMqUyrTK1Mr0yxTLNMtky5TLxMvkzATMJMxEzHTMlMy0zNTNBM0kzVTNhM2kzcTN5M +4EziTORM5kzoTOtM7kzwTPJM9Ez3TPlM/Ez/TQFNBE0GTQhNC00NTQ9NEk0UTRZNGE0bTR1NH00iTSRN +Jk0pTTJNNU4iTiVOKE4rTi5OMU40TjdOOk49TkBOQ05GTklOTE5PTlJOVU5YTltOXk5hTmROZ05qTm1O +cE5zTnZOeU58Tn9Ogk6FTohOi06OTpFOlE6XTppOnU6gTqNOpk6pTqxOr06yTrVOuE67Tr5OwU7ETsdO +yk7NTtBO007WTtlO3E7fTuJO5U7oTutO7k7xTvRO9076Tv1PAE8DTwZPCU8MTw9PEk8VTxhPG08eTyFP +JE8nTypPLU8wTzNPNk85TzxPP09CT0VPSE9LT05PUU9UT1dPWk9dT2BPY09mT2lPbE9vT3JPdU94T3tP +fk+BT4pPsk/MT+dP9lAoUDtQVFCPUKpQwlDOUO5Q+1ESUSVRMVFOUapRwVHlUfBSD1IiUjVSbVKDUp9S +slLQUuVS/lMfU1NTjFO/U+BT8lP+VBRUNVRKVGBUfVSYVLdUwFTHVNNU7VUEVRlVMFVHVYxVuVXYVepW +AlYWViNWW1ZqVoFWlFagVsBW31cTVydXL1dDV0pXqlfrV/lYGFgwWFFYW1h1WIxYo1juWRVZS1lqWX1Z +iVmpWcNZ2FnxWgZaKFpEWlxaZlqAWqJarlrAWtla5VsMWyBbNFtLW2Zbclt+W51btFvIW9Fb1FvbW91b +4FviW+tb7lv1W/db+lv8XAVcCF05XTxdPl1AXUNdRl1IXUpdTF1PXVJdVF1XXVpdXF1fXWFdZF1mXWhd +al1tXXBdc111XXddel19XYBdgl2FXYddiV2MXY9dkV2TXZZdmV2cXZ5doV2kXaddql2sXa9dsl21Xbhd +u129XcBdw13GXcldy13NXdBd013VXddd2l3dXd9d4l3lXehd613uXfBd8l31Xfdd+V38Xf5eAV4DXgZe +CF4LXg1eEF4SXhReF14aXh1eIF4jXiZeKF4rXi1eMF4yXjReN145XjtePV4/XkJeRV5IXkpeTF5PXlJe +VF5XXlpeXF5fXmFeY15lXmdeal5tXnBecl51XndeeV58Xn5egV6EXoZeiF6LXo1ekF6SXpVel16aXpxe +nl6hXqRepl6pXqxerl6wXrNetV63Xrpew17GX/df+l/9YABgA2AGYAlgDGAPYBJgFWAYYBtgHmAhYCRg +J2AqYC1gMGAzYDZgOWA8YD9gQmBFYEhgS2BOYFFgVGBXYFpgXWBgYGNgZmBpYGxgb2ByYHVgeGB7YH5g +gWCEYIdgimCNYJBgk2CWYJlgnGCfYKJgpWCoYKtgrmCxYLRgt2C6YL1gwGDDYMZgyWDMYM9g0mDVYNhg +22DeYOFg5GDnYOpg7WDwYPNg9mD5YPxg/2ECYQVhCGELYQ5hEWEUYRdhGmEdYSBhI2EmYSlhLGEvYTJh +NWE4YTthPmFBYURhR2FKYU1hUGFTYVZhWWFcYV9hYmFlYWhha2FuYXFhdGF3YXphfWGAYYNhhmGJYYxh +j2GSYZVhmGGbYZ5hoWGkYadhqmGtYbBhs2G2YblhvGHBYcNhxWHKYc9h1GHWYdhh2mHfYeFh5mHrYe1h +8mH3Yfxh/mIDYghiDWISYhdiGWIeYiBiJWInYixiLmIzYjhiPWJCYkdiTGJRYlZiW2JgYmJiZ2JpYm5i +c2J4Yn1igmKEYolijmKTYphimmKcYqFipmKrYq1ismK3YrxiwWLGYsti0GLSYtdi3GLeYuNi6GLqYu9i +8WL2YvtjAGMFYwpjD2MUYxljHmMjYyhjLWMyYzdjOWM+Y0NjRWNKY09jUWNWY1tjYGNlY2pjbGNxY3Zj +eGN9Y4Jjh2OJY45jk2OVY5pjn2OkY6ljrmOzY7VjumO/Y8RjyWPOY9Bj1WPXY9xj4WPjY+hj7WPvY/Rj +9mP7ZABkBWQKZA9kEWQaZB9kIWQmZC9kMWQyZDtkPmQ/ZEhkS2RMZFVkWgAAAAAAAAICAAAAAAAADFcA +AAAAAAAAAAAAAAAAAGRpA + + + diff --git a/src/kim/agent/mac/resources/English.lproj/MainMenu.xib b/src/kim/agent/mac/resources/English.lproj/MainMenu.xib new file mode 100644 index 000000000..12d33cbef --- /dev/null +++ b/src/kim/agent/mac/resources/English.lproj/MainMenu.xib @@ -0,0 +1,3882 @@ + + + + 1050 + 9A581 + 629 + 949 + 343.00 + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + + NSApplication + + + + FirstResponder + + + NSApplication + + + MainMenu + + YES + + + KerberosAgent + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + + NSMenuMixedState + + submenuAction: + + + + YES + + + About KerberosAgent + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + UHJlZmVyZW5jZXPigKY + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + + Services + + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide KerberosAgent + h + 1048576 + 2147483647 + + + + + + Hide Others + + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit KerberosAgent + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + + Window + + + YES + + + Close + w + 1048576 + 2147483647 + + + + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + + Help + + + YES + + + KerberosAgent Help + ? + 1048576 + 2147483647 + + + + + + + + _NSMainMenu + + + + YES + cachesArray + + KerberosCacheCollection + + + + + + + + YES + credentialsArray + principalString + shortTimeRemainingString + timeRemainingString + + KerberosCache + + YES + YES + YES + YES + YES + + + + YES + servicePrincipalString + + + KerberosCredential + YES + + YES + YES + YES + YES + YES + + + 13 + 2 + {{279, 363}, {419, 465}} + 1886912512 + Choose a Kerberos Identity + + NSWindow + + + View + + + + 256 + + YES + + + 268 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{17, 386}, {62, 62}} + + + YES + + 537001472 + 33554432 + + + KerberosAgent + + 0 + 0 + 0 + NO + + YES + + + + 266 + {{84, 377}, {318, 29}} + + + YES + + 67239424 + 4325376 + Checking mail for account lxs@mit.edu + + LucidaGrande + 1.100000e+01 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {380, 291} + + + YES + + + 256 + {380, 17} + + + + + + + -2147483392 + {{-26, 0}, {16, 17}} + + + + + YES + + 2.739741e+02 + 1.499741e+02 + 1.000000e+03 + + 75628032 + 0 + Identity + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 1411513920 + 1346372608 + Text Cell + + + 1.300000e+01 + 1044 + + + YES + + + + 3 + YES + YES + + + + 1.003135e+02 + 1.000000e+02 + 1.500000e+02 + + 67239424 + 67108864 + Time Remaining + + + 6 + + headerColor + + 3 + MQA + + + + + + 1140981312 + 1145046016 + + + + + + + 3 + YES + YES + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 1119879168 + 5 + 15 + 0 + YES + + + {{1, 17}, {380, 291}} + + + + + 6 + + controlBackgroundColor + + + 4 + + + + -2147483392 + {{-30, 17}, {15, 285}} + + + + _doScroller: + 9.684210e-01 + + + + -2147483392 + {{1, -30}, {362, 15}} + + + 1 + + + 9.040768e-01 + + + + 2304 + + YES + + + {{1, 0}, {380, 17}} + + + + + 4 + + + + {{17, 60}, {382, 309}} + + + 562 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 289 + {{320, 12}, {87, 32}} + + + YES + + 67239424 + 134217728 + Choose + + + -2038284033 + 1 + + + DQ + 200 + 25 + + + + + 289 + {{238, 12}, {82, 32}} + + + YES + + 67239424 + 134217728 + Cancel + + + -2038284033 + 1 + + + Gw + 200 + 25 + + + + + 270 + {{84, 414}, {318, 21}} + + + YES + + 67239424 + 4194304 + Mail requires that you choose a Kerberos Identity + + + + + + + + + 292 + {{17, 30}, {23, 22}} + + + YES + + 67239424 + 134217728 + + + + 138690815 + 6 + + + Add + + + + Add_Pressed + + + + + + 200 + 25 + + + + + 292 + {{39, 30}, {23, 22}} + + + YES + + 67239424 + 134217728 + + + + 138690815 + 6 + + + Remove + + + + Remove_Pressed + + + + + + 200 + 25 + + + + {419, 465} + + + + {{0, 0}, {1920, 1178}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + + 13 + 2 + {{378, 247}, {484, 199}} + 1886912512 + Authenticate to Kerberos + + NSWindow + + + View + + + + 256 + + YES + + + 274 + + YES + + + 256 + + YES + + + 268 + + YES + + YES + + + + + NeXT Encapsulated PostScript v1.2 pasteboard type + + + + {{20, 119}, {62, 62}} + + + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 266 + {{87, 119}, {387, 51}} + + + + YES + + 67239424 + 272629760 + Please enter your Kerberos identity + + + + + + + + + 266 + {{90, 89}, {378, 22}} + + + + YES + + -1804468671 + 272630784 + + + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{17, 94}, {68, 17}} + + + + YES + + 67239424 + 71303168 + Name: + + + + + + + + + 266 + {{90, 57}, {381, 26}} + + + YES + + 343014976 + 272630784 + + + + + + YES + + + 5 + YES + + + + 274 + {15, 0} + + + YES + + YES + + + 1.200000e+01 + 1.000000e+01 + 1.000000e+03 + + 75628032 + 0 + + + + 1.200000e+01 + 16 + + + 3 + MC4zMzMzMzI5OQA + + + + + 338820672 + 1024 + + + YES + + + + 3 + YES + + + + 3.000000e+00 + 2.000000e+00 + + + 1.900000e+01 + tableViewAction: + -767524864 + + + + 1 + 15 + 0 + YES + + + + + + 268 + {{17, 61}, {68, 17}} + + + + YES + + 67239424 + 71303168 + Realm: + + + + + + + + + 289 + {{376, 12}, {98, 32}} + + + + YES + + 67239424 + 134217728 + Continue + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 292 + {{18, 15}, {51, 27}} + + + YES + + 67239424 + 134217728 + + + + 1.000000e+01 + 16 + + + -2033434369 + 2 + + + Gear + + + + 400 + 75 + + + + {488, 201} + + + + + + {{-4, 0}, {488, 201}} + + + + YES + + 1 + + Select Identity + + + + + 2 + + + 256 + + YES + + + 268 + + YES + + YES + + + + + + + + + {{17, 122}, {62, 62}} + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 290 + {{87, 60}, {381, 22}} + + YES + + -1804468671 + 272630784 + + + + YES + + + + + + + 289 + {{376, 12}, {98, 32}} + + YES + + 67239424 + 134217728 + + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 289 + {{283, 12}, {93, 32}} + + YES + + 67239424 + 134217728 + Go Back + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 266 + {{84, 167}, {387, 17}} + + YES + + 67239424 + 272629760 + Please enter your Kerberos password + + + + + + + + + 274 + {{84, 90}, {387, 69}} + + YES + + 67239424 + 272629760 + Mail wants to connect to the account lxs@mit.edu + + + + + + + + + 292 + {{18, 16}, {51, 26}} + + YES + + 67239424 + 134217728 + + + + -2033958657 + 2 + + + + 400 + 75 + + + + {488, 201} + + Authentication Information + + + + + 3 + + + 256 + + YES + + + 289 + {{392, 12}, {82, 32}} + + YES + + 67239424 + 134217728 + Done + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 268 + + YES + + YES + + + + + + + + + {{17, 122}, {62, 62}} + + YES + + 130560 + 33554432 + + 0 + 0 + 0 + NO + + YES + + + + 266 + {{84, 167}, {387, 17}} + + YES + + 67239424 + 272629760 + Conclusion + + + + + + + + + 274 + {{84, 60}, {387, 99}} + + YES + + 67239424 + 272760832 + Congratulations! You have acquired Kerberos tickets for lxs@ATHENA.MIT.EDU. + + + + 3 + MSAwLjk3MDAwMDAzAA + + + + + + + 289 + {{305, 12}, {91, 32}} + + YES + + 67239424 + 134217728 + + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + {488, 201} + + Result + + + + + + + 6 + YES + YES + + + {484, 199} + + + + {{0, 0}, {1920, 1178}} + {239.32, 129} + {3.40282e+38, 131} + + + + Menu + + YES + + + VGlja2V0IE9wdGlvbnPigKY + + 1048576 + 2147483647 + + + + + + Q2hhbmdlIFBhc3N3b3Jk4oCmA + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + About Kerberos... + + 1048576 + 2147483647 + + + + + + + + + + YES + + + arrangeInFront: + + + + 39 + + + + showHelp: + + + + 122 + + + + terminate: + + + + 139 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + hideOtherApplications: + + + + 146 + + + + hide: + + + + 152 + + + + unhideAllApplications: + + + + 153 + + + + cut: + + + + 175 + + + + paste: + + + + 176 + + + + redo: + + + + 178 + + + + selectAll: + + + + 179 + + + + undo: + + + + 180 + + + + copy: + + + + 181 + + + + delete: + + + + 201 + + + + performZoom: + + + + 204 + + + + pasteAsPlainText: + + + + 211 + + + + performMiniaturize: + + + + 235 + + + + performClose: + + + + 246 + + + + content + + + + 280 + + + + contentArray: selection.cachesArray + + + + + + + contentArray + selection.cachesArray + + NSConditionallySetsEditable + + + 2 + + + 321 + + + + + + + + 323 + + + + contentArray: selection.credentialsArray + + + + + + + + selection.credentialsArray + + + + + 2 + + + 326 + + + + performClose: + + + + 300356 + + + + nextKeyView + + + + 300406 + + + + selectPreviousTabViewItem: + + + + 300407 + + + + + + + + 300408 + + + + selectNextTabViewItem: + + + + 300409 + + + + + + + + 300410 + + + + + + + + 300411 + + + + value: arrangedObjects.principalString + + + + + + + value + arrangedObjects.principalString + + YES + + YES + NSAllowsEditingMultipleValuesSelection + NSAlwaysPresentsApplicationModalAlerts + NSConditionallySetsEditable + NSConditionallySetsEnabled + NSContinuouslyUpdatesValue + NSCreatesSortDescriptor + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + NSRaisesForNotApplicableKeys + NSValidatesImmediately + + + YES + + + + + + + + + + + + + + + 2 + + + 300413 + + + + value: arrangedObjects.shortTimeRemainingString + + + + + + + + arrangedObjects.shortTimeRemainingString + + YES + + YES + + + + + + + + + + + + + + + YES + + + + + + + + + + + + + + + 2 + + + 300415 + + + + popupMenu + + + + 300422 + + + + + + + + 300423 + + + + menu + + + + 300424 + + + + + + + + 300425 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + 29 + + + YES + + + + + + + + + + 19 + + + YES + + + + + + 24 + + + YES + + + + + + + + + + 5 + + + + + 23 + + + + + 203 + + + + + 233 + + + + + 234 + + + + + 56 + + + YES + + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 129 + + + + + 131 + + + YES + + + + + + 130 + + + + + 134 + + + + + 136 + + + + + 143 + + + + + 144 + + + + + 145 + + + + + 149 + + + + + 150 + + + + + 202 + + + + + 103 + + + YES + + + + + + 106 + + + YES + + + + + + 111 + + + + + 163 + + + YES + + + + + + 169 + + + YES + + + + + + + + + + + + + + 156 + + + + + 157 + + + + + 158 + + + + + 160 + + + + + 164 + + + + + 171 + + + + + 172 + + + + + 173 + + + + + 210 + + + + + 262 + + + CacheCollectionController + + + 279 + + + + + + 310 + + + CachesController + + + 322 + + + CredentialsController + + + -3 + + + Application + + + 300335 + + + YES + + + + + + + 300336 + + + YES + + + + + + + + + + + + + 300338 + + + YES + + + + + + 300339 + + + YES + + + + + + 300340 + + + YES + + + + + + + + + 300341 + + + YES + + + + + + 300342 + + + YES + + + + + + 300343 + + + + + 300344 + + + + + 300345 + + + + + 300346 + + + + + 300347 + + + + + 300348 + + + YES + + + + + + + 300349 + + + YES + + + + + + 300350 + + + YES + + + + + + 300351 + + + + + 300352 + + + + + 300353 + + + + + 300354 + + + + + 300357 + + + YES + + + + Window1 + + + 300358 + + + YES + + + + + + 300359 + + + YES + + + + + + + + 300360 + + + YES + + + + + + 300361 + + + YES + + + + + + 300362 + + + YES + + + + + + 300363 + + + YES + + + + + + + + + + + + 300364 + + + YES + + + + + + 300365 + + + YES + + + + + + 300366 + + + YES + + + + + + 300367 + + + YES + + + + + + 300368 + + + YES + + + + + + 300369 + + + YES + + + + + + 300370 + + + YES + + + + + + 300371 + + + + + 300372 + + + + + 300373 + + + + + 300374 + + + + + 300375 + + + + + 300376 + + + + + 300377 + + + + + 300378 + + + YES + + + + + + + + + + + + + 300379 + + + YES + + + + + + 300380 + + + YES + + + + + + 300381 + + + YES + + + + + + 300382 + + + YES + + + + + + 300383 + + + YES + + + + + + 300384 + + + YES + + + + + + 300385 + + + YES + + + + + + 300386 + + + YES + + + + + + 300387 + + + + + 300388 + + + + + 300389 + + + + + 300390 + + + + + 300391 + + + + + 300392 + + + + + 300393 + + + + + 300394 + + + + + 300395 + + + YES + + + + + + + + + + 300396 + + + YES + + + + + + 300397 + + + YES + + + + + + 300398 + + + YES + + + + + + 300399 + + + YES + + + + + + 300400 + + + YES + + + + + + 300401 + + + + + 300402 + + + + + 300403 + + + + + 300404 + + + + + 300405 + + + + + 300417 + + + YES + + + + + + + GearMenu + + + 300418 + + + + + 300419 + + + + + 300420 + + + + + 300421 + + + + + 300437 + + + YES + + + + + + 300438 + + + + + 300443 + + + YES + + + + + + 300444 + + + + + 300445 + + + YES + + + + + + 300446 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBPluginDependency + 106.ImportedFromIB2 + 106.editorWindowContentRectSynchronizationRect + 111.IBPluginDependency + 111.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 156.IBPluginDependency + 156.ImportedFromIB2 + 157.IBPluginDependency + 157.ImportedFromIB2 + 158.IBPluginDependency + 158.ImportedFromIB2 + 160.IBPluginDependency + 160.ImportedFromIB2 + 163.IBPluginDependency + 163.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 169.IBPluginDependency + 169.ImportedFromIB2 + 169.editorWindowContentRectSynchronizationRect + 171.IBPluginDependency + 171.ImportedFromIB2 + 172.IBPluginDependency + 172.ImportedFromIB2 + 173.IBPluginDependency + 173.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 210.IBPluginDependency + 210.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 233.IBPluginDependency + 233.ImportedFromIB2 + 234.IBPluginDependency + 234.ImportedFromIB2 + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 262.IBPluginDependency + 262.ImportedFromIB2 + 279.IBPluginDependency + 279.ImportedFromIB2 + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.editorWindowContentRectSynchronizationRect + 300335.IBPluginDependency + 300335.IBWindowTemplateEditedContentRect + 300335.ImportedFromIB2 + 300335.NSWindowTemplate.visibleAtLaunch + 300335.editorWindowContentRectSynchronizationRect + 300335.windowTemplate.hasMaxSize + 300335.windowTemplate.hasMinSize + 300335.windowTemplate.maxSize + 300335.windowTemplate.minSize + 300336.IBPluginDependency + 300336.ImportedFromIB2 + 300338.IBPluginDependency + 300338.ImportedFromIB2 + 300339.IBPluginDependency + 300339.ImportedFromIB2 + 300340.IBPluginDependency + 300340.ImportedFromIB2 + 300341.IBPluginDependency + 300341.ImportedFromIB2 + 300342.IBPluginDependency + 300342.ImportedFromIB2 + 300345.IBShouldRemoveOnLegacySave + 300346.IBShouldRemoveOnLegacySave + 300347.IBShouldRemoveOnLegacySave + 300348.IBPluginDependency + 300348.ImportedFromIB2 + 300349.IBPluginDependency + 300349.ImportedFromIB2 + 300350.IBPluginDependency + 300350.ImportedFromIB2 + 300351.IBPluginDependency + 300352.IBPluginDependency + 300357.IBPluginDependency + 300357.IBWindowTemplateEditedContentRect + 300357.ImportedFromIB2 + 300357.NSWindowTemplate.visibleAtLaunch + 300357.editorWindowContentRectSynchronizationRect + 300357.windowTemplate.hasMaxSize + 300357.windowTemplate.hasMinSize + 300357.windowTemplate.maxSize + 300357.windowTemplate.minSize + 300358.IBPluginDependency + 300358.ImportedFromIB2 + 300359.IBPluginDependency + 300359.ImportedFromIB2 + 300360.IBPluginDependency + 300360.ImportedFromIB2 + 300361.IBPluginDependency + 300361.ImportedFromIB2 + 300362.IBPluginDependency + 300362.ImportedFromIB2 + 300363.IBPluginDependency + 300363.ImportedFromIB2 + 300364.CustomClassName + 300364.IBPluginDependency + 300364.ImportedFromIB2 + 300365.IBPluginDependency + 300365.ImportedFromIB2 + 300366.IBPluginDependency + 300366.ImportedFromIB2 + 300367.IBPluginDependency + 300367.ImportedFromIB2 + 300368.IBPluginDependency + 300368.ImportedFromIB2 + 300369.CustomClassName + 300369.IBPluginDependency + 300369.ImportedFromIB2 + 300370.IBPluginDependency + 300370.ImportedFromIB2 + 300378.IBPluginDependency + 300378.ImportedFromIB2 + 300379.CustomClassName + 300379.IBPluginDependency + 300379.ImportedFromIB2 + 300380.IBPluginDependency + 300380.ImportedFromIB2 + 300381.IBPluginDependency + 300381.ImportedFromIB2 + 300382.IBPluginDependency + 300382.ImportedFromIB2 + 300383.IBPluginDependency + 300383.ImportedFromIB2 + 300384.IBPluginDependency + 300384.ImportedFromIB2 + 300385.IBPluginDependency + 300385.ImportedFromIB2 + 300386.IBPluginDependency + 300386.ImportedFromIB2 + 300395.IBPluginDependency + 300395.ImportedFromIB2 + 300396.IBPluginDependency + 300396.ImportedFromIB2 + 300397.IBPluginDependency + 300397.ImportedFromIB2 + 300398.IBPluginDependency + 300398.ImportedFromIB2 + 300399.IBPluginDependency + 300399.ImportedFromIB2 + 300400.IBPluginDependency + 300400.ImportedFromIB2 + 300417.IBPluginDependency + 300417.ImportedFromIB2 + 300417.editorWindowContentRectSynchronizationRect + 300418.IBPluginDependency + 300418.ImportedFromIB2 + 300419.IBPluginDependency + 300419.ImportedFromIB2 + 300420.IBPluginDependency + 300420.ImportedFromIB2 + 300421.IBPluginDependency + 300421.ImportedFromIB2 + 300437.IBPluginDependency + 300438.IBPluginDependency + 300443.IBAttributePlaceholdersKey + 300443.IBPluginDependency + 300443.ImportedFromIB2 + 300445.IBAttributePlaceholdersKey + 300445.IBPluginDependency + 300445.ImportedFromIB2 + 310.IBPluginDependency + 310.ImportedFromIB2 + 322.IBPluginDependency + 322.ImportedFromIB2 + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + + + YES + + + + + + + {{1137, 580}, {211, 23}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{1022, 430}, {253, 173}} + + + + + + + + + + + + + + + + + + + + + + + {{1066, 510}, {197, 93}} + + + + + + + {{884, 603}, {314, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + {{719, 105}, {419, 465}} + + + {{719, 105}, {419, 465}} + + + {3.40282e+38, 3.40282e+38} + {213, 107} + + + + + + + + + + + + + + + + + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + {{932, 664}, {484, 199}} + + + + + + {3.40282e+38, 109} + {239.32, 107} + + + + + + + + + + + + + PopupButton + + + + + + + + + + + NSSecureTextField + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + {{444, 717}, {203, 73}} + + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + + ToolTip + + + + Create an account + + + com.apple.InterfaceBuilder.CocoaPlugin + + + ToolTip + + + + Remove an account + + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + + + + + + + {{896, 420}, {240, 183}} + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300446 + + + + YES + + FirstResponder + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBUserSource + + + + + KerberosCacheCollection + NSObject + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + KerberosCacheCollection + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBProjectSource + ../../../Common/Headers/KerberosCacheCollection.h + + + + PopupButton + NSButton + + YES + + YES + + + YES + + + + + NSMenu + + + IBUserSource + + + + + + 0 + ../../../Projects/KerberosIdentityManagement.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEDKAALAAwAMQA1ADkAQABD +AEQASgBkAGUAZgBpAGwACwB5AH0AkgCWAJ8AqgC6AMMAxADFAMYAxwDIAMkAzQDOANgA5gDrAOwA7QDw +APMA+QD6AQMBEAERARkBGgEcASUBJgEnASwBLgEzATQBNwE7AT4BRwFIAVEBWwFcAWEBYgFlAWoBawFs +AXQBdQF9AX4BjwGRAAsBsgALAbMBtwDiAckB0AHUAdcB2gHjAegB6QHsAfAB9QH2AfkB+gH9AgACAwIE +AgsCDAIWAhcCKgIrAi0CMAIzAj8CMgJAAkECTQJQAlQCVQJYAlkCWwJcAmICawJsAm0CcAJ3AngCfQKH +ApACmQKaAp4CqAE9AqkCqgKyAhYCuQLBAsMCygLLAtMC1ALWAt0C3gLlAuYC7QLuAvUC9gL7AwQDBQJY +Aw4DDwMWAxcDHAMkAysDLAM0AzUDNwNAApkDSQNNAt0DVANbA1wDYwNkA2wDbQNwA3cDeAOAAlgDggOD +A4UACwOGA4cDiAOJA4wDlwOYA5kDmwOdAAsDpAOvA7kAxQPCA8MDyAPQA9ED2QPaA+8D9wQFBAkEGQQa +BCEEKgQrBC4ENQQ2BDkEPgRIBE4ETwRRBFYEVwRhBGIEbARzBHQEeQR6BIMEhQSGBIkEkwSUBJUEmASh +BKIEpgSnBKgEqwSzBLQEvAS9BL4ExgTHBM8E0ATRBNoE2wTjBOQE7ATtBPcE+wT8BQAFAQUDBQsFDAUV +BRkFGgUeBR8FIQALA4YFIgUjBSQFTAVTBVsFXwVgBWEFYwVlBWkFbAVtBXEFdgV9BX4FhQWUBZUFmQWa +BZ4FnwWiBakFqgW1BbwFvQW/BcAFxAXLBdAF0QXSBdMF1gXaBeEF5QXmBecF6AXsBfMF9AX1BfoGAQYG +BgcGCAYNBg4GEgYaBhsGHAYdBiIGMgY3BjgGOQY6BjwGPwZJBlAGUQZSBlMGVAZWBdIGWwZgBmQGawZv +BnAGcQZyBncGeAZ9BoQGhQaGBocGjAaQBpcGmAaZBp4GnwakBqUGqQawBrEGsgazBrcGvga/BsAGwQbF +BswGzQbOBtMG2gbbBtwG4AbnBugG6QbqBu8G9gb3BvgG/QcEBwUHBgcHBwsHEgcTBxQHFQcaBx4HJQcm +BycHKActBzEHOAc5BzoHOwdAB0wHTQdOB08HVgDlB1gHWwdeB2YHZwdoB2kHhQeGB4cHiAeJB4oHiweM +B40HjgePB1YBWwCOB5EHmQeaB5sHoQepB6oHqwfHB84HzwfQB9MH2AfZCFQIXghjCGQIZQhnAOwIawh5 +CIIIiQiKCIsIlAidCGQIngijCKUIqAipCLIIuwi8CL0IxAhkCM0I1ghkCNcI4wjsCPUIZAj2CPgJAAkJ +CQoJCwA4CQ4JEAmLCgcKgwqECoUKhgqHCogKiQqKCosKjAqNCo4KjwqQCpEKkgqTCpQKlQqWCpcKmAqZ +CpoKmwqcCp0KngqfCqAKoQqiCqMKpAqlCqYKpwqoCqkKqgqrCqwKrQquCq8KsAqxCrIAawqzCrQKtQq2 +CrcKuAq5CroKuwq8Cr0Kvgq/CsAKwQrCCsMKxArFCsYKxwrICskKygrLCswKzQrOCs8K0ArRCtIK0wrU +CtUK1grXCtgK2QraCtsK3ArdCt4K3wrgCuEK4grjCuQK5QrmCucK6ArpCuoK6wrsCu0K7grvCvAK8Qry +CvMK9Ar1CvYK9wr4Cv4LBAulDEYMRwxIDEkMSgxLDEwMTQxODE8MUAxRDFIMUwxUDFUMVgxXDFgMWQxa +DFsMXAxdDF4MXwxgDGEMYgxjDGQMZQxmDGcMaAxpDGoMawxsDG0MbgGLDG8McAxxDHIMcwx0DHUMdgx3 +DHgMeQx6DHsMfAx9DH4MfwyADIEMggyDDIQMhQyGDIcMiAyJDIoMiwyMDI0MjgyPDJAMkQySDJMMlAyV +DJYMlwyYDJkMmgybDJwMnQyeDJ8MoAyhDKIMowykDKUMpgynDKgMqQyqDKsMrAytDK4MrwywDLEMsgyz +DLQMtQy2DLcMuAy5DLoMuwy8DL0Mvgy/DMAMwQzCDMMMxAzFDMYMxwzIDMkMygzLDMwBpgzNDM4MzwzQ +DNEM0gzTDNQM1QzWDNcM2AzZDNoM2wzcDN0M3gzfDOAM4QziDOUM6AzrVSRudWxs3xASAA0ADgAPABAA +EQASABMAFAAVABYAFwAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4A +LwAwVk5TUm9vdFYkY2xhc3NdTlNPYmplY3RzS2V5c18QD05TQ2xhc3Nlc1ZhbHVlc18QGU5TQWNjZXNz +aWJpbGl0eU9pZHNWYWx1ZXNdTlNDb25uZWN0aW9uc1tOU05hbWVzS2V5c1tOU0ZyYW1ld29ya11OU0Ns +YXNzZXNLZXlzWk5TT2lkc0tleXNdTlNOYW1lc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eUNvbm5lY3Rv +cnNdTlNGb250TWFuYWdlcl8QEE5TVmlzaWJsZVdpbmRvd3NfEA9OU09iamVjdHNWYWx1ZXNfEBdOU0Fj +Y2Vzc2liaWxpdHlPaWRzS2V5c1lOU05leHRPaWRcTlNPaWRzVmFsdWVzgAKBAyeBAd2BAoOBAyaBAR6B +AgqABoECgoEChIECC4EDJIAAgAeBAgmBAyUSAASVoYEChdIADgAyADMANFtOU0NsYXNzTmFtZYAFgAPS +AA4ANgA3ADhZTlMuc3RyaW5ngARdTlNBcHBsaWNhdGlvbtIAOgA7ADwAPVgkY2xhc3Nlc1okY2xhc3Nu +YW1lowA9AD4AP18QD05TTXV0YWJsZVN0cmluZ1hOU1N0cmluZ1hOU09iamVjdNIAOgA7AEEAQqIAQgA/ +Xk5TQ3VzdG9tT2JqZWN0XxAQSUJDb2NvYUZyYW1ld29ya9IADgBFAEYAR1pOUy5vYmplY3RzgByiAEgA +SYAIgMHdAEsADgBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAGIAY1xO +U1dpbmRvd1ZpZXdcTlNTY3JlZW5SZWN0XxATTlNGcmFtZUF1dG9zYXZlTmFtZV1OU1dpbmRvd1RpdGxl +WU5TV1RGbGFnc11OU1dpbmRvd0NsYXNzXE5TV2luZG93UmVjdFlOU01heFNpemVfEA9OU1dpbmRvd0Jh +Y2tpbmdfEBFOU1dpbmRvd1N0eWxlTWFza1lOU01pblNpemVbTlNWaWV3Q2xhc3OADYDAgL2AO4AKEnB4 +AACAC4AJgL8QAhANgL6ADF8QGHt7Mzc4LCAyNDd9LCB7NDg0LCAxOTl9fV8QGEF1dGhlbnRpY2F0ZSB0 +byBLZXJiZXJvc9IADgA2ADcAaIAEWE5TV2luZG930gAOADYANwBrgARUVmlld9cAbQAOAG4AbwBwAGgA +cQByAHMAdAB1AHYAcgB4XxAPTlNOZXh0UmVzcG9uZGVyWk5TU3Vidmlld3NYTlN2RmxhZ3NbTlNGcmFt +ZVNpemVbTlNTdXBlcnZpZXeADoBzgA8RAQCAu4AOgLzSAA4ARQB6AHuAV6EAfIAQ3QBtAA4AfgB/AIAA +gQBuAG8AaACCAHEAgwCEAFcAhgCHAIgAiQCKAIsAjAByAI4AVwCOAJFeTlNUYWJWaWV3SXRlbXNZTlNU +dkZsYWdzV05TRnJhbWVWTlNGb250XxARTlNEcmF3c0JhY2tncm91bmRfEBZOU0FsbG93VHJ1bmNhdGVk +TGFiZWxzXxAVTlNTZWxlY3RlZFRhYlZpZXdJdGVtgA2AuoB1EAaAdIAqgBERARKADgmADQmAdtIADgBF +AHoAlIBXoQCVgBLYAG0ADgBuAG8AcABoAHEAlwB8AHMAmgB1AJsAcgB8AJ5dTlNOZXh0S2V5Vmlld4AQ +gHOAE4BygA6AEIAU0gAOAEUAegChgFeoAJ4AowCkAKUApgCnAKgAqYAUgB6AN4AngESAOYBigGnbAG0A +DgCAAKsArACtAG8ArgBoAHEAlwCVALAAsQCOALMAtAC1AI4AcgCVAKNaTlNFZGl0YWJsZVZOU0NlbGxb +TlNEcmFnVHlwZXNZTlNFbmFibGVkgBKAJYAdCYAfgBURAQwJgA6AEoAe0gAOAEUARgC8gBymAL0AvgC/ +AMAAwQDCgBaAF4AYgBmAGoAbXxAZQXBwbGUgUERGIHBhc3RlYm9hcmQgdHlwZV8QGUFwcGxlIFBORyBw +YXN0ZWJvYXJkIHR5cGVfEDFOZVhUIEVuY2Fwc3VsYXRlZCBQb3N0U2NyaXB0IHYxLjIgcGFzdGVib2Fy +ZCB0eXBlXxAVTlNGaWxlbmFtZXNQYm9hcmRUeXBlXxAeTmVYVCBUSUZGIHY0LjAgcGFzdGVib2FyZCB0 +eXBlXxAaQXBwbGUgUElDVCBwYXN0ZWJvYXJkIHR5cGXSADoAOwDKAMujAMsAzAA/XE5TTXV0YWJsZVNl +dFVOU1NldF8QFXt7MjAsIDExOX0sIHs2MiwgNjJ9fdkAbQAOAIAArABvAK4AaABxAJcAlQDQANEA0gDT +AI4AcgCVAKWAEoA2gCaAKBEBCgmADoASgCfYANkADgDaANsA3ADdAN4A3wDgAOEA4gDjAOIA4gDkAOVb +TlNDZWxsRmxhZ3NXTlNTdHlsZVpOU0NvbnRlbnRzV05TQWxpZ25XTlNTY2FsZVxOU0NlbGxGbGFnczJa +TlNBbmltYXRlcxIAAf4AgCQQAIAgEgIAAAAI0wAOADIA5wDoAOkA6l5OU1Jlc291cmNlTmFtZYAjgCGA +IldOU0ltYWdlXUtlcmJlcm9zQWdlbnTSADoAOwDuAO+iAO8AP18QEE5TQ3VzdG9tUmVzb3VyY2XSADoA +OwDxAPKjAPIArAA/W05TSW1hZ2VDZWxs0gA6ADsA9AD1pQD1APYA9wD4AD9bTlNJbWFnZVZpZXdZTlND +b250cm9sVk5TVmlld1tOU1Jlc3BvbmRlcl8QFnt7ODcsIDExOX0sIHszODcsIDUxfX3ZAG0ADgCAAKwA +bwCuAGgAcQCXAJUA0AD9AP4AtQCOAHIAlQCkgBKANoBBgEIJgA6AEoA32ADZAA4BBADbAQUBBgDeAQcB +CAEJAQoBCwCKAKMBDgEPXxARTlNCYWNrZ3JvdW5kQ29sb3JZTlNTdXBwb3J0XU5TQ29udHJvbFZpZXdb +TlNUZXh0Q29sb3ISBAH+AIA1gC2AKYAqgB4SEEAAAIAyXxAjUGxlYXNlIGVudGVyIHlvdXIgS2VyYmVy +b3MgaWRlbnRpdHnUAA4BEgETARQBFQEWARcBGFZOU1NpemVWTlNOYW1lWE5TZkZsYWdzgCwjQCoAAAAA +AACAKxEEFFxMdWNpZGFHcmFuZGXSADoAOwEbAIGiAIEAP9UADgEdAR4BHwEgASEBIgCIASMBJFdOU0Nv +bG9yXE5TQ29sb3JTcGFjZVtOU0NvbG9yTmFtZV1OU0NhdGFsb2dOYW1lgDGAMIAvgC5WU3lzdGVtXGNv +bnRyb2xDb2xvctMADgEeASgBIQEqAStXTlNXaGl0ZYAxEANLMC42NjY2NjY2OQDSADoAOwEtAR2iAR0A +P9UADgEdAR4BHwEgASEBMACIATEBJIAxgDSAM4AuXxAQY29udHJvbFRleHRDb2xvctMADgEeASgBIQEq +ATaAMUIwANIAOgA7ATgBOaQBOQE6AKwAP18QD05TVGV4dEZpZWxkQ2VsbFxOU0FjdGlvbkNlbGzSADoA +OwE8AT2lAT0A9gD3APgAP1tOU1RleHRGaWVsZNkAbQAOAIAArABvAK4AaABxAJcAlQDQAUEBQgDTAI4A +cgCVAKeAEoA2gDiAOgmADoASgDlfEBV7ezkwLCA4OX0sIHszNzgsIDIyfX3ZAG0ADgCAAKwAbwCuAGgA +cQCXAJUA0AFLAUwAtQCOAHIAlQCmgBKANoBfgGAJgA6AEoBE2QDZAA4BBADbAQUBBgDeAIIBBwFSAQkB +VABaAIoApAFYAI4BWhP/////lHH+QYA1gDyAO4AqgDcSEEAEAAmAP1DVAA4BHQEeAR8BIAEhAV4AiAFf +ASSAMYA+gD2ALl8QE3RleHRCYWNrZ3JvdW5kQ29sb3LTAA4BHgEoASEBKgFkgDFCMQDVAA4BHQEeAR8B +IAEhATAAiAFoASSAMYA0gECALll0ZXh0Q29sb3JfEBR7ezE3LCA5NH0sIHs2OCwgMTd9fdgA2QAOAQQA +2wEFAQYA3gEHAQgBCQEKAW8AigClAXIBD4A1gC2AQ4AqgCcSBEAAAIAyVU5hbWU62ABtAA4AgACsAG8A +rgBoAHEAlQF3AXgBeQDTAI4AcgCVgBKAXoBFgEYJgA6AEl8QFXt7OTAsIDU3fSwgezM4MSwgMjZ9fd0B +fwDZAA4BBADbAQUBBgDeAIIBgAGBAYIBBwCmAYQBhQFUAYcAigCmAVgAjgGLAYwAjgEPWk5TRGVsZWdh +dGVfEBJOU1Zpc2libGVJdGVtQ291bnRbTlNUYWJsZVZpZXdfEBVOU0hhc1ZlcnRpY2FsU2Nyb2xsZXKA +RBIUcf5AgF2APIBHgCqARAkQBYBICYAy0gAOADYANwFbgATfEBYAbQF/AZIAfwGTAA4BBAGUAZUBlgGX +AZgAbwBwAK4AaAGZAZoBmwBxAZwBnQGeAXkBeQGhAOIBogGjAI4BpQGmAacBeQCMAakAjgGrAawBrQGu +AZ4BsAGxWE5TVGFyZ2V0XxAfTlNEcmFnZ2luZ1NvdXJjZU1hc2tGb3JOb25Mb2NhbF8QEk5TQWxsb3dz +VHlwZVNlbGVjdF8QF05TSW50ZXJjZWxsU3BhY2luZ1dpZHRoXxAZTlNDb2x1bW5BdXRvcmVzaXppbmdT +dHlsZV8QGE5TSW50ZXJjZWxsU3BhY2luZ0hlaWdodFxOU0RhdGFTb3VyY2VYTlNBY3Rpb25bTlNHcmlk +Q29sb3JfEBxOU0RyYWdnaW5nU291cmNlTWFza0ZvckxvY2FsXk5TVGFibGVDb2x1bW5zW05TUm93SGVp +Z2h0gEmARoBGE//////SQIAAgFyAVAkjQAgAAAAAAAAQASNAAAAAAAAAAIBGgEoJgEuAW4BYEA+ASYBM +I0AzAAAAAAAAV3sxNSwgMH3SAA4ARQB6AbWAV6EBtoBN2gG4AA4BuQG6AbsBvAG9Ab4BvwGBAI4BwQHC +AcMBxAHFASoBxgHHAYxeTlNJc1Jlc2l6ZWFibGVcTlNIZWFkZXJDZWxsXE5TSWRlbnRpZmllcldOU1dp +ZHRoWk5TRGF0YUNlbGxeTlNSZXNpemluZ01hc2taTlNNaW5XaWR0aFpOU01heFdpZHRoCYBWgE+ATiNA +KAAAAAAAAIBTI0AkAAAAAAAAI0CPQAAAAAAAgEjXANkADgEEANsBBQDeAQcBygHLAcwBhwHOAOIBXhIE +gf4AgFKAUYBHgFCAPtQADgESARMBFAEVAcQBFwHTgCyAKxAQ0wAOAR4BKAEhASoB1oAxSzAuMzMzMzMy +OTkA0gA6ADsB2AHZpQHZATkBOgCsAD9fEBFOU1RhYmxlSGVhZGVyQ2VsbNgA2QAOAQQBBQEGAN4AggEH +AdsBCQGjAIoBjAHgAI4BDxIUMf5AgDWAVIAqgEgRBAAJgDLVAA4BHQEeAR8BIAEhASIAiAHmASSAMYAw +gFWALl8QFmNvbnRyb2xCYWNrZ3JvdW5kQ29sb3LSADoAOwHqAeuiAesAP11OU1RhYmxlQ29sdW1u0gA6 +ADsB7QHuowHuAe8AP15OU011dGFibGVBcnJheVdOU0FycmF51QAOAR0BHgEfASABIQHyAIgB8wEkgDGA +WoBZgC5ZZ3JpZENvbG9y0wAOAR4BKAEhASoB+IAxRDAuNQBfEBB0YWJsZVZpZXdBY3Rpb2460gA6ADsB ++wH8pgH8AYEA9gD3APgAP18QEE5TQ29tYm9UYWJsZVZpZXfSADoAOwH+Af+lAf8BOQE6AKwAP15OU0Nv +bWJvQm94Q2VsbNIAOgA7AgECAqYCAgE9APYA9wD4AD9aTlNDb21ib0JveF8QFHt7MTcsIDYxfSwgezY4 +LCAxN3192ADZAA4BBADbAQUBBgDeAQcBCAEJAQoCBwCKAKcBcgEPgDWALYBhgCqAOYAyVlJlYWxtOtkA +bQAOAIAArABvAK4AaABxAJcAlQIOAg8CEAIRAI4AcgCVAHyAEoBogGOAZBEBIQmADoASgBBfEBV7ezM3 +NiwgMTJ9LCB7OTgsIDMyfX3dANkADgIYAhkCGgIbAhwA2wEFAQYCHQDeAh4BCAIfAFoCIQGmAIoCIwIk +AIoAqAInAigCKV8QE05TQWx0ZXJuYXRlQ29udGVudHNfEBJOU1BlcmlvZGljSW50ZXJ2YWxeTlNCdXR0 +b25GbGFnczJfEBBOU0FsdGVybmF0ZUltYWdlXxAPTlNLZXlFcXVpdmFsZW50XxAPTlNQZXJpb2RpY0Rl +bGF5XU5TQnV0dG9uRmxhZ3OAZ4A7EBmAKoBmgGWAKoBiEMgSCAAAABP/////hoJA/1hDb250aW51ZdIA +DgA2ADcBW4AE0gA6ADsCLgIvpAIvAToArAA/XE5TQnV0dG9uQ2VsbNIAOgA7AjECMqUCMgD2APcA+AA/ +WE5TQnV0dG9u2gBtAA4AgAI0AKwAbwCuAGgAMgBxAJUCNgI3AjgCOQI6AI4AcgI9AJVfEBNOU09yaWdp +bmFsQ2xhc3NOYW1lgBKAcYBsgGuAbREBJAmADoBqgBJbUG9wdXBCdXR0b25fEBR7ezE4LCAxNX0sIHs1 +MSwgMjd9fd0A2QAOAhgCQgIZAhoCHADbAQUBBgIdAN4CHgEIAh8AWgJFAkYAYABaAFoCSQCpAksCKAJM +XU5TTm9ybWFsSW1hZ2WAZ4A7gG8QS4A7gDuAboBpEQGQE/////+GzED/1AAOARIBEwEUARUBxgEXAdOA +LIAr0wAOADIA5wDoAOkCU4AjgCGAcFRHZWFy0gA6ADsCVgJXogJXAD9eTlNDbGFzc1N3YXBwZXJaezQ4 +OCwgMjAxfdIAOgA7AloA96MA9wD4AD9fEBV7ey00LCAwfSwgezQ4OCwgMjAxfX3SAA4ARQB6Al6AV6MA +kQJgAmGAdoB6gJ7WAA4BugD3AmMBHQJkAmUCZgCVAHwBCgJqWU5TVGFiVmlld1dOU0xhYmVsgHmAd4AS +gBCALYB4UTFfEA9TZWxlY3QgSWRlbnRpdHnSADoAOwJuAm+iAm8AP11OU1RhYlZpZXdJdGVt1gAOAboA +9wJjAR0CZAJlAnICcwB8AQoCdoB5gHuAfIAQgC2AnVEy1QBtAA4AbgBvAHAAKwBzAnsAdQJ8gACAc4B9 +gJzSAA4ARQB6An+AV6cCgAKBAoICgwKEAoUChoB+gIKAh4CLgJCAlICZ2QBtAA4AgACrAKwArQBvAK4A +cQJzALACigCOAowCjQC1AI4Cc4B8gCWAgAmAgYB/CYB80gAOAEUARgKSgBymAL0AvgC/AMAAwQDCgBaA +F4AYgBmAGoAbXxAVe3sxNywgMTIyfSwgezYyLCA2Mn192ADZAA4A2gDbANwA3QDeAN8A4ADhAOIA4wDi +AOIA5ADlgCSAIAjZAG0ADgCAAjQArABvAK4AMgBxAnMCNgKhAqICowKkAI4CpgJzgHyAcYCFgISAhhEB +IgmAg4B8XxARTlNTZWN1cmVUZXh0RmllbGRfEBV7ezg3LCA2MH0sIHszODEsIDIyfX3ZANkADgEEANsB +BQEGAN4AggEHAVIBCQFUAFoAigKBAVgAjgFagDWAPIA7gCqAggmAP9cAbQAOAIAArABvAK4AcQJzAg4C +tQK2AhEAjgJzgHyAaICIgIkJgHzdANkADgIYAhkCGgIbAhwA2wEFAQYCHQDeAh4BCAIfAFoCIQGmAIoC +vQIkAIoCggInAigCKYBngDuAKoCKgGWAKoCH0gAOADYANwFbgATXAG0ADgCAAKwAbwCuAHECcwIOAsYC +xwIRAI4Cc4B8gGiAjICNCYB8XxAVe3syODMsIDEyfSwgezkzLCAzMn193QDZAA4CGAIZAhoCGwIcANsB +BQEGAh0A3gIeAQgCHwBaAiEBpgCKAs8C0ACKAoMCJwIoAimAZ4A7gCqAj4COgCqAi1dHbyBCYWNr0gAO +ADYANwFbgATXAG0ADgCAAKwAbwCuAHECcwDQAtkC2gDTAI4Cc4B8gDaAkYCSCYB8XxAWe3s4NCwgMTY3 +fSwgezM4NywgMTd9fdgA2QAOAQQA2wEFAQYA3gEHAQgBCQEKAuEAigKEAQ4BD4A1gC2Ak4AqgJCAMl8Q +I1BsZWFzZSBlbnRlciB5b3VyIEtlcmJlcm9zIHBhc3N3b3Jk1wBtAA4AgACsAG8ArgBxAnMA0ALpAuoA +jACOAnOAfIA2gJWAlgmAfF8QFXt7ODQsIDkwfSwgezM4NywgNjl9fdgA2QAOAQQA2wEFAQYA3gEHAQgB +CQEKAvEC8gKFAQ4BD4A1gC2Al4CYgJSAMl8QME1haWwgd2FudHMgdG8gY29ubmVjdCB0byB0aGUgYWNj +b3VudCBseHNAbWl0LmVkddQADgESARMBFAEVAvgBFwL6gCwjQCYAAAAAAACAKxEMHNkAbQAOAIACNACs +AG8ArgAyAHECcwI2Av4COAMAAjoAjgI9AnOAfIBxgJqAa4CbCYBqgHxfEBR7ezE4LCAxNn0sIHs1MSwg +MjZ9fd0A2QAOAhgCQgIZAhoCHADbAQUBBgIdAN4CHgEIAh8AWgJFAkYAYABaAFoCSQKGAksCKAMNgGeA +O4BvgDuAO4BugJkT/////4bEQP9fEBpBdXRoZW50aWNhdGlvbiBJbmZvcm1hdGlvbtYADgG6APcCYwEd +AmQCZQMRAxIAfAEKAxWAeYCfgKCAEIAtgLlRM9UAbQAOAG4AbwBwACsAcwMaAHUDG4AAgHOAoYC40gAO +AEUAegMegFelAx8DIAMhAyIDI4CigKeAq4CvgLTXAG0ADgCAAKwAbwCuAHEDEgIOAycDKAIRAI4DEoCg +gGiAo4CkCYCgXxAVe3szOTIsIDEyfSwgezgyLCAzMn193QDZAA4CGAIZAhoCGwIcANsBBQEGAh0A3gIe +AQgCHwBaAiEBpgCKAzADMQCKAx8CJwIoAimAZ4A7gCqApoClgCqAolREb25l0gAOADYANwFbgATZAG0A +DgCAAKsArACtAG8ArgBxAxIAsAM6AI4DPAM9ALUAjgMSgKCAJYCpCYCqgKgJgKDSAA4ARQBGA0KAHKYA +vQC+AL8AwADBAMKAFoAXgBiAGYAagBvYANkADgDaANsA3ADdAN4A3wDgAOEA4gDjAOIA4gDkAOWAJIAg +CNcAbQAOAIAArABvAK4AcQMSANADUANRANMAjgMSgKCANoCsgK0JgKDYANkADgEEANsBBQEGAN4BBwEI +AQkBCgNXAIoDIQEOAQ+ANYAtgK6AKoCrgDJaQ29uY2x1c2lvbtcAbQAOAIAArABvAK4AcQMSANADXwNg +AIwAjgMSgKCANoCwgLEJgKBfEBV7ezg0LCA2MH0sIHszODcsIDk5fX3YANkADgEEANsBBQEGAN4BBwEI +AQkDZgNnAvIDIgNqAQ+ANYCzgLKAmICvEhBCAACAMl8QS0NvbmdyYXR1bGF0aW9ucyEgWW91IGhhdmUg +YWNxdWlyZWQgS2VyYmVyb3MgdGlja2V0cyBmb3IgbHhzQEFUSEVOQS5NSVQuRURVLtMADgEeASgBIQEq +A2+AMU0xIDAuOTcwMDAwMDMA1wBtAA4AgACsAG8ArgBxAxICDgNzA3QCEQCOAxKAoIBogLWAtgmAoF8Q +FXt7MzA1LCAxMn0sIHs5MSwgMzJ9fd0A2QAOAhgCGQIaAhsCHADbAQUBBgIdAN4CHgEIAh8AWgIhAaYA +igN8AtAAigMjAicCKAIpgGeAO4AqgLeAjoAqgLTSAA4ANgA3AVuABFZSZXN1bHTSADoAOwOEAmOkAmMA +9wD4AD9aezQ4NCwgMTk5fV8QFnt7MCwgMH0sIHsxOTIwLCAxMTc4fX1dezIzOS4zMiwgMTI5fV8QEnsz +LjQwMjgyZSszOCwgMTMxfdIAOgA7A4oDi6IDiwA/XxAQTlNXaW5kb3dUZW1wbGF0Zd0ASwAOAEwATQBO +AE8AUABRAFIAUwBUAFUAVgONAFgDjwBaA5EAXAOSA5MDlABgAGEDlQOWgMaAwIEBG4A7gMOAxIDCgQEd +gQEcgMVfEBh7ezI3OSwgMzYzfSwgezQxOSwgNDY1fX1fEBpDaG9vc2UgYSBLZXJiZXJvcyBJZGVudGl0 +edIADgA2ADcAaIAE0gAOADYANwBrgATXAG0ADgBuAG8AcABoAHEDngBzA6AAdQOhA54Do4DHgHOAyIEB +GYDHgQEa0gAOAEUAegOmgFeoA6cDqAOpA6oDqwOsA60DroDJgM6A0oD7gQEAgQEFgQEJgQER2gBtAA4A +gACrAKwArQBvAK4AaABxA40AsAOyAI4DtAO1ALUAjgOeA42AxoAlgMwJgM2AygmAx4DG0gAOAEUARgO7 +gBymAL0AvgO+AMAAwQDCgBaAF4DLgBmAGoAbXxAVe3sxNywgMzg2fSwgezYyLCA2Mn192ADZAA4A2gDb +ANwA3QDeAN8DxADhAOIA4wDiAOIA5ADlEiAB/gCAJIAgCNgAbQAOAIAArABvAK4AaABxA40A0APLA8wA +0wCOA54DjYDGgDaAz4DQCYDHgMZfEBZ7ezg0LCAzNzd9LCB7MzE4LCAyOX192ADZAA4BBADbAQUBBgDe +AQcBCAEJAQoD1ALyA6gD1wEPgDWALYDRgJiAzhIAQgAAgDJfECVDaGVja2luZyBtYWlsIGZvciBhY2Nv +dW50IGx4c0BtaXQuZWR13gBtA9sADgCAA9wD3QPeAG4D3wBvAGgAcQPgA+EDjQPjA+QD5QPmA+cD6APp +A+oAjAOeA40D7QPuW05TSFNjcm9sbGVyWE5Tc0ZsYWdzXE5TQ29ybmVyVmlld18QEE5TSGVhZGVyQ2xp +cFZpZXdcTlNTY3JvbGxBbXRzW05TVlNjcm9sbGVyXU5TQ29udGVudFZpZXeAxoD1gPqA+RECMoDcgNmA +008QEEEgAABBIAAAQZgAAEGYAACAx4DGgPGA1NIADgBFAHoD8YBXpQPuA+0D4wPoA+eA1IDxgPWA2YDc +2gBtAA4AgAP4AG4AbwP5AGgD+gBxA6kD/AP9A/4D/wQABAEDngGjA6lZTlNjdkZsYWdzWU5TRG9jVmll +d1lOU0JHQ29sb3KA0oDwgO8QBIDVEQkAgNaAx4BUgNLSAA4ARQB6BAeAV6EEAYDW3xAUAG0BkwAOAH8E +CgEEAZQD3QGVAZYBlwBvAHAArgBoAZoBmwBxAZwBnQPuAOIEDAQNBA4BXgCOA+cBpQGLAacAdQQSAI4D +ngGtAa4D7gQXBBhcTlNIZWFkZXJWaWV3gNSA7hJCwAAAgNiAPgmA3IDXCYDHgFiA1IDfI0AxAAAAAAAA +WnszODAsIDI5MX3XAG0ADgBvAHAAaABxAYED6AQcAHUEHQOeA+gEAYDZgNuA2oDHgNmA1toAbQAOAIAD ++ABuAG8D+QBoA/oAcQOpA/wEJAP+BCUEAAQOA54BowOpgNKA8ID4gPeA2IDHgFSA0ll7MzgwLCAxN33S +ADoAOwQsBC2kBC0A9wD4AD9fEBFOU1RhYmxlSGVhZGVyVmlld9YAbQAOAIAAbwBoAHEDqQQwBDEEMgOe +A6mA0oDegN0T/////4AAAQCAx4DSXxAUe3stMjYsIDB9LCB7MTYsIDE3fX3SADoAOwQ3BDikBDgA9wD4 +AD9dX05TQ29ybmVyVmlld9IADgBFAHoEO4BXogQ8BD2A4IDo2gG4AA4BuQG7AbwBvQG+Ab8EPwGBAI4B +wQRCBEMERAEqBEUBxwCOBAFcTlNJc0VkaXRhYmxlCYBWgOEjQHEflgAAAACA5iNAYr8sAAAAAAmA1tcA +2QAOAQQA2wEFAN4BBwHKAcsESgRLAvIA4gRNgFKA44DigJiA5FhJZGVudGl0edMADgEeASgBIQEqAdaA +MdUADgEdAR4BHwEgASEBMACIBFQBJIAxgDSA5YAuXxAPaGVhZGVyVGV4dENvbG9y2QDZAA4BBADbAQUB +BgDeAIIBBwRYAQkBCgRbAIoEAQReAI4BDxJUIf5AgDWALYDngCqA1hJQQAQACYAyWVRleHQgQ2VsbNoB +uAAOAbkBuwG8Ab0BvgG/BD8BgQCOAcEEZQRmBGcBKgRoBGkAjgQBCYBWgOkjQFkUEAAAAACA7SNAWQAA +AAAAACNAYsAAAAAAAAmA1tcA2QAOAQQA2wEFAN4BBwEIAcsEbgRvAvIEcQRNgFKA64DqgJgSBAAAAIDk +XlRpbWUgUmVtYWluaW5n1QAOAR0BHgEfASABIQFeAIgEdwEkgDGAPoDsgC5baGVhZGVyQ29sb3LYANkA +DgEEANsBBQEGAN4BBwR7AQkBCgRbAIoEAQSBAQ8SRAH+QIA1gC2A54AqgNYSREAEAIAy0gA6ADsEhAGB +pQGBAPYA9wD4AD9fEBV7ezEsIDE3fSwgezM4MCwgMjkxfX3SADoAOwSHBIikBIgA9wD4AD9aTlNDbGlw +Vmlld9kAbQGSAA4AgABvAGgBmQBxBIoDqQOpBI0EjgQyA54EkAOpBJJZTlNQZXJjZW50gNKA0oD0gPKA +x4DzgNIjP+79TiAAAABfEBZ7ey0zMCwgMTd9LCB7MTUsIDI4NX19XF9kb1Njcm9sbGVyOtIAOgA7BJYE +l6UElwD2APcA+AA/Wk5TU2Nyb2xsZXLaAG0BkgAOAIAD3ABvAGgBmQBxBIoDqQOpBI0EnAGmBDIDngSQ +A6kEoIDSgNKA9ID2gMeA84DSIz/s7jJgAAAAXxAVe3sxLCAtMzB9LCB7MzYyLCAxNX190gAOAEUAegSk +gFehBA6A2F8QE3t7MSwgMH0sIHszODAsIDE3fX1fEBZ7ezE3LCA2MH0sIHszODIsIDMwOX190gA6ADsE +qQSqpASqAPcA+AA/XE5TU2Nyb2xsVmlld9gAbQAOAIAArABvAK4AaABxA40CDgSuBK8CEQCOA54DjYDG +gGiA/ID9CYDHgMZfEBV7ezMyMCwgMTJ9LCB7ODcsIDMyfX3dANkADgIYAhkCGgIbAhwA2wEFAQYCHQDe +Ah4BCAIfAFoCIQGmAIoEuAS5AIoDqgInAigCKYBngDuAKoD/gP6AKoD7VkNob29zZVEN2ABtAA4AgACs +AG8ArgBoAHEDjQIOBMEEwgIRAI4DngONgMaAaIEBAYEBAgmAx4DGXxAVe3syMzgsIDEyfSwgezgyLCAz +Mn193QDZAA4CGAIZAhoCGwIcANsBBQEGAh0A3gIeAQgCHwBaAiEBpgCKBMsEzACKA6sCJwIoAimAZ4A7 +gCqBAQSBAQOAKoEBAFZDYW5jZWxRG9gAbQAOAIAArABvAK4AaABxA40A0ATUBNUE1gCOA54DjYDGgDaB +AQaBAQcRAQ4JgMeAxl8QFnt7ODQsIDQxNH0sIHszMTgsIDIxfX3YANkADgEEANsBBQEGAN4BBwEIAQkB +CgTeAIoDrAThAQ+ANYAtgQEIgCqBAQUSAEAAAIAyXxAxTWFpbCByZXF1aXJlcyB0aGF0IHlvdSBjaG9v +c2UgYSBLZXJiZXJvcyBJZGVudGl0edgAbQAOAIAArABvAK4AaABxA40CDgTnBOgCOgCOA54DjYDGgGiB +AQqBAQsJgMeAxl8QFHt7MTcsIDMwfSwgezIzLCAyMn193gDZAA4CGAJCAhkCGgIbAhwA2wEFAQYCHQDe +Ah4BCAIfAFoE8AIhAIgE8QTyAFoC8gOtAicCKAT2gGeAO4EBDIEBDoEBEIA7gJiBAQkSCERA/9MADgAy +AOcA6ADpBPqAI4AhgQENU0FkZNMADgAyAOcA6ADpBP+AI4AhgQEPW0FkZF9QcmVzc2Vk0gAOADYANwFb +gATYAG0ADgCAAKwAbwCuAGgAcQONAg4FBgUHAjoAjgOeA42AxoBogQESgQETCYDHgMZfEBR7ezM5LCAz +MH0sIHsyMywgMjJ9fd4A2QAOAhgCQgIZAhoCGwIcANsBBQEGAh0A3gIeAQgCHwBaBQ8CIQCIBRAFEQBa +AvIDrgInAigE9oBngDuBARSBARaBARiAO4CYgQER0wAOADIA5wDoAOkFGIAjgCGBARVWUmVtb3Zl0wAO +ADIA5wDoAOkFHYAjgCGBARdeUmVtb3ZlX1ByZXNzZWTSAA4ANgA3AVuABFp7NDE5LCA0NjV9WnsyMTMs +IDEyOX1fEBp7My40MDI4MmUrMzgsIDMuNDAyODJlKzM4fdIADgBFAHoFJoBXrxAlBScFKAUpBSoFKwUs +BS0FLgUvBTAFMQUyBTMFNAU1BTYFNwU4BTkFOgU7BTwFPQU+BT8FQAVBBUIFQwVEBUUFRgVHBUgFSQVK +BUuBAR+BASqBATyBAUOBAUmBAU2BAVKBAVSBAVmBAWiBAWqBAWuBAXGBAXOBAXiBAXmBAX2BAX+BAYGB +AYaBAYuBAY+BAZOBAZiBAZyBAaGBAaaBAaeBAayBAa2BAbKBAbOBAbyBAc+BAdOBAdeBAdvUAA4FTQVO +AmQFTwVQBVEFUl1OU0Rlc3RpbmF0aW9uWE5TU291cmNlgQEpgQEngQEggQEo1AAOBVQFVQVWBVcFWAVZ +BVpfEA9fTlNNYW5hZ2VkUHJveHlfEBFOU09iamVjdENsYXNzTmFtZV5OU0RlY2xhcmVkS2V5c4EBJoEB +JIEBI4EBIdIADgBFAHoFXYBXoQVegQEiW2NhY2hlc0FycmF5XxAXS2VyYmVyb3NDYWNoZUNvbGxlY3Rp +b27RAA4FYoEBJdIAOgA7BWQFVKIFVAA/0gA6ADsFZgVnowVnBWgAP18QEk5TT2JqZWN0Q29udHJvbGxl +clxOU0NvbnRyb2xsZXLSAA4AMgAzBVmABYEBI1djb250ZW500gA6ADsFbgVvowVvBXAAP18QFE5TTmli +T3V0bGV0Q29ubmVjdG9yXk5TTmliQ29ubmVjdG9y1AAOBU0FTgJkBU8FcwKGBXWBASmBASuAmYEBO9QA +DgV3ARMFeAV5BXoAWgV8V05TVGl0bGVbTlNNZW51SXRlbXOBATqBASyAO4EBLVRNZW510gAOAEUAegWA +gFekBYEFggWDBYSBAS6BATWBATeBATjYAA4FdwWGBYcFiAWJBYoFiwWMBY0FjgBaBZAFkQWSBXNfEBFO +U0tleUVxdWl2TW9kTWFza1pOU0tleUVxdWl2XU5TTW5lbW9uaWNMb2NZTlNPbkltYWdlXE5TTWl4ZWRJ +bWFnZVZOU01lbnWBATSBAS8SABAAAIA7En////+BATCBATKBAStvEA8AVABpAGMAawBlAHQAIABPAHAA +dABpAG8AbgBzICbTAA4AMgDnAOgA6QWYgCOAIYEBMV8QD05TTWVudUNoZWNrbWFya9MADgAyAOcA6ADp +BZ2AI4AhgQEzXxAQTlNNZW51TWl4ZWRTdGF0ZdIAOgA7BaAFoaIFoQA/Wk5TTWVudUl0ZW3YAA4FdwWG +BYcFiAWJBYoFiwWMBaQFjgBaBZAFkQWSBXOBATSBATaAO4EBMIEBMoEBK28QEABDAGgAYQBuAGcAZQAg +AFAAYQBzAHMAdwBvAHIAZCAm2gAOBXcFhgWrBYcFrAWIBYkFigWLBYwAWgWOAI4AWgCOBZAFkQWSBXNd +TlNJc1NlcGFyYXRvclxOU0lzRGlzYWJsZWSBATSAOwmAOwmBATCBATKBASvYAA4FdwWGBYcFiAWJBYoF +iwWMBbcFjgBaBZAFkQWSBXOBATSBATmAO4EBMIEBMoEBK18QEUFib3V0IEtlcmJlcm9zLi4u0gA6ADsF +vgWLogWLAD9UbWVuddMADgVOAmQFwQXCBcOBAUKBAT2BAUHYAA4FdwWGBYcFiAWJBYoFiwWMBcYFjgXH +BZAFkQWSBcqBATSBAT+BAUCBATCBATKBAT7UAA4FdwETBXgFeQXNBc4Fz4EBOoECAYECBIECAlVDbG9z +ZVF3XXBlcmZvcm1DbG9zZTrSADoAOwXUBdWjBdUFcAA/XxAVTlNOaWJDb250cm9sQ29ubmVjdG9y0wAO +BU4CZAXBBdgF2YEBQoEBRIEBSNgADgV3BYYFhwWIBYkFigWLBYwF3AWOBd0FkAWRBZIF4IEBNIEBRoEB +R4EBMIEBMoEBRdMADgV3BXgFeQXjBeSBATqBAfqBAfxaU2VsZWN0IEFsbFFhWnNlbGVjdEFsbDrTAA4F +TgJkBcEF6gXrgQFCgQFKgQFM2AAOBXcFhgWHBYgFiQWKBYsFjAXuBY4AWgWQBZEFkgXKgQE0gQFLgDuB +ATCBATKBAT5UWm9vbVxwZXJmb3JtWm9vbTrUAA4FTQVOAmQFwQAfBfgF+YEBQoACgQFOgQFR1wAOBXcF +hwWIBYkFigWLBYwF/ABaBZAFkQWSBgCBATSBAVCAO4EBMIEBMoEBT9QADgV3ARMFeAV5BgMGBAYFgQE6 +gQHkgQH0gQHlXxATQWJvdXQgS2VyYmVyb3NBZ2VudF8QHW9yZGVyRnJvbnRTdGFuZGFyZEFib3V0UGFu +ZWw61AAOBU0FTgJkBU8FcwCpBgyBASmBASuAaYEBU1lwb3B1cE1lbnXTAA4FTgJkBcEGEAYRgQFCgQFV +gQFY2AAOBXcFhgWHBYgFiQWKBYsFjAYUBhUGFgWQBZEFkgXggQE0gQFWEgAYAACBAVeBATCBATKBAUVf +EBVQYXN0ZSBhbmQgTWF0Y2ggU3R5bGVRVl8QEXBhc3RlQXNQbGFpblRleHQ61AAOBU0FTgJkBU8GHwYg +BVKBASmBAWGBAVqBASjaBiMADgYkBVQAqwVVBVYGJQYmBicAjgYpAI4GKwCOBi0GLgCOAI4Ajl8QGk5T +RmlsdGVyUmVzdHJpY3RzSW5zZXJ0aW9uXxAUTlNQcmVzZXJ2ZXNTZWxlY3Rpb25fECJOU0NsZWFyc0Zp +bHRlclByZWRpY2F0ZU9uSW5zZXJ0aW9uXxAYTlNTZWxlY3RzSW5zZXJ0ZWRPYmplY3RzXxAWTlNBdm9p +ZHNFbXB0eVNlbGVjdGlvbgmBAWAJgQFfCYEBXoEBWwkJCdIADgBFAHoGNIBXogY1BjaBAVyBAV1fEBZz +ZXJ2aWNlUHJpbmNpcGFsU3RyaW5nXxAYc2hvcnRUaW1lUmVtYWluaW5nU3RyaW5nXxASS2VyYmVyb3ND +cmVkZW50aWFs0QAOBWKBASXSADoAOwY9Bj6kBj4FZwVoAD9fEBFOU0FycmF5Q29udHJvbGxlctkGIwAO +BiQFVAVVBVYGJQYmBicAjgYpAI4GQwZEBkUAjgCOAI4JgQFgCYEBZ4EBZoEBYgkJCdIADgBFAHoGS4BX +pAZMBk0GNgZPgQFjgQFkgQFdgQFlXxAQY3JlZGVudGlhbHNBcnJheV8QD3ByaW5jaXBhbFN0cmluZ18Q +E3RpbWVSZW1haW5pbmdTdHJpbmddS2VyYmVyb3NDYWNoZdEADgVigQEl1AAOBU0FTgJkBcEASQTCBlqB +AUKAwYEBAoEBadQADgVNBU4CZAVPBXMAqQV1gQEpgQErgGmBATvTAA4FTgJkBcEGYgZjgQFCgQFsgQFw +2AAOBXcFhgWHBYgFiQWKBYsFjAZmBY4GZwWQBZEFkgZqgQE0gQFugQFvgQEwgQEygQFt0wAOBXcFeAV5 +Bm0GboEBOoEB4oEB418QEktlcmJlcm9zQWdlbnQgSGVscFE/WXNob3dIZWxwOtQADgVNBU4CZAVPAKQA +pgZ2gQEpgDeARIEBcltuZXh0S2V5Vmlld9QADgVNBU4CZAXBAB8GewZ8gQFCgAKBAXSBAXfYAA4FdwWG +BYcFiAWJBYoFiwWMBn8GFQaABZAFkQWSBgCBATSBAXWBAXaBATCBATKBAU9bSGlkZSBPdGhlcnNRaF8Q +FmhpZGVPdGhlckFwcGxpY2F0aW9uczrUAA4FTQVOAmQFTwCmAKQGdoEBKYBEgDeBAXLTAA4FTgJkBcEG +jgaPgQFCgQF6gQF82AAOBXcFhgWHBYgFiQWKBYsFjAaSBY4AWgWQBZEFkgXKgQE0gQF7gDuBATCBATKB +AT5fEBJCcmluZyBBbGwgdG8gRnJvbnRfEA9hcnJhbmdlSW5Gcm9udDrUAA4FTQVOAmQFwQB8AyMGnYEB +QoAQgLSBAX5fEBpzZWxlY3RQcmV2aW91c1RhYlZpZXdJdGVtOtQADgVNBU4CZAXBAHwAqAajgQFCgBCA +YoEBgF8QFnNlbGVjdE5leHRUYWJWaWV3SXRlbTrTAA4FTgJkBcEGpwaogQFCgQGCgQGF2AAOBXcFhgWH +BYgFiQWKBYsFjAarBY4GrAWQBZEFkgXggQE0gQGDgQGEgQEwgQEygQFFVVBhc3RlUXZWcGFzdGU60wAO +BU4CZAXBBrUGtoEBQoEBh4EBitgADgV3BYYFhwWIBYkFigWLBYwGuQWOBroFkAWRBZIF4IEBNIEBiIEB +iYEBMIEBMoEBRVRDb3B5UWNVY29weTrTAA4FTgJkBcEGwwbEgQFCgQGMgQGO2AAOBXcFhgWHBYgFiQWK +BYsFjAbHBY4AWgWQBZEFkgXggQE0gQGNgDuBATCBATKBAUVWRGVsZXRlV2RlbGV0ZTrUAA4FTQVOAmQF +wQAfBtEG0oEBQoACgQGQgQGS2AAOBXcFhgWHBYgFiQWKBYsFjAbVBY4GgAWQBZEFkgYAgQE0gQGRgQF2 +gQEwgQEygQFPXxASSGlkZSBLZXJiZXJvc0FnZW50VWhpZGU60wAOBU4CZAXBBt4G34EBQoEBlIEBl9gA +DgV3BYYFhwWIBYkFigWLBYwG4gWOBuMFkAWRBZIF4IEBNIEBlYEBloEBMIEBMoEBRVNDdXRReFRjdXQ6 +1AAOBU0FTgJkBcEAHwbtBu6BAUKAAoEBmYEBm9gADgV3BYYFhwWIBYkFigWLBYwG8QWOAFoFkAWRBZIG +AIEBNIEBmoA7gQEwgQEygQFPWFNob3cgQWxsXxAWdW5oaWRlQWxsQXBwbGljYXRpb25zOtQADgVNBU4C +ZAXBAB8G+wb8gQFCgAKBAZ2BAaDYAA4FdwWGBYcFiAWJBYoFiwWMBv8FjgcABZAFkQWSBgCBATSBAZ6B +AZ+BATCBATKBAU9fEBJRdWl0IEtlcmJlcm9zQWdlbnRRcVp0ZXJtaW5hdGU60wAOBU4CZAXBBwkHCoEB +QoEBooEBpdgADgV3BYYFhwWIBYkFigWLBYwHDQWOBw4FkAWRBZIF4IEBNIEBo4EBpIEBMIEBMoEBRVRV +bmRvUXpVdW5kbzrUAA4FTQVOAmQFTwVzAoYGDIEBKYEBK4CZgQFT0wAOBU4CZAXBBxwHHYEBQoEBqIEB +q9gADgV3BYYFhwWIBYkFigWLBYwHIAWOByEFkAWRBZIF4IEBNIEBqYEBqoEBMIEBMoEBRVRSZWRvUVpV +cmVkbzrUAA4FTQVOAmQFwQB8AoIGo4EBQoAQgIeBAYDTAA4FTgJkBcEHLwcwgQFCgQGugQGx2AAOBXcF +hgWHBYgFiQWKBYsFjAczBY4HNAWQBZEFkgXKgQE0gQGvgQGwgQEwgQEygQE+WE1pbmltaXplUW1fEBNw +ZXJmb3JtTWluaWF0dXJpemU61AAOBU0FTgJkBcEAfAKDBp2BAUKAEICLgQF+2AAOBU0HQQdCBU4CZAdD +B0QHRQVRB0cHSAYfB0oHSwBgWU5TS2V5UGF0aFlOU0JpbmRpbmdZTlNPcHRpb25zXxAcTlNOaWJCaW5k +aW5nQ29ubmVjdG9yVmVyc2lvboEBu4EBIIEBtoEBtYEBYYEBtIEBt18QI2NvbnRlbnRBcnJheTogc2Vs +ZWN0aW9uLmNhY2hlc0FycmF5XGNvbnRlbnRBcnJheV8QFXNlbGVjdGlvbi5jYWNoZXNBcnJhedMADgdQ +AEUHUQdSB1RXTlMua2V5c4EBuqEHU4EBuKEHVYEBuV8QG05TQ29uZGl0aW9uYWxseVNldHNFZGl0YWJs +ZQjSADoAOwdZB1qiB1oAP1xOU0RpY3Rpb25hcnnSADoAOwdcB12jB10FcAA/XxAVTlNOaWJCaW5kaW5n +Q29ubmVjdG9y2AAOBU0HQQdCBU4CZAdDB0QHRQYfB2EHYgQ9B2QHZQBggQG7gQFhgQG/gQG+gOiBAb2B +AcBfEC92YWx1ZTogYXJyYW5nZWRPYmplY3RzLnNob3J0VGltZVJlbWFpbmluZ1N0cmluZ1V2YWx1ZV8Q +KGFycmFuZ2VkT2JqZWN0cy5zaG9ydFRpbWVSZW1haW5pbmdTdHJpbmfTAA4HUABFB1EHawd4gQG6rAds +B20HbgdvB3AHcQdyB3MHdAd1B3YHd4EBwYEBwoEBw4EBxIEBxYEBxoEBx4EByIEByYEByoEBy4EBzKwH +VQdVB1UHVQd9B30HVQdVB30HfQdVB4SBAbmBAbmBAbmBAbmBAc2BAc2BAbmBAbmBAc2BAc2BAbmBAc5f +EBpOU0NvbmRpdGlvbmFsbHlTZXRzRW5hYmxlZF8QHE5TUmFpc2VzRm9yTm90QXBwbGljYWJsZUtleXNf +EBZOU1ZhbGlkYXRlc0ltbWVkaWF0ZWx5XxAmTlNBbHdheXNQcmVzZW50c0FwcGxpY2F0aW9uTW9kYWxB +bGVydHNfEBtOU011bHRpcGxlVmFsdWVzUGxhY2Vob2xkZXJfEBFOU051bGxQbGFjZWhvbGRlcl8QGk5T +Q29udGludW91c2x5VXBkYXRlc1ZhbHVlXxAXTlNDcmVhdGVzU29ydERlc2NyaXB0b3JfEBpOU05vdEFw +cGxpY2FibGVQbGFjZWhvbGRlcl8QGE5TTm9TZWxlY3Rpb25QbGFjZWhvbGRlcl8QJk5TQWxsb3dzRWRp +dGluZ011bHRpcGxlVmFsdWVzU2VsZWN0aW9uCdgADgVNB0EHQgVOAmQHQwdEB0UGHweUB0gGIAeXB5gA +YIEBu4EBYYEB0YEBtYEBWoEB0IEB0l8QKGNvbnRlbnRBcnJheTogc2VsZWN0aW9uLmNyZWRlbnRpYWxz +QXJyYXlfEBpzZWxlY3Rpb24uY3JlZGVudGlhbHNBcnJhedMADgdQAEUHUQedB5+BAbqhB1OBAbihB1WB +AbnYAA4FTQdBB0IFTgJkB0MHRAdFBh8HpAdiBDwHpweoAGCBAbuBAWGBAdWBAb6A4IEB1IEB1l8QJnZh +bHVlOiBhcnJhbmdlZE9iamVjdHMucHJpbmNpcGFsU3RyaW5nXxAfYXJyYW5nZWRPYmplY3RzLnByaW5j +aXBhbFN0cmluZ9MADgdQAEUHUQetB7qBAbqsB2wHbQduB28HcAdxB3IHcwd0B3UHdgd3gQHBgQHCgQHD +gQHEgQHFgQHGgQHHgQHIgQHJgQHKgQHLgQHMrAdVB1UHVQdVB30HfQdVB1UHfQd9B1UHhIEBuYEBuYEB +uYEBuYEBzYEBzYEBuYEBuYEBzYEBzYEBuYEBztQADgVNB8gHyQfKA60HzAfNWE5TTWFya2VyVk5TRmls +ZYEB2oEBCYEB2YEB2F8QEE5TVG9vbFRpcEhlbHBLZXlfEBFDcmVhdGUgYW4gYWNjb3VudNIAOgA7B9EH +0qIH0gA/XxARTlNJQkhlbHBDb25uZWN0b3LUAA4FTQfIB8kHygOuB9YHzYEB2oEBEYEB3IEB2F8QEVJl +bW92ZSBhbiBhY2NvdW500gAOAEUH2gfbgQIIrxB4BYIE6AfeBfgGAAZqBtEEAQKCAuoF6gLHB+gH6QOp +B+sAVwK2AJEChgQ8AoEGpwWDA2AGIAOuAUwDIAaOB/oHHAD+B/0GtQTCCAADrQbeAKkC2gREA6wDKAMh +AKgAngYQCAwDIwCVBNUAswBJAUIIEwPtCBUF4AbtA6oFBwKFBXMDqAWBBVEFygNRBcICowXYAKMDdAbD +BGcCjACnBwkDPAQOAXkApQKDAmAD4wgyBK8DEgONBh8G+wMiCDkDqwZ7AjkDzAg+AKYGYgcvBD0DHwIQ +AnMASAKEA6cDtAB8CEsCYQVQAKQA0gKAAwAIUgWEgQE1gQELgQHegQFOgQFPgQFtgQGQgNaAh4CWgQFK +gI2BAeuBAe6A0oEB34ANgImAdoCZgOCAgoEBgoEBN4CxgQFagQERgGCAp4EBeoECA4EBqIBCgQHzgQGH +gQECgQHygQEJgQGUgGmAkoDmgQEFgKSAq4BigBSBAVWBAeaAtIASgQEHgB+AwYA6gQH5gPGBAfeBAUWB +AZmA+4EBE4CUgQErgM6BAS6BASCBAT6ArYEBPYCGgQFEgB6AtoEBjIDtgIGAOYEBooCqgNiARoAngIuA +eoD1gQH9gP2AoIDGgQFhgQGdgK+BAeqBAQCBAXSAbYDQgQHngESBAWyBAa6A6ICigGSAfIAIgJCAyYDN +gBCBAf6AnoEBJ4A3gCiAfoCbgQIGgQE42gAOCFUFdwWGBYcFiAWJBYoFiwGZBYwGaghYBY4AWgWQBZEF +kgfrCF1ZTlNTdWJtZW51gQE0gQFtgQHggDuBATCBATKBAd+BAeHUAA4FdwETBXgFeQhgCGEIYoEBOoEB +9YECBYEB9lRIZWxwXnN1Ym1lbnVBY3Rpb2460gAOADYANwhjgATSAA4ARQB6CGmAV6EGYoEBbNIADgBF +AHoIbYBXqwX4CAwIPgg5B+gIAAbRBnsG7Qf9BvuBAU6BAeaBAeeBAeqBAeuBAfKBAZCBAXSBAZmBAfOB +AZ3aAA4FdwWGBasFhwWsBYgFiQWKBYsFjABaBY4AjgBaAI4FkAWRBZIGAIEBNIA7CYA7CYEBMIEBMoEB +T9gADgV3BYYFhwWIBYkFigWLBYwIhAWOCIUFkAWRBZIGAIEBNIEB6IEB6YEBMIEBMoEBT2wAUAByAGUA +ZgBlAHIAZQBuAGMAZQBzICZRLNoADgV3BYYFqwWHBawFiAWJBYoFiwWMAFoFjgCOAFoAjgWQBZEFkgYA +gQE0gDsJgDsJgQEwgQEygQFP2gAOCFUFdwWGBYcFiAWJBYoFiwGZBYwH6QiXBY4AWgWQBZEFkgYACJyB +ATSBAe6BAeyAO4EBMIEBMoEBT4EB7VhTZXJ2aWNlc9QADgV3ARMFeAV5CKAIoQiigQE6gQHvgQHxgQHw +0gAOADYANwidgATSAA4ARQB6CKeAV6BfEA9fTlNTZXJ2aWNlc01lbnXaAA4FdwWGBasFhwWsBYgFiQWK +BYsFjABaBY4AjgBaAI4FkAWRBZIGAIEBNIA7CYA7CYEBMIEBMoEBT9oADgV3BYYFqwWHBawFiAWJBYoF +iwWMAFoFjgCOAFoAjgWQBZEFkgYAgQE0gDsJgDsJgQEwgQEygQFPXF9OU0FwcGxlTWVudVhNYWluTWVu +ddIADgBFAHoIv4BXpAgVCBMISwfegQH3gQH5gQH+gQHe2gAOCFUFdwWGBYcFiAWJBYoFiwGZBYwGAAYD +BY4AWgWQBZEFkgfrCMyBATSBAU+BAeSAO4EBMIEBMoEB34EB+NoADghVBXcFhgWHBYgFiQWKBYsBmQWM +BeAF4wWOAFoFkAWRBZIH6wjVgQE0gQFFgQH6gDuBATCBATKBAd+BAftURWRpdNIADgBFAHoI2YBXqQcJ +BxwIMgbeBrUGpwYQBsMF2IEBooEBqIEB/YEBlIEBh4EBgoEBVYEBjIEBRNoADgV3BYYFqwWHBawFiAWJ +BYoFiwWMAFoFjgCOAFoAjgWQBZEFkgXggQE0gDsJgDsJgQEwgQEygQFF2gAOCFUFdwWGBYcFiAWJBYoF +iwGZBYwFygjvBY4AWgWQBZEFkgfrCPSBATSBAT6BAf+AO4EBMIEBMoEB34ECAFZXaW5kb3fSAA4ANgA3 +CPWABNIADgBFAHoI+oBXpQXCBy8F6gf6Bo6BAT2BAa6BAUqBAgOBAXraAA4FdwWGBasFhwWsBYgFiQWK +BYsFjABaBY4AjgBaAI4FkAWRBZIFyoEBNIA7CYA7CYEBMIEBMoEBPl5fTlNXaW5kb3dzTWVudVtfTlNN +YWluTWVuddIADgAyADMJDYAFgQIH0gA6ADsJDwHvogHvAD/SAA4ARQfaCRKBAgivEHgFcwOtB+sGAAgV +B94GAAOpAnMChQXKAoMGAAfoA40AHwBIAoIAfAJzBAECcwXgBXMDIgAfA40ApwMSBcoFygXgAKUGAAXg +A6sGAAONBeAAlQKEBDwDjQMfAxIAlQCVBeAGAAMSAJEDrACeAB8ApAfrA6kH6wgTBgADjQOuAnMAHwON +BXMAHwhLAyEFygKBBeAAlQMjBeAEPQKAAJUF4AMgA6kApgCVAnMAfAOpBeADqgJhAEkAHwYAAxIGAAON +BgAAqQOoBgAAlQZqBcoEAQMSAKgCYAAfAnMDjQOnAFcH6wB8AB8AlQCjAnMChgAfBXOBASuBAQmBAd+B +AU+BAfeBAd6BAU+A0oB8gJSBAT6Ai4EBT4EB64DGgAKACICHgBCAfIDWgHyBAUWBASuAr4ACgMaAOYCg +gQE+gQE+gQFFgCeBAU+BAUWBAQCBAU+AxoEBRYASgJCA4IDGgKKAoIASgBKBAUWBAU+AoIB2gQEFgBSA +AoA3gQHfgNKBAd+BAfmBAU+AxoEBEYB8gAKAxoEBK4ACgQH+gKuBAT6AgoEBRYASgLSBAUWA6IB+gBKB +AUWAp4DSgESAEoB8gBCA0oEBRYD7gJ6AwYACgQFPgKCBAU+AxoEBT4BpgM6BAU+AEoEBbYEBPoDWgKCA +YoB6gAKAfIDGgMmADYEB34AQgAKAEoAegHyAmYACgQEr0gAOAEUH2gmNgQIIrxB5BYIE6AfeBfgGagYA +BtEEAQKCAuoF6gLHB+gH6QOpB+sAVwK2AJEChgQ8AoEGpwWDA2AGIAOuAUwDIAaOB/oHHAD+B/0GtQTC +A60IAAbeAKkC2gOsBEQAqACeAyEDKAYQCAwAlQMjBNUAswBJA+0IEwFCCBUF4AbtA6oFBwKFBXMFgQOo +BVEFygNRBcICowXYAKMDdAbDBGcCjACnBwkDPAQOAKUCgwF5AmAD4wgyBK8DEgYfA40G+wMiCDkDqwZ7 +AjkDzAg+AKYGYgcvAB8EPQJzAoQASAMfAhADpwB8A7QISwJhBVAApADSAoADAAhSBYSBATWBAQuBAd6B +AU6BAW2BAU+BAZCA1oCHgJaBAUqAjYEB64EB7oDSgQHfgA2AiYB2gJmA4ICCgQGCgQE3gLGBAVqBARGA +YICngQF6gQIDgQGogEKBAfOBAYeBAQKBAQmBAfKBAZSAaYCSgQEFgOaAYoAUgKuApIEBVYEB5oASgLSB +AQeAH4DBgPGBAfmAOoEB94EBRYEBmYD7gQETgJSBASuBAS6AzoEBIIEBPoCtgQE9gIaBAUSAHoC2gQGM +gO2AgYA5gQGigKqA2IAngIuARoB6gPWBAf2A/YCggQFhgMaBAZ2Ar4EB6oEBAIEBdIBtgNCBAeeARIEB +bIEBroACgOiAfICQgAiAooBkgMmAEIDNgQH+gJ6BASeAN4AogH6Am4ECBoEBONIADgBFB9oKCYECCK8Q +eQoKCgsKDAoNCg4KDwoQChEKEgoTChQKFQoWChcKGAhgChoKGwocCh0KHgofCiAKIQoiCiMKJAolCiYK +JwooCikKKgorCiwKLQouCi8KMAoxCjIKMwo0CjUKNgo3CjgKOQo6CjsKPAo9Cj4I7wpACkEKQgpDCkQK +RQpGCkcKSApJCkoKSwpMCk0KTgpPClAKUQpSClMKVApVClYKVwpYClkKWgpbClwKXQpeCl8KYAphCmIK +YwpkCmUKZgpnCmgKaQpqCmsKbAptCm4KbwpwCnEKcgpzCnQKdQp2CncKeAp5CnoKewVZCn0Kfgp/CoAK +gQqCgQIMgQINgQIOgQIPgQIQgQIRgQISgQITgQIUgQIVgQIWgQIXgQIYgQIZgQIagQH1gQIbgQIcgQId +gQIegQIfgQIggQIhgQIigQIjgQIkgQIlgQImgQIngQIogQIpgQIqgQIrgQIsgQItgQIugQIvgQIwgQIx +gQIygQIzgQI0gQI1gQI2gQI3gQI4gQI5gQI6gQI7gQI8gQI9gQI+gQI/gQH/gQJAgQJBgQJCgQJDgQJE +gQJFgQJGgQJHgQJIgQJJgQJKgQJLgQJMgQJNgQJOgQJPgQJQgQJRgQJSgQJTgQJUgQJVgQJWgQJXgQJY +gQJZgQJagQJbgQJcgQJdgQJegQJfgQJggQJhgQJigQJjgQJkgQJlgQJmgQJngQJogQJpgQJqgQJrgQJs +gQJtgQJugQJvgQJwgQJxgQJygQJzgQJ0gQJ1gQJ2gQJ3gQJ4gQJ5gQJ6gQJ7gQEjgQJ8gQJ9gQJ+gQJ/ +gQKAgQKBbxAcAE0AZQBuAHUAIABJAHQAZQBtACAAKABDAGgAYQBuAGcAZQAgAFAAYQBzAHMAdwBvAHIA +ZCAmAClfEBFCdXR0b24gQ2VsbCAoQWRkKV8QEE1lbnUgSXRlbSAoSGVscClfEB9NZW51IEl0ZW0gKEFi +b3V0IEtlcmJlcm9zQWdlbnQpW01lbnUgKEhlbHApXxAUTWVudSAoS2VyYmVyb3NBZ2VudClfEB5NZW51 +IEl0ZW0gKEhpZGUgS2VyYmVyb3NBZ2VudClfECVUYWJsZSBWaWV3IChJZGVudGl0eSwgVGltZSBSZW1h +aW5pbmcpXxAYUHVzaCBCdXR0b24gKENvbnRpbnVlKS0xXxBCVGV4dCBGaWVsZCBDZWxsIChNYWlsIHdh +bnRzIHRvIGNvbm5lY3QgdG8gdGhlIGFjY291bnQgbHhzQG1pdC5lZHUpXxAQTWVudSBJdGVtIChab29t +KV8QFUJ1dHRvbiBDZWxsIChHbyBCYWNrKV8QFE1lbnUgSXRlbSAoU2VydmljZXMpXxAPTWVudSAoU2Vy +dmljZXMpW1Njcm9sbCBWaWV3XkNvbnRlbnQgVmlldy0xXxAYQnV0dG9uIENlbGwgKENvbnRpbnVlKS0x +XxAfVGFiIFZpZXcgSXRlbSAoU2VsZWN0IElkZW50aXR5KV8QFVBvcHVwIEJ1dHRvbiAoR2VhciktMV8Q +F1RhYmxlIENvbHVtbiAoSWRlbnRpdHkpXxARU2VjdXJlIFRleHQgRmllbGRfEBFNZW51IEl0ZW0gKFBh +c3RlKVlTZXBhcmF0b3JfEF1UZXh0IEZpZWxkIENlbGwgKENvbmdyYXR1bGF0aW9ucyEgWW91IGhhdmUg +YWNxdWlyZWQgS2VyYmVyb3MgdGlja2V0cyBmb3IgbHhzQEFUSEVOQS5NSVQuRURVLilfEBVDcmVkZW50 +aWFsc0NvbnRyb2xsZXJfEBZTcXVhcmUgQnV0dG9uIChSZW1vdmUpXxAYVGV4dCBGaWVsZCBDZWxsIChS +ZWFsbTopXxAcSW1hZ2UgVmlldyAoS2VyYmVyb3NBZ2VudCktM18QHk1lbnUgSXRlbSAoQnJpbmcgQWxs +IHRvIEZyb250KVtTZXBhcmF0b3ItNl8QEE1lbnUgSXRlbSAoUmVkbylfEBdUZXh0IEZpZWxkIENlbGwg +KE5hbWU6KVtTZXBhcmF0b3ItMl8QEE1lbnUgSXRlbSAoQ29weSlfEBRCdXR0b24gQ2VsbCAoQ2FuY2Vs +KV8QE1NxdWFyZSBCdXR0b24gKEFkZClbU2VwYXJhdG9yLTNfEA9NZW51IEl0ZW0gKEN1dClfEBNQb3B1 +cCBCdXR0b24gKEdlYXIpXxA1VGV4dCBGaWVsZCBDZWxsIChQbGVhc2UgZW50ZXIgeW91ciBLZXJiZXJv +cyBwYXNzd29yZClfED9TdGF0aWMgVGV4dCAoTWFpbCByZXF1aXJlcyB0aGF0IHlvdSBjaG9vc2UgYSBL +ZXJiZXJvcyBJZGVudGl0eSlfEBtUZXh0IEZpZWxkIENlbGwgKFRleHQgQ2VsbClfEBZQdXNoIEJ1dHRv +biAoQ29udGludWUpXxAcSW1hZ2UgVmlldyAoS2VyYmVyb3NBZ2VudCktMV8QGFN0YXRpYyBUZXh0IChD +b25jbHVzaW9uKV8QEkJ1dHRvbiBDZWxsIChEb25lKV8QIU1lbnUgSXRlbSAoUGFzdGUgYW5kIE1hdGNo +IFN0eWxlKVtTZXBhcmF0b3ItNF8QF1B1c2ggQnV0dG9uIChHbyBCYWNrKS0xXxBDVGV4dCBGaWVsZCBD +ZWxsIChNYWlsIHJlcXVpcmVzIHRoYXQgeW91IGNob29zZSBhIEtlcmJlcm9zIElkZW50aXR5KV8QHElt +YWdlIENlbGwgKEtlcmJlcm9zQWdlbnQpLTFfEBFWZXJ0aWNhbCBTY3JvbGxlcl8QEE1lbnUgSXRlbSAo +RWRpdClfEA9UZXh0IEZpZWxkIENlbGxfEBlNZW51IEl0ZW0gKEtlcmJlcm9zQWdlbnQpW01lbnUgKEVk +aXQpXxAUTWVudSBJdGVtIChTaG93IEFsbClfEBRQdXNoIEJ1dHRvbiAoQ2hvb3NlKV8QFEJ1dHRvbiBD +ZWxsIChSZW1vdmUpXxA+U3RhdGljIFRleHQgKE1haWwgd2FudHMgdG8gY29ubmVjdCB0byB0aGUgYWNj +b3VudCBseHNAbWl0LmVkdSlYR2Vhck1lbnVvEBsATQBlAG4AdQAgAEkAdABlAG0AIAAoAFQAaQBjAGsA +ZQB0ACAATwBwAHQAaQBvAG4AcyAmAClfEDNTdGF0aWMgVGV4dCAoQ2hlY2tpbmcgbWFpbCBmb3IgYWNj +b3VudCBseHNAbWl0LmVkdSlfEBlDYWNoZUNvbGxlY3Rpb25Db250cm9sbGVyXU1lbnUgKFdpbmRvdylf +EBxUZXh0IEZpZWxkIENlbGwgKENvbmNsdXNpb24pXxARTWVudSBJdGVtIChDbG9zZSlfEBFUZXh0IEZp +ZWxkIENlbGwtMV8QFk1lbnUgSXRlbSAoU2VsZWN0IEFsbClfEDFTdGF0aWMgVGV4dCAoUGxlYXNlIGVu +dGVyIHlvdXIgS2VyYmVyb3MgaWRlbnRpdHkpXxAXQnV0dG9uIENlbGwgKEdvIEJhY2spLTFfEBJNZW51 +IEl0ZW0gKERlbGV0ZSlfEB1UZXh0IEZpZWxkIENlbGwgKFRleHQgQ2VsbCktMV8QHEltYWdlIENlbGwg +KEtlcmJlcm9zQWdlbnQpLTJfEBRTdGF0aWMgVGV4dCAoUmVhbG06KV8QEE1lbnUgSXRlbSAoVW5kbylf +EBxJbWFnZSBDZWxsIChLZXJiZXJvc0FnZW50KS0zXxARVGFibGUgSGVhZGVyIFZpZXdfEBNTdGF0aWMg +VGV4dCAoTmFtZTopXxAVUHVzaCBCdXR0b24gKEdvIEJhY2spXkNvbWJvIEJveCBDZWxsXxAqVGFiIFZp +ZXcgSXRlbSAoQXV0aGVudGljYXRpb24gSW5mb3JtYXRpb24pXxATSG9yaXpvbnRhbCBTY3JvbGxlcltT +ZXBhcmF0b3ItMV8QFEJ1dHRvbiBDZWxsIChDaG9vc2UpVlZpZXctMl8QEENhY2hlc0NvbnRyb2xsZXJc +Q29udGVudCBWaWV3XxAeTWVudSBJdGVtIChRdWl0IEtlcmJlcm9zQWdlbnQpXxBZU3RhdGljIFRleHQg +KENvbmdyYXR1bGF0aW9ucyEgWW91IGhhdmUgYWNxdWlyZWQgS2VyYmVyb3MgdGlja2V0cyBmb3IgbHhz +QEFUSEVOQS5NSVQuRURVLilbU2VwYXJhdG9yLTVfEBRQdXNoIEJ1dHRvbiAoQ2FuY2VsKV8QF01lbnUg +SXRlbSAoSGlkZSBPdGhlcnMpXxASQnV0dG9uIENlbGwgKEdlYXIpXxA3VGV4dCBGaWVsZCBDZWxsIChD +aGVja2luZyBtYWlsIGZvciBhY2NvdW50IGx4c0BtaXQuZWR1KW8QGABNAGUAbgB1ACAASQB0AGUAbQAg +ACgAUAByAGUAZgBlAHIAZQBuAGMAZQBzICYAKVlDb21ibyBCb3hfEB5NZW51IEl0ZW0gKEtlcmJlcm9z +QWdlbnQgSGVscClfEBRNZW51IEl0ZW0gKE1pbmltaXplKVxGaWxlJ3MgT3duZXJfEB1UYWJsZSBDb2x1 +bW4gKFRpbWUgUmVtYWluaW5nKVZWaWV3LTFfEDFTdGF0aWMgVGV4dCAoUGxlYXNlIGVudGVyIHlvdXIg +S2VyYmVyb3MgcGFzc3dvcmQpV1dpbmRvdzFfEBJQdXNoIEJ1dHRvbiAoRG9uZSlfEBZCdXR0b24gQ2Vs +bCAoQ29udGludWUpXxAaSW1hZ2UgVmlldyAoS2VyYmVyb3NBZ2VudClfEEhObyBTaGFkb3cgVGFiIFZp +ZXcgKFNlbGVjdCBJZGVudGl0eSwgQXV0aGVudGljYXRpb24gSW5mb3JtYXRpb24sIFJlc3VsdClfEBpJ +bWFnZSBDZWxsIChLZXJiZXJvc0FnZW50KV8QEk1lbnUgSXRlbSAoV2luZG93KV8QFlRhYiBWaWV3IEl0 +ZW0gKFJlc3VsdClaVGV4dCBGaWVsZF8QNVRleHQgRmllbGQgQ2VsbCAoUGxlYXNlIGVudGVyIHlvdXIg +S2VyYmVyb3MgaWRlbnRpdHkpXxAcSW1hZ2UgVmlldyAoS2VyYmVyb3NBZ2VudCktMl8QFEJ1dHRvbiBD +ZWxsIChHZWFyKS0xW0FwcGxpY2F0aW9uXxAdTWVudSBJdGVtIChBYm91dCBLZXJiZXJvcy4uLinSAA4A +RQfaCvqBAgijAoYCgQCpgJmAgoBp0gAOAEUH2gsAgQIIowI9AqYCPYBqgIOAatIADgBFB9oLBoECCK8Q +ngWCBOgFPQfeBUUF+AYABmoG0QQBAoIC6gXqAscH6AVGB+kFLQVLBT8DqQfrBUoAVwK2BUAAkQKGBDwC +gQUyBqcFgwNgBiADrgFMAyAFNwUuBTkGjgVDB/oHHAD+BTYH/Qa1BMIIAAOtBt4AqQLaBTEERAOsAygD +IQCoAJ4FMAU8BUkFOwYQCAwDIwCVBNUFJwCzBTgASQFCCBMD7QgVBeAG7QOqBQcChQVzA6gFgQVRBcoD +UQVEBcICowXYAKMDdAUrBsMFKQRnAowFMwU+AKcHCQM8BA4BeQClAoMCYAPjBSwIMgSvBS8DEgU0A40G +Hwb7AyIIOQUqBUgDqwZ7AjkDzAg+AKYFQgVHBTUGYgcvAB8EPQMfAhAASAJzAoQFQQOnA7QAfAU6CEsC +YQVQAKQA0gKAAwAIUgUoBYSBATWBAQuBAZOBAd6BAbKBAU6BAU+BAW2BAZCA1oCHgJaBAUqAjYEB64EB +s4EB7oEBUoEB24EBnIDSgQHfgQHXgA2AiYEBoYB2gJmA4ICCgQFrgQGCgQE3gLGBAVqBARGAYICngQF9 +gQFUgQGBgQF6gQGsgQIDgQGogEKBAXmBAfOBAYeBAQKBAfKBAQmBAZSAaYCSgQFqgOaBAQWApICrgGKA +FIEBaIEBj4EB04EBi4EBVYEB5oC0gBKBAQeBAR+AH4EBf4DBgDqBAfmA8YEB94EBRYEBmYD7gQETgJSB +ASuAzoEBLoEBIIEBPoCtgQGtgQE9gIaBAUSAHoC2gQFJgQGMgQE8gO2AgYEBcYEBmIA5gQGigKqA2IBG +gCeAi4B6gPWBAU2BAf2A/YEBWYCggQFzgMaBAWGBAZ2Ar4EB6oEBQ4EBz4EBAIEBdIBtgNCBAeeARIEB +p4EBvIEBeIEBbIEBroACgOiAooBkgAiAfICQgQGmgMmAzYAQgQGGgQH+gJ6BASeAN4AogH6Am4ECBoEB +KoEBONIADgBFB9oLp4ECCK8QnguoC6kLqgurC6wLrQuuC68LsAuxC7ILswu0C7ULtgu3C7gLuQu6C7sL +vAu9C74LvwvAC8ELwgvDC8QLxQvGC8cLyAvJC8oLywvMC80LzgvPC9AL0QvSC9ML1AvVC9YL1wvYC9kL +2gvbC9wL3QveC98L4AvhC+IL4wvkC+UL5gvnC+gL6QvqC+sL7AvtC+4L7wvwC/EL8gvzC/QL9Qv2C/cL ++Av5C/oL+wv8C/0L/gv/DAAMAQwCDAMMBAwFDAYMBwwIDAkMCgwLDAwMDQwODA8MEAwRDBIMEwwUDBUM +FgwXDBgMGQwaDBsMHAwdDB4MHwwgDCEMIgwjDCQMJQwmDCcMKAwpDCoMKwwsDC0MLgwvDDAMMQwyDDMM +NAw1DDYMNww4DDkMOgw7DDwMPQw+DD8MQAxBDEIMQwxEDEWBAoaBAoeBAoiBAomBAoqBAouBAoyBAo2B +Ao6BAo+BApCBApGBApKBApOBApSBApWBApaBApeBApiBApmBApqBApuBApyBAp2BAp6BAp+BAqCBAqGB +AqKBAqOBAqSBAqWBAqaBAqeBAqiBAqmBAqqBAquBAqyBAq2BAq6BAq+BArCBArGBArKBArOBArSBArWB +AraBAreBAriBArmBArqBAruBAryBAr2BAr6BAr+BAsCBAsGBAsKBAsOBAsSBAsWBAsaBAseBAsiBAsmB +AsqBAsuBAsyBAs2BAs6BAs+BAtCBAtGBAtKBAtOBAtSBAtWBAtaBAteBAtiBAtmBAtqBAtuBAtyBAt2B +At6BAt+BAuCBAuGBAuKBAuOBAuSBAuWBAuaBAueBAuiBAumBAuqBAuuBAuyBAu2BAu6BAu+BAvCBAvGB +AvKBAvOBAvSBAvWBAvaBAveBAviBAvmBAvqBAvuBAvyBAv2BAv6BAv+BAwCBAwGBAwKBAwOBAwSBAwWB +AwaBAweBAwiBAwmBAwqBAwuBAwyBAw2BAw6BAw+BAxCBAxGBAxKBAxOBAxSBAxWBAxaBAxeBAxiBAxmB +AxqBAxuBAxyBAx2BAx6BAx+BAyCBAyGBAyKBAyMSAASVgxIABJWcEK8QZxIABJV7EDoQORBqEIYSAASV +PBIABJVQEgAElVgQyxIABJVWEIMRAUEQghIABJWGEgAElaAQixIABJU0EB0SAASVnxIABJVGEgAElVUQ +tBIABJVJEgAElUwSAASVPhIABJVREHoQqxIABJWEEgAElXQRAUISAASVnRIABJVoEgAElW8SAASVdxDT +ELASAASVehDpEK0SAASVZhAnEJUQnRIABJVCEJASAASVmxCgEgAElVsSAASVVxIABJWJEgAElT8SAASV +lRIABJVxEgAElW4SAASVXBIABJViEgAElUQQmBIABJV9EMkQ0hDKEgAElWwSAASVWhIABJWWEQEYEgAE +lWMSAASVeRIABJUvEgAElWUQoxIABJU7EDgQqRCWEgAElTMSAASVnhIABJVNEgAElYESAASVNRIABJWC +EQEGEBgSAASVcxDrEBcSAASVVBCsEgAElWESAASVdRDMEKQQ9hIABJVAEgAElVMSAASVdhCZEgAElV0Q +nhIABJVyEgAElTkSAASVZxIABJVfEgAElU8SAASVShIABJU6EI4QnBIABJVBEQFDEgAElWsQkhIABJUw +EQE2EIgSAASVbRCPELMRAUYSAASVMhCREgAElWoSAASVOBCBEgAElV4QshIABJV/EgAElXgQbxDqEgAE +lT0SAASVcBIABJVpEgAElUUSAASVSxIABJVOEgAElYcSAASVNhIABJU3EgAElUcQtRATEgAElUgRARcS +AASVYBIABJVkEgAElVISAASVWRP//////////RIABJWIEgAElYXSAA4ARQB6DOSAV6DSAA4ARQfaDOeB +Agig0gAOAEUH2gzqgQIIoNIAOgA7DOwM7aIM7QA/Xk5TSUJPYmplY3REYXRhAAgAGQAiACcAMQA6AD8A +RABSAFQAZga6BsAHCwcSBxkHJwc5B1UHYwdvB3sHiQeUB6IHvgfMB98H8QgLCBUIIggkCCcIKggtCDAI +Mwg2CDgIOwg+CEEIRAhGCEgISwhOCFMIVghfCGsIbQhvCHgIggiECJIImwikCK8ItgjICNEI2gjjCOgI +9wkKCRMJHgkgCSUJJwkpCV4Jawl4CY4JnAmmCbQJwQnLCd0J8Qn7CgcKCQoLCg0KDwoRChYKGAoaChwK +HgogCiIKJAo/CloKYwplCm4Kdwp5Cn4KmwqtCrgKwQrNCtkK2wrdCt8K4grkCuYK6ArxCvMK9gr4Cy0L +PAtGC04LVQtpC4ILmgucC54LoAuiC6QLpguoC6sLrQuuC7ALsQuzC7wLvgvBC8ML5AvyC/QL9gv4C/oL +/Av+DAAMCQwLDBwMHgwgDCIMJAwmDCgMKgwsDFkMZAxrDHcMgQyDDIUMhwyIDIoMjAyPDJAMkgyUDJYM +nwyhDK4MsAyyDLQMtgy4DLoM1gzyDSYNPg1fDXwNhQ2MDZkNnw23DdwN3g3gDeIN5A3nDegN6g3sDe4O +Dw4bDiMOLg42Dj4OSw5WDlsOXQ5fDmEOZg5nDnQOgw6FDocOiQ6RDp8OqA6tDsAOyQ7QDtwO5Q7wDvwP +Bg8NDxkPMg9XD1kPWw9dD18PYA9iD2QPZg+HD5sPpQ+zD78PxA/GD8gPyg/MD84P0w/VD/sQDBATEBoQ +IxAlEC4QMBAzEEAQSRBOEGMQaxB4EIQQkhCUEJYQmBCaEKEQrhC7EMMQxRDHENMQ3BDhEPYQ+BD6EPwQ +/hERER4RIBEjESwRNRFHEVQRXRFoEXQRmRGbEZ0RnxGhEaIRpBGmEagRwBHlEecR6RHrEe0R7hHwEfIR +9BIZEiISJBImEigSKhIsEjESMhI0EjUSShJMEk4SUBJSEmgSdRJ3EnoSjxKREpMSlRKXEqESuBLZEtsS +3RLfEuES4xLoEuoS8BMRExMTFRMXExkTGhMcEx4TNhNrE3YTixOXE68TsRO2E7gTuhO8E74TwBPBE8MT +xRPGE8gT0RPTFC4UNxRZFG4UiBSkFL8UzBTVFOEVABUPFRsVHRUfFSEVKhUsFS4VLxU4FToVQxVFFUcV +SBVKFUwVThVQFVIVVBVdFWUVbhVwFXMVdRWeFa0VuhXHFc8V2hXpFfQV/xYAFgIWBBYGFg8WERYaFiMW +JRZCFkcWSRZLFk0WTxZRFmIWZBZmFmgWdRZ3FoMWjBaXFqsWzBbRFtMW1RbXFtkW3BbdFt8W9Bb2FvgW ++hb8FxUXHhcjFzEXOhdBF1AXWBdtF28XcRdzF3UXfxeMF44XkxemF68XvBfPF9gX4xfyF/sYCBgTGCoY +SxhNGE8YURhTGFUYVxheGIMYhRiHGIkYixiOGI8YkRiTGJUYrRjiGPgZDRkcGS8ZQRlTGWEZYxllGWcZ +aRlrGW0ZbxlxGXMZeBmBGYoZkxmVGZ4Zpxm0Gb0ZyBnRGfoaEBoSGhQaFhoYGhoaHRoeGiAaIhokGjAa +Rxp8GooajBqOGpAakhqUGpYamBqaGp0aphq3GrkauxrIGsoazBrOGtMa3BrhGvAa+xsEGwsbIxssGy4b +NRs3GzkbOxtUG14bZhtoG2obbBtuG3Abcht0G4YbjxuUG6Ibuxu9G78bwRvDG8UbxxvJG94b4BviG+Qb +5hvvG/EcABwCHAQcBhwIHAocDBwOHDMcNRw3HDkcOhw8HD4cPxxBHEocTBxZHFscXRxfHGEcYxxlHH0c +nhygHKIcoxzIHMoczBzOHNAc0hzVHNYc2BzaHO4dBh0rHS0dLx0xHTMdNR02HTgdVR1XHVkdWx1dHV4d +YB2VHZcdmR2bHZ0dnx2hHaMdrB2uHcsdzR3PHdEd0x3UHdYd7h4jHiUeJx4pHiseLR4vHjEeOR5CHkQe +YR5jHmUeZx5pHmoebB6FHqYeqB6qHqwerh6wHrIe2B71Hvce+R77Hv0e/h8AHxgfOR87Hz0fPx9BH0Mf +RR94H4kfix+UH5YfmR++H8Afwh/EH8YfyB/JH8sfzR/kIBkgGyAdIB8gISAjICUgJyAwIE0gZiBoIGog +bCBuIHAgciB0IIkgiyCNII8gkSCaIJwgpyCpIKsgrSCvILEgziDQINIg1CDWINcg2SDxISYhKCEqISwh +LiEwITIhNCE5IUIhRCFpIWshbSFvIXAhciF0IXUhdyGAIYIhjyGRIZMhlSGXIZkhmyG8Ib4hwCHBId4h +4CHiIeQh5iHnIekiCiIMIg4iECISIhQiFiIhIj4iQCJCIkQiRiJHIkkiYSKCIoQihiKIIooijCKRIpMi +4SLuIvAi/iMbIx0jHyMhIyMjJCMmIz4jcyN1I3cjeSN7I30jfyOBI4ojjCOTI5wjpSOwI8kj1yPsI/Uj ++iQNJEIkRCRGJEkkSyRNJE8kUSRUJFckWSR0JJEkmiScJKUkpyTEJMYkyCTKJM0kzyTSJNsk3STuJPAk +8iT0JPYk+ST8JP8lAiUrJS0lLyUxJTIlNCU2JTclOSU7JUQlRiVTJVUlVyVZJVslXSVfJXclmCWdJZ8l +oSWiJcMlxSXHJcklyyXMJc4l0CXpJgomDCYOJhAmEiYUJhkmGyZDJnwmiCaRJp4msSa+Jsom2CbaJtwm +3ibgJuMm5SbnJukm/Cb+JwAnAicEJw0nDycaJxwnHicgJyInJCdNJ1cnYSdrJ20nbydxJ3MndSd4J3on +fCd+J4AniSeLJ44nkCfjJ/An8if0J/kn+yf9J/4oACgCKAMoBSgHKAkoCygUKB8oPCg+KEAoQihEKEYo +SChxKHModSh3KHkoeyh9KH8ogSiLKJQonSixKMoozCjOKNAo2SjbKN0o9Cj9KQYpFCkdKR8pJCkmKSgp +USleKV8pYSljKWwpbil3KXgpeimXKZkpmymdKZ8poSmqKbcpuSnOKdAp0inUKdYp6CoNKhIqFCoWKhgq +GiocKiEqIiokKi4qVypYKloqXCplKmcqcCp5KnoqfCqZKpsqnSqfKqEqpiqoKrcqzCrOKtAq0irUKuAr +ASsGKwgrCisMKw4rECsVKxcrICsrK0MrTCtVK2ArhSuPK5ErkyuVK5crmSubK50rpiu/K8wr1SvgK+ss +FCwWLBgsGiwcLB4sICwiLCssQyxMLE4sUSxTLGksgiyLLJQsoSzCLMQsxizILMosyyzNLM8s5y0cLR4t +IC0iLSQtJi0oLSotMS0zLVQtVi1YLVstXi1fLWEtYy17LbAtsi20LbYtuS28Lb4twS3ILcot6y3tLe8t +8i31Lfgt+S37Lf0uFi43LjkuOy4+LkAuQy5ILkoufi6fLqEuoy6mLqkuqi6sLq4uxS7+LwAvAi8FLwgv +Cy8NLw8vEi8XLyQvJi8oLysvLy88Lz4vQC9DL08vWC9aL3svfS9/L4IvhS+GL4gvii+hL9ov3C/eL+Ev +5C/nL+kv6y/uL/sv/S//MAIwCTAWMBgwGjAdMCwwNTA3MEIwTTBqMHMwdTDCMMUwyDDLMM4w0TDUMNcw +2jDdMOAw4zDmMOkw7DDvMPIw9TD4MPsw/jEBMQQxBzEKMQ0xEDETMRYxGTEcMR8xIjElMSgxKzEuMTEx +QjFQMVkxXDFfMWIxZTF2MYgxnDGrMa4xsTG0MbcxwDHCMcUxyDHUMe4x8zH2Mf8yBDINMhQyKTI2Mj8y +QTJEMkwyVTJcMnMygjKTMpYymTKbMp4yrzK3MsMyxjLJMssyzjLTMtwy3jLnMuoy7TLwMvMzFDMoMzMz +QTNLM1gzXzNiM2UzajNsM3EzdDN3M3ozmzOoM6ozrDOvM8EzzjPQM9Iz1TPoM/Ez9jQBNCI0JTQoNCo0 +LTQwNDM0VjR/NI00mjSdNJ80oDSiNKM0pjSpNKw0zTTQNNM01TTYNNs03jTyNPs1ADUFNRI1FTUYNRs1 +PDU/NUI1RTVINUs1TjVfNWI1ZTVoNWs1cTVzNYE1ijWRNak1tjW5Nbw1vzXgNeM15jXpNew17zXyNf82 +AjYFNgg2EzYVNiA2LTYwNjM2NjZXNlo2XTZfNmI2ZTZoNm02ejaLNo42kDaTNpY2sza2Nrk2uza+NsE2 +xDbVNtg22zbeNuE29zcXNyg3KzcuNzA3Mzc9N0o3TTdQN1M3dDd3N3o3fzeCN4U3iDeLN6M3pTe5N8o3 +zTfQN9M31jf/OBw4MzhYOHM4jDiNOJA4kTiUOJU4mDibOJw4nTieOKc4qTiuOLE4tDjNOOg4/TkCOQU5 +DjkXOSs5UDlROVQ5VTlYOVs5XjlfOWA5YTlqOWw5dTl4OXs5fjmBOZQ5pjm8Oco5zznSOeM55jnoOes5 +7jn/OgI6BToHOgo6FzoaOh06IDpBOkQ6RzpKOk06UDpTOmA6YzpmOmk6fjqAOoo6mzqeOqA6ojqlOrE6 +wjrFOsc6yjrNOu468Tr0Ovc6+jr9OwA7DDsOOyc7ODs7Oz07PztCO087UjtVO1g7eTt8O387gTuEO4c7 +ijufO7E7wjvFO8c7yTvMO+k7+jv9O/88ATwEPB08KjwtPDA8MzxUPFc8WjxdPGA8YzxmPGw8bjx1PII8 +hTyIPIs8rDyvPLI8tTy4PLs8vjzDPMU8yzzYPNs83jzhPQI9BT0IPQo9DT0QPRM9Gj0iPTM9Nj04PTs9 +Pj1fPWI9ZT1oPWs9bj1xPYY9jD2ZPZw9nz2iPcM9xj3JPcw9zz3SPdU92T3bPeA98T30PfY9+T38Ph0+ +ID4jPiU+KD4rPi4+Nz5QPmE+ZD5mPmk+bD6NPpA+kz6WPpk+nD6fPrQ+tj7BPs4+0T7UPtc++D77Pv4/ +AT8EPwc/Cj8PPxE/Fz8oPys/Lj8wPzM/QD9DP0Y/ST9qP20/cD9zP3Y/eT98P4E/gz+JP5o/nT+fP6E/ +pD+xP7Q/tz+6P9s/3j/hP+Q/5z/qP+0/9j/4QA5AH0AiQCRAJkApQEpAVEBeQGhAh0CKQI1AkECTQJZA +mUCcQMJAz0DnQPRA/ED/QQJBBUEIQQtBKUEqQTNBOEFFQU5BVUFtQY5BkUGUQZdBmkGcQZ9BokHUQdpC +BUISQhVCLkIxQjRCN0I6Qj1CQEJDQkZCSUJMQk9CUkJrQm5CcUJ0QndCekJ9QoBCg0KGQolCjEKPQqxC +y0LkQw1DK0M/Q1xDdkOTQ65D10PYQ/lD/EP/RAJEBUQIRAtEDkQ5RFZEY0RmRGlEbERvRHJEk0SWRJlE +nESfRKFEpESnRNBE8kT/RQJFG0UeRSFFJEUnRSpFLUUwRTNFNkU5RTxFP0VYRVtFXkVhRWRFZ0VqRW1F +cEVzRXZFeUV8RY1FlkWdRaBFo0WmRalFvEXQRdlF3kXyRgNGBkYJRgxGD0YjRixGL0ciRyVHKEcrRy5H +MUc0RzdHOUc7Rz1HQEdCR0VHSEdKR01HT0dRR1NHVUdXR1lHXEdfR2FHZEdnR2lHa0duR3FHdEd2R3lH +fEd/R4JHhUeIR4pHjEeOR5FHk0eVR5dHmUecR59HoUejR6ZHqEeqR6xHr0exR7RHt0e6R7xHv0fBR8RH +xkfJR8xHz0fRR9RH1kfZR9tH3UfgR+JH5EfmR+lH60ftR+9H8UfzR/VH90f6R/xH/kgASANIBkgISAtI +DkgRSBNIFUgYSBpIHUggSCJIJEgmSChIKkgsSC5IMEgySDVIN0g6SDxIPkhASEJIRUhISHFIe0h+SIFI +hEiGSIlIjEiPSJJIo0imSKlIrEivSLRIw0jMSM5I10jZSNxI30joSOpJAUkESQdJCkkNSRBJE0kWSRlJ +HEkfSSJJS0lOSVBJUUlTSVRJV0laSV1JfkmBSYRJh0mKSY1JkEmpSatJ1EnXSdlJ2kncSd1J4EnjSeZK +D0oSShVKGEoaSh1KIEojSiZKL0pASkNKRkpJSkxKVUpXSmBKYkpjSnVKnkqhSqNKpEqmSqdKqkqtSrBK +2UrcSt5K30rhSuJK5UroSutK+EsBSwpLDEsVSxhLG0seSyFLSktNS1BLU0tVS1hLW0teS2FLikuNS5BL +k0uVS5hLm0ueS6FLpkuvS7FLxEvHS8pLzUvQS9NL1kvZS9xL30wITAtMDUwOTBBMEUwUTBdMGkxDTEZM +SUxMTE5MUUxUTFdMWkxhTGpMbEx1THdMgkyFTIhMi0yOTJFMuky9TL9MwEzCTMNMxkzJTMxM20znTPBM +8kz1TP5NA00MTQ9OAk4FTghOC04OThFOFE4XThlOG04dTiBOIk4lTihOKk4sTi5OME4yTjRONk44TjtO +Pk5ATkJORE5GTkhOS05OTlFOU05WTllOXE5fTmFOZE5mTmhOak5sTm5OcE5yTnROd056TnxOfk6BToNO +hU6HTopOjE6PTpJOlU6XTppOnE6eTqBOo06lTqhOqk6tTq9Osk60TrZOuU67Tr1Ov07CTsROxk7ITspO +zE7OTtBO007VTtdO2U7bTt5O4E7jTuVO6E7qTuxO707xTvRO9075TvtO/U7/TwFPA08FTwdPCU8MTw5P +EE8STxRPFk8YTxpPHU8mTylQHlAhUCRQJ1AqUC1QMFAzUDVQN1A5UDxQPlBBUERQRlBJUEtQTVBPUFFQ +U1BVUFhQW1BdUGBQY1BlUGdQalBtUHBQclB1UHhQe1B+UIFQhFCGUIhQi1CNUI9QkVCTUJVQmFCbUJ1Q +n1CiUKRQplCoUKtQrVCwULNQtlC4ULtQvVDAUMNQxVDIUMtQzVDQUNJQ1VDXUNlQ3FDeUOBQ4lDlUOdQ +6VDrUO1Q71DxUPNQ9lD4UPpQ/VD/UQJRBFEHUQpRDVEPURFRFFEWURlRHFEeUSBRIlEkUSZRKFEqUSxR +LlEwUTNRNVE4UTpRPFE+UUBRQ1FGUU9RUlJHUkpSTVJQUlNSVlJZUlxSX1JiUmVSaFJrUm5ScVJ0UndS +elJ9UoBSg1KGUolSjFKPUpJSlVKYUptSnlKhUqRSp1KqUq1SsFKzUrZSuVK8Ur9SwlLFUshSy1LOUtFS +1FLXUtpS3VLgUuNS5lLpUuxS71LyUvVS+FL7Uv5TAVMEUwdTClMNUxBTE1MWUxlTHFMfUyJTJVMoUytT +LlMxUzRTN1M6Uz1TQFNDU0ZTSVNMU09TUlNVU1hTW1NeU2FTZFNnU2pTbVNwU3NTdlN5U3xTf1OCU4VT +iFOLU45TkVOUU5dTmlOdU6BTo1OmU6lTrFOvU7JT7VQBVBRUNlRCVFlUelSiVL1VAlUVVS1VRFVWVWJV +cVWMVa5VxlXgVfRWCFYSVnJWilajVr5W3Vb+VwpXHVc3V0NXVldtV4NXj1ehV7dX71gxWE9YaFiHWKJY +t1jbWOdZAVlHWWZZelmNWZ9Zu1nHWd5Z9VoMWk1aVlqPWsVa4VrvWw5bIls2W09bg1udW7Jb0lvxXAhc +G1w6XE5cZFx8XItcuFzOXNpc8Vz4XQtdGF05XZVdoV24XdJd514hXlReXl5/XpZeo17DXspe/l8GXxtf +NF9RX5xfuV/OX+df8mAqYElgYGBsYIxglWCYYJ9goWCjYKVgrmCxYLhgumC8YL5gx2DKYgliDGIPYhJi +FWIYYhtiHmIhYiRiJmIoYipiLWIvYjJiNWI4YjtiPmJBYkNiRmJJYktiTWJQYlJiVGJWYlhiW2JeYmFi +Y2JmYmlia2JtYnBic2J2YnlifGJ/YoJihGKHYopijWKQYpNilmKZYptinWKgYqJipWKnYqliq2KtYrBi +s2K2YrlivGK/YsFiw2LGYsliy2LOYtBi0mLVYtdi2mLdYuBi4mLlYudi6mLsYu9i8mL1Yvdi+mL9Yv9j +AmMEYwZjCWMMYw9jEWMTYxZjGWMbYx5jIGMiYyRjJmMoYypjLGMvYzJjNGM3YzljPGM+Y0FjRGNGY0lj +TGNPY1JjVWNXY1ljXGNeY2FjZGNnY2pjbWNvY3Fjc2N1Y3djeWN7Y35jgGOCY4Rjh2OKY4xjj2ORY5Nj +lWOXY5pjnWOgY6ljrGTrZO5k8WT0ZPdk+mT9ZQBlA2UGZQllDGUPZRJlFWUYZRtlHmUhZSRlJ2UqZS1l +MGUzZTZlOWU8ZT9lQmVFZUhlS2VOZVFlVGVXZVplXWVgZWNlZmVpZWxlb2VyZXVleGV7ZX5lgWWEZYdl +imWNZZBlk2WWZZllnGWfZaJlpWWoZatlrmWxZbRlt2W6Zb1lwGXDZcZlyWXMZc9l0mXVZdhl22XeZeFl +5GXnZepl7WXwZfNl9mX5Zfxl/2YCZgVmCGYLZg5mEWYUZhdmGmYdZiBmI2YmZilmLGYvZjJmNWY4Zjtm +PmZBZkRmR2ZKZk1mUGZTZlZmWWZcZl9mYmZlZmhma2ZuZnFmdGZ3ZnpmfWaAZoNmhmaJZoxmj2aSZpVm +mGabZp5moWakZqdmqmatZrBms2a2ZrlmvGa/ZsJmxWbKZs9m0WbTZthm2mbcZt5m4GblZupm72bxZvZm ++Gb7Zv1nAmcHZwlnDmcQZxVnGmcfZyFnJmcrZzBnNWc3ZzlnPmdDZ0ZnS2dQZ1VnWmdcZ15nY2dlZ2dn +bGduZ3Bncmd3Z3lnfmeAZ4VnimePZ5RnmWeeZ6NnqGetZ7JntGe5Z7tnvWe/Z8RnyWfOZ9Fn1mfbZ+Bn +5WfnZ+xn7mfwZ/Jn92f8aAFoBmgLaBBoE2gVaBpoHGgeaCNoJWgqaC9oMWgzaDVoOmg/aERoRmhLaE1o +UmhXaFxoYWhmaGtocGhyaHRoeWh8aIFog2iIaItojWiSaJRolmiZaJ5ooGilaKporGixaLNouGi9aL9o +wWjGaMto0GjVaNpo32jkaOlo7mjzaPVo92j8aP9pBGkJaQ5pE2kcaSFpJmkvaTFpMmk7aT5pP2lIaUtp +TGlVaVoAAAAAAAACAgAAAAAAAAzuAAAAAAAAAAAAAAAAAABpaQ + + + diff --git a/src/kim/agent/mac/resources/Gear.tiff b/src/kim/agent/mac/resources/Gear.tiff new file mode 100644 index 0000000000000000000000000000000000000000..c1382588f21ed6dd329a4925404fd06744f2fb71 GIT binary patch literal 22360 zcmeG^2Y3_5(tE0wEO%otRW@!wWLa{Td$++2cRI2p+rqLWRB!=84L!5~p(WG@0TODc zp_>Gf@MxhWkYFGL2n3vj7K-)Ho+Mi~wt>8teD8nHr|<4&XJ=<;XXj?;c5juIhI%4| ztWXD3hiCv1aN-M|LlM_S1yh4&JRrUOH;?mO618^8d-K#dXA*3R1zxnj*4W5SBNUg z)MYZAm|Ib%P-;aL5#C~4w+LX2=6iEV5?yJ8cR^YvSEo`bO65APzpo!p81fdkNFy$j zm8&$RT!C+pH!##li$q!Ri6(ZCitsMc>C_@Vzr4KMx7^=Xr77kMLPJCOenP%b$O8(V zwo<7RSMZeD?ob;khPH>_6sJCIQhV$)|`Ae|E01P9HlCW9{jToI2wmO zK)~qH;3hwO*qj$91WMFH^{{GPUr?3QQd>m57Q|nliC2QeGx5mXSUzoZoAz#9>_Sy!97-be#)MBHXwZR&a>4ZIO? zn{~CR>y0$)4y539!4GSGIC9H0g!!kx^Sbw3wFC;c4jfU5p z5MnmMd6GE$M9yzhy>{h&w|ObCV)NSB<(QuOGtTR>8Q7L7xFcj5JWn6t4yz3AT$xe= zlZDFS2>0BaL|%wHUNCWs&xp(Emzls-D_~NYo0%J%ni9u#=kfV5YPCYfjZ>AW^*Wh` z&yUZE=Vqp+Waq%rh##MyknYZPw^#}hV{&p)0|>SJOpQt{)95Nwfh!LR_)2wBcc{-; zO(VGgE0s%hk!)6YsjM;*)1WFSHo7E~~xlUFVqtS>f8(52R z5(I;SX3<*U@#@B5CLp>3CeC3B2?LmKs}e~Ra+tEJ7YB&nq^+&lVIWU(c4m_Tkk zaUCiO3=jo`_J>I^ob^h1VFpwuOH_!9ExAJ1%b*t`VdmQmk_zRUH5$%0s~PHfUHi$< zVAY86N`$$x`YvkL`u_h|Pcu!!c4Af@EbE!US9VC70k!CWvL#{#_=x{D<7v{YdZz16QN4G)6j1uhq$<7?~xLGz6CQR@izp+9dFP z!)Ua%sO;s?n-y*j3bEqMp(2J~`D)Q(GjVAPu8<;!L_c2$O&bQsSD=Ia1Dc}_3=V39 zZWv&o*5-vaN2E=I{5RBV>)c-s^FfjB0k+7T&B`!#BHy*Yvi&>N4LBaqD zHLYBw`0p9f{{w8SmA1Cv8vo~qbwP8}cda^wzhzi&@EewW3fRAJT>lR<;HHgj!Gc>D z#alrC7Y^?K;TEh`OaB)J_?DV!G05j;b5q*}dA!Q;ZyDrGAz(~q3e2nEb-#w#(G*qr z`S}XrHS6^jwqW^f1ibR4swAZijEckxt&ES?bXto-b@0J^Lz(7RpI~9nSsJ!tK;Qnnq$YNt!#};GmQ+dZ)0l(Et_brxM`T#Fpbi*iRKo<4j#Pj8p$TtZf}$8 zx)tQ!ykrDB>|h=E2THWzp5Y#qLCK;P(|xGV$sK*>+=~<+yh{&B;e>sNRB~717z4l< zTYr>TAy;BHndH_ZUjj%Oej(BJWbK#>mIChNTC^9LXWLLPYAhmGsj2r!oDC;OBUWl* z&jTzqR~px^+mSh4;cA0&kOqm75^0ed?iwTmSb{1|JnoZ}z};XVA!rq0zVgW+GvBQ=9rCQ962mKyJflhg>f7~6`I2*FNOgmq6tNzj+LM@e3AZG2eT zBBsaX8XcAd<_wcyIH}Yt6mY}hGhjocQmV};943L3sa%#sCx8@bAUUwTRH0H9Lz+BN zWkov6bgWKQW=fM{WEGc~^RZ|QtwJu5Y4a4RT1XL%>Y zx4*jmRM`8HlTeTYRhg8FmERbXrY)8=!Z5@NU5>c85zba3gL1M8U5Yk2CoQ!Qc8Fny zR%Ub~VpfStQx&6-7n?O@XKXxt?qNhjNv2d*B-Sf*0J28Fl3Fw5csYSYHzv0(EGGB2 zS}51S$UiozJ_CxNbd?h8nx#{zGxR#G%xoAqkF5YhdI`%ZRO!H=Ucy^L7nd|8C!a_k z%nWhhB%*3b*!XP19&I?V3DZeBOo6D_dt$YjpPE!40S!?8fc@x%;i91i+4oLNNra8( z*i(bAHvED(n- zbs!AiL(?gSBSaKPNhiny1jYvy8b_lum{u&dH4H?JGYLeYnltSYK_jRX8imeeSkb8* zf5>#8(%c*c^q9V4C->pwgbe37E4IXT_h^@OsL($^GyZfO({pZi?bTl;+CZ0;TPJwM zzn3FTIJ`<1)c#EFHQ8_5CRTr=zy5oo_q^5bd^-Qg*&iSLWBcdlZamB@8Zl|XnjK%9 z|LIY1Qhssy^;*PS^T- z6kgSgpBtCmPNEI?)suk*V0r}}J_ACmYA;R54bsW3)r-)w1l|oI7`{UsDp{ukib7|9 z9z9^4{=~ZX&g{v{7S{f@(2C{~_^$Ye``QP4m8bhgZZasB!F4 zG-HG0+s|&lUH;M5r&rRx?2_-l?m_D1ygdu3?@paqaOcA_BMPo;J2U7(dDYCf&z1kY zsm}=SfuRw7g6+I6PrLXySF%tuVC24EHl3+_*kkM3k)zl3_*pdh{D<#+BOEmKgSqm3 z%N@TNv^I%Vy7*DvPhrn|GYly7Y1I8I2DEv1x2G|_8)iMMx_mtUVes{xJ=fkl>ay>X z;Zcdy!DCV$EO~JtZODs^I=i}Qy~h~7H9j$Jq zBKDLhB_WQh1tJWa4iM?ld7}^ghL@bl<(e~I^X?!~Yv4&IGJ)P-3V#a#@I-(W=cI2yy!G9?T%6fCn^S|-Kt5rC(R(Bt8U3f=`x z8X=dJ1AG=>4~4!=4ls^joXccl7!qmNFL~%>k`jRZ0On|Nvf=;^2j9*qwtx#QU>%GD zK#Ss3>Ppfxa(yH{xbU|LA>3qHIiBd{Wx~Hbh&593gF~^h5`sUn&A1eD#Jc5zl7j@H zK|wsBFCOj9N2{4;qF3B;-x^7N?@RU>;c~nI*HE|tr&dhm|Y^{@|qg)pDt)2mW3Q|!R~4qem9VD^T5jBJ1XdIrBqJ--GyA)QV;*hViq!B z&}i2HIIXTj&O>~W{Yhs;{pC2KIawp>zz-pZsPDH_Rvx$zgz~%HG}=ADkWXG4T`034 zNzuy5;0(oO%?Kk24d3YSj$wzKVFd4rx+5RNLqZgc!ca6yK)q20yc6|9gW)Zx z1eHPjT#iPeacB~nj%K6zXfaxbR-<)jBif2~qP^%4`T~88PN8$?5~@Kr&>d8Z9-(J2 zS~3Y6!ijJtx)Gj4FG5I!5RpUzkxFC{{fMDNF`*>%#3*6{F`bx8EGAYE>xeDHZsHK} zC2^YgmbgmXCVnHHQm7OT#hKED;zi+8LMSnmK9n5FAWAW1IHig*kusCAkg}5UF=Yqk z5anyiIZ6%X4&@>I$&EGDh3ZD-QA4P4)C_8WY7tdKeVaOsx`4Wpx{F{)oX~xG=mK!Hi@^KgKY|NX87t2aHXOgN)OR>x_p?CexMK ziy6VpV2YVK<|O7K=Euwf%rndz%qLdXR^6qt&~>dtrl3Vx7u%Y#_FckGnPHe zlO&@23tgl-C!ExmD;oVA>ToNqb5+t}E6*+kp)x6#?mv{`3UZS#Z8 z6I&-+U)vO0sqGlsCAK?k&)D9xW7&Dy#n=tDtFl{Qx6SUP-CcW@y_bEQz1aS3`z7|f z?Jw9rbZ~MIIAl1KIm~ca@9?F=Ek~xKmt%sX)N!KYD#vQa>rPas?oM$|5~m4HtDQb~ zy5Y=l_I6Hj9_BpV`4i_8&b93v+Xb}CYgf^3NxOaRYFwx;UM?vv3YS?fTV2k(JZ;~l zeN1~<`>E|Wwm;MUafePFqC3bsOzW_z!?_MmUAe9auBEPXTz9&D-;vg_XUD!B%R7G9 z@$-&%J2`g>>m=zkt<%;{-*u*R?%6r3^T^JtIv?-+uuIo2NnO-k7Imraa<{8X*XXXp zy3Xsmzw7O8PTeBA$-B+#cA(qO+;-d;u7bOedxTr-*4ZuDP4BkC?S$JicW?Jx_wnwV z+`sS6?jF*;sQbL`hr8E$bn{5_80E3S<2z55XNYHs=lh;VJs*2{d*ypg_S)%n%e#Yj zig%Uw2Jg#0Ha<~4!+lozobAEr5z?cy#|J%5^`!I+>{-%tNzaqLD7}Ju$$Nd!>-1an zw?f}izP0kL3p@@lhBtz@fp^u{#W&S=yzfrmdwdUmfBqc)QGT6Ypr68TwcjOyqoB87 zykL*uH(^hqM7TtF*5AfI(SMBpF8^NxdIiV=J`A`R=oFY1I63fe;ESN(AWhK5pxeP7 z!9#eOQY9B-;C)IGc4xgn0v8)v6|TJv5(_Kaiil7 z##7^y;-|%*NN`NZO<0`pL!x_PapH!=+N8jwkx2)W>B)VP=OkZ9>5?KzS(j4VJGl4g z-beay`egT6+~-z9$QqS(G}|eANcQ^dM>(-MvvMxw`sC_!59itC4a{4Y_c%X3e{OzF0l#2$!Posd z_LKM9)t}ivum76<4+kU+m_Ojgz`%i%2A&_}J*Z;PmxDVFRt(-Z#BPXq$hM*Mq4`7C z4}C7q5U&(JEKDg}T39QIle{mvBaN2Mlirp^$mYs!7DW`zExJ`4QT$%Xf>9*br#dcL@1@wp|jOExX-ymac)#~(;PIP+ojhZ~o5SvGyy)8+Ez7gr>$ z*s;=U<$_g=RU=mYv^sC~(SL;eW8IpLYo@Pxu~xaZ=A*2Sj;sq_w{Css^|LlmHt09p z{&?WWr#^}QWY-6s4yASUP+q3;M-_O?Wb=$jSU%P#?_Otg-+;2Egb>QJa^})M`iVs~s zJmm1D>VoRCNBSN)@p+%mkA0E&MfK5`qX&*f9NYV)=*!(-g?zR1>!7c991lFc{hxvV z+mzhCe}=O0#H>2YOKO;F9gtMONlU(3FB>AK|logcM7KEE;X zC!3!Z-0XI9-7Vp*y|)u@pZa;g&o}OxIZ=@*AzmfrKZ_v!uU`^RewYH$3i`PJ~- zjNe^^{5kXa!WTVW?5XQhciB*8 zFpNdq%n%P38*0+wA$O+?MZ@h-=fda?;d#9FGZG80`gBNVc#PY^oxJVz?H?~GgAt{# z$2j(+IO_82$2za4{W9*eRoT0LbnZQ6dC;O9KXjzf4|U5}dv)sir6;Sxe;A5}26RMy z@)CUlItR(tQ0Lp#MqkAjH!|f?V~T-N9_NpYFcg7 z_k;bYJCCFCs%X0_L($jwk!tF*g5W`gzopehqo^9>0s2`Aul=WdP*{xBN?VVZnFBPQ zvCL=TNTgi#gcxfhuSGqov#(u=oYL!VWiO9y0Vg6R-$K_yHe6&Qm(w$xr!&<j_@aKFUEeF78)ud5;aeX>)jdA33XSTuB*8VlZ@|f0-T4Pj zAs;qnIl+#dJ!f%G$K~SW3Be0|){P0Bddqd=gbBwwq<3Q;x~)5qaHl#_5OQlv_)Wig ztS2WICOS;&&Mk`Db?l^%eV$u3eaq;yEyo<@ECm z^3SJF{mJUgyvSuY?kSK}uj_JZJ#3o8 zZThEA=05@A!Y&~(H&y|QN95ajT8;+Kc#?3cxDsZH>8BdS)0m$tqLFj|S1=_^1wrj_ zrQj=`6H~%2Faw0paNsvmd_V%1c=gGFN@W_Z0A6_1@N!AYq0lSg?G|;0lY>%W{<;#T z4m5}hZ}(h?$W<_BUy195KN7?<+cX8pO{Od5@U~29f{N1;MWF(xL0pnAd~RD|$|uvM z%2JhOTA@`JHA+hqm_Mm{xRYgyDl}>U?Qy!%LBQ;T1N>ON7b zE}4>)i0C7WbK_%|)gkt>*!bMykvIp{#TXDB?vb&Pb;}uW#jNY94Tu^GkX_^`(RQ(oO0UzN9{(uo?9U zg_i1zA=MW{stVtb@^$E@D6E;fK`ZMpTI;VQ)u%M7Pl$w| zK8e4?*!X8cllp|OZGFwDZ#)_X>KkvgzSh*2Rd0Qm3al@E>z1v5T79Fy`Wnu7{_GkYG$KweS literal 0 HcmV?d00001 diff --git a/src/kim/agent/mac/resources/KerberosAgent.icns b/src/kim/agent/mac/resources/KerberosAgent.icns new file mode 100644 index 0000000000000000000000000000000000000000..c6a725213c8f6ded5b4a56c944d4e66f86434211 GIT binary patch literal 40494 zcmeFa1$3;|M%Ye|9!vjh9a5FthM&?z1Oo(PaZvck)k~2oV>Jf0Yx2p zm%`Wh3w{Ms^tjTahhxd3_=oD_`}giYyjL?qO&TA6^qWj7yL<1!Z;cX472l3Zq@&DT z#ozAb4&$xL4@M*-W8Axn_r`n&*`ME)N@a%lzEic2{QMz#zu{danRG!(%uUU-l&qCT3AsWSHlt!50TF(j>)84$@J0sk11*| zm5?aNDQs%3EKS&3LEdz4NZ2a_;ZdofL}4V8rl#@&GBO4Ay}ez{WvRTqdrK%auo~-W z>m0z_WKtu^1gX?fV=*r+JG+wURq5-jN#pT~Kui6hOgzxjCmq8|A*iWDUDki~Mo3y# zQ8&}8+SgZ;o#uVHfSL|<$a;okqoa34?R{f+@5;b>vQ#=El}N{`jKWfK+nC-7eSIwj zIfBgKYkPB90ignjymwY(ZX1i^&#`F!lT{=gkW37VfHep{aKAW3Y zP*_x4QktLSmqNWZ{$LyeclFA~#w0y`{nAnCV4tvSh%1>rB1-;x##^5r`Qh-jOcDH~ zHm|OuK1YDh%L!Ce6eU|}Cc-5iJh&&7j&jB(j}8rpL_;EpSUlJ>$dP<9(#L;o_VQb1 zj*i^uL8)X!nqOR&l?@s4^9zbgOUuhD%Zmk`I`Py@7)RVA9vc&hWYW>GvC*N9mL6K7 zJk-(FC#EHzN_!$coTFoHmy&cRMI@C-WW8B&NtxM1hh_E6&8@Xn)m0URF(xt8tK$zI z{Wj3kFBuyf>g?)~j>#mFfv!GUGH(dXJI3DV1ae#bxB=Rdr;Uoc;B@O?E|9ZC!n3Ax|%y4NaLuEEyaSOJwxeba8hd zL=p844)=>B5>ak46~DEU9!TIwCo%SeSSA`y)pW9lVuLURB@F zP*cn|34x!@x-XT;#^98~w!yKnk-nDZK8a+wtFx_31dS<(M2uvD?A}miQC0u_$D{g^ zS+dTksMNZ4`shu4Lle%A#@V3O*wj!K@5m3LR7H<|CvnY;&FpP!>qbxvwRa8n4@-u7 zgo6VE9LeNi*i|xe|H0h{j~|X2NZyv-tqqE=OE^m#SyqOgzFJqeS!oeoH2SOj|b=JJjFa!;vTsw)Zj;WhARn#P-|L(ZYyQzJsBqt-X^I z?^bvNh&8vgG}jgSIcoZlP<(h_Di)9Ek4+kt4s>Fv;@&QzHKm@8u3nHE>g>0cyd#xK z5>jJ)jjiqM9i1F_>GedUmX_A`*3w`{O)qMK^wHxn$;gOUEQ7nkB7IHm!~nhB!|=Aj z{=tD^BsEdrppE2R>8LO%F51e{*3sVCDW0s3P-<=OY^zMQ(bIIN-Wb3C=pll3bW{qf z!b3*j`29_+{wEH1ck~d-eZnC|qBPXk4e@3`XYn~P4vvma-ih^%AlTf(w5qjtwbx}^ z=xMqVe|z{Fh#&+;CHNC5R;)ZA91;`h`dgaX2C?A5*5(0_>}_fvvX)Gj4kzbS6ePt) zXE!uAu_AInv9qJ4=#C?wM@<{Ug2iOPBV%JSP?m7TlZLzd1_mXP{*JC55L6Zk+xuW3 zrJ=4)P@E~!Tdph5#Ew!C zJs_s#jw)fXprRr|7U~mmB-004J79?+Vb{Pg!hd*hXxIXzWZk*GF@mxhLJF2OX;tZ} zEpW4Uw4qelB}!!mqmurm_*Rrk2P(5<#Bh`vu@u9-gdIcB&tOkiABj=Lo+c6q!@@q6P=A!KCqFsA zqE5axlUB8^?v!vIv~EeMj66g%$v{YIBUX|O_6~6*6Nh{Hv7VtJI4y_`w)Ppr9mWO{ ztj+wR)1S~G0;#w6)p>ZjI@{@)Q)<<}JtBc5(;rokN$lVYgCr-4`?^}XAd=ERYm*Rv zi9pj(GJRyMJ=B8h6P2D@Qdw6|G;Y$WB#ivXMCd4^2;h~s%jeuJ@wAvDjS}&qrIIOHBt8H z@hAcct{|m}fR5rxgFW3{{g7~Qa9GSpCW(dy1_ohHVN(Y}b*6MwEa2U^TV zH#TF%6WfQnJ$O70Dz!cmt3%y` zjO5jk(XL3FD~A4&3E8C;uxbM+Ha8o#PHgKRiZ$UmJMqlSK(qHZvT}(?9{ysft5~VK zoed}GvK5P05(_(r3?#2%%>hQ2twZBdG78J9SkG-~YG?w%?%_NWS7#?@dn-L7N<#_@ zOJ%YVu?WE>lZY&_S|jmfc=2#=FCt5bGyuzpyShYN$s1UAHjmbI3s1_*EiSLDsA*u+ zNn?{i>!eOmt)-PK&ymN|;~7%QGGxvXMy5hEAd^~(707prD13%u<^Il&9#BQL>POH( z#{*Eo8`80^2%9V1J7G!Lg{74>l@+zfqcDCm-9$Art&`h_gghf250X2c;TcfMz4w2U zj&NnF(1t`T5}Aq>BqRExXn!OIV&%RrVLwgBF>>NRvH@W zM{QCcZ0#B(p#b`?k4p0$nXAqrQHhxaC8bq0sKGUaG}AJp38rjq?GvRLgRrBMy&jKm zNGS}A8Oju-VndOEn39?h!Up2W1Koo|#LdKgtzD?3^QB{bF;aMSE zAO>x!Z{)OSv<>xpneuqf&h~aZo?P~r7|v23N2PfrIQh_85~5e=SW_W zj`k&3UcT-Y9GjMrQ&du3QCU@8P1n4|mZ9b+su>U^njsW<_SR-ZEe4cA-w=mb(p;o3 zCr*u^${{#mVPtJ#S62@-K1({%>~ExF;1eF7mYtPbP*_q?Rb5+CQw>vcT3%^}DO-BP z1x7ABXP%Rtqn;);PH1;Z7@`s@5hGTVjy#o|8ljOn;f$hwVS5h)ok$8CZ|PY1M<%7C z?<>kL0CnOibqz!@bjwUAr=?TeXlVuV4)#_$a`LMDjxmI#w)Qi^n}%&Hs2M~tA5 z52U00P{s>;e*w$ZAsYixlRYP1jcxSJ9OVNTQph+hoTLk3r9yY zvokzu+=x<&>y=3yL^Pts67rddmtfaY>UitCg>MLQ5l9skmzKd~<>ko4jck@{BQn5WKDT>5W(2^R9UzLoC(#$Sjbnp*POara_qT=GR@(Q|Y zQcVr&S0hnha}(X7EEH9kS#fwOjyj+;ZbB*Wx*bF+670D~Wah-JjKm)x-2~{Lx#boV zm6}snSb$$eC8gz+#2|Gbhq}Xs) zkxJ(qXNV80%)Y0m0S9T6G2hk z+0MpPb8!5zTDHdT!JDq8-%_6gCLFQ8tJM+ z2t5h>hK9NZx>=)JRA}r-yv1IRrEEzlu1ObLh-M(+jesVaPV8a91^Y`HafOC#`G~a2 z>Beb8Z+>)2Z5MWRgTvUsjf~xs^{as($<0n3Iwe6kwhlRGQM=))-NO`p|GpF7p>&*u&}XR^xOS=-Gn;m>Njh& z4flGWS~|jy^v>|d$89LZ*8}>EMHFfQN2Vr)UZkiMmP8d@ew?${c>!TDi78b=LUCxg ztE9N3Np}C?XbYkeCAhhX(>!%xC>EL9*};})M$BeMDQ?I^a-}3(nF>(@k`zgW=Hl5> zStIYpY3`k%$hgF$w7RaI-u{8%;Rg6swfMoq`vaf}>v5Z>_K0!~o#9oEHhN_J4wRx% zBT4}!=Ad9=)gxj?WFej{ljfORI%nw{5*442l$=rD-NP=s1-{iV^5Eg4(KeE-=_a~a zwNqSgVdd;Z^HfauBx^WON^b`Ciw#65iZmfQf`uE3jL5?K!fv0r;^ZG51u2tLGHZoB z{n%uR2U{x}2JSw1`1pYkVse_MHMfbncn0#?!loi8N^yU_gd>_nL?juB>}iY$QKU_m ztEcqb1H)rt6O)osQq#&i;Q*kD{iNjHg9ncuKj>*_f+WpN@!g40V1r^9l@)j!htPrsmdl^}^~R$;jx~z55RyJ$n4Grx7Hl z_6(Eh#NHm1(yZ_yv6JX? zv1~B<`l&OPo&li|k)RV7Pga_qky$1jK-Z0cB-(rU__2&Ev~Ms$-vzbIhR3r38Ba=a zTdCAh^o3-!)9upnD-K?Ppc2K310$qo2?Vu014AsCdxXrxu@164;c$+=gR`S6&q{~X z0WV5P)m$jC7kwd>mYSYBs>kyQ3Js5hI57~1I9+Ldld!*EIE0KmDjU1^+vq?Wq0=F% zH?zX#!`_Y!3?Hm2VMuHv`cf<#PQH2Sgt4112(c2yLE%_fMkP+C`WxzyYeq-=+q-*O zkP4WlsjZ?O4|Aj#Ya25X5Pp>6zUmQc(ci?fPPeNkbgbQd0z<;&gyLBLN=qk{I*Qu+ zntP=qZ8f!YJ<~X?scB#^8gdQ#!pFjemv&f+37IoZ)XMJPLfQhFn47->&SJTjPY`TTjV z;~n3i;IN2@C^!I1EIBnJySA&lv#h?Sg)N&bF;Hu2>JU|$Sn(X~ZLG{lrVFB!zONh+ zn~3I$W$m`dzq)Gao|<0O(J5>yY3Z&g z!8*~TT93GS7^QTqVnoapEfC9kJbzj+<;y+Wb{%vM34u$%0z_%? z2`TxNRgImUEu~FewWJDz8Y!EQZEELmfxaE8nuQIUdq7UvTsXoJO_z)|80~pS?VTn2 zk8Ry`$SFEHl8$;u?)8Z&W$hguo$bZ7UA3e`V5>0bfn57=gN2ogo2vs)hX)fxP}@G_ z_sYa5szX^9H%y_Xe5UoC&ZXVE54)wt66r`VCM2Z@>N+~RDoeT=(N)RKK*U4)G9sPC~CicqTS?5r(rYb#=f zV+}z!Xf!tV55ySoT%D|Wa1UxchEkh-F*t&EVaF_nTB-Kwmg5%~!`p{y5jn>sq1^K0A63ZH5pUPd=e=@3<$Vk=>9&niEgTK=}?Q9VOzJ8Pp;yMM4q z&qyG_gN~=Oyu7`Nw25qIkIzCmEki;V6P}y1ja<{&)UsFiUcF#~(>5F9(_j5yoSc`8 ztddsL2KgJ%t+LIRT>gf}fq`&+Y+1*<%yI{du2GOkE}R8**MxBXwXX`Zl5zO$|{>F*TQbNYBK~g6HULW2Ce1VpLsK zKFf$WJZlAX=rl?q&jx#AYP^*Ca(tZP>FHV6IojJ=nO*(P5;Cac!un^{@II<_|EN6&axv^P|lqjjiJRRySdEPpSxqzb{`fdi zu$=|Z0itk@b0aD`I|>}HJ2^U9$pven-hHA`q;*#(2mO=3+_JK|ec6HMY-NLXmKtxP z-hQeRTa;TTE5q}@oW0?Kx63`PlX{bA7%IamoSaeDtzDdWb{39A1R?c0Q4!C|3=4E7 zAIDiWbPK2H!9<3r#Ky(~f$rcyqST7jYcD1}#G6bkP-s|5tym@YQ!~ixdS*Ne8*6JD z3mZ0N4dN`B@X#|efz5Fc1eu2^6}-mR`Xy+c5Y1@-J#go zz#pd6BM_UJSw#))+zy30t3gUVDLp+SLr~t*PPZ`~3Jnqcl=^s5T1I+CPHh{F7gai1 zEAle!da3Dg$*F0n8AXk)ZEfxCZ5TmIfkMQ$ zPo|LtWn>zuHjW~cn3z*Xw@|G#-quu>lg$xK&+(l_hLVx7nOV7T{30@xR1b@fPmGVr zsKfT3ZdGZk&CANp<_Z+j9msIfFF85AN+@ivFOd%?CxxTWic#MIOhA=9ngTA4zIl+*c9q>D(+DXPI46!ymT1$3q&S~xnJ5vb?J9=Q>a zn3C7Q?N+WxOCrO{8QzgGQDO0g*h^PtmteD2o|VqYoR%#RBIIJ}E`(~v1gAjl#E%r$6ef_=kFYwK!ji?b>ltCJ(5!(+jpw8{;c`@bYZOzlgV z#ksjjnH9|)-4*bYjLd8}V19uh9z)DG(4vM!gvaJo*49-Zl~Ym6C#0s*X`f`| zS6I#=bIk7#o%bxt$<0d2scx-KPs0kbKq4={xFkb$tPsL<%dgrvC8kQj8_se(Kgj55um;$o6A>Om_vC&Du< zJ{1+Mw7xVY4ukS?rOLuYOfqLW-SPAf3{A=^sj8|h$}cKNN2@`{tE1A#$AD@Aoi?d7 z`?xL>6dRLT-Bim6-pol4^o>YL&n#ZlJ|mcwBS`k)$EM_UX8-#AVazjc zL>HBz23F>hdFDG-E*@Tf;eztAiqeea{HludP+#BB=$Pox5PoP}A}5(nnUt1aUzrA~ zZ4J2y%vm`J0ig+1Ew_GpGS5saBQ>U`B$~`K-!gOc@b-_*FDolA&q_+p%#RBX^$!RO zi;j&A52ItKL?(G^BDRTXIW-N{b=9RgB-Y=~D~a=qs*XMVbfy_DuhDg7+3ZYH&B58j z+b^-Wgeg-3N+5v_ry}UkiT)wzCu5>QA`+A6DhTXnGEv5+xf-OA%*^S zPbZsUwGa{&xxOZ!oor6fbK<#s`uW8dm(Zo}l~)wT2lxhsg+~Mj`iG(&3=0m4OvFAu zIUzg_Wa)G|Q#m^evBwd-lT(r(niT2v$FLK}+-&>W(p1C_JKr*~b$0WMPw??eEh&K_ z1d$>B9zNLpMFjIB>6ka8BBRNUJ~=rtCLZygo{pH!gd1jMaRu+@mSjXmM?Rf?vZTr0 zsim!@oJ>DGsM$7l&aR&E1$mJ^!PzCHMM=SYzMqG0XlOWL5D*d-8ygc57Rw~5B*ewU zr$KZ@Q0GGM83kntF_}^FF(`tbsDo)#YiVz(l8-?@GqbYgxp{bm<`iZJ`9u`tMg{}~ z@&o*kXu_cxUmyPncyA0pD27f_PLAP+C(~(khGJ%R77dM0FUShdEX_}hj?S!W0CjYM zoJO_gw$_F`b|m_tp{1Sko!BT3&)CAE6kornVE=%?z@XqDUw=M7EQ0SJ7#u;zPKpT& ziAFD;6dw_hK&L7rKr=GwECu2xYWZbR;dynfEp)w#+?x7?L3_P?L^@-$p@prpUs}E( z#KSwas36+M&yV(>78nEtd3s~l92OoO85N5i*W{S6P-219#E9q=Br7Ga0*d-V?sP+JN(@AK-!n%uNue?_VWt~rNfnR z+z=5?$0|c_i0q`P@u6YK#3|V_Oje*#knigefiaxi!Rs508daOyo3q(Ts`^bMOFNHv zz%=N*3GtpDfj*vI-hRFeIA94u0pKtz0=^W0V2Tb6g_hu+_yg&XxDcI5XVU_W+=?`N z_lR`NypRN81d~RU_Uat@fORw1(83`gjmen+FiB3BhX)wYK5zT^!4hEMX|A>MTr`&s|k7b4{$A!ZT^X zBm$Pni}wNt!eX|spRZqFAfNB=@9RyRDU=@=is+4v4-Z6~B`2pOM#rYWS6>GkL>UfQ zO6kjvezAgL`BntWz`Q;&1nnfovZ}W@Cf2SASxmMn_+*8-yVD-ZVC3V&_$mYh(m~3> zA%R3+Z$w5$goI&NhC~q)hvTF;%mZPM3}|a+R(5_vnc;DL@35q-Vz$F?XfSS6Z|^-Ue3GZH=Q`E-_Yil37+k9Jj+^YWy!L7 ze~f^q@4a!`z{1ihFg*i#CpI(6)rodi;<>oE(e5f9o_DZ3#`hI}f8+r^-`B$nc_1<@ zG%7M8CYFwWH!?CIDJd>8K82hpHRR|WJ;Ct`iAgIcE+Z#5b#y(|V9+?Rsb?SxyFdAS zc>1~Pw~d^_+^ifTGctng-29y!X(xt9xzcW`a(+Iv?+iablGu3C=v%J27m^U~*p1 zO$+t)4}d%IgMtE4&T%Rh>K_yl&BV@%Pe?AWzw^_N=WYE%qm#1ou-=NwO7uN7wM_kt zTK0gfdnnZqT%V4pXPkFRL@SyMcS!KIGPkm_wy~jYDSO&c1xF$-AsKWQg^k26CW zKj=Crh#ws2#}5f1hjF2PNTN87ORFt3IDGhub0CJEf;@C?@Ln=*BExrN0{p6PA1*X{ zZiKCR_-Y_&Kj>uj2%fpQg{7qxIMd*)?9B5>KtZIlCkQeyM9obM@C%>==LGQsJiUAa z!XwCvgAuB+*)=Iw4*#U*5*VGF0mNQTeldFx2&T2wwVe8CI8th~uy}5=t@x{D9G$3w zra8dYoVK7Xm94C7Y@z8_9UPr_{web0oE0Aw5S^8m9f|5XHvoYf;P1~TjLDH)Y+ON& z-?2l#T6hIV0V$YQkY9k|Hyf{DTvfxV*Jv8(bq9>$nYs70y61?@^;S|%q;Bhk;((>p#lFEvOWzI3n(j*8J%G0_W?%JWSR9YMhei%Uq$#?%@| zfjA&S3_-;}IIx=TBV(**C*l)NF%dX(OANI)HKolI%qN8S7MaaOYPLzV-E; z7!5o>E}wGJHYOn<&fDC?#MIQ>j5b%dfQfO6U<(^7xdue0a59%?CHQ)Hc*W)92%>!b z{Qdk%iVX1eMjS^(JqPMGW6V;j_?>{p~ zpSbsocS1shqp^vR32mxwW^M*mTUsHYCfV59+XtalA$w0cOFi1pKO#LBdq>7k5nUS1 z|Af@I@VJtEvmbsqZ{vjlAJ!|##g@3Zs0fi%T~k#7AW$6x`vRuIB0c^bv>&|{9`9#C z8!4E8?`vje#KhpsuGrq*FDadDE$K{^tjx63Ok@z{!~m?B33wmJ$3oOq zcr#P`N)2Ywb@g4tX&mVInW6oA*X%ilj1h&EDq^Ly#Uw@Cmu^%srey6rluyR5Ir(k zDgr^AFRAJ8`1=JUR|NmO|0hhoB6F%6J3HE&YYPSJDQsyOQ?7_JRZgu+U44JIr@=Fr z01WV#UNdG46=5@m*bI?54JNa)=aW1f8kj%@%-nqA z%Uip8`-U(i?W;rXK|GdWqYNK`lhC!wb;5yI!{-45O6vQtCM?a!*oZNq%(&*$E$sZ` zlT(rd9Ya%7($kZZi5}<0BOH-|e7t>w^HVPF`~H%RM?hRvJDG704PzuLZ7V|BK@cJq zs;jChs_0scj=>y*zXK7xqhrZ2q>LEjBc^ajb8~wScRt#xFi)4D7#upJ#|K3uBqcP?ftHB{ z(WMb|`6k#|`NkzABx7NB!Xw>modRR{E<8`KJB-`s?xd;pPc3xXx9^0pi+5mX7N%Pm zw+{}sRW}XZ`)#b9jHTGJMb}KPt!)?(x**7(!5Jv29k(?hOQTIx%d(e_4+A#F6n#Do_e6Nxp2x;q6$xsYN-+Al|Ew0thK+b8bfg;BqJ zbLhH*2dZ*-OhtRAu)A+?pb9QuJaqrz{ay?aNHo_>tF7rA!ff(6NWwQZR*a#Vk&&T^ ziEVg1ouKM#?-d&2gmkGyN(AkoguVw$`qa%Mq1bKz-edYabd;FR##MH734x~=sK>CP zN^<|l*gk(k6s5TE_3iLcsVbW)j+pr)`^|YFX!8Tj{=b#Hpi;bL3 zXhRJn3oo4Q#0NV$28TJClPIQblwcw>a94-OYU}PB7)16pk*vdQw(t z`!4~(c6N@;SwW<%L`J2Q6Bcl648!mJ24DwRphx0VPwnhaVvvWPfi}E) z$<=~3oM7x77wu;47vW(^o2gh>SRtv?))aL0g@c3Ros<;4ulD?E%Ddx>rW<>vh{%L0 z*aSR=3DiN(<9-KLF|}ndkNX_bLFo%q59mqVBh=EYch}IC^Kq zoZh+rvbBq+A9l`U5s^`e)$MG+3OdPpXcHGN zOPWnNi!9_t3DiS#T=wofc-zL+i|kT@aL5}DctvJyCx8`zMiLkVfmJ;2uB>8UgMQ&1 zv*%$D6R!9n#VC+OO`9r_eu}Z2i{91N#w9V`V8`yC4IErNeXt)1;)f8;V2+yG1_KWf z1nd}#*^pK;Rnw}gg?-VS=dlrUZN0dLO2)<}CLGhn^2QX!)W$s{@$9xeXG|R3JbloB zKcO5Aj7eEXFDMh7{$1Gh!AMgj4a!w*0~w6{pKu9pO7T0sGY6T<5K^llGolZ)Ko|H$ zP_Fm)JHEYQ;pFa#jc|Z}06#bso5a}Midx{z+WTS75eaTHjPyX)RZ|-WtBovhNbo0o zViLo*=w=NhgsHa0>7XF|wYSlA~doZr3o7kwKiS5GfF)gS~Z znei94cXqdAGzn`u#o~sdViej6x^h}oRZnjynulkR6~{u{NMfT+mXP8A!rqy8zWe%n z9TOX8*E?S9Q2>;S?gR*lxaXA!$Op>Ky~A^z$H#35H=-&;eBY z3JR*JZAhZomc1u+&2Ub6$IAx?=l*lhUBZhaV&co%I+}ATI_glWNgK@;G^DP^!D55w z!4_-WgUsYaH<+5)#zY@mp{c_)vvYDKr%F&NIZ`5mCgxYPbaWIHcGQsSPbxC$R3U2J zK(h@R@@Fs?r$SvBww+n#p60%D&IhZ{8QVFyx`7_LL3uDhMnOYMTSs+9YZERa$yrk{adgi}g~!voQL%rJ6E5Bm1=DNpbh-&+Oxp)um&ioe+Ponvee z)(b~RLme8PSdJSt1=+1t*gME=0&=iPbzeIG=Fi|Sw#+yl6p!Q0Q}bp%i@!K%A0LiQ z7&)~;c8V=;X)VicY$Tl|5spY~f1Gz^v*y$FBtopAyNMhJoElIE7&+@?YtJi~Ta4tj7Xr>3vX@x}Oy z@3}E)VNfhkr4{KlmDrPkvE12-V!M0z%nR`s`}E?nqQfGh5;6SC#e9ry$HA6ed+R{_ zO{B9I;4iMl6~qEk7RTa;z!Fn6xz5TOhjQtc;4iL56{Y~jgcERfnNPy9w5(>h!Te?T zi_m~S$lh(y;)vU#vhl=%qM0^qc;%CeJuBNJz zvX-Gb%a`FVEJ`|id-@0Ka18lk`~?$Q+SVelz4}u8#SIIK8~?#ytXjNu+4>zDfWP?g zv%k$MnFVL$K}AAB}%-S$1#(1R*kUfTxz#mtXCU9x%G zA+821>qn_c^V z*t28zjzzb4zG0>HoCdX`NOS?7Ec9a5JD;r3-m7DZ0|HZyp{3E$Z8SbMZTt2;r!QW* zdPC={-D?indS%yf8YYxRp~u4*({!(Zn(_9#pRe6|SeJ{w4Ij0*`R$f1^ww9lZQHu_ zFs*xFtM&ozwcR_{UJS#XN4jBBRV>-2pp%PmBEX9o@4Po(Yu80XQ*#qz>rx7MYk{K_2mt7+a$IZDHPM`XE$By;( zY3Xcf1S~fWz=fpLq~v%Wxjp{ctPhrLIz$`m>l>53>$R`7u?X$Co3}s`{QmarW&*h| z_wzBkasI%L9b3P`DO3>I&qOBX78jQa($dq?l0r;kscEmz`fSzKlN>`6?u}b|MnuP@x<(neFc4EOfI4I4ITYj47DEREVqZ~1)d>C@YEZ@GK>ge4oD+YYbax$c<# zoxsrW^vbB4Cr_La`n}_pDzJ@tBPY+7OS~tg&S?OmObP*^}Sx z-m)Fh&Fpx4_uiizeDZ^Sc>=UJ&(F_6n_rNZl_&>VOrQDoLd_rbxQxCkef#DyZ7nTk zJ+n!9<0crEqph%cE3-vq+pe7(wYD3pisfogWMdWHy>06@edu)Nw}(yz#s%83 zm&+>)S-OREky>e0MP4Fqm%CA~Oq(%#>8cgGujyUXUiJ0$%eyr-*J!SzwUpOuK^gQW zW~1V!&0834B@#EBEy^SdAyMk@n10{~^Eg3jbVOuoc}adA9^O#M0pW^@;xraf@y66? zuYJDe@OLX#9@3+auU-AM_L|jeG$A&KtF2qV0a}JXY*HXWNg)6fk!0vCs-REw4f=bJ z|LT=oTvJ(EQh>)xfP5jhNDUL};v|gOohXf|8nfnUpT2Q*_lmWr^ltB8MX#pTFq$*h z$?dpaYdvhaX%h(uc!x6Fjbu?~8wYBc_Vq7^Z37bWii+~sPPm}Zph&H_qB1W6*Uud& zl_?q@EZujRzM+14P-s-7T3S^Kv2i2)4V5V~Kh-*>d;R*2n>T-4y+T`a<*Jpd z*Q{B+nxjdtQ-jji6N@5HhzF`|Mi`T9#evT09n-!#bLs~x??}wd2uKYo0qNqZyktP0 zZ7J0WQ{J4n^PDboUG3&g-90N-tX#>gQdqs3hTs#m))T!T2seSY5|Le=QsIUmJ!8j? zJ(n+UJLcdUm4aOtc@#&XF|=Kq7E8br)|A?$sUIx+{<1EJE8VzpY%RTlTFF_ZPArQ5 zwARtNI=jz+(mBEZM$_j+S`n9l8onSJe6L(xT-ER@Ck52ed6>_ zHGjFv=+f7fF7I8roLQl;awQGjb2OER;yGFh8;Hwngl3fCkHlf#U>$PX_WgRCZR;*O zdWFPc#72S=@~f9N71+BIw1fqvu0G+7c}rIPa`hUct9JdA_OfNmS1hMj&?}WzuU@r= ztEsRS$TR>TIxuIB*kn*N#kh0{fCma zpR+ixx#5iMo#5D%^sGz*AS7$3=oFAEa|9nTT}@r%lNCQ-KC^N0o~zfco!zo@DZNZ# zIrK!YR3LnqH7c5#>kt8q7H5MJ65}R)Vu~%VZr!^3y2TGmx1O`T!;eWy0~D7WpcofT zEbXYbagg7MH=#6CRbQRAaN#$XF8{Q2+0m;61H?G&h zI@WJw^BG551=*K$2U}j-`kk@f=2hQbb-05Yw7CDt?oSH}4T~n0GqNxXsC zmQyAdwX_b?Eg#*i(#mtgvOP4NN3P01_b?BF~r;i`lvW|fg zC$C+*VLf4o97qy3S9{jx&EFc_)?TsalBHWfcx+;7W;&L|I&mRgKov4Ylgm2ltqh)m z9SkWIl{eQO(*ZlR)7lG{YcE-}h+eF)1nbj)U#~lI`I@foHQj6H4r#MyQv^ef76o1^ z#PhUK(_cGja%KIR?{3<<_=LnJrC=C`K^%O7DO3bsx@dAqYv&zuw*3U;prZ8Z=3jMm zI2V+DTDD*Ty@<0|f2pG8Pgm({99_k0r?wOCP*}H)L25*J(i`6a7j5lrS8R_h-te=b z^Bw=N*u+FKrxf77{CuWxMnOKVa}+fRqj1Rh2dF_wZROc>G*RQ}-HR72Sh$e0NOAF! zCCpOA72jN>uTt0abrnu;rB{3h$g14qRRN44nrN(&c~#VIY8^VB+Vh47?x)pX8X z)zv+w#imjf6t4~Iu~0=Qliu|D5#t*hR(*5X(gimcfeubcPQxA_DTppm$S>d&PAe)G zmK))=`!kpH6&1fcO`liR(K)++88e@=PB9P!>p$_ytir7IqMTk*Z;_|clQg4j3GEXb|lNDrGmVCPN7CgXNv=zExCL^XU2X0?tAj?39*%b>RxUxHR;<)KqN{s+JqbQdO?s^|TjuEX z?{C=n^)2o33 z^gL$1!h!|NLIvWZ^b*CTJI`~kPCm2}x$)b}x)*jK<{8b8u|NdHIxKU;e){_Q6?-pP zI(r3#gh%512Ofx$jnGbLl#*z506Vmwk2K%--E600)39vKNDDvWXxrN`7H^MeUuw+-I-}#+1^s z)7o>na}?&zrRQ+s<#qVy3G-9SSMS%g zaJu6c6ihe~>?a_y>6=SNE`$e4NWN0C4MVc3$wYF z&pu&b=9l_&6z9$bFM8h8`SVF?S+HQ^?v1;Cz5q^~tIRc}bK6J>rPsWzskxiO*|c=W zuZA`*{^0;|N0VdBcsK^m2K@i)R?cQC&yQy3R}QoLp|c zNorOQ{a7UgTdY+OP5rq%Cn zJZ*Sr;kqNYEgggNYMa|}?SEjXs{~**9IdiGK<6ps=h6Af1x;PSVD#s+3M?qa50}j2 z&UqV3n?u$^ls13)w`VV1ys&TCcb6_)#*J7oQ~7y4>2l~*AFSSd{nn<%+fV6RI|mij zH?`qGQvG-U%TQga{6sbr?z&I9_m@3!K+&9=IYORZSGv{)2C0I zyKv!0?KKCEYb|7J$&w`oODU{j_P6?1)-K(B&d3_0uRFfEcu-1fhftW0_{`|L_uyWe zJQHy9r`8J-Z$FPP_-w}tR#mHxGZ$3%YVQ2;@M<`;!eV4`F694o`RWtq#}}{pj*RMD z-95bm^6{jQ_D-R&6w#75go&!S1mfdRfX-K~=q@z)J79s*itS4|bKYFAX#V$Q#?T|1vaHKG=wL0kISvSmAO zUe{W(^R$7bBhSU1Kz@Bws+-$7gg{~p3I}Arf&Tq^1Y@2?L3^7Airlk+g3rFvV&+U= zcu?oU+1)F5o;bLI4H8s4P)9pPFa7ifgP#|#`u2*6m4mYjP%J>`gy+@}`hWqEC%QpN zkAG_*^rzN$M%+R=c?L`{N$aa+^qdLv_UWA6x_tlfor{@y6r@)o`e7D-zI5q2om*co zSbyY}nGFUHt}J3cxB!nvff0HMmS7ZP?MILA)e-WQ!qnT(BMDyJxqAUUM{U=!Et&@p zubRV|r%pnAK2blRzV+Jm4GXuP;+osSv0aGt-aa9CVjGkX|w|Kcn|{pNU??F4UMACCMT2Ukpv1_hv1(ip`(R@%D>P&!|a_0cQmrF zbpVjV#ns&d`o<(MskV(MgeV+OeR%YEC>QQBwYfd`_Vf6Gw+?(`D6e@8dZf7v7c5+~ zNtf21xAEvLW6%UqR!%@!gM#^jh9^W>P{@68MGl=iEibP|7=QD5?7)JP+N3BfB}o+7 zjV(RtMxT2iH zE=T_C%r=~R&vExi~tvp-6BWdCm#xDhM^;O7mNr-B5&|c{ao3Z_aEq zm@{+f;oAp4oH28O_U;=t=9Yk60YyYR&*Bje_w~!bWtyV+lKN!yC`mxc%4M6<(80g; z96aE&lRqxz&RMYHJgq%zA`1@q#t1jp31o=1gsYo(B%WweRa2T+SeuQ;n@nIys|eYq zR(2&Lef;rk27m*W=^UDm?5C}Faq+84Pp|>|O+1_dJ$nbv;_Bw^?wt%IMRjFdHX%nE zLNqrhX&_hHRiytsHelk;o2S+;Tzf?K$j2JgAJ~9>Rw1rfEXj(l?yy5HFvRKcbp@zj zWUkD1F|ccXN4?$i*Z|d4=XH;tJiFmd)n~B*-&=)y6SNwU&eJm>r>a^IS5XlY9T$fQ zFfjz#1?RN1`7_U915`fQ_4V3Mrz<><4X_CJmuCdez__f^s@$03sw7#?9@X-w4-42WK@&L>xraU*QFb`84wr?kmF4075 zn-~3JXuz$AB!3`Pf)m0clh_uSwFo|n&(;XTu0IP6_~^MuGh7bO3aCYR11^VTM*tc|zzoqbau0xxa7nhW%TYsIPxWGGz!`o?8X%J}2f-B(I0D+s)(-#cFM|f0<(Fh(EBP#=x}F%_mqG))OAF%U zs+Thx5)UzdX| z6?tn@cf|FVK?5ucnhJ2;Dl@yPHw%!C7efOq1g%y1nSz?$Qd6UsJet9@psf=q*m67a zT#pw$nt=%}ZmLhWxbkvnfSK9#OaK3PG{dE3XMUxqwd&;Q@c;Sxj|Tpuf&XaWKN|S| ztOgYD^?ytFg;t4$SfiIF@O8tpizokn zU^9EB+P}#KUNw>aJ^S(T;bZS>sQg<5n4kIIx#N|}?Qcz1BFRYOfaR41YX2%rC>&`2 zOZMaAk@H`hsHFJut@yUz?hT|Io$)VmhndE*7qB0{Z~5`e2_KmhJs@v*e82d>D@y-# z5$2`6h&x^v{q*4*rUiezL1g%@%0Jf%O5e5qLH)1(QM>DA>f=9uAoKdKDgR6XUbDRW zC-J}hSAODe&%XXBd&|s!o(Y!cK1=)gfB%jPa(Ly@-wRlG_Kkm%026*?i{0~VpM3ZG z1N;w)WDo!9XMKk6|5KdcEmzj9|BA)<@4pEP;v$nr9{tr%N5dBU6P#du)nCzm^4f#8 za7#l@LDleIzbAk1^#7$7s-N!tYtrnize~by{JhtGWg7g1*U zKdRIDdd(F^J{t!vA@Lw7&R-gysCxztcXN=Jm4Fa6fDv>jeJ?LQJ_l@N)L! zkJ9)5knNoJm+$(We373NeWd~B|8xHD2eBXjOX5#o?7f)dlJJ+#|ME2)c{PEauPkX2MH#Rp(Y(Af&_&<|i8YB5Dvj6S1-(_R} z?KQmc``wP>q_Fs^!F#N7ev4i`UG;w=!F$1fMfD$l?QwUq#o2wwY-`5k@$-A`;h7T= ziT|Js-@eSF{}CzfS2!G6zjEcaVA+%0`Ka^uhtr7w|3EgFM*m~Fe~BRX1p3I5|P(1jeChs3) z&A#pTq2S}Im+a;hKijfBsyhDGWM##dI>Bp}f9cQv6W($GL^%$Y(G}w7KKT29)n_k{ zgT?93ec*rb&F>E-J-vSz|0yBqjoBJ6&P8gc`=7q?U-fs_&+lodzQ6(AaJc`kV*3Z( z--B1aJLRwW&rSQ|UH`&=6z}HiGW4_Hwvy(!%pQrRS4&~Gq{(Gk@-dr557*-OqqF3l}mDZLbng8?7s)`U3c|msg zh^^vwBxzbR|2*H)l3=eQ$_Z=%KtV9QvOUmme?RW)&hpff;9+Hk+0WENYd<5*|M&Im zlze|fDFF^<_=vp%9b^B;%_YUb=JGRZz=98! z_81tL-Ea@u5Hyk$1%E#s>`wR8k>X=xLz*XIWU-)+|NnizI4#rLNKTLwXg$LFjI6fD z=u-3kyt$;v-$Y)BiybNLF)%W6I!r?CoRJcy#H;=DWPN3nrIHw^*hlytXg`}#>K@v6 zcm96d+nVI8Cdvyccu?$TQ3|iVL+cp$_w8g~s=KZP4+k5H^+5kKbDJj3qIjHxKt%kw zHap8*PllHh&3Xn#CT3PK&zu9aEdqZ(Tv;A$A}7ef4xAl>H9EjFBO?;_qdvn^XJL(91mS7K6b20AMOmG1K4?VliHqAIS>5(u&p#eUxpvG&V^eGX8>yfRt|o< zY%0gVzpp17gN@|`I8hp0a9fcWpcvqi2`oK>bqa@+5Jy(~ZB^l4J!B0ti#}fV?f9@dnNTo|Ng$+kn5=_Nhs|>odPa}q=Snu zP^%F5`}shHkG>Q?7p`&-EeybUKqs*sOAP$`O?s^S`+lxH*g!^r8>7lcvlqsNX(tJ4UTLVX!%9BXMevtJ0;oG z(Qm@#C*Qy`xP(IjaOLR{F|dvLq3GJp=?5Om6$_2T!xe}8{}y1P8zRbNY6-`F;6`sJse zi41|iA2*~qsEPn{9x0Izb`K~Ma><4iPQUs2>&KTTCMR0ys3|I_X&74j^q;)Q~)Y? zytO63X%A}{;}--c0kyEa(yGctdwq2UX>nm85pfw69TV5mefNoQ!0)@WLQP~yPkXQc z0A)fxc}s6UCp`^ic_~pLJ{}%^5h*1tQ^%a0_h0=akP832Ss81gAjnO6+JiX&*dyfR z6_%8dlarH?5aH+H;^5%m5t2~WFtkb7aP8?2ybk#LXVr8#@Ol2L~G~xZw&8 z57r5c@a-eWHa{(jh!Vh-$Plb!b9?gK8NWdZ^Cx_*!YIC#aRm32(qn)cp#{}W3=@bBy4 z0vlyfKG5hY+$3@tpjEs;0M`y4sNfNjQqneaFWh>4{jd@1Ly!? zCq+n7QOndme#7;r-_TRxpNG@@b;W@N4;6fmVLMO&>;P^-2?bz5kht{9qpzqA`1^iM zw27=Bu+l>cHEiaPF9r+&Hf{lNU_oFVJLCL=PsrKe-7h99k_V&&e=6N zwki}>dPG|fR0t}GfCYh~hLLqp(WJ%8CM1}vNej{;@?nky6$D%YVsfgwCic#Lfew1g z65vV?CQox3ToCYyN-JsT=;`Td$OEmXVY!FJJKzc#cyg|cf|8Pgw3q;G%RL+p;NS+f z(!|AqO;j?vI#|pkNeGk;*f_X(`S^e>R4fDgBpF7IVxR+9%}Qx|M%?Vq^)=Qmr~{^R|d zI3<7i{GTkpRH)KtU8wF1ZvsJ5BMtk4L*|yqr+s*uqmvY6qV~@Se+a0^E zeD>OJAM18s&adEp`k%LPZ@9sZ9yN1*pM#G--Z(Q&UU^FW z!S`JHW|u!l>z=*2VSjhG?X=5jZ*RRb`}vpCxOS=P{*yL#;==9+?+csH>TTZm|LpTO zRkFPIo#Qv&{Z@8&@Acy1`)|w3W$mU^1m?sKj8Q4S-B(oAAxem03UI-gA;00*_|fzXmdCV%+cjNY_RKDg$w&1hYFWK z9(SQ56Kz6;T_^fPi?|8K#7Z7Bta?^)oc&Kh#qq#1<#NX-{%Mu>n8eeq;3*{2t9sJN z>RF{1`=14sUO_#}wN6divsK%B(wW^lK0yAmQIG*B57f)Rz`-aA z6$gqiiowJgOBluB?B|S93_ue=>Y0`?O2gUf7-gV(fijF5NahD2#DV&Nwm>1*p9Tg7 JbpZw_4FG|-{1X5G literal 0 HcmV?d00001 diff --git a/src/kim/agent/mac/resources/Remove_Pressed.tiff b/src/kim/agent/mac/resources/Remove_Pressed.tiff new file mode 100644 index 0000000000000000000000000000000000000000..de3f877911039bd5f5e95f4acbcbd6b5218514ed GIT binary patch literal 656 zcmebEWzb?^U_8~ppx_uGz{JVibnwB287WG#-0g=J1>9%}Qx|M%?Vq^)=Qmr~{^R|d zI3<7i{GTkpRH)KtU8wF1ZvsJ5BMtk4L*|yqr+s*uqmvY6qV~@Se+a0^E zeD>OJAM18s&adEp`k%LPZ@9sZ9yN1*pM#G--Z(Q&UU^FW z!S`JHW|u!l>z*yWVgHRQcG~gut+#T&y#JK;-OIavQbmlMTz}(Uwe&C6b!oTf?tFf2 z^_+X{;f2>d8$tb2Uy)YFVp!>eH01TBn8HY}Z*=%(c@{*I0R}(V1eOi>7Bj=bSY^8(g$w zuHWP}Z>`S@pZRKge)6fN1q&1y_!t=&m>GbP$sh(K7?IdaU^WW_15*VfGgO=vsD=&7 zW&*MW8G!OYy$lQ-jG|C+pa`QFOq{WVQ5?=b#3;o8Gy$ZZsew@%&hB88f$9ayFlr!~ XAA}GG>I2#Wg +#include +#include "kim_private.h" + +struct kim_ccache_iterator_opaque { + krb5_context context; + krb5_cccol_cursor cursor; +}; + +struct kim_ccache_iterator_opaque kim_ccache_iterator_initializer = { NULL, NULL }; + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_iterator_create (kim_ccache_iterator_t *out_ccache_iterator) +{ + kim_error_t err = KIM_NO_ERROR; + kim_ccache_iterator_t ccache_iterator = NULL; + + if (!err && !out_ccache_iterator) { err = param_error (1, "out_ccache_iterator", "NULL"); } + + if (!err) { + ccache_iterator = malloc (sizeof (*ccache_iterator)); + if (ccache_iterator) { + *ccache_iterator = kim_ccache_iterator_initializer; + } else { + err = os_error (errno); + } + } + + if (!err) { + err = krb5_error (krb5_init_context (&ccache_iterator->context)); + } + + if (!err) { + err = krb5_error (krb5_cccol_cursor_new (ccache_iterator->context, + &ccache_iterator->cursor)); + } + + if (!err) { + *out_ccache_iterator = ccache_iterator; + ccache_iterator = NULL; + } + + kim_ccache_iterator_free (&ccache_iterator); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_iterator_next (kim_ccache_iterator_t in_ccache_iterator, + kim_ccache_t *out_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + krb5_ccache ccache = NULL; + + if (!err && !in_ccache_iterator) { err = param_error (1, "in_ccache_iterator", "NULL"); } + if (!err && !out_ccache ) { err = param_error (2, "out_ccache", "NULL"); } + + if (!err) { + krb5_error_code terr = krb5_cccol_cursor_next (in_ccache_iterator->context, + in_ccache_iterator->cursor, + &ccache); + + if (!terr) { + err = kim_ccache_create_from_krb5_ccache (out_ccache, + in_ccache_iterator->context, + ccache); + } else if (terr == KRB5_CC_END) { + *out_ccache = NULL; /* no more ccaches */ + + } else { + err = krb5_error (terr); + } + } + + if (ccache) { krb5_cc_close (in_ccache_iterator->context, ccache); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_ccache_iterator_free (kim_ccache_iterator_t *io_ccache_iterator) +{ + if (io_ccache_iterator && *io_ccache_iterator) { + if ((*io_ccache_iterator)->context) { + if ((*io_ccache_iterator)->cursor) { + krb5_cccol_cursor_free ((*io_ccache_iterator)->context, + &(*io_ccache_iterator)->cursor); + } + krb5_free_context ((*io_ccache_iterator)->context); + } + free (*io_ccache_iterator); + *io_ccache_iterator = NULL; + } +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +struct kim_ccache_opaque { + krb5_context context; + krb5_ccache ccache; +}; + +struct kim_ccache_opaque kim_ccache_initializer = { NULL, NULL }; + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_ccache_create_resolve_name (kim_string_t *out_resolve_name, + kim_string_t in_name, + kim_string_t in_type) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !out_resolve_name) { err = param_error (1, "out_resolve_name", "NULL"); } + if (!err && !in_name ) { err = param_error (2, "in_name", "NULL"); } + if (!err && !in_type ) { err = param_error (2, "in_type", "NULL"); } + + if (!err) { + err = kim_string_create_from_format (out_resolve_name, "%s:%s", + in_type, in_name); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static kim_boolean_t kim_ccache_k5ccaches_are_equal (krb5_context in_context, + krb5_ccache in_ccache, + krb5_context in_compare_to_context, + krb5_ccache io_compare_to_ccache) +{ + kim_boolean_t equal = FALSE; + + if (in_context && in_ccache && in_compare_to_context && io_compare_to_ccache) { + const char *type = krb5_cc_get_type (in_context, in_ccache); + const char *compare_to_type = krb5_cc_get_type (in_compare_to_context, + io_compare_to_ccache); + const char *name = krb5_cc_get_name (in_context, in_ccache); + const char *compare_to_name = krb5_cc_get_name (in_compare_to_context, + io_compare_to_ccache); + + equal = (!strcmp (type, compare_to_type) && + !strcmp (name, compare_to_name)); + } + + return equal; +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_ccache_allocate (kim_ccache_t *out_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + kim_ccache_t ccache = NULL; + + if (!err && !out_ccache) { err = param_error (1, "out_ccache", "NULL"); } + + if (!err) { + ccache = malloc (sizeof (*ccache)); + if (!ccache) { err = os_error (errno); } + } + + if (!err) { + *ccache = kim_ccache_initializer; + *out_ccache = ccache; + ccache = NULL; + } + + kim_ccache_free (&ccache); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_new (kim_ccache_t *out_ccache, + kim_identity_t in_client_identity, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + kim_identity_t client_identity = NULL; + + if (!err && !out_ccache) { err = param_error (1, "out_ccache", "NULL"); } + + if (!err) { + err = kim_credential_create_new (&credential, in_client_identity, in_options); + } + + if (!err) { + err = kim_credential_get_client_identity (credential, &client_identity); + } + + if (!err) { + err = kim_credential_store (credential, client_identity, out_ccache); + } + + kim_identity_free (&client_identity); + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_new_if_needed (kim_ccache_t *out_ccache, + kim_identity_t in_client_identity, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !out_ccache ) { err = param_error (1, "out_ccache", "NULL"); } + if (!err && !in_client_identity) { err = param_error (2, "in_client_identity", "NULL"); } + + if (!err) { + err = kim_ccache_create_from_client_identity (out_ccache, in_client_identity); + + if (err) { + kim_error_free (&err); /* toss error since we don't care */ + err = kim_ccache_create_new (out_ccache, in_client_identity, in_options); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_from_client_identity (kim_ccache_t *out_ccache, + kim_identity_t in_client_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_ccache_iterator_t iterator = NULL; + kim_boolean_t found = FALSE; + + if (!err && !out_ccache ) { err = param_error (1, "out_ccache", "NULL"); } + if (!err && !in_client_identity) { err = param_error (2, "in_client_identity", "NULL"); } + + if (!err) { + err = kim_ccache_iterator_create (&iterator); + } + + while (!err && !found) { + kim_ccache_t ccache = NULL; + kim_identity_t identity = NULL; + + err = kim_ccache_iterator_next (iterator, &ccache); + + if (!err && !ccache) { + kim_string_t string = NULL; + + err = kim_identity_get_display_string (in_client_identity, &string); + + if (!err) { + err = kim_error_create_from_code (KIM_NO_SUCH_PRINCIPAL_ECODE, + string); + } + + kim_string_free (&string); + } + + if (!err) { + err = kim_ccache_get_client_identity (ccache, &identity); + } + + if (!err) { + err = kim_identity_compare (in_client_identity, identity, &found); + } + + if (!err && found) { + *out_ccache = ccache; + ccache = NULL; + } + + kim_identity_free (&identity); + kim_ccache_free (&ccache); + } + + kim_ccache_iterator_free (&iterator); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_from_keytab (kim_ccache_t *out_ccache, + kim_identity_t in_identity, + kim_options_t in_options, + kim_string_t in_keytab) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + kim_identity_t client_identity = NULL; + + if (!err && !out_ccache) { err = param_error (1, "out_ccache", "NULL"); } + + if (!err) { + err = kim_credential_create_from_keytab (&credential, in_identity, + in_options, in_keytab); + } + + if (!err) { + err = kim_credential_get_client_identity (credential, &client_identity); + } + + if (!err) { + err = kim_credential_store (credential, client_identity, out_ccache); + } + + kim_identity_free (&client_identity); + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_from_default (kim_ccache_t *out_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + kim_ccache_t ccache = NULL; + + if (!err && !out_ccache) { err = param_error (1, "out_ccache", "NULL"); } + + if (!err) { + err = kim_ccache_allocate (&ccache); + } + + if (!err) { + err = krb5_error (krb5_init_context (&ccache->context)); + } + + if (!err) { + err = krb5_error (krb5_cc_default (ccache->context, &ccache->ccache)); + } + + if (!err) { + *out_ccache = ccache; + ccache = NULL; + } + + kim_ccache_free (&ccache); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_from_display_name (kim_ccache_t *out_ccache, + kim_string_t in_display_name) +{ + kim_error_t err = KIM_NO_ERROR; + kim_ccache_t ccache = NULL; + + if (!err && !out_ccache ) { err = param_error (1, "out_ccache", "NULL"); } + if (!err && !in_display_name) { err = param_error (2, "in_display_name", "NULL"); } + + if (!err) { + err = kim_ccache_allocate (&ccache); + } + + if (!err) { + err = krb5_error (krb5_init_context (&ccache->context)); + } + + if (!err) { + err = krb5_error (krb5_cc_resolve (ccache->context, in_display_name, + &ccache->ccache)); + } + + if (!err) { + *out_ccache = ccache; + ccache = NULL; + } + + kim_ccache_free (&ccache); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_from_type_and_name (kim_ccache_t *out_ccache, + kim_string_t in_type, + kim_string_t in_name) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t resolve_name = NULL; + + if (!err && !out_ccache) { err = param_error (1, "out_ccache", "NULL"); } + if (!err && !in_name ) { err = param_error (2, "in_name", "NULL"); } + if (!err && !in_type ) { err = param_error (2, "in_type", "NULL"); } + + if (!err) { + err = kim_ccache_create_resolve_name (&resolve_name, in_name, in_type); + } + + if (!err) { + err = kim_ccache_create_from_display_name (out_ccache, resolve_name); + } + + kim_string_free (&resolve_name); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_create_from_krb5_ccache (kim_ccache_t *out_ccache, + krb5_context in_krb5_context, + krb5_ccache in_krb5_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !out_ccache ) { err = param_error (1, "out_ccache", "NULL"); } + if (!err && !in_krb5_ccache ) { err = param_error (2, "in_krb5_ccache", "NULL"); } + if (!err && !in_krb5_context) { err = param_error (3, "in_krb5_context", "NULL"); } + + if (!err) { + kim_string_t type = krb5_cc_get_type (in_krb5_context, in_krb5_ccache); + kim_string_t name = krb5_cc_get_name (in_krb5_context, in_krb5_ccache); + + err = kim_ccache_create_from_type_and_name (out_ccache, type, name); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_copy (kim_ccache_t *out_ccache, + kim_ccache_t in_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t name = NULL; + kim_string_t type = NULL; + + if (!err && !out_ccache) { err = param_error (1, "out_ccache", "NULL"); } + if (!err && !in_ccache ) { err = param_error (2, "in_ccache", "NULL"); } + + if (!err) { + err = kim_ccache_get_name (in_ccache, &name); + } + + if (!err) { + err = kim_ccache_get_type (in_ccache, &type); + } + + if (!err) { + err = kim_ccache_create_from_type_and_name (out_ccache, type, name); + } + + kim_string_free (&name); + kim_string_free (&type); + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_compare (kim_ccache_t in_ccache, + kim_ccache_t in_compare_to_ccache, + kim_boolean_t *out_equal) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !in_compare_to_ccache) { err = param_error (2, "in_compare_to_ccache", "NULL"); } + if (!err && !out_equal ) { err = param_error (3, "out_equal", "NULL"); } + + if (!err) { + *out_equal = kim_ccache_k5ccaches_are_equal (in_ccache->context, + in_ccache->ccache, + in_compare_to_ccache->context, + in_compare_to_ccache->ccache); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_krb5_ccache (kim_ccache_t in_ccache, + krb5_context in_krb5_context, + krb5_ccache *out_krb5_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t resolve_name = NULL; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !in_krb5_context) { err = param_error (2, "in_krb5_context", "NULL"); } + if (!err && !out_krb5_ccache) { err = param_error (2, "out_krb5_ccache", "NULL"); } + + if (!err) { + err = kim_ccache_get_display_name (in_ccache, &resolve_name); + } + + if (!err) { + err = krb5_error (krb5_cc_resolve (in_krb5_context, resolve_name, + out_krb5_ccache)); + } + + kim_string_free (&resolve_name); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_type (kim_ccache_t in_ccache, + kim_string_t *out_type) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_ccache) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_type ) { err = param_error (2, "out_type", "NULL"); } + + if (!err) { + err = kim_string_copy (out_type, krb5_cc_get_type (in_ccache->context, + in_ccache->ccache)); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_name (kim_ccache_t in_ccache, + kim_string_t *out_name) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_ccache) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_name ) { err = param_error (2, "out_name", "NULL"); } + + if (!err) { + err = kim_string_copy (out_name, krb5_cc_get_name (in_ccache->context, + in_ccache->ccache)); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_display_name (kim_ccache_t in_ccache, + kim_string_t *out_display_name) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_display_name) { err = param_error (2, "out_display_name", "NULL"); } + + if (!err) { + kim_string_t type = krb5_cc_get_type (in_ccache->context, + in_ccache->ccache); + kim_string_t name = krb5_cc_get_name (in_ccache->context, + in_ccache->ccache); + + err = kim_ccache_create_resolve_name (out_display_name, type, name); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_client_identity (kim_ccache_t in_ccache, + kim_identity_t *out_client_identity) +{ + kim_error_t err = KIM_NO_ERROR; + krb5_principal principal = NULL; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_client_identity) { err = param_error (2, "out_client_identity", "NULL"); } + + if (!err) { + err = krb5_error (krb5_cc_get_principal (in_ccache->context, + in_ccache->ccache, + &principal)); + } + + if (!err) { + err = kim_identity_create_from_krb5_principal (out_client_identity, + in_ccache->context, + principal); + } + + if (principal) { krb5_free_principal (in_ccache->context, principal); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_valid_credential (kim_ccache_t in_ccache, + kim_credential_t *out_credential) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_iterator_t iterator = NULL; + kim_boolean_t out_of_credentials = FALSE; + kim_boolean_t found_valid_tgt = FALSE; + kim_boolean_t found_invalid_tgt = FALSE; + kim_credential_state_t invalid_tgt_state = kim_credentials_state_valid; + kim_credential_t valid_credential = NULL; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_credential) { err = param_error (2, "out_credential", "NULL"); } + + if (!err) { + err = kim_credential_iterator_create (&iterator, in_ccache); + } + + while (!err && !found_valid_tgt && !out_of_credentials) { + kim_credential_t credential = NULL; + + err = kim_credential_iterator_next (iterator, &credential); + + if (!err && !credential) { + out_of_credentials = TRUE; + + } else if (!err) { + kim_identity_t service_identity = NULL; + kim_credential_state_t state = kim_credentials_state_valid; + kim_boolean_t is_tgt = FALSE; + + err = kim_credential_get_service_identity (credential, + &service_identity); + + if (!err) { + err = kim_credential_get_state (credential, &state); + } + + if (!err) { + err = kim_identity_is_tgt_service (service_identity, &is_tgt); + } + + if (!err) { + if (state == kim_credentials_state_valid) { + kim_credential_free (&valid_credential); + + valid_credential = credential; + credential = NULL; + + if (is_tgt) { + found_valid_tgt = TRUE; + } + } else { + if (is_tgt && !found_invalid_tgt) { + found_invalid_tgt = TRUE; + invalid_tgt_state = state; + } + } + } + + kim_identity_free (&service_identity); + } + + kim_credential_free (&credential); + } + + if (!err && (!valid_credential || found_invalid_tgt)) { + kim_identity_t identity = NULL; + kim_string_t identity_string = NULL; + + err = kim_ccache_get_client_identity (in_ccache, &identity); + + if (!err) { + err = kim_identity_get_display_string (identity, + &identity_string); + } + + if (!err && found_invalid_tgt) { + switch (invalid_tgt_state) { + case kim_credentials_state_expired: + err = kim_error_create_from_code (KIM_CREDENTIALS_EXPIRED_ECODE, + identity_string); + break; + case kim_credentials_state_not_yet_valid: + case kim_credentials_state_needs_validation: + err = kim_error_create_from_code (KIM_NEEDS_VALIDATION_ECODE, + identity_string); + break; + case kim_credentials_state_address_mismatch: + err = kim_error_create_from_code (KIM_BAD_IP_ADDRESS_ECODE, + identity_string); + break; + } + } + + if (!err && !valid_credential) { + err = kim_error_create_from_code (KIM_NO_CREDENTIALS_ECODE, + identity_string); + } + + kim_string_free (&identity_string); + kim_identity_free (&identity); + } + + if (!err) { + *out_credential = valid_credential; + valid_credential = NULL; + } + + kim_credential_free (&valid_credential); + kim_credential_iterator_free (&iterator); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_start_time (kim_ccache_t in_ccache, + kim_time_t *out_start_time) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_start_time) { err = param_error (2, "out_start_time", "NULL"); } + + if (!err) { + err = kim_ccache_get_valid_credential (in_ccache, &credential); + } + + if (!err) { + err = kim_credential_get_start_time (credential, out_start_time); + } + + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_expiration_time (kim_ccache_t in_ccache, + kim_time_t *out_expiration_time) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_expiration_time) { err = param_error (2, "out_expiration_time", "NULL"); } + + if (!err) { + err = kim_ccache_get_valid_credential (in_ccache, &credential); + } + + if (!err) { + err = kim_credential_get_expiration_time (credential, + out_expiration_time); + } + + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_get_renewal_expiration_time (kim_ccache_t in_ccache, + kim_time_t *out_renewal_expiration_time) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !in_ccache ) { err = param_error (1, "in_ccache", "NULL"); } + if (!err && !out_renewal_expiration_time) { err = param_error (2, "out_renewal_expiration_time", "NULL"); } + + if (!err) { + err = kim_ccache_get_valid_credential (in_ccache, &credential); + } + + if (!err) { + err = kim_credential_get_renewal_expiration_time (credential, + out_renewal_expiration_time); + } + + kim_credential_free (&credential); + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_set_default (kim_ccache_t io_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_ccache) { err = param_error (1, "io_ccache", "NULL"); } + + if (!err) { + char *environment_ccache_name = getenv ("KRB5CCNAME"); + + if (environment_ccache_name) { + krb5_ccache environment_ccache = NULL; + krb5_principal client_principal = NULL; + + err = krb5_error (krb5_cc_resolve (io_ccache->context, + environment_ccache_name, + &environment_ccache)); + + if (!err && !kim_ccache_k5ccaches_are_equal (io_ccache->context, + io_ccache->ccache, + io_ccache->context, + environment_ccache)) { + /* KRB5CCNAME is set and does not point to this ccache. + * Move the creds and make this kim_ccache_t object refer to that ccache. */ + + err = krb5_error (krb5_cc_get_principal (io_ccache->context, + io_ccache->ccache, + &client_principal)); + + if (!err) { + err = krb5_error (krb5_cc_initialize (io_ccache->context, + environment_ccache, + client_principal)); + } + + if (!err) { + err = krb5_error (krb5_cc_copy_creds (io_ccache->context, + io_ccache->ccache, + environment_ccache)); + } + + if (!err) { + krb5_cc_destroy (io_ccache->context, io_ccache->ccache); + io_ccache->ccache = environment_ccache; + environment_ccache = NULL; /* take ownership */ + } + } + + if (environment_ccache) { krb5_cc_close (io_ccache->context, + environment_ccache); } + + } else { + kim_string_t type = NULL; + kim_string_t name = NULL; + cc_context_t cc_context = NULL; + cc_ccache_t cc_ccache = NULL; + + err = kim_ccache_get_type (io_ccache, &type); + + if (!err && strcmp (type, "API")) { + kim_string_t display_name = NULL; + /* Not a CCAPI ccache; can't set to default */ + + err = kim_ccache_get_display_name (io_ccache, &display_name); + + if (!err) { + err = kim_error_create_from_code (KIM_CANT_BECOME_DEFAULT_ECODE, + display_name); + } + + kim_string_free (&display_name); + } + + if (!err) { + err = kim_ccache_get_name (io_ccache, &name); + } + + /* get a CCAPI ccache for this cache */ + if (!err) { + err = ccapi_error (cc_initialize (&cc_context, ccapi_version_4, + NULL, NULL)); + } + + if (!err) { + err = ccapi_error (cc_context_open_ccache (cc_context, name, + &cc_ccache)); + } + + if (!err) { + err = ccapi_error (cc_ccache_set_default (cc_ccache)); + } + + if (cc_context) { cc_context_release (cc_context); } + if (cc_ccache ) { cc_ccache_release (cc_ccache); } + kim_string_free (&name); + kim_string_free (&type); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_verify (kim_ccache_t in_ccache, + kim_identity_t in_service_identity, + kim_string_t in_keytab, + kim_boolean_t in_fail_if_no_service_key) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !in_ccache) { err = param_error (1, "in_ccache", "NULL"); } + + if (!err) { + err = kim_ccache_get_valid_credential (in_ccache, &credential); + } + + if (!err) { + err = kim_credential_verify (credential, in_service_identity, + in_keytab, in_fail_if_no_service_key); + } + + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_renew (kim_ccache_t in_ccache, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + kim_identity_t client_identity = NULL; + + if (!err && !in_ccache) { err = param_error (1, "in_ccache", "NULL"); } + + if (!err) { + err = kim_ccache_get_valid_credential (in_ccache, &credential); + } + + if (!err) { + err = kim_credential_renew (&credential, in_options); + } + + if (!err) { + err = kim_ccache_get_client_identity (in_ccache, &client_identity); + } + + if (!err) { + err = kim_credential_store (credential, client_identity, NULL); + } + + kim_identity_free (&client_identity); + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_validate (kim_ccache_t in_ccache, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + kim_identity_t client_identity = NULL; + + if (!err && !in_ccache) { err = param_error (1, "in_ccache", "NULL"); } + + if (!err) { + err = kim_ccache_get_valid_credential (in_ccache, &credential); + } + + if (!err) { + err = kim_credential_validate (&credential, in_options); + } + + if (!err) { + err = kim_ccache_get_client_identity (in_ccache, &client_identity); + } + + if (!err) { + err = kim_credential_store (credential, client_identity, NULL); + } + + kim_identity_free (&client_identity); + kim_credential_free (&credential); + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_ccache_destroy (kim_ccache_t *io_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + + if (io_ccache && *io_ccache) { + err = krb5_error (krb5_cc_destroy ((*io_ccache)->context, + (*io_ccache)->ccache)); + + if (!err) { + (*io_ccache)->ccache = NULL; + kim_ccache_free (io_ccache); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_ccache_free (kim_ccache_t *io_ccache) +{ + if (io_ccache && *io_ccache) { + if ((*io_ccache)->context) { + if ((*io_ccache)->ccache) { + krb5_cc_close ((*io_ccache)->context, (*io_ccache)->ccache); + } + krb5_free_context ((*io_ccache)->context); + } + free (*io_ccache); + *io_ccache = NULL; + } +} + diff --git a/src/kim/lib/kim_ccache_private.h b/src/kim/lib/kim_ccache_private.h new file mode 100644 index 000000000..23af8b2f1 --- /dev/null +++ b/src/kim/lib/kim_ccache_private.h @@ -0,0 +1,39 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_CCACHE_PRIVATE_H +#define KIM_CCACHE_PRIVATE_H + +#include + +kim_error_t kim_ccache_create_from_display_name (kim_ccache_t *out_ccache, + kim_string_t in_display_name); + +kim_error_t kim_ccache_compare (kim_ccache_t in_ccache, + kim_ccache_t in_compare_to_ccache, + kim_boolean_t *out_equal); + +#endif /* KIM_CCACHE_PRIVATE_H */ diff --git a/src/kim/lib/kim_credential.c b/src/kim/lib/kim_credential.c new file mode 100644 index 000000000..2ef618c0f --- /dev/null +++ b/src/kim/lib/kim_credential.c @@ -0,0 +1,908 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include +#include "kim_private.h" + +struct kim_credential_iterator_opaque { + krb5_context context; + krb5_ccache ccache; + krb5_cc_cursor cursor; +}; + +struct kim_credential_iterator_opaque kim_credential_iterator_initializer = { NULL, NULL, NULL }; + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_iterator_create (kim_credential_iterator_t *out_credential_iterator, + kim_ccache_t in_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_iterator_t credential_iterator = NULL; + + if (!err && !out_credential_iterator) { err = param_error (1, "out_credential_iterator", "NULL"); } + if (!err && !in_ccache ) { err = param_error (2, "in_ccache", "NULL"); } + + if (!err) { + credential_iterator = malloc (sizeof (*credential_iterator)); + if (credential_iterator) { + *credential_iterator = kim_credential_iterator_initializer; + } else { + err = os_error (errno); + } + } + + if (!err) { + err = krb5_error (krb5_init_context (&credential_iterator->context)); + } + + if (!err) { + err = kim_ccache_get_krb5_ccache (in_ccache, + credential_iterator->context, + &credential_iterator->ccache); + } + + if (!err) { + err = krb5_error (krb5_cc_start_seq_get (credential_iterator->context, + credential_iterator->ccache, + &credential_iterator->cursor)); + } + + if (!err) { + *out_credential_iterator = credential_iterator; + credential_iterator = NULL; + } + + kim_credential_iterator_free (&credential_iterator); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_iterator_next (kim_credential_iterator_t in_credential_iterator, + kim_credential_t *out_credential) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_credential_iterator) { err = param_error (1, "in_credential_iterator", "NULL"); } + if (!err && !out_credential ) { err = param_error (2, "out_credential", "NULL"); } + + if (!err) { + krb5_creds creds; + + krb5_error_code terr = krb5_cc_next_cred (in_credential_iterator->context, + in_credential_iterator->ccache, + &in_credential_iterator->cursor, + &creds); + + if (!terr) { + err = kim_credential_create_from_krb5_creds (out_credential, + in_credential_iterator->context, + &creds); + + krb5_free_cred_contents (in_credential_iterator->context, &creds); + + } else if (terr == KRB5_CC_END) { + *out_credential = NULL; /* no more ccaches */ + + } else { + err = krb5_error (terr); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_credential_iterator_free (kim_credential_iterator_t *io_credential_iterator) +{ + if (io_credential_iterator && *io_credential_iterator) { + if ((*io_credential_iterator)->context) { + if ((*io_credential_iterator)->ccache) { + if ((*io_credential_iterator)->cursor) { + krb5_cc_end_seq_get ((*io_credential_iterator)->context, + (*io_credential_iterator)->ccache, + &(*io_credential_iterator)->cursor); + } + krb5_cc_close ((*io_credential_iterator)->context, + (*io_credential_iterator)->ccache); + } + krb5_free_context ((*io_credential_iterator)->context); + } + free (*io_credential_iterator); + *io_credential_iterator = NULL; + } +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +struct kim_credential_opaque { + krb5_context context; + krb5_creds *creds; +}; + +struct kim_credential_opaque kim_credential_initializer = { NULL, NULL }; + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_credential_allocate (kim_credential_t *out_credential) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !out_credential) { err = param_error (1, "out_credential", "NULL"); } + + if (!err) { + credential = malloc (sizeof (*credential)); + if (!credential) { err = os_error (errno); } + } + + if (!err) { + *credential = kim_credential_initializer; + *out_credential = credential; + credential = NULL; + } + + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_create_new (kim_credential_t *out_credential, + kim_identity_t in_client_identity, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !out_credential) { err = param_error (1, "out_credential", "NULL"); } + + if (!err) { + err = kim_credential_allocate (&credential); + } + + if (!err) { + err = krb5_error (krb5_init_context (&credential->context)); + } + + if (!err) { +#warning Get tickets here + } + + if (!err) { + *out_credential = credential; + credential = NULL; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_create_from_keytab (kim_credential_t *out_credential, + kim_identity_t in_identity, + kim_options_t in_options, + kim_string_t in_keytab) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + krb5_keytab keytab = NULL; + krb5_creds creds; + kim_boolean_t free_creds = FALSE; + krb5_principal principal = NULL; + kim_time_t start_time = 0; + kim_string_t service_name = NULL; + krb5_get_init_creds_opt *init_cred_options = NULL; + + if (!err && !out_credential) { err = param_error (1, "out_credential", "NULL"); } + + if (!err) { + err = kim_credential_allocate (&credential); + } + + if (!err) { + err = krb5_error (krb5_init_context (&credential->context)); + } + + if (!err) { + kim_options_t options = in_options; + + if (!options) { + err = kim_options_create (&options); + } + + if (!err) { + err = kim_options_get_start_time (options, &start_time); + } + + if (!err) { + err = kim_options_get_service_name (options, &service_name); + } + + if (!err) { + err = kim_options_get_init_cred_options (options, credential->context, + &init_cred_options); + } + + if (options != in_options) { kim_options_free (&options); } + } + + if (!err) { + if (in_keytab) { + err = krb5_error (krb5_kt_resolve (credential->context, in_keytab, &keytab)); + } else { + err = krb5_error (krb5_kt_default (credential->context, &keytab)); + } + } + + if (!err) { + if (in_identity) { + err = kim_identity_get_krb5_principal (in_identity, credential->context, &principal); + } else { + krb5_kt_cursor cursor = NULL; + krb5_keytab_entry entry; + kim_boolean_t entry_allocated = FALSE; + + err = krb5_error (krb5_kt_start_seq_get (credential->context, keytab, &cursor)); + + if (!err) { + err = krb5_error (krb5_kt_next_entry (credential->context, keytab, &entry, &cursor)); + entry_allocated = (err == KIM_NO_ERROR); /* remember to free later */ + } + + if (!err) { + err = krb5_error (krb5_copy_principal (credential->context, entry.principal, &principal)); + } + + if (entry_allocated) { krb5_free_keytab_entry_contents (credential->context, &entry); } + if (cursor ) { krb5_kt_end_seq_get (credential->context, keytab, &cursor); } + } + } + + if (!err) { + err = krb5_error (krb5_get_init_creds_keytab (credential->context, &creds, principal, keytab, + start_time, (char *) service_name, init_cred_options)); + if (!err) { free_creds = TRUE; } + } + + if (!err) { + err = krb5_error (krb5_copy_creds (credential->context, &creds, &credential->creds)); + } + + if (!err) { + *out_credential = credential; + credential = NULL; + } + + if (principal ) { krb5_free_principal (credential->context, principal); } + if (free_creds) { krb5_free_cred_contents (credential->context, &creds); } + kim_options_free_init_cred_options (credential->context, &init_cred_options); + kim_string_free (&service_name); + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_create_from_krb5_creds (kim_credential_t *out_credential, + krb5_context in_krb5_context, + krb5_creds *in_krb5_creds) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !out_credential ) { err = param_error (1, "out_credential", "NULL"); } + if (!err && !in_krb5_creds ) { err = param_error (2, "in_krb5_creds", "NULL"); } + if (!err && !in_krb5_context) { err = param_error (3, "in_krb5_context", "NULL"); } + + if (!err) { + err = kim_credential_allocate (&credential); + } + + if (!err) { + err = krb5_error (krb5_init_context (&credential->context)); + } + + if (!err) { + err = krb5_error (krb5_copy_creds (credential->context, in_krb5_creds, &credential->creds)); + } + + if (!err) { + *out_credential = credential; + credential = NULL; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_copy (kim_credential_t *out_credential, + kim_credential_t in_credential) +{ + kim_error_t err = KIM_NO_ERROR; + kim_credential_t credential = NULL; + + if (!err && !out_credential) { err = param_error (1, "out_credential", "NULL"); } + if (!err && !in_credential ) { err = param_error (2, "in_credential", "NULL"); } + + if (!err) { + err = kim_credential_allocate (&credential); + } + + if (!err) { + err = krb5_error (krb5_init_context (&credential->context)); + } + + if (!err) { + err = krb5_error (krb5_copy_creds (credential->context, in_credential->creds, &credential->creds)); + } + + if (!err) { + *out_credential = credential; + credential = NULL; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_get_krb5_creds (kim_credential_t in_credential, + krb5_context in_krb5_context, + krb5_creds **out_krb5_creds) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_credential ) { err = param_error (1, "in_credential", "NULL"); } + if (!err && !in_krb5_context) { err = param_error (2, "in_krb5_context", "NULL"); } + if (!err && !out_krb5_creds ) { err = param_error (3, "out_krb5_creds", "NULL"); } + + if (!err) { + err = krb5_error (krb5_copy_creds (in_krb5_context, in_credential->creds, out_krb5_creds)); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_get_client_identity (kim_credential_t in_credential, + kim_identity_t *out_client_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_credential ) { err = param_error (1, "in_credential", "NULL"); } + if (!err && !out_client_identity) { err = param_error (2, "out_client_identity", "NULL"); } + + if (!err) { + err = kim_identity_create_from_krb5_principal (out_client_identity, + in_credential->context, + in_credential->creds->client); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_get_service_identity (kim_credential_t in_credential, + kim_identity_t *out_service_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_credential ) { err = param_error (1, "in_credential", "NULL"); } + if (!err && !out_service_identity) { err = param_error (2, "out_service_identity", "NULL"); } + + if (!err) { + err = kim_identity_create_from_krb5_principal (out_service_identity, + in_credential->context, + in_credential->creds->server); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_is_tgt (kim_credential_t in_credential, + kim_boolean_t *out_is_tgt) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t service = NULL; + + if (!err && !in_credential) { err = param_error (1, "in_credential", "NULL"); } + if (!err && !out_is_tgt ) { err = param_error (2, "out_is_tgt", "NULL"); } + + if (!err) { + err = kim_credential_get_service_identity (in_credential, &service); + } + + if (!err) { + err = kim_identity_is_tgt_service (service, out_is_tgt); + } + + kim_identity_free (&service); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_get_state (kim_credential_t in_credential, + kim_credential_state_t *out_state) +{ + kim_error_t err = KIM_NO_ERROR; + kim_time_t expiration_time = 0; + kim_time_t start_time = 0; + krb5_timestamp now = 0; + + if (!err && !in_credential) { err = param_error (1, "in_credential", "NULL"); } + if (!err && !out_state ) { err = param_error (2, "out_state", "NULL"); } + + if (!err) { + err = kim_credential_get_expiration_time (in_credential, &expiration_time); + } + + if (!err) { + err = kim_credential_get_start_time (in_credential, &start_time); + } + + if (!err) { + krb5_int32 usec; + + err = krb5_error (krb5_us_timeofday (in_credential->context, &now, &usec)); + } + + if (!err) { + *out_state = kim_credentials_state_valid; + + if (expiration_time <= now) { + *out_state = kim_credentials_state_expired; + + } else if ((in_credential->creds->ticket_flags & TKT_FLG_POSTDATED) && + (in_credential->creds->ticket_flags & TKT_FLG_INVALID)) { + if (start_time > now) { + *out_state = kim_credentials_state_not_yet_valid; + } else { + *out_state = kim_credentials_state_needs_validation; + } + + } else if (in_credential->creds->addresses) { /* ticket contains addresses */ + krb5_address **laddresses = NULL; + + krb5_error_code code = krb5_os_localaddr (in_credential->context, &laddresses); + if (!code) { laddresses = NULL; } + + if (laddresses) { /* assume valid if the local host has no addresses */ + kim_boolean_t found_match = FALSE; + kim_count_t i = 0; + + for (i = 0; in_credential->creds->addresses[0]; i++) { + if (krb5_address_search (in_credential->context, + in_credential->creds->addresses[i], laddresses)) { + found_match = TRUE; + break; + } + } + + if (!found_match) { + *out_state = kim_credentials_state_address_mismatch; + } + } + + if (laddresses) { krb5_free_addresses (in_credential->context, laddresses); } + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_get_start_time (kim_credential_t in_credential, + kim_time_t *out_start_time) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_credential) { err = param_error (1, "in_credential", "NULL"); } + + if (!err) { + *out_start_time = (in_credential->creds->times.starttime ? + in_credential->creds->times.starttime : + in_credential->creds->times.authtime); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_get_expiration_time (kim_credential_t in_credential, + kim_time_t *out_expiration_time) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_credential) { err = param_error (1, "in_credential", "NULL"); } + + if (!err) { + *out_expiration_time = in_credential->creds->times.endtime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_get_renewal_expiration_time (kim_credential_t in_credential, + kim_time_t *out_renewal_expiration_time) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_credential) { err = param_error (1, "in_credential", "NULL"); } + + if (!err) { + *out_renewal_expiration_time = in_credential->creds->times.renew_till; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_store (kim_credential_t in_credential, + kim_identity_t in_client_identity, + kim_ccache_t *out_ccache) +{ + kim_error_t err = KIM_NO_ERROR; + krb5_context context = NULL; + krb5_ccache k5ccache = NULL; + kim_string_t type = NULL; + krb5_principal client_principal = NULL; + kim_boolean_t destroy_ccache_on_error = FALSE; + + if (!err && !in_credential ) { err = param_error (1, "in_credential", "NULL"); } + if (!err && !in_client_identity) { err = param_error (2, "in_client_identity", "NULL"); } + + if (!err) { + err = krb5_error (krb5_init_context (&context)); + } + + if (!err) { + err = kim_identity_get_krb5_principal (in_client_identity, context, + &client_principal); + } + + if (!err) { + char *environment_ccache = getenv ("KRB5CCNAME"); + + if (environment_ccache) { + err = krb5_error (krb5_cc_resolve (context, environment_ccache, &k5ccache)); + + } else { + kim_ccache_t ccache = NULL; + + err = kim_ccache_create_from_client_identity (&ccache, in_client_identity); + + if (!err) { + err = kim_ccache_get_krb5_ccache (ccache, context, &k5ccache); + + } else if (kim_error_get_code (err) == KIM_NO_SUCH_PRINCIPAL_ECODE) { + /* Nothing to replace, toss error and create a new ccache */ + kim_error_free (&err); + err = krb5_error (krb5_cc_new_unique (context, "API", NULL, &k5ccache)); + if (!err) { destroy_ccache_on_error = TRUE; } + } + + kim_ccache_free (&ccache); + } + } + + if (!err) { + err = krb5_error (krb5_cc_initialize (in_credential->context, + k5ccache, client_principal)); + } + + if (!err) { + err = krb5_error (krb5_cc_store_cred (in_credential->context, + k5ccache, in_credential->creds)); + } + +#warning Call plugins here + + if (!err && out_ccache) { + err = kim_ccache_create_from_krb5_ccache (out_ccache, context, k5ccache); + } + + if (k5ccache) { + if (err && destroy_ccache_on_error) { + krb5_cc_destroy (in_credential->context, k5ccache); + } else { + krb5_cc_close (in_credential->context, k5ccache); + } + } + if (client_principal) { krb5_free_principal (context, client_principal); } + if (context ) { krb5_free_context (context); } + kim_string_free (&type); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_verify (kim_credential_t in_credential, + kim_identity_t in_service_identity, + kim_string_t in_keytab, + kim_boolean_t in_fail_if_no_service_key) +{ + kim_error_t err = KIM_NO_ERROR; + krb5_context scontext = NULL; + krb5_principal service_principal = NULL; + krb5_keytab keytab = NULL; + + if (!err && !in_credential) { err = param_error (1, "in_credential", "NULL"); } + + if (!err) { + err = krb5_error (krb5_init_secure_context (&scontext)); + } + + if (!err && in_service_identity) { + err = kim_identity_get_krb5_principal (in_service_identity, scontext, &service_principal); + } + + if (in_keytab) { + err = krb5_error (krb5_kt_resolve (scontext, in_keytab, &keytab)); + } + + if (!err) { + krb5_verify_init_creds_opt options; + + /* That's "no key == fail" not "no fail" >.< */ + krb5_verify_init_creds_opt_init (&options); + krb5_verify_init_creds_opt_set_ap_req_nofail (&options, in_fail_if_no_service_key); + + err = krb5_error (krb5_verify_init_creds (scontext, in_credential->creds, + service_principal, + keytab, + NULL /* don't store creds in ccache */, + &options)); + + if (err && !service_principal && in_fail_if_no_service_key) { + /* If the service principal wasn't specified but we are supposed to fail without a key + we should walk the keytab trying to find one that succeeds. */ + krb5_error_code code = 0; + kim_boolean_t verified = 0; + krb5_kt_cursor cursor = NULL; + krb5_keytab_entry entry; + + + if (!keytab) { + code = krb5_kt_default (scontext, &keytab); + } + + if (!err) { + code = krb5_kt_start_seq_get (scontext, keytab, &cursor); + } + + while (!code && !verified) { + kim_boolean_t free_entry = 0; + + code = krb5_kt_next_entry (scontext, keytab, &entry, &cursor); + free_entry = !code; /* remember to free */ + + if (!code) { + code = krb5_verify_init_creds (scontext, in_credential->creds, + entry.principal /* get principal for the 1st entry */, + keytab, + NULL /* don't store creds in ccache */, + &options); + } + + if (!code) { + verified = 1; + } + + if (free_entry) { krb5_free_keytab_entry_contents (scontext, &entry); } + } + + if (!code && verified) { + /* We found a key that verified! */ + kim_error_free (&err); + } + + if (cursor) { krb5_kt_end_seq_get (scontext, keytab, &cursor); } + } + } + + if (keytab ) { krb5_kt_close (scontext, keytab); } + if (service_principal) { krb5_free_principal (scontext, service_principal); } + if (scontext ) { krb5_free_context (scontext); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_renew (kim_credential_t *io_credential, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t service_name = NULL; + krb5_ccache ccache = NULL; + + if (!err && !io_credential) { err = param_error (1, "io_credential", "NULL"); } + + if (!err) { + kim_options_t options = in_options; + + if (!options) { + err = kim_options_create (&options); + } + + if (!err) { + err = kim_options_get_service_name (options, &service_name); + } + + if (options != in_options) { kim_options_free (&options); } + } + + if (!err) { + err = krb5_error (krb5_cc_new_unique ((*io_credential)->context, + "MEMORY", NULL, + &ccache)); + } + + if (!err) { + err = krb5_error (krb5_cc_initialize ((*io_credential)->context, ccache, + (*io_credential)->creds->client)); + } + + if (!err) { + err = krb5_error (krb5_cc_store_cred ((*io_credential)->context, ccache, + (*io_credential)->creds)); + } + + if (!err) { + krb5_creds creds; + krb5_creds *renewed_creds = NULL; + kim_boolean_t free_creds = 0; + + err = krb5_error (krb5_get_renewed_creds ((*io_credential)->context, + &creds, (*io_credential)->creds->client, + ccache, (char *) service_name)); + if (!err) { free_creds = 1; } + + if (!err) { + err = krb5_error (krb5_copy_creds ((*io_credential)->context, &creds, &renewed_creds)); + } + + if (!err) { + /* replace the credentials */ + krb5_free_creds ((*io_credential)->context, (*io_credential)->creds); + (*io_credential)->creds = renewed_creds; + } + + if (free_creds) { krb5_free_cred_contents ((*io_credential)->context, &creds); } + } + + if (ccache) { krb5_cc_destroy ((*io_credential)->context, ccache); } + kim_string_free (&service_name); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_credential_validate (kim_credential_t *io_credential, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t service_name = NULL; + krb5_ccache ccache = NULL; + + if (!err && !io_credential) { err = param_error (1, "io_credential", "NULL"); } + + if (!err) { + kim_options_t options = in_options; + + if (!options) { + err = kim_options_create (&options); + } + + if (!err) { + err = kim_options_get_service_name (options, &service_name); + } + + if (options != in_options) { kim_options_free (&options); } + } + + if (!err) { + err = krb5_error (krb5_cc_new_unique ((*io_credential)->context, + "MEMORY", NULL, + &ccache)); + } + + if (!err) { + err = krb5_error (krb5_cc_initialize ((*io_credential)->context, ccache, + (*io_credential)->creds->client)); + } + + if (!err) { + err = krb5_error (krb5_cc_store_cred ((*io_credential)->context, ccache, + (*io_credential)->creds)); + } + + if (!err) { + krb5_creds creds; + krb5_creds *validated_creds = NULL; + kim_boolean_t free_creds = 0; + + err = krb5_error (krb5_get_validated_creds ((*io_credential)->context, + &creds, (*io_credential)->creds->client, + ccache, (char *) service_name)); + if (!err) { free_creds = 1; } + + if (!err) { + err = krb5_error (krb5_copy_creds ((*io_credential)->context, &creds, &validated_creds)); + } + + if (!err) { + /* replace the credentials */ + krb5_free_creds ((*io_credential)->context, (*io_credential)->creds); + (*io_credential)->creds = validated_creds; + } + + if (free_creds) { krb5_free_cred_contents ((*io_credential)->context, &creds); } + } + + if (ccache) { krb5_cc_destroy ((*io_credential)->context, ccache); } + kim_string_free (&service_name); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_credential_free (kim_credential_t *io_credential) +{ + if (io_credential && *io_credential) { + if ((*io_credential)->context) { + if ((*io_credential)->creds) { + krb5_free_creds ((*io_credential)->context, (*io_credential)->creds); + } + krb5_free_context ((*io_credential)->context); + } + free (*io_credential); + *io_credential = NULL; + } +} diff --git a/src/kim/lib/kim_error.c b/src/kim/lib/kim_error.c new file mode 100644 index 000000000..090204f3a --- /dev/null +++ b/src/kim/lib/kim_error.c @@ -0,0 +1,244 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "kim_private.h" +#include +#include + +struct kim_error_opaque { + kim_error_code_t code; + kim_string_t message; +}; + +struct kim_error_opaque kim_no_memory_error_struct = { KIM_OUT_OF_MEMORY_ECODE, NULL, }; +kim_error_t kim_no_memory_error = &kim_no_memory_error_struct; + +struct kim_error_opaque kim_error_initializer = { KIM_NO_ERROR_ECODE, NULL }; + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_error_allocate (kim_error_t *out_error) +{ + kim_error_t err = KIM_NO_ERROR; + kim_error_t error = NULL; + + if (!err) { + error = malloc (sizeof (*error)); + if (!error) { + err = kim_no_memory_error; + } else { + *error = kim_error_initializer; + } + } + + if (!err) { + *out_error = error; + error = NULL; + } + + kim_error_free (&error); + + return check_error (err); +} + +#pragma mark -- Helper Functions -- + +/* These helper functions exist so we get type checking on the most common errors */ + +/* ------------------------------------------------------------------------ */ + +kim_error_t _kim_error_create_for_param (kim_string_t in_function, + unsigned int in_argument_position, + kim_string_t in_argument_name, + kim_string_t in_invalid_value) +{ + return kim_error_create_from_code (KIM_PARAMETER_ECODE, + in_function, + in_argument_position, + in_argument_name, + in_invalid_value); +} + +#pragma mark -- Generic Functions -- + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_error_create_from_code (kim_error_code_t in_code, + ...) +{ + kim_error_t err = KIM_NO_ERROR; + va_list args; + + va_start (args, in_code); + err = kim_error_create_from_code_va (in_code, args); + va_end (args); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_error_create_from_code_va (kim_error_code_t in_code, + va_list in_args) +{ + kim_error_t err = KIM_NO_ERROR; + kim_error_t error = KIM_NO_ERROR; + kim_string_t message = NULL; + + /* short circuit out of memory errors so we don't loop */ + switch (in_code) { + case 0: + return KIM_NO_ERROR; + + case KIM_OUT_OF_MEMORY_ECODE: + case ENOMEM: + case KRB5_CC_NOMEM: + case ccErrNoMem: +// case klMemFullErr: +// case memFullErr: + err = kim_no_memory_error; + break; + } + + if (!err) { + err = kim_string_create_from_format_va_retcode (&message, error_message (in_code), in_args); + } + + if (!err) { + err = kim_error_allocate (&error); + } + + if (!err) { + error->code = in_code; + error->message = message; + message = NULL; /* take ownership */ + + err = error; + error = NULL; /* take ownership */ + } + + kim_error_free (&error); + kim_string_free (&message); + + return err; +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_error_copy (kim_error_t *out_error, + kim_error_t in_error) +{ + kim_error_t err = KIM_NO_ERROR; + kim_error_t error = KIM_NO_ERROR; + + if (!err && !out_error) { err = param_error (1, "out_error", "NULL"); } + if (!err && !in_error ) { err = param_error (2, "in_error", "NULL"); } + + if (!err) { + if (in_error == kim_no_memory_error) { + error = kim_no_memory_error; + + } else { + err = kim_error_allocate (&error); + + if (!err) { + error->code = in_error->code; + if (in_error->message) { + err = kim_string_copy (&error->message, in_error->message); + } + } + } + } + + if (!err) { + *out_error = error; + error = KIM_NO_ERROR; + } + + kim_error_free (&error); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_code_t kim_error_get_code (kim_error_t in_error) +{ + if (!in_error) { /* KIM_NO_ERROR */ + return KIM_NO_ERROR_ECODE; + } else { + return in_error->code; + } +} + +/* ------------------------------------------------------------------------ */ + +kim_string_t kim_error_get_display_string (kim_error_t in_error) +{ + if (!in_error) { /* KIM_NO_ERROR */ + return error_message (KIM_NO_ERROR_ECODE); + + } else if (in_error->message) { + return in_error->message; + + } else { + /* Note that kim_no_memory_error will get here because its string is NULL */ + return error_message (in_error->code); + } +} + +/* ------------------------------------------------------------------------ */ + +void kim_error_free (kim_error_t *io_error) +{ + if (io_error && *io_error) { + if (*io_error != kim_no_memory_error) { + kim_string_free (&(*io_error)->message); + free (*io_error); + *io_error = KIM_NO_ERROR; + } + } +} + +#pragma mark -- Debugging Functions -- + +/* ------------------------------------------------------------------------ */ + +kim_error_t _check_error (kim_error_t in_err, + kim_string_t in_function, + kim_string_t in_file, + int in_line) +{ + if (in_err) { + kim_error_code_t code = kim_error_get_code (in_err); + kim_string_t message = kim_error_get_display_string (in_err); + + kim_debug_printf ("%s(): got %d ('%s') at %s: %d", + in_function, code, message, in_file, in_line); + } + + return in_err; +} diff --git a/src/kim/lib/kim_error_code.et b/src/kim/lib/kim_error_code.et new file mode 100644 index 000000000..c9f0330e3 --- /dev/null +++ b/src/kim/lib/kim_error_code.et @@ -0,0 +1,72 @@ +# Copyright 2005-2006 by the Massachusetts Institute of Technology. +# +# Export of this software from the United States of America may +# 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. Furthermore if you modify this software you must label +# your software as modified software and not distribute it in such a +# fashion that it might be confused with the original M.I.T. software. +# 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. + +error_table_manager "Kerberos Identity Management" +error_table KIM + +# Configuration and System Errors +error_code KIM_OUT_OF_MEMORY_ECODE, "Out of memory" +error_code KIM_PARAMETER_ECODE, "%s(): argument %d (%s) may not be %s. Please consult the KIM API documentation" +error_code KIM_KRB5_INIT_FAILED_ECODE, "Unable to initialize Kerberos v5" +error_code KIM_NO_REALMS_ECODE, "There are no Kerberos realms configured" +error_code KIM_NO_SUCH_REALM_ECODE, "The realm '%s' is not in your configuration file or does not exist" + +index 25 +# Principal Errors +error_code KIM_BAD_PRINCIPAL_STRING_ECODE, "'%s' is not a valid Kerberos principal" +error_code KIM_BAD_COMPONENT_INDEX_ECODE, "Principal does not have a component at index %d" +error_code KIM_PASSWORD_MISMATCH_ECODE, "New and verify passwords do not match" +error_code KIM_INSECURE_PASSWORD_ECODE, "Your new password for '%s' is insecure; please pick another one" +error_code KIM_PASSWORD_CHANGE_FAILED_ECODE, "Unable to change password for %s" + +index 50 +# Options Errors +error_code KIM_BAD_OPTIONS_ECODE, "Invalid options" +error_code KIM_BAD_OPTIONS_VALUE_ECODE, "Invalid value for Kerberos default login option" + +index 75 +# User Interface Errors +error_code KIM_CAPS_LOCK_ECODE, "Password Incorrect (check your Caps Lock)" +error_code KIM_USER_CANCELED_ECODE, "The user cancelled the operation" +error_code KIM_NO_SERVER_ECODE, "Unable to launch or contact %s" +error_code KIM_NO_GUI_ECODE, "Unable to display a graphical user interface from this environment" +error_code KIM_NO_CLI_ECODE, "Unable to display a command line user interface from this environment" + +index 100 +# Preferences Errors +error_code KIM_PREFERENCES_READ_ECODE, "Unable to read user preferences. The file may be missing, inaccessible or corrupted" +error_code KIM_PREFERENCES_WRITE_ECODE, "Unable to write user preferences. The file may be inaccessible" +error_code KIM_IDENTITY_NOT_IN_IDENTITIES_LIST, "Identity %s is not in the favorite identities list" +error_code KIM_IDENTITY_ALREADY_IN_IDENTITIES_LIST, "Identity %s is already in the favorite identities list" +error_code KIM_BAD_IDENTITY_INDEX_ECODE, "No identity at index %d in the favorite identities list" + +index 125 +# Cache Collection Errors +error_code KIM_NO_SUCH_PRINCIPAL_ECODE, "Principal '%s' does not exist in the cache collection" +error_code KIM_CANT_BECOME_DEFAULT_ECODE, "The credentials cache '%s' cannot become the system default ccache." +error_code KIM_CREDENTIALS_EXPIRED_ECODE, "The Kerberos credentials for '%s' have expired" +error_code KIM_NO_CREDENTIALS_ECODE, "You do not have Kerberos credentials for principal '%s'" +error_code KIM_BAD_IP_ADDRESS_ECODE, "The IP addresses in the Kerberos credentials for '%s' do not match any of your computer's IP addresses" +error_code KIM_NO_SUCH_CCACHE_ECODE, "The credentials cache '%s' does not exist" +error_code KIM_BAD_HOST_CONFIGURATION_ECODE, "Unable to get local hostname or address information" +error_code KIM_NEEDS_VALIDATION_ECODE, "The Kerberos credentials for '%s' need to be validated" + +end diff --git a/src/kim/lib/kim_error_private.h b/src/kim/lib/kim_error_private.h new file mode 100644 index 000000000..73a67fcc7 --- /dev/null +++ b/src/kim/lib/kim_error_private.h @@ -0,0 +1,55 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_ERROR_PRIVATE_H +#define KIM_ERROR_PRIVATE_H + +#include + +kim_error_t _kim_error_create_for_param (kim_string_t in_function, + unsigned int in_argument_position, + kim_string_t in_argument_name, + kim_string_t in_invalid_value); +#define param_error(pos, name, value) _kim_error_create_for_param(__FUNCTION__, \ + pos, name, value) + +kim_error_t kim_error_create_from_code (kim_error_code_t in_code, + ...); +kim_error_t kim_error_create_from_code_va (kim_error_code_t in_code, + va_list args); + +#define ccapi_error(code) kim_error_create_from_code(code) +#define krb5_error(code) kim_error_create_from_code(code) +#define gss_error(code) kim_error_create_from_code(code) +#define os_error(code) kim_error_create_from_code(code) + +kim_error_t _check_error (kim_error_t in_err, + kim_string_t in_function, + kim_string_t in_file, + int in_line); +#define check_error(err) _check_error(err, __FUNCTION__, __FILE__, __LINE__) + +#endif /* KIM_ERROR_PRIVATE_H */ diff --git a/src/kim/lib/kim_identity.c b/src/kim/lib/kim_identity.c new file mode 100644 index 000000000..abb1b64e0 --- /dev/null +++ b/src/kim/lib/kim_identity.c @@ -0,0 +1,544 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include +#include +#include "kim_private.h" + +/* ------------------------------------------------------------------------ */ + +struct kim_identity_opaque { + krb5_context context; + krb5_principal principal; +}; + +struct kim_identity_opaque kim_identity_initializer = { NULL, NULL }; + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_identity_allocate (kim_identity_t *out_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = NULL; + + if (!err && !out_identity) { err = param_error (1, "out_identity", "NULL"); } + + if (!err) { + identity = malloc (sizeof (*identity)); + if (!identity) { err = os_error (errno); } + } + + if (!err) { + *identity = kim_identity_initializer; + *out_identity = identity; + identity = NULL; + } + + kim_identity_free (&identity); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_create_from_string (kim_identity_t *out_identity, + kim_string_t in_string) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = NULL; + + if (!err && !out_identity) { err = param_error (1, "out_identity", "NULL"); } + if (!err && !in_string ) { err = param_error (2, "in_string", "NULL"); } + + if (!err) { + err = kim_identity_allocate (&identity); + } + + if (!err) { + err = krb5_error (krb5_init_context (&identity->context)); + } + + if (!err) { + krb5_error_code code = krb5_parse_name (identity->context, in_string, &identity->principal); + if (code == KRB5_PARSE_MALFORMED) { + err = kim_error_create_from_code (KIM_BAD_PRINCIPAL_STRING_ECODE, in_string); + } else if (code) { + err = krb5_error (code); + } + } + + if (!err) { +#warning Run translator here + } + + if (!err) { + *out_identity = identity; + identity = NULL; + } + + if (identity) { kim_identity_free (&identity); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_create_from_components (kim_identity_t *out_identity, + kim_string_t in_realm, + kim_string_t in_1st_component, + ...) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = NULL; + krb5_principal_data principal_data; /* allocated by KIM so can't be returned */ + + if (!err && !out_identity ) { err = param_error (1, "out_identity", "NULL"); } + if (!err && !in_realm ) { err = param_error (2, "in_realm", "NULL"); } + if (!err && !in_1st_component) { err = param_error (3, "in_1st_component", "NULL"); } + + if (!err) { + err = kim_identity_allocate (&identity); + } + + if (!err) { + err = krb5_error (krb5_init_context (&identity->context)); + } + + if (!err) { + va_list args; + kim_count_t component_count = 1; + + va_start (args, in_1st_component); + while (va_arg (args, kim_string_t)) { component_count++; } + va_end (args); + + principal_data.length = component_count; + principal_data.data = (krb5_data *) malloc (component_count * sizeof (krb5_data)); + if (!principal_data.data) { err = os_error (errno); } + } + + if (!err) { + va_list args; + krb5_int32 i; + + krb5_princ_set_realm_length (context, &principal_data, strlen (in_realm)); + krb5_princ_set_realm_data (context, &principal_data, (char *) in_realm); + + va_start (args, in_1st_component); + for (i = 0; !err && (i < principal_data.length); i++) { + kim_string_t component = NULL; + if (i == 0) { + err = kim_string_copy (&component, in_1st_component); + } else { + err = kim_string_copy (&component, va_arg (args, kim_string_t)); + } + + if (!err) { + principal_data.data[i].data = (char *) component; + principal_data.data[i].length = strlen (component); + } + } + va_end (args); + } + + if (!err) { + /* make a copy that has actually been allocated by the krb5 + * library so krb5_free_principal can be called on it */ + err = krb5_error (krb5_copy_principal (identity->context, &principal_data, &identity->principal)); + } + + if (!err) { +#warning Run translator here + } + + if (!err) { + *out_identity = identity; + identity = NULL; + } + + if (principal_data.data) { + krb5_int32 i; + + for (i = 0; i < principal_data.length; i++) { + kim_string_t component = principal_data.data[i].data; + kim_string_free (&component); + } + free (principal_data.data); + } + kim_identity_free (&identity); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_create_from_krb5_principal (kim_identity_t *out_identity, + krb5_context in_krb5_context, + krb5_principal in_krb5_principal) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = NULL; + + if (!err && !out_identity ) { err = param_error (1, "out_identity", "NULL"); } + if (!err && !in_krb5_principal) { err = param_error (2, "in_krb5_principal", "NULL"); } + if (!err && !in_krb5_context ) { err = param_error (3, "in_krb5_context", "NULL"); } + + if (!err) { + err = kim_identity_allocate (&identity); + } + + if (!err) { + err = krb5_error (krb5_init_context (&identity->context)); + } + + if (!err) { + err = krb5_error (krb5_copy_principal (identity->context, in_krb5_principal, + &identity->principal)); + } + + if (!err) { +#warning Run translator here + } + + if (!err) { + *out_identity = identity; + identity = NULL; + } + + kim_identity_free (&identity); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_copy (kim_identity_t *out_identity, + kim_identity_t in_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = KIM_IDENTITY_ANY; + + if (!err && !out_identity) { err = param_error (1, "out_identity", "NULL"); } + + if (!err && in_identity != KIM_IDENTITY_ANY) { + err = kim_identity_allocate (&identity); + + if (!err) { + err = krb5_error (krb5_init_context (&identity->context)); + } + + if (!err) { + err = krb5_error (krb5_copy_principal (identity->context, in_identity->principal, + &identity->principal)); + } + } + + if (!err) { + *out_identity = identity; + identity = NULL; + } + + kim_identity_free (&identity); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_compare (kim_identity_t in_identity, + kim_identity_t in_compare_to_identity, + kim_comparison_t *out_comparison) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !in_compare_to_identity) { err = param_error (2, "in_compare_to_identity", "NULL"); } + if (!err && !out_comparison ) { err = param_error (3, "out_comparison", "NULL"); } + + if (!err) { + if (krb5_principal_compare (in_identity->context, + in_identity->principal, + in_compare_to_identity->principal)) { + *out_comparison = 0; + } else { + kim_string_t string = NULL; + kim_string_t compare_to_string = NULL; + + err = kim_identity_get_string (in_identity, &string); + + if (!err) { + err = kim_identity_get_string (in_compare_to_identity, &compare_to_string); + } + + if (!err) { + err = kim_string_compare (string, compare_to_string, out_comparison); + } + + kim_string_free (&string); + kim_string_free (&compare_to_string); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_get_string (kim_identity_t in_identity, + kim_string_t *out_string) +{ + kim_error_t err = KIM_NO_ERROR; + char *unparsed_name = NULL; + + if (!err && !in_identity) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !out_string ) { err = param_error (2, "out_string", "NULL"); } + + if (!err) { + err = krb5_error (krb5_unparse_name (in_identity->context, + in_identity->principal, + &unparsed_name)); + } + + if (!err) { + err = kim_string_copy (out_string, unparsed_name); + } + + if (unparsed_name) { krb5_free_unparsed_name (in_identity->context, unparsed_name); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_get_display_string (kim_identity_t in_identity, + kim_string_t *out_display_string) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t string = NULL; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !out_display_string) { err = param_error (2, "out_display_string", "NULL"); } + + if (!err) { + err = kim_identity_get_string (in_identity, &string); + } + + if (!err) { + kim_count_t i, j; + kim_count_t length = strlen (string) + 1; /* Copy the '\0' */ + char *display_string = (char *) string; /* so we can modify it */ + + /* In place copy, skipping escaped separators. + * Note that we do not want to remove other escaped characters + * (tab, break, newline, NULL) because they are less readable + * when unescaped (and NULL isn't a valid string character). */ + for (i = 0, j = 0; i < length; i++) { + if (string[i] == '\\') { + switch (string[i + 1]) { + case '/': /* component separator */ + case '@': /* realm separator */ + continue; /* skip the '\' */ + } + } + + display_string[j++] = string[i]; /* Copy this char */ + } + + *out_display_string = string; + string = NULL; + } + + if (string) { kim_string_free (&string); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_get_realm (kim_identity_t in_identity, + kim_string_t *out_realm_string) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !out_realm_string) { err = param_error (2, "out_realm_string", "NULL"); } + + if (!err) { + krb5_data *realm = krb5_princ_realm (in_identity->context, in_identity->principal); + + err = kim_string_create_from_buffer (out_realm_string, realm->data, realm->length); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_get_number_of_components (kim_identity_t in_identity, + kim_count_t *out_number_of_components) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !out_number_of_components) { err = param_error (2, "out_number_of_components", "NULL"); } + + if (!err) { + *out_number_of_components = krb5_princ_size (in_identity->context, in_identity->principal); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_get_component_at_index (kim_identity_t in_identity, + kim_count_t in_index, + kim_string_t *out_component_string) +{ + kim_error_t err = KIM_NO_ERROR; + krb5_data *component = NULL; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !out_component_string) { err = param_error (3, "out_component_string", "NULL"); } + + if (!err) { + krb5_int32 i = in_index; + component = krb5_princ_component (in_identity->context, in_identity->principal, i); + if (!component) { err = kim_error_create_from_code (KIM_BAD_COMPONENT_INDEX_ECODE, i); } + } + + if (!err) { + err = kim_string_create_from_buffer (out_component_string, component->data, component->length); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_get_krb5_principal (kim_identity_t in_identity, + krb5_context in_krb5_context, + krb5_principal *out_krb5_principal) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !in_krb5_context ) { err = param_error (2, "in_krb5_context", "NULL"); } + if (!err && !out_krb5_principal) { err = param_error (3, "out_krb5_principal", "NULL"); } + + if (!err) { + err = krb5_error (krb5_copy_principal (in_identity->context, + in_identity->principal, + out_krb5_principal)); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_get_gss_name (kim_identity_t in_identity, + gss_name_t *out_gss_name) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !out_gss_name) { err = param_error (2, "out_gss_name", "NULL"); } + + if (!err) { +#warning kim_identity_get_gss_name not implemented + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_is_tgt_service (kim_identity_t in_identity, + kim_boolean_t *out_is_tgt_service) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !out_is_tgt_service) { err = param_error (2, "out_is_tgt_service", "NULL"); } + + if (!err) { + kim_count_t count = krb5_princ_size (in_identity->context, in_identity->principal); + krb5_data *name = krb5_princ_name (in_identity->context, in_identity->principal); + + /* krbtgt/@ (usually REALM1 == REALM2, but not always) */ + *out_is_tgt_service = ((count == 2) && + (strlen (KRB5_TGS_NAME) == name->length) && + (strncmp (name->data, KRB5_TGS_NAME, name->length) == 0)); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_change_password (kim_identity_t in_identity, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity) { err = param_error (1, "in_identity", "NULL"); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_identity_change_password_to_password (kim_identity_t in_identity, + kim_options_t in_options, + kim_string_t in_new_password) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_identity ) { err = param_error (1, "in_identity", "NULL"); } + if (!err && !in_new_password) { err = param_error (3, "in_new_password", "NULL"); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_identity_free (kim_identity_t *io_identity) +{ + if (io_identity && *io_identity) { + kim_identity_t identity = *io_identity; + + if (identity->context) { + if (identity->principal) { + krb5_free_principal (identity->context, identity->principal); + } + krb5_free_context (identity->context); + } + + free (identity); + *io_identity = NULL; + } +} diff --git a/src/kim/lib/kim_identity_private.h b/src/kim/lib/kim_identity_private.h new file mode 100644 index 000000000..f638707f0 --- /dev/null +++ b/src/kim/lib/kim_identity_private.h @@ -0,0 +1,38 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_IDENTITY_PRIVATE_H +#define KIM_IDENTITY_PRIVATE_H + +#include +#include "kim_library_private.h" + +kim_error_t kim_os_identity_create_for_username (kim_identity_t *out_identity); + +kim_error_t kim_identity_is_tgt_service (kim_identity_t in_identity, + kim_boolean_t *out_is_tgt_service); + +#endif /* KIM_IDENTITY_PRIVATE_H */ diff --git a/src/kim/lib/kim_library.c b/src/kim/lib/kim_library.c new file mode 100644 index 000000000..702486130 --- /dev/null +++ b/src/kim/lib/kim_library.c @@ -0,0 +1,214 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#define KRB5_PRIVATE 1 + +#include +#include +#include +#include + +#include "kim_private.h" +#include "kim_os_private.h" + +/* ------------------------------------------------------------------------ */ + +void __kim_library_debug_printf (kim_string_t in_function, + kim_string_t in_format, + ...) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t format = NULL; + kim_string_t string = NULL; + + if (!err && !in_function) { err = param_error (1, "in_function", "NULL"); } + if (!err && !in_format ) { err = param_error (2, "in_format", "NULL"); } + + if (!err) { + err = kim_string_create_from_format (&format, "%s(): %s", in_function, in_format); + } + + if (!err) { + va_list args; + va_start (args, in_format); + err = kim_string_create_from_format_va (&string, format, args); + va_end (args); + } + + if (!err) { + kim_os_library_debug_print (string); + } + + kim_string_free (&format); + kim_string_free (&string); + kim_error_free (&err); +} + +#pragma mark -- Allow Home Directory Access -- + +static pthread_mutex_t g_allow_home_directory_access_mutex = PTHREAD_MUTEX_INITIALIZER; +kim_boolean_t g_allow_home_directory_access = TRUE; + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_library_set_allow_home_directory_access (kim_boolean_t in_allow_access) +{ + kim_error_t err = KIM_NO_ERROR; + + int mutex_err = pthread_mutex_lock (&g_allow_home_directory_access_mutex); + if (mutex_err) { err = os_error (mutex_err); } + + if (!err) { + g_allow_home_directory_access = in_allow_access; + } + + if (!mutex_err) { pthread_mutex_unlock (&g_allow_home_directory_access_mutex); } + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_library_get_allow_home_directory_access (kim_boolean_t *out_allow_access) +{ + kim_error_t err = KIM_NO_ERROR; + int mutex_err = 0; + + if (!err && !out_allow_access) { err = param_error (3, "out_allow_access", "NULL"); } + + if (!err) { + mutex_err = pthread_mutex_lock (&g_allow_home_directory_access_mutex);; + if (mutex_err) { err = os_error (mutex_err); } + } + + if (!err) { + *out_allow_access = g_allow_home_directory_access; + } + + if (!mutex_err) { pthread_mutex_unlock (&g_allow_home_directory_access_mutex); } + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_boolean_t kim_library_allow_home_directory_access (void) +{ + kim_boolean_t allow_access = FALSE; + kim_error_t err = kim_library_get_allow_home_directory_access (&allow_access); + + kim_error_free (&err); + + return allow_access; +} + + +#pragma mark -- Allow Automatic Prompting -- + +static pthread_mutex_t g_allow_automatic_prompting_mutex = PTHREAD_MUTEX_INITIALIZER; +kim_boolean_t g_allow_automatic_prompting = TRUE; + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_library_set_allow_automatic_prompting (kim_boolean_t in_allow_automatic_prompting) +{ + kim_error_t err = KIM_NO_ERROR; + + int mutex_err = pthread_mutex_lock (&g_allow_automatic_prompting_mutex); + if (mutex_err) { err = os_error (mutex_err); } + + if (!err) { + g_allow_automatic_prompting = in_allow_automatic_prompting; + } + + if (!mutex_err) { pthread_mutex_unlock (&g_allow_automatic_prompting_mutex); } + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_library_get_allow_automatic_prompting (kim_boolean_t *out_allow_automatic_prompting) +{ + kim_error_t err = KIM_NO_ERROR; + int mutex_err = 0; + + if (!err && !out_allow_automatic_prompting) { err = param_error (3, "out_allow_automatic_prompting", "NULL"); } + + if (!err) { + mutex_err = pthread_mutex_lock (&g_allow_automatic_prompting_mutex);; + if (mutex_err) { err = os_error (mutex_err); } + } + + if (!err) { + *out_allow_automatic_prompting = g_allow_automatic_prompting; + } + + if (!mutex_err) { pthread_mutex_unlock (&g_allow_automatic_prompting_mutex); } + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_boolean_t kim_library_allow_automatic_prompting (void) +{ + kim_boolean_t allow_automatic_prompting = TRUE; + kim_error_t err = kim_library_get_allow_automatic_prompting (&allow_automatic_prompting); + + if (allow_automatic_prompting && getenv ("KERBEROSLOGIN_NEVER_PROMPT")) { + kim_debug_printf ("KERBEROSLOGIN_NEVER_PROMPT is set."); + allow_automatic_prompting = FALSE; + } + + if (allow_automatic_prompting && getenv ("KIM_NEVER_PROMPT")) { + kim_debug_printf ("KIM_NEVER_PROMPT is set."); + allow_automatic_prompting = FALSE; + } + + if (allow_automatic_prompting) { + /* Make sure there is at least 1 config file. We don't support DNS + * domain-realm lookup, so if there is no config, Kerberos won't work. */ + + kim_boolean_t kerberos_config_exists = FALSE; + char **files = NULL; + profile_t profile = NULL; + + if (krb5_get_default_config_files (&files) == 0) { + if (profile_init ((const_profile_filespec_t *) files, &profile) == 0) { + kerberos_config_exists = TRUE; + } + } + + if (!kerberos_config_exists) { + kim_debug_printf ("No valid config file."); + allow_automatic_prompting = FALSE; + } + + if (profile) { profile_abandon (profile); } + if (files ) { krb5_free_config_files (files); } + } + + kim_error_free (&err); + + return allow_automatic_prompting; +} diff --git a/src/kim/lib/kim_library_private.h b/src/kim/lib/kim_library_private.h new file mode 100644 index 000000000..afe24bcf4 --- /dev/null +++ b/src/kim/lib/kim_library_private.h @@ -0,0 +1,54 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_LIBRARY_PRIVATE_H +#define KIM_LIBRARY_PRIVATE_H + +#include + + +#define kim_debug_printf(format, ...) __kim_library_debug_printf(__FUNCTION__, format, ## __VA_ARGS__) +void __kim_library_debug_printf (kim_string_t in_function, + kim_string_t in_format, + ...); + +kim_error_t kim_library_set_allow_home_directory_access (kim_boolean_t in_allow_access); + +kim_error_t kim_library_get_allow_home_directory_access (kim_boolean_t *out_allow_access); + +kim_boolean_t kim_library_allow_home_directory_access (void); + +kim_error_t kim_library_set_allow_automatic_prompting (kim_boolean_t in_allow_automatic_prompting); + +kim_error_t kim_library_get_allow_automatic_prompting (kim_boolean_t *out_allow_automatic_prompting); + +kim_boolean_t kim_library_allow_automatic_prompting (void); + +void kim_os_library_debug_print (kim_string_t in_string); + +kim_boolean_t kim_os_library_caller_is_server (void); + +#endif /* KIM_LIBRARY_PRIVATE_H */ diff --git a/src/kim/lib/kim_options.c b/src/kim/lib/kim_options.c new file mode 100644 index 000000000..c68d4a57b --- /dev/null +++ b/src/kim/lib/kim_options.c @@ -0,0 +1,694 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "kim_private.h" + +/* ------------------------------------------------------------------------ */ + +struct kim_options_opaque { + kim_prompt_callback_t prompt_callback; + const void *prompt_callback_data; +#warning add prompt responses here + kim_time_t start_time; + kim_lifetime_t lifetime; + kim_boolean_t renewable; + kim_lifetime_t renewal_lifetime; + kim_boolean_t forwardable; + kim_boolean_t proxiable; + kim_boolean_t addressless; + kim_string_t service_name; +}; + +struct kim_options_opaque kim_options_initializer = { + NULL, NULL, + 0, + kim_default_lifetime, + kim_default_renewable, + kim_default_renewal_lifetime, + kim_default_forwardable, + kim_default_proxiable, + kim_default_addressless, + NULL }; + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_options_allocate (kim_options_t *out_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_options_t options = NULL; + + if (!err && !out_options) { err = param_error (1, "out_options", "NULL"); } + + if (!err) { + options = malloc (sizeof (*options)); + if (!options) { err = os_error (errno); } + } + + if (!err) { + *options = kim_options_initializer; + *out_options = options; + options = NULL; + } + + kim_options_free (&options); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_create_from_defaults (kim_options_t *out_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_options_t options = NULL; + + if (!err && !out_options) { err = param_error (1, "out_options", "NULL"); } + + if (!err) { + err = kim_options_allocate (&options); + } + + if (!err) { + *out_options = options; + options = NULL; + } + + kim_options_free (&options); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_create (kim_options_t *out_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_preferences_t preferences = NULL; + + if (!err && !out_options) { err = param_error (1, "out_options", "NULL"); } + + if (!err) { + err = kim_preferences_create (&preferences); + } + + if (!err) { + err = kim_preferences_get_options (preferences, out_options); + } + + kim_preferences_free (&preferences); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_copy (kim_options_t *out_options, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_options_t options = NULL; + + if (!err && !out_options) { err = param_error (1, "out_options", "NULL"); } + if (!err && !in_options ) { err = param_error (2, "in_options", "NULL"); } + + if (!err) { + err = kim_options_allocate (&options); + } + + if (!err) { + options->prompt_callback = in_options->prompt_callback; + options->prompt_callback_data = in_options->prompt_callback_data; +#warning copy prompt responses here + } + + if (!err) { + options->start_time = in_options->start_time; + options->lifetime = in_options->lifetime; + options->renewable = in_options->renewable; + options->renewal_lifetime = in_options->renewal_lifetime; + options->forwardable = in_options->forwardable; + options->proxiable = in_options->proxiable; + options->addressless = in_options->addressless; + + if (in_options->service_name) { + err = kim_string_copy (&options->service_name, in_options->service_name); + } + } + + if (!err) { + *out_options = options; + options = NULL; + } + + kim_options_free (&options); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_prompt_callback (kim_options_t io_options, + kim_prompt_callback_t in_prompt_callback) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->prompt_callback = in_prompt_callback; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_prompt_callback (kim_options_t in_options, + kim_prompt_callback_t *out_prompt_callback) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_prompt_callback) { err = param_error (2, "out_prompt_callback", "NULL"); } + + if (!err) { + *out_prompt_callback = in_options->prompt_callback; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_data (kim_options_t io_options, + const void *in_data) + +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->prompt_callback_data = in_data; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_data (kim_options_t in_options, + const void **out_data) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_data ) { err = param_error (2, "out_data", "NULL"); } + + if (!err) { + *out_data = in_options->prompt_callback_data; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_prompt_response (kim_options_t io_options, + kim_prompt_type_t in_prompt_type, + void *in_response) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options ) { err = param_error (1, "io_options", "NULL"); } + if (!err && !in_response) { err = param_error (2, "in_response", "NULL"); } + + if (!err) { +#warning kim_options_set_prompt_response unimplemented + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_prompt_response (kim_options_t in_options, + kim_prompt_type_t in_prompt_type, + void **out_response) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_response) { err = param_error (2, "out_response", "NULL"); } + + if (!err) { +#warning kim_options_get_prompt_response unimplemented + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_start_time (kim_options_t io_options, + kim_time_t in_start_time) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->start_time = in_start_time; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_start_time (kim_options_t in_options, + kim_time_t *out_start_time) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_start_time) { err = param_error (2, "out_start_time", "NULL"); } + + if (!err) { + *out_start_time = in_options->start_time; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_lifetime (kim_options_t io_options, + kim_lifetime_t in_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->lifetime = in_lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_lifetime (kim_options_t in_options, + kim_lifetime_t *out_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_lifetime) { err = param_error (2, "out_lifetime", "NULL"); } + + if (!err) { + *out_lifetime = in_options->lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_renewable (kim_options_t io_options, + kim_boolean_t in_renewable) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->renewable = in_renewable; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_renewable (kim_options_t in_options, + kim_boolean_t *out_renewable) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_renewable) { err = param_error (2, "out_renewable", "NULL"); } + + if (!err) { + *out_renewable = in_options->renewable; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_renewal_lifetime (kim_options_t io_options, + kim_lifetime_t in_renewal_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->renewal_lifetime = in_renewal_lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_renewal_lifetime (kim_options_t in_options, + kim_lifetime_t *out_renewal_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_renewal_lifetime) { err = param_error (2, "out_renewal_lifetime", "NULL"); } + + if (!err) { + *out_renewal_lifetime = in_options->renewal_lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_forwardable (kim_options_t io_options, + kim_boolean_t in_forwardable) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->forwardable = in_forwardable; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_forwardable (kim_options_t in_options, + kim_boolean_t *out_forwardable) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_forwardable) { err = param_error (2, "out_forwardable", "NULL"); } + + if (!err) { + *out_forwardable = in_options->forwardable; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_proxiable (kim_options_t io_options, + kim_boolean_t in_proxiable) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->proxiable = in_proxiable; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_proxiable (kim_options_t in_options, + kim_boolean_t *out_proxiable) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_proxiable) { err = param_error (2, "out_proxiable", "NULL"); } + + if (!err) { + *out_proxiable = in_options->proxiable; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_addressless (kim_options_t io_options, + kim_boolean_t in_addressless) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + + if (!err) { + io_options->addressless = in_addressless; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_addressless (kim_options_t in_options, + kim_boolean_t *out_addressless) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_addressless) { err = param_error (2, "out_addressless", "NULL"); } + + if (!err) { + *out_addressless = in_options->addressless; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_set_service_name (kim_options_t io_options, + kim_string_t in_service_name) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t service_name = NULL; + + if (!err && !io_options ) { err = param_error (1, "io_options", "NULL"); } + if (!err && !in_service_name) { err = param_error (2, "in_service_name", "NULL"); } + + if (!err) { + err = kim_string_copy (&service_name, in_service_name); + } + + if (!err) { + kim_string_free (&io_options->service_name); + io_options->service_name = service_name; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_service_name (kim_options_t in_options, + kim_string_t *out_service_name) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !out_service_name) { err = param_error (2, "out_service_name", "NULL"); } + + if (!err) { + err = kim_string_copy (out_service_name, in_options->service_name); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_get_init_cred_options (kim_options_t in_options, + krb5_context in_context, + krb5_get_init_creds_opt **out_init_cred_options) +{ + kim_error_t err = KIM_NO_ERROR; + krb5_get_init_creds_opt *init_cred_options; + krb5_address **addresses = NULL; + + if (!err && !in_options ) { err = param_error (1, "in_options", "NULL"); } + if (!err && !in_context ) { err = param_error (2, "in_context", "NULL"); } + if (!err && !out_init_cred_options) { err = param_error (3, "out_init_cred_options", "NULL"); } + + if (!err && !in_options->addressless) { + err = krb5_error (krb5_os_localaddr (in_context, &addresses)); + } + + if (!err) { + krb5_get_init_creds_opt_alloc (in_context, &init_cred_options); + krb5_get_init_creds_opt_set_tkt_life (init_cred_options, in_options->lifetime); + krb5_get_init_creds_opt_set_renew_life (init_cred_options, in_options->renewable ? in_options->renewal_lifetime : 0); + krb5_get_init_creds_opt_set_forwardable (init_cred_options, in_options->forwardable); + krb5_get_init_creds_opt_set_proxiable (init_cred_options, in_options->proxiable); + krb5_get_init_creds_opt_set_address_list (init_cred_options, addresses); + + *out_init_cred_options = init_cred_options; + init_cred_options = NULL; + addresses = NULL; + } + + if (init_cred_options) { krb5_get_init_creds_opt_free (in_context, init_cred_options); } + if (addresses ) { krb5_free_addresses (in_context, addresses); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_options_free_init_cred_options (krb5_context in_context, + krb5_get_init_creds_opt **io_init_cred_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_context) { err = param_error (1, "in_context", "NULL"); } + + if (!err && io_init_cred_options && *io_init_cred_options) { + if ((*io_init_cred_options)->address_list) { + krb5_free_addresses (in_context, (*io_init_cred_options)->address_list); + } + krb5_get_init_creds_opt_free (in_context, *io_init_cred_options); + *io_init_cred_options = NULL; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_options_free (kim_options_t *io_options) +{ + if (io_options && *io_options) { + kim_string_free (&(*io_options)->service_name); + free (*io_options); + *io_options = NULL; + } +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +kim_error_code_t kim_prompt_callback_default (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply) +{ + kim_error_t err = KIM_NO_ERROR; + kim_error_code_t code = KIM_NO_ERROR_ECODE; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + if (!err && !out_reply ) { err = param_error (6, "out_reply", "NULL"); } + + if (!err) { + } + + code = kim_error_get_code (check_error (err)); + kim_error_free (&err); + return code; +} + +/* ------------------------------------------------------------------------ */ + +kim_error_code_t kim_prompt_callback_gui (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply) +{ + kim_error_t err = KIM_NO_ERROR; + kim_error_code_t code = KIM_NO_ERROR_ECODE; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + if (!err && !out_reply ) { err = param_error (6, "out_reply", "NULL"); } + + if (!err) { + } + + code = kim_error_get_code (check_error (err)); + kim_error_free (&err); + return code; +} + +/* ------------------------------------------------------------------------ */ + +kim_error_code_t kim_prompt_callback_cli (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply) +{ + kim_error_t err = KIM_NO_ERROR; + kim_error_code_t code = KIM_NO_ERROR_ECODE; + + if (!err && !io_options) { err = param_error (1, "io_options", "NULL"); } + if (!err && !out_reply ) { err = param_error (6, "out_reply", "NULL"); } + + if (!err) { + } + + code = kim_error_get_code (check_error (err)); + kim_error_free (&err); + return code; +} + +/* ------------------------------------------------------------------------ */ + +kim_error_code_t kim_prompt_callback_none (kim_options_t *io_options, + kim_prompt_type_t in_type, + kim_string_t in_title, + kim_string_t in_message, + kim_string_t in_description, + void **out_reply) +{ + return KIM_USER_CANCELED_ECODE; +} diff --git a/src/kim/lib/kim_options_private.h b/src/kim/lib/kim_options_private.h new file mode 100644 index 000000000..ce75e4893 --- /dev/null +++ b/src/kim/lib/kim_options_private.h @@ -0,0 +1,41 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_OPTIONS_PRIVATE_H +#define KIM_OPTIONS_PRIVATE_H + +#include + +kim_error_t kim_options_create_from_defaults (kim_options_t *out_options); + +kim_error_t kim_options_get_init_cred_options (kim_options_t in_options, + krb5_context in_context, + krb5_get_init_creds_opt **out_init_cred_options); + +kim_error_t kim_options_free_init_cred_options (krb5_context in_context, + krb5_get_init_creds_opt **io_init_cred_options); + +#endif /* KIM_OPTIONS_PRIVATE_H */ diff --git a/src/kim/lib/kim_preferences.c b/src/kim/lib/kim_preferences.c new file mode 100644 index 000000000..cf2ad8f2b --- /dev/null +++ b/src/kim/lib/kim_preferences.c @@ -0,0 +1,1120 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "kim_private.h" + +#pragma mark -- KIM Favorite Realms -- + +struct kim_favorite_identities_opaque { + kim_count_t count; + kim_identity_t *identities; +}; + +struct kim_favorite_identities_opaque kim_favorite_identities_initializer = { 0, NULL }; +struct kim_favorite_identities_opaque kim_empty_favorite_identities_struct = { 0, NULL }; +const kim_favorite_identities_t kim_empty_favorite_identities = &kim_empty_favorite_identities_struct; + + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_favorite_identities_allocate (kim_favorite_identities_t *out_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + kim_favorite_identities_t favorite_identities = NULL; + + if (!err && !out_favorite_identities) { err = param_error (1, "out_favorite_identities", "NULL"); } + + if (!err) { + favorite_identities = malloc (sizeof (*favorite_identities)); + if (!favorite_identities) { err = os_error (errno); } + } + + if (!err) { + *favorite_identities = kim_favorite_identities_initializer; + *out_favorite_identities = favorite_identities; + favorite_identities = NULL; + } + + kim_favorite_identities_free (&favorite_identities); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_favorite_identities_resize (kim_favorite_identities_t io_favorite_identities, + kim_count_t in_new_count) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_favorite_identities) { err = param_error (1, "io_favorite_identities", "NULL"); } + + if (!err && io_favorite_identities->count != in_new_count) { + kim_identity_t *identities = NULL; + + if (in_new_count == 0) { + if (io_favorite_identities->identities) { + free (io_favorite_identities->identities); + } + } else { + if (!io_favorite_identities->identities) { + identities = malloc (sizeof (*identities) * in_new_count); + } else { + identities = realloc (io_favorite_identities->identities, + sizeof (*identities) * in_new_count); + } + if (!identities) { err = os_error (errno); } + } + + if (!err) { + io_favorite_identities->count = in_new_count; + io_favorite_identities->identities = identities; + identities = NULL; + } + + if (identities) { free (identities); } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_favorite_identities_create (kim_favorite_identities_t *out_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + kim_favorite_identities_t favorite_identities = NULL; + + if (!err && !out_favorite_identities) { err = param_error (1, "out_favorite_identities", "NULL"); } + + if (!err) { + err = kim_favorite_identities_allocate (&favorite_identities); + } + + if (!err) { + *out_favorite_identities = favorite_identities; + favorite_identities = NULL; + } + + kim_favorite_identities_free (&favorite_identities); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_favorite_identities_copy (kim_favorite_identities_t *out_favorite_identities, + kim_favorite_identities_t in_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + kim_favorite_identities_t favorite_identities = NULL; + + if (!err && !out_favorite_identities) { err = param_error (1, "out_favorite_identities", "NULL"); } + if (!err && !in_favorite_identities ) { err = param_error (2, "in_favorite_identities", "NULL"); } + + if (!err) { + err = kim_favorite_identities_allocate (&favorite_identities); + } + + if (!err) { + err = kim_favorite_identities_resize (favorite_identities, in_favorite_identities->count); + } + + if (!err) { + kim_count_t i; + + for (i = 0; !err && i < favorite_identities->count; i++) { + err = kim_identity_copy (&favorite_identities->identities[i], + in_favorite_identities->identities[i]); + } + } + + if (!err) { + *out_favorite_identities = favorite_identities; + favorite_identities = NULL; + } + + kim_favorite_identities_free (&favorite_identities); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_favorite_identities_get_number_of_identities (kim_favorite_identities_t in_favorite_identities, + kim_count_t *out_number_of_identities) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_favorite_identities ) { err = param_error (1, "in_favorite_identities", "NULL"); } + if (!err && !out_number_of_identities) { err = param_error (2, "out_number_of_identities", "NULL"); } + + if (!err) { + *out_number_of_identities = in_favorite_identities->count; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_favorite_identities_get_identity_at_index (kim_favorite_identities_t in_favorite_identities, + kim_count_t in_index, + kim_identity_t *out_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_favorite_identities) { err = param_error (1, "in_favorite_identities", "NULL"); } + if (!err && !out_identity ) { err = param_error (3, "out_identity", "NULL"); } + + if (!err) { + if (in_index >= in_favorite_identities->count) { + err = kim_error_create_from_code (KIM_BAD_IDENTITY_INDEX_ECODE, in_index); + } + } + + if (!err) { + err = kim_identity_copy (out_identity, in_favorite_identities->identities[in_index]); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_favorite_identities_add_identity (kim_favorite_identities_t io_favorite_identities, + kim_identity_t in_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = NULL; + kim_count_t insert_at = 0; + + if (!err && !io_favorite_identities) { err = param_error (1, "io_favorite_identities", "NULL"); } + if (!err && !in_identity ) { err = param_error (2, "in_identity", "NULL"); } + + if (!err) { + err = kim_identity_copy (&identity, in_identity); + } + + if (!err) { + kim_count_t i; + + for (i = 0; !err && i < io_favorite_identities->count; i++) { + kim_comparison_t identity_comparison = 0; + + err = kim_identity_compare (in_identity, io_favorite_identities->identities[i], &identity_comparison); + + if (!err) { + if (kim_comparison_is_greater_than (identity_comparison)) { + break; /* found the first greater one so insert here */ + + } else if (kim_comparison_is_equal_to (identity_comparison)) { + /* already in list */ + kim_string_t display_string = NULL; + + err = kim_identity_get_display_string (in_identity, &display_string); + + if (!err) { + err = kim_error_create_from_code (KIM_IDENTITY_ALREADY_IN_IDENTITIES_LIST, + display_string); + } + + kim_string_free (&display_string); + } + } + } + + insert_at = i; /* Remember where we are going to insert */ + } + + if (!err) { + err = kim_favorite_identities_resize (io_favorite_identities, io_favorite_identities->count + 1); + } + + if (!err) { + kim_count_t move_count = io_favorite_identities->count - 1 - insert_at; + + memmove (&io_favorite_identities->identities[insert_at + 1], + &io_favorite_identities->identities[insert_at], + move_count * sizeof (*io_favorite_identities->identities)); + io_favorite_identities->identities[insert_at] = identity; + identity = NULL; + } + + kim_identity_free (&identity); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_favorite_identities_remove_identity (kim_favorite_identities_t io_favorite_identities, + kim_identity_t in_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_boolean_t found = FALSE; + kim_count_t i; + + if (!err && !io_favorite_identities) { err = param_error (1, "io_favorite_identities", "NULL"); } + if (!err && !in_identity ) { err = param_error (2, "in_identity", "NULL"); } + + if (!err) { + for (i = 0; !err && !found && i < io_favorite_identities->count; i++) { + kim_identity_t identity = io_favorite_identities->identities[i]; + + err = kim_identity_compare (in_identity, identity, &found); + + if (!err && found) { + kim_count_t new_count = io_favorite_identities->count - 1; + memmove (&io_favorite_identities->identities[i], + &io_favorite_identities->identities[i + 1], + (new_count - i) * sizeof (*io_favorite_identities->identities)); + + kim_error_t terr = kim_favorite_identities_resize (io_favorite_identities, new_count); + if (terr) { + kim_debug_printf ("failed to resize list to %d. Continuing.", new_count); + kim_error_free (&terr); + } + + kim_identity_free (&identity); + } + } + } + + if (!err && !found) { + kim_string_t display_string = NULL; + + err = kim_identity_get_display_string (in_identity, &display_string); + + if (!err) { + err = kim_error_create_from_code (KIM_IDENTITY_NOT_IN_IDENTITIES_LIST, display_string); + } + + kim_string_free (&display_string); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_favorite_identities_remove_all_identities (kim_favorite_identities_t io_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_favorite_identities) { err = param_error (1, "io_favorite_identities", "NULL"); } + + if (!err) { + kim_count_t i; + + for (i = 0; i < io_favorite_identities->count; i++) { + kim_identity_free (&io_favorite_identities->identities[i]); + } + free (io_favorite_identities->identities); + io_favorite_identities->count = 0; + io_favorite_identities->identities = NULL; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_favorite_identities_free (kim_favorite_identities_t *io_favorite_identities) +{ + if (io_favorite_identities && *io_favorite_identities && + *io_favorite_identities != kim_default_favorite_identities) { + kim_count_t i; + + for (i = 0; i < (*io_favorite_identities)->count; i++) { + kim_identity_free (&(*io_favorite_identities)->identities[i]); + } + free ((*io_favorite_identities)->identities); + free (*io_favorite_identities); + *io_favorite_identities = NULL; + } +} + +#pragma mark -- KIM Preferences -- + +struct kim_preferences_opaque { + kim_options_t options; + kim_boolean_t options_changed; + kim_boolean_t remember_options; + kim_boolean_t remember_options_changed; + kim_identity_t client_identity; + kim_boolean_t client_identity_changed; + kim_boolean_t remember_client_identity; + kim_boolean_t remember_client_identity_changed; + kim_lifetime_t minimum_lifetime; + kim_lifetime_t maximum_lifetime; + kim_boolean_t lifetime_range_changed; + kim_lifetime_t minimum_renewal_lifetime; + kim_lifetime_t maximum_renewal_lifetime; + kim_boolean_t renewal_lifetime_range_changed; + kim_favorite_identities_t favorite_identities; + kim_boolean_t favorite_identities_changed; +}; + +struct kim_preferences_opaque kim_preferences_initializer = { + NULL, + FALSE, + kim_default_remember_options, + FALSE, + kim_default_client_identity, + FALSE, + kim_default_remember_client_identity, + FALSE, + kim_default_minimum_lifetime, + kim_default_maximum_lifetime, + FALSE, + kim_default_minimum_renewal_lifetime, + kim_default_maximum_renewal_lifetime, + FALSE, + NULL, + FALSE +}; + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_preferences_read (kim_preferences_t in_preferences) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences) { err = param_error (1, "in_preferences", "NULL"); } + + if (!err) { + kim_lifetime_t lifetime = kim_default_lifetime; + + err = kim_os_preferences_get_lifetime_for_key (kim_preference_key_lifetime, + kim_default_lifetime, + &lifetime); + + if (!err) { + err = kim_options_set_lifetime (in_preferences->options, lifetime); + } + } + + if (!err) { + kim_boolean_t renewable = kim_default_renewable; + + err = kim_os_preferences_get_boolean_for_key (kim_preference_key_renewable, + kim_default_renewable, + &renewable); + + if (!err) { + err = kim_options_set_renewable (in_preferences->options, renewable); + } + } + + if (!err) { + kim_lifetime_t renewal_lifetime = kim_default_renewal_lifetime; + + err = kim_os_preferences_get_lifetime_for_key (kim_preference_key_renewal_lifetime, + kim_default_renewal_lifetime, + &renewal_lifetime); + + if (!err) { + err = kim_options_set_renewal_lifetime (in_preferences->options, renewal_lifetime); + } + } + + if (!err) { + kim_boolean_t forwardable = kim_default_forwardable; + + err = kim_os_preferences_get_boolean_for_key (kim_preference_key_forwardable, + kim_default_forwardable, + &forwardable); + + if (!err) { + err = kim_options_set_forwardable (in_preferences->options, forwardable); + } + } + + if (!err) { + kim_boolean_t proxiable = kim_default_proxiable; + + err = kim_os_preferences_get_boolean_for_key (kim_preference_key_proxiable, + kim_default_proxiable, + &proxiable); + + if (!err) { + err = kim_options_set_proxiable (in_preferences->options, proxiable); + } + } + + if (!err) { + kim_boolean_t addressless = kim_default_addressless; + + err = kim_os_preferences_get_boolean_for_key (kim_preference_key_addressless, + kim_default_addressless, + &addressless); + + if (!err) { + err = kim_options_set_addressless (in_preferences->options, addressless); + } + } + + if (!err) { + err = kim_os_preferences_get_boolean_for_key (kim_preference_key_remember_options, + kim_default_remember_options, + &in_preferences->remember_options); + } + + if (!err) { + kim_identity_t default_identity = kim_default_client_identity; + + err = kim_os_identity_create_for_username (&default_identity); + + if (!err) { + err = kim_os_preferences_get_identity_for_key (kim_preference_key_client_identity, + default_identity, + &in_preferences->client_identity); + } + + kim_identity_free (&default_identity); + } + + if (!err) { + err = kim_os_preferences_get_boolean_for_key (kim_preference_key_remember_client_identity, + kim_default_remember_client_identity, + &in_preferences->remember_client_identity); + } + + if (!err) { + err = kim_os_preferences_get_favorite_identities_for_key (kim_preference_key_favorite_identities, + kim_default_favorite_identities, + &in_preferences->favorite_identities); + } + + if (!err) { + err = kim_os_preferences_get_lifetime_for_key (kim_preference_key_minimum_lifetime, + kim_default_minimum_lifetime, + &in_preferences->minimum_lifetime); + } + + if (!err) { + err = kim_os_preferences_get_lifetime_for_key (kim_preference_key_maximum_lifetime, + kim_default_maximum_lifetime, + &in_preferences->maximum_lifetime); + } + + if (!err) { + err = kim_os_preferences_get_lifetime_for_key (kim_preference_key_minimum_renewal_lifetime, + kim_default_minimum_renewal_lifetime, + &in_preferences->minimum_renewal_lifetime); + } + + if (!err) { + err = kim_os_preferences_get_lifetime_for_key (kim_preference_key_maximum_renewal_lifetime, + kim_default_maximum_renewal_lifetime, + &in_preferences->maximum_renewal_lifetime); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_preferences_write (kim_preferences_t in_preferences) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences) { err = param_error (1, "in_preferences", "NULL"); } + + if (!err && in_preferences->remember_options && in_preferences->options_changed) { + kim_lifetime_t lifetime = kim_default_lifetime; + + err = kim_options_get_lifetime (in_preferences->options, &lifetime); + + if (!err) { + err = kim_os_preferences_set_lifetime_for_key (kim_preference_key_lifetime, + lifetime); + } + + if (!err) { + kim_boolean_t renewable = kim_default_renewable; + + err = kim_options_get_renewable (in_preferences->options, &renewable); + + if (!err) { + err = kim_os_preferences_set_boolean_for_key (kim_preference_key_renewable, + renewable); + } + } + + if (!err) { + kim_lifetime_t renewal_lifetime = kim_default_renewal_lifetime; + + err = kim_options_get_renewal_lifetime (in_preferences->options, &renewal_lifetime); + + if (!err) { + err = kim_os_preferences_set_lifetime_for_key (kim_preference_key_renewal_lifetime, + renewal_lifetime); + } + } + + if (!err) { + kim_boolean_t forwardable = kim_default_forwardable; + + err = kim_options_get_forwardable (in_preferences->options, &forwardable); + + if (!err) { + err = kim_os_preferences_set_boolean_for_key (kim_preference_key_forwardable, + forwardable); + } + } + + if (!err) { + kim_boolean_t proxiable = kim_default_proxiable; + + err = kim_options_get_proxiable (in_preferences->options, &proxiable); + + if (!err) { + err = kim_os_preferences_set_boolean_for_key (kim_preference_key_proxiable, + proxiable); + } + } + + if (!err) { + kim_boolean_t addressless = kim_default_addressless; + + err = kim_options_get_addressless (in_preferences->options, &addressless); + + if (!err) { + err = kim_os_preferences_set_boolean_for_key (kim_preference_key_addressless, + addressless); + } + } + } + + if (!err && in_preferences->remember_options_changed) { + err = kim_os_preferences_set_boolean_for_key (kim_preference_key_remember_options, + in_preferences->remember_options); + } + + if (!err && in_preferences->remember_client_identity && in_preferences->client_identity_changed) { + kim_identity_t default_identity = kim_default_client_identity; + + err = kim_os_identity_create_for_username (&default_identity); + + if (!err) { + err = kim_os_preferences_set_identity_for_key (kim_preference_key_client_identity, + in_preferences->client_identity); + } + + kim_identity_free (&default_identity); + } + + if (!err && in_preferences->remember_client_identity_changed) { + err = kim_os_preferences_set_boolean_for_key (kim_preference_key_remember_client_identity, + in_preferences->remember_client_identity); + } + + if (!err && in_preferences->favorite_identities_changed) { + err = kim_os_preferences_set_favorite_identities_for_key (kim_preference_key_favorite_identities, + in_preferences->favorite_identities); + } + + if (!err && in_preferences->lifetime_range_changed) { + err = kim_os_preferences_set_lifetime_for_key (kim_preference_key_minimum_lifetime, + in_preferences->minimum_lifetime); + if (!err) { + err = kim_os_preferences_set_lifetime_for_key (kim_preference_key_maximum_lifetime, + in_preferences->maximum_lifetime); + } + } + + if (!err && in_preferences->renewal_lifetime_range_changed) { + err = kim_os_preferences_set_lifetime_for_key (kim_preference_key_minimum_renewal_lifetime, + in_preferences->minimum_renewal_lifetime); + if (!err) { + err = kim_os_preferences_set_lifetime_for_key (kim_preference_key_maximum_renewal_lifetime, + in_preferences->maximum_renewal_lifetime); + } + } + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_preferences_allocate (kim_preferences_t *out_preferences) +{ + kim_error_t err = KIM_NO_ERROR; + kim_preferences_t preferences = NULL; + + if (!err && !out_preferences) { err = param_error (1, "out_preferences", "NULL"); } + + if (!err) { + preferences = malloc (sizeof (*preferences)); + if (!preferences) { err = os_error (errno); } + } + + if (!err) { + *preferences = kim_preferences_initializer; + *out_preferences = preferences; + preferences = NULL; + } + + kim_preferences_free (&preferences); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_create (kim_preferences_t *out_preferences) +{ + kim_error_t err = KIM_NO_ERROR; + kim_preferences_t preferences = NULL; + + if (!err && !out_preferences) { err = param_error (1, "out_preferences", "NULL"); } + + if (!err) { + err = kim_preferences_allocate (&preferences); + } + + if (!err) { + err = kim_options_create_from_defaults (&preferences->options); + } + + if (!err) { + err = kim_preferences_read (preferences); + } + + if (!err) { + *out_preferences = preferences; + preferences = NULL; + } + + kim_preferences_free (&preferences); + + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_copy (kim_preferences_t *out_preferences, + kim_preferences_t in_preferences) +{ + kim_error_t err = KIM_NO_ERROR; + kim_preferences_t preferences = NULL; + + if (!err && !out_preferences) { err = param_error (1, "out_preferences", "NULL"); } + if (!err && !in_preferences ) { err = param_error (2, "in_preferences", "NULL"); } + + if (!err) { + err = kim_preferences_allocate (&preferences); + } + + if (!err) { + preferences->remember_options = in_preferences->remember_options; + err = kim_options_copy (&preferences->options, in_preferences->options); + } + + if (!err) { + preferences->remember_client_identity = in_preferences->remember_client_identity; + err = kim_identity_copy (&preferences->client_identity, in_preferences->client_identity); + } + + if (!err) { + err = kim_favorite_identities_copy (&preferences->favorite_identities, in_preferences->favorite_identities); + } + + if (!err) { + *out_preferences = preferences; + preferences = NULL; + } + + kim_preferences_free (&preferences); + + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_options (kim_preferences_t io_preferences, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + kim_options_t options = NULL; + + if (!err && !io_preferences) { err = param_error (1, "io_preferences", "NULL"); } + if (!err && !in_options ) { err = param_error (2, "in_options", "NULL"); } + + if (!err) { + err = kim_options_copy (&options, in_options); + } + + if (!err) { + kim_options_free (&io_preferences->options); + io_preferences->options = options; + io_preferences->options_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_options (kim_preferences_t in_preferences, + kim_options_t *out_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_options ) { err = param_error (2, "out_options", "NULL"); } + + if (!err) { + err = kim_options_copy (out_options, in_preferences->options); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_remember_options (kim_preferences_t io_preferences, + kim_boolean_t in_remember_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_preferences) { err = param_error (1, "io_preferences", "NULL"); } + + if (!err) { + io_preferences->remember_options = in_remember_options; + io_preferences->remember_options_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_remember_options (kim_preferences_t in_preferences, + kim_boolean_t *out_remember_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_remember_options) { err = param_error (2, "out_remember_options", "NULL"); } + + if (!err) { + *out_remember_options = in_preferences->remember_options; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_client_identity (kim_preferences_t io_preferences, + kim_identity_t in_client_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = KIM_IDENTITY_ANY; + + if (!err && !io_preferences ) { err = param_error (1, "io_preferences", "NULL"); } + /* in_client_identity may be KIM_IDENTITY_ANY */ + + if (!err && in_client_identity) { + err = kim_identity_copy (&identity, in_client_identity); + } + + if (!err) { + kim_identity_free (&io_preferences->client_identity); + io_preferences->client_identity = identity; + io_preferences->client_identity_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_client_identity (kim_preferences_t in_preferences, + kim_identity_t *out_client_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_client_identity) { err = param_error (2, "out_client_identity", "NULL"); } + + if (!err) { + err = kim_identity_copy (out_client_identity, in_preferences->client_identity); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_remember_client_identity (kim_preferences_t io_preferences, + kim_boolean_t in_remember_client_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_preferences) { err = param_error (1, "io_preferences", "NULL"); } + + if (!err) { + io_preferences->remember_client_identity = in_remember_client_identity; + io_preferences->remember_client_identity_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_remember_client_identity (kim_preferences_t in_preferences, + kim_boolean_t *out_remember_client_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_remember_client_identity) { err = param_error (2, "out_remember_client_identity", "NULL"); } + + if (!err) { + *out_remember_client_identity = in_preferences->remember_client_identity; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_minimum_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_minimum_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_preferences) { err = param_error (1, "io_preferences", "NULL"); } + + if (!err) { + io_preferences->minimum_lifetime = in_minimum_lifetime; + io_preferences->lifetime_range_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_minimum_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_minimum_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_minimum_lifetime) { err = param_error (2, "out_minimum_lifetime", "NULL"); } + + if (!err) { + *out_minimum_lifetime = in_preferences->minimum_lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_maximum_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_maximum_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_preferences) { err = param_error (1, "io_preferences", "NULL"); } + + if (!err) { + io_preferences->maximum_lifetime = in_maximum_lifetime; + io_preferences->lifetime_range_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_maximum_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_maximum_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_maximum_lifetime) { err = param_error (2, "out_maximum_lifetime", "NULL"); } + + if (!err) { + *out_maximum_lifetime = in_preferences->maximum_lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_minimum_renewal_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_minimum_renewal_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_preferences) { err = param_error (1, "io_preferences", "NULL"); } + + if (!err) { + io_preferences->minimum_renewal_lifetime = in_minimum_renewal_lifetime; + io_preferences->renewal_lifetime_range_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_minimum_renewal_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_minimum_renewal_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_minimum_renewal_lifetime) { err = param_error (2, "out_minimum_renewal_lifetime", "NULL"); } + + if (!err) { + *out_minimum_renewal_lifetime = in_preferences->minimum_renewal_lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_maximum_renewal_lifetime (kim_preferences_t io_preferences, + kim_lifetime_t in_maximum_renewal_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_preferences) { err = param_error (1, "io_preferences", "NULL"); } + + if (!err) { + io_preferences->maximum_renewal_lifetime = in_maximum_renewal_lifetime; + io_preferences->renewal_lifetime_range_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_maximum_renewal_lifetime (kim_preferences_t in_preferences, + kim_lifetime_t *out_maximum_renewal_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_maximum_renewal_lifetime) { err = param_error (2, "out_maximum_renewal_lifetime", "NULL"); } + + if (!err) { + *out_maximum_renewal_lifetime = in_preferences->maximum_renewal_lifetime; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_set_favorite_identities (kim_preferences_t io_preferences, + kim_favorite_identities_t in_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + kim_favorite_identities_t favorite_identities = NULL; + + if (!err && !io_preferences ) { err = param_error (1, "io_preferences", "NULL"); } + if (!err && !in_favorite_identities) { err = param_error (2, "in_favorite_identities", "NULL"); } + + if (!err) { + err = kim_favorite_identities_copy (&favorite_identities, in_favorite_identities); + } + + if (!err) { + kim_favorite_identities_free (&io_preferences->favorite_identities); + io_preferences->favorite_identities = favorite_identities; + io_preferences->favorite_identities_changed = TRUE; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_get_favorite_identities (kim_preferences_t in_preferences, + kim_favorite_identities_t *out_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences ) { err = param_error (1, "in_preferences", "NULL"); } + if (!err && !out_favorite_identities) { err = param_error (2, "out_favorite_identities", "NULL"); } + + if (!err) { + err = kim_favorite_identities_copy (out_favorite_identities, in_preferences->favorite_identities); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_preferences_synchronize (kim_preferences_t in_preferences) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_preferences) { err = param_error (1, "in_preferences", "NULL"); } + + if (!err) { + err = kim_preferences_write (in_preferences); + } + + if (!err) { + err = kim_preferences_read (in_preferences); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_preferences_free (kim_preferences_t *io_preferences) +{ + if (io_preferences && *io_preferences) { + kim_options_free (&(*io_preferences)->options); + kim_identity_free (&(*io_preferences)->client_identity); + kim_favorite_identities_free (&(*io_preferences)->favorite_identities); + free (*io_preferences); + *io_preferences = NULL; + } +} + diff --git a/src/kim/lib/kim_preferences_private.h b/src/kim/lib/kim_preferences_private.h new file mode 100644 index 000000000..a629fbbdb --- /dev/null +++ b/src/kim/lib/kim_preferences_private.h @@ -0,0 +1,102 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_PREFERENCES_PRIVATE_H +#define KIM_PREFERENCES_PRIVATE_H + +#include + +extern const kim_favorite_identities_t kim_empty_favorite_identities; + +typedef enum kim_preference_key_enum { + kim_preference_key_lifetime, + kim_preference_key_renewable, + kim_preference_key_renewal_lifetime, + kim_preference_key_forwardable, + kim_preference_key_proxiable, + kim_preference_key_addressless, + kim_preference_key_remember_options, + kim_preference_key_client_identity, + kim_preference_key_remember_client_identity, + kim_preference_key_favorite_identities, + kim_preference_key_minimum_lifetime, + kim_preference_key_maximum_lifetime, + kim_preference_key_minimum_renewal_lifetime, + kim_preference_key_maximum_renewal_lifetime +} kim_preference_key_t; + +#define kim_default_lifetime 10*60*60 +#define kim_default_renewable TRUE +#define kim_default_renewal_lifetime 7*24*60*60 +#define kim_default_forwardable TRUE +#define kim_default_proxiable TRUE +#define kim_default_addressless TRUE +#define kim_default_remember_options TRUE +#define kim_default_client_identity KIM_IDENTITY_ANY +#define kim_default_remember_client_identity TRUE +#define kim_default_favorite_identities kim_empty_favorite_identities +#define kim_default_minimum_lifetime 10*60 +#define kim_default_maximum_lifetime 10*60*60 +#define kim_default_minimum_renewal_lifetime 10*60 +#define kim_default_maximum_renewal_lifetime 7*24*60*60 + + +kim_error_t kim_os_preferences_get_identity_for_key (kim_preference_key_t in_key, + kim_identity_t in_hardcoded_default, + kim_identity_t *out_identity); + +kim_error_t kim_os_preferences_set_identity_for_key (kim_preference_key_t in_key, + kim_identity_t in_identity); + +kim_error_t kim_os_preferences_get_favorite_identities_for_key (kim_preference_key_t in_key, + kim_favorite_identities_t in_hardcoded_default, + kim_favorite_identities_t *out_favorite_identities); + +kim_error_t kim_os_preferences_set_favorite_identities_for_key (kim_preference_key_t in_key, + kim_favorite_identities_t in_favorite_identities); + +kim_error_t kim_os_preferences_get_time_for_key (kim_preference_key_t in_key, + kim_time_t in_hardcoded_default, + kim_time_t *out_time); + +kim_error_t kim_os_preferences_set_time_for_key (kim_preference_key_t in_key, + kim_time_t in_time); + +kim_error_t kim_os_preferences_get_lifetime_for_key (kim_preference_key_t in_key, + kim_lifetime_t in_hardcoded_default, + kim_lifetime_t *out_lifetime); + +kim_error_t kim_os_preferences_set_lifetime_for_key (kim_preference_key_t in_key, + kim_lifetime_t in_lifetime); + +kim_error_t kim_os_preferences_get_boolean_for_key (kim_preference_key_t in_key, + kim_boolean_t in_hardcoded_default, + kim_boolean_t *out_boolean); + +kim_error_t kim_os_preferences_set_boolean_for_key (kim_preference_key_t in_key, + kim_boolean_t in_boolean); + +#endif /* KIM_PREFERENCES_PRIVATE_H */ diff --git a/src/kim/lib/kim_private.h b/src/kim/lib/kim_private.h new file mode 100644 index 000000000..cf9ed81c2 --- /dev/null +++ b/src/kim/lib/kim_private.h @@ -0,0 +1,45 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_PRIVATE_H +#define KIM_PRIVATE_H + +#include +#include +#include + +#include + +#include "kim_error_private.h" +#include "kim_identity_private.h" +#include "kim_ccache_private.h" +#include "kim_library_private.h" +#include "kim_options_private.h" +#include "kim_preferences_private.h" +#include "kim_selection_hints_private.h" +#include "kim_string_private.h" + +#endif /* KIM_PRIVATE_H */ diff --git a/src/kim/lib/kim_selection_hints.c b/src/kim/lib/kim_selection_hints.c new file mode 100644 index 000000000..4ed3fbada --- /dev/null +++ b/src/kim/lib/kim_selection_hints.c @@ -0,0 +1,702 @@ +/* + * Copyright 2005-2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "kim_private.h" + +/* ------------------------------------------------------------------------ */ + +struct kim_selection_hints_opaque { + kim_string_t application_identifier; + kim_string_t application_name; + kim_string_t explanation; + kim_options_t options; + kim_boolean_t allow_user_interaction; + kim_boolean_t use_cached_results; + kim_string_t service_identity; + kim_string_t client_realm; + kim_string_t user; + kim_string_t service_realm; + kim_string_t service; + kim_string_t server; +}; + +struct kim_selection_hints_opaque kim_selection_hints_initializer = { + NULL, + NULL, + NULL, + KIM_OPTIONS_DEFAULT, + TRUE, + TRUE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_selection_hints_allocate (kim_selection_hints_t *out_selection_hints) +{ + kim_error_t err = KIM_NO_ERROR; + kim_selection_hints_t selection_hints = NULL; + + if (!err && !out_selection_hints) { err = param_error (1, "out_selection_hints", "NULL"); } + + if (!err) { + selection_hints = malloc (sizeof (*selection_hints)); + if (!selection_hints) { err = os_error (errno); } + } + + if (!err) { + *selection_hints = kim_selection_hints_initializer; + *out_selection_hints = selection_hints; + selection_hints = NULL; + } + + kim_selection_hints_free (&selection_hints); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_create (kim_selection_hints_t *out_selection_hints, + kim_string_t in_application_identifier) +{ + kim_error_t err = KIM_NO_ERROR; + kim_selection_hints_t selection_hints = NULL; + + if (!err && !out_selection_hints ) { err = param_error (1, "out_selection_hints", "NULL"); } + if (!err && !in_application_identifier) { err = param_error (1, "in_application_identifier", "NULL"); } + + if (!err) { + err = kim_selection_hints_allocate (&selection_hints); + } + + if (!err) { + err = kim_string_copy (&selection_hints->application_identifier, + in_application_identifier); + } + + if (!err) { + *out_selection_hints = selection_hints; + selection_hints = NULL; + } + + kim_selection_hints_free (&selection_hints); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_copy (kim_selection_hints_t *out_selection_hints, + kim_selection_hints_t in_selection_hints) +{ + kim_error_t err = KIM_NO_ERROR; + kim_selection_hints_t selection_hints = NULL; + + if (!err && !out_selection_hints) { err = param_error (1, "out_selection_hints", "NULL"); } + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + + if (!err) { + err = kim_selection_hints_allocate (&selection_hints); + } + + if (!err) { + err = kim_string_copy (&selection_hints->application_identifier, + in_selection_hints->application_identifier); + } + + if (!err && in_selection_hints->application_name) { + err = kim_string_copy (&selection_hints->application_name, + in_selection_hints->application_name); + } + + if (!err && in_selection_hints->explanation) { + err = kim_string_copy (&selection_hints->explanation, + in_selection_hints->explanation); + } + + if (!err && in_selection_hints->options) { + err = kim_options_copy (&selection_hints->options, + in_selection_hints->options); + } + + if (!err && in_selection_hints->service_identity) { + err = kim_string_copy (&selection_hints->service_identity, + in_selection_hints->service_identity); + } + + if (!err && in_selection_hints->client_realm) { + err = kim_string_copy (&selection_hints->client_realm, + in_selection_hints->client_realm); + } + + if (!err && in_selection_hints->user) { + err = kim_string_copy (&selection_hints->user, + in_selection_hints->user); + } + + if (!err && in_selection_hints->service_realm) { + err = kim_string_copy (&selection_hints->service_realm, + in_selection_hints->service_realm); + } + + if (!err && in_selection_hints->service) { + err = kim_string_copy (&selection_hints->service, + in_selection_hints->service); + } + + if (!err && in_selection_hints->server) { + err = kim_string_copy (&selection_hints->server, + in_selection_hints->server); + } + + if (!err) { + selection_hints->allow_user_interaction = in_selection_hints->allow_user_interaction; + selection_hints->use_cached_results = in_selection_hints->use_cached_results; + + *out_selection_hints = selection_hints; + selection_hints = NULL; + } + + kim_selection_hints_free (&selection_hints); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_service_identity_hint (kim_selection_hints_t io_selection_hints, + kim_identity_t in_service_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints ) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_service_identity) { err = param_error (2, "in_service_identity", "KIM_IDENTITY_ANY"); } + + if (!err) { + err = kim_identity_get_string (in_service_identity, &io_selection_hints->service_identity); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_service_identity_hint (kim_selection_hints_t in_selection_hints, + kim_identity_t *out_service_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_service_identity) { err = param_error (2, "out_service_identity", "NULL"); } + + if (!err) { + err = kim_identity_create_from_string (out_service_identity, in_selection_hints->service_identity); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_client_realm_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_client_realm) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_client_realm ) { err = param_error (2, "in_client_realm", "NULL"); } + + if (!err) { + err = kim_string_copy (&io_selection_hints->client_realm, in_client_realm); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_client_realm_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_client_realm) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_client_realm ) { err = param_error (2, "out_client_realm", "NULL"); } + + if (!err) { + if (in_selection_hints->client_realm) { + err = kim_string_copy (out_client_realm, in_selection_hints->client_realm); + } else { + *out_client_realm = NULL; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_user_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_user) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_user ) { err = param_error (2, "in_user", "NULL"); } + + if (!err) { + err = kim_string_copy (&io_selection_hints->user, in_user); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_user_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_user) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_user ) { err = param_error (2, "out_user", "NULL"); } + + if (!err) { + if (in_selection_hints->user) { + err = kim_string_copy (out_user, in_selection_hints->user); + } else { + *out_user = NULL; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_service_realm_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_service_realm) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_service_realm ) { err = param_error (2, "in_service_realm", "NULL"); } + + if (!err) { + err = kim_string_copy (&io_selection_hints->service_realm, in_service_realm); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_service_realm_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_service_realm) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_service_realm ) { err = param_error (2, "out_service_realm", "NULL"); } + + if (!err) { + if (in_selection_hints->service_realm) { + err = kim_string_copy (out_service_realm, in_selection_hints->service_realm); + } else { + *out_service_realm = NULL; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_service_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_service) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_service ) { err = param_error (2, "in_service", "NULL"); } + + if (!err) { + err = kim_string_copy (&io_selection_hints->service, in_service); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_service_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_service) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_service ) { err = param_error (2, "out_service", "NULL"); } + + if (!err) { + if (in_selection_hints->service) { + err = kim_string_copy (out_service, in_selection_hints->service); + } else { + *out_service = NULL; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_server_hint (kim_selection_hints_t io_selection_hints, + kim_string_t in_server_hostname) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_server_hostname) { err = param_error (2, "in_server_hostname", "NULL"); } + + if (!err) { + err = kim_string_copy (&io_selection_hints->server, in_server_hostname); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_server_hint (kim_selection_hints_t in_selection_hints, + kim_string_t *out_server_hostname) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_server_hostname) { err = param_error (2, "out_server_hostname", "NULL"); } + + if (!err) { + if (in_selection_hints->server) { + err = kim_string_copy (out_server_hostname, in_selection_hints->server); + } else { + *out_server_hostname = NULL; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_application_name (kim_selection_hints_t io_selection_hints, + kim_string_t in_application_name) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints ) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_application_name) { err = param_error (2, "in_application_name", "NULL"); } + + if (!err) { + err = kim_string_copy (&io_selection_hints->application_name, in_application_name); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_application_name (kim_selection_hints_t in_selection_hints, + kim_string_t *out_application_name) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_application_name) { err = param_error (2, "out_application_name", "NULL"); } + + if (!err) { + if (in_selection_hints->application_name) { + err = kim_string_copy (out_application_name, in_selection_hints->application_name); + } else { + *out_application_name = NULL; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_explanation (kim_selection_hints_t io_selection_hints, + kim_string_t in_explanation) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_explanation ) { err = param_error (2, "in_explanation", "NULL"); } + + if (!err) { + err = kim_string_copy (&io_selection_hints->explanation, in_explanation); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_explanation (kim_selection_hints_t in_selection_hints, + kim_string_t *out_explanation) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_explanation ) { err = param_error (2, "out_explanation", "NULL"); } + + if (!err) { + if (in_selection_hints->explanation) { + err = kim_string_copy (out_explanation, in_selection_hints->explanation); + } else { + *out_explanation = NULL; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_options (kim_selection_hints_t io_selection_hints, + kim_options_t in_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints) { err = param_error (1, "io_selection_hints", "NULL"); } + if (!err && !in_options ) { err = param_error (2, "in_options", "NULL"); } + + if (!err) { + err = kim_options_copy (&io_selection_hints->options, in_options); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_options (kim_selection_hints_t in_selection_hints, + kim_options_t *out_options) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_options ) { err = param_error (2, "out_options", "NULL"); } + + if (!err) { + if (in_selection_hints->options) { + err = kim_options_copy (out_options, in_selection_hints->options); + } else { + *out_options = KIM_OPTIONS_DEFAULT; + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_allow_user_interaction (kim_selection_hints_t io_selection_hints, + kim_boolean_t in_allow_user_interaction) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints ) { err = param_error (1, "io_selection_hints", "NULL"); } + + if (!err) { + io_selection_hints->allow_user_interaction = in_allow_user_interaction; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_allow_user_interaction (kim_selection_hints_t in_selection_hints, + kim_boolean_t *out_allow_user_interaction) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_allow_user_interaction) { err = param_error (2, "out_allow_user_interaction", "NULL"); } + + if (!err) { + *out_allow_user_interaction = in_selection_hints->allow_user_interaction; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_set_remember_identity (kim_selection_hints_t io_selection_hints, + kim_boolean_t in_use_cached_results) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !io_selection_hints ) { err = param_error (1, "io_selection_hints", "NULL"); } + + if (!err) { + io_selection_hints->use_cached_results = in_use_cached_results; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_remember_identity (kim_selection_hints_t in_selection_hints, + kim_boolean_t *out_use_cached_results) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_use_cached_results) { err = param_error (2, "out_use_cached_results", "NULL"); } + + if (!err) { + *out_use_cached_results = in_selection_hints->use_cached_results; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t *out_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_identity_t identity = NULL; + kim_ccache_t ccache = NULL; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_identity ) { err = param_error (2, "out_identity", "NULL"); } + + if (!err && in_selection_hints->use_cached_results) { + err = kim_os_selection_hints_lookup_identity (in_selection_hints, &identity); + } + + if (!err && !identity && in_selection_hints->allow_user_interaction) { +#warning GUI to let user pick identity here + } + + if (!err) { + *out_identity = identity; + identity = NULL; + } + + kim_identity_free (&identity); + kim_ccache_free (&ccache); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_remember_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t in_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !in_identity ) { err = param_error (2, "in_identity", "NULL"); } + + if (!err) { + err = kim_os_selection_hints_remember_identity (in_selection_hints, + in_identity); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_forget_identity (kim_selection_hints_t in_selection_hints) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + + if (!err) { + err = kim_os_selection_hints_forget_identity (in_selection_hints); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_selection_hints_get_preference_strings (kim_selection_hints_t in_selection_hints, + kim_selection_hints_preference_strings *io_preference_strings) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !io_preference_strings) { err = param_error (2, "io_preference_strings", "NULL"); } + + if (!err) { + io_preference_strings->application_identifier = in_selection_hints->application_identifier; + io_preference_strings->service_identity = in_selection_hints->service_identity; + io_preference_strings->client_realm = in_selection_hints->client_realm; + io_preference_strings->user = in_selection_hints->user; + io_preference_strings->service_realm = in_selection_hints->service_realm; + io_preference_strings->service = in_selection_hints->service; + io_preference_strings->server = in_selection_hints->server; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +void kim_selection_hints_free (kim_selection_hints_t *io_selection_hints) +{ + if (io_selection_hints && *io_selection_hints) { + kim_string_free (&(*io_selection_hints)->application_identifier); + kim_string_free (&(*io_selection_hints)->application_name); + kim_string_free (&(*io_selection_hints)->explanation); + kim_options_free (&(*io_selection_hints)->options); + kim_string_free (&(*io_selection_hints)->service_identity); + kim_string_free (&(*io_selection_hints)->client_realm); + kim_string_free (&(*io_selection_hints)->user); + kim_string_free (&(*io_selection_hints)->service_realm); + kim_string_free (&(*io_selection_hints)->service); + kim_string_free (&(*io_selection_hints)->server); + free (*io_selection_hints); + *io_selection_hints = NULL; + } +} + diff --git a/src/kim/lib/kim_selection_hints_private.h b/src/kim/lib/kim_selection_hints_private.h new file mode 100644 index 000000000..a975ff95f --- /dev/null +++ b/src/kim/lib/kim_selection_hints_private.h @@ -0,0 +1,55 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_SELECTION_HINTS_PRIVATE_H +#define KIM_SELECTION_HINTS_PRIVATE_H + +#include + +typedef struct kim_selection_hints_preference_strings { + kim_string_t application_identifier; + kim_string_t service_identity; + kim_string_t client_realm; + kim_string_t user; + kim_string_t service_realm; + kim_string_t service; + kim_string_t server; +} kim_selection_hints_preference_strings; + + +kim_error_t kim_selection_hints_get_preference_strings (kim_selection_hints_t in_selection_hints, + kim_selection_hints_preference_strings *io_preference_strings); + +kim_error_t kim_os_selection_hints_lookup_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t *out_identity); + +kim_error_t kim_os_selection_hints_remember_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t in_identity); + +kim_error_t kim_os_selection_hints_forget_identity (kim_selection_hints_t in_selection_hints); + + +#endif /* KIM_SELECTION_HINTS_PRIVATE_H */ diff --git a/src/kim/lib/kim_string.c b/src/kim/lib/kim_string.c new file mode 100644 index 000000000..941467b1e --- /dev/null +++ b/src/kim/lib/kim_string.c @@ -0,0 +1,181 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "kim_private.h" + + +/* ------------------------------------------------------------------------ */ + +static inline kim_error_t kim_string_allocate (kim_string_t *out_string, + kim_count_t in_length) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t string = NULL; + + if (!err && !out_string) { err = param_error (1, "out_string", "NULL"); } + + if (!err) { + string = calloc (in_length, sizeof (char *)); + if (!string) { err = os_error (errno); } + } + + if (!err) { + *out_string = string; + string = NULL; + } + + kim_string_free (&string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_string_create_from_format (kim_string_t *out_string, + kim_string_t in_format, + ...) +{ + kim_error_t err = KIM_NO_ERROR; + va_list args; + + va_start (args, in_format); + err = kim_string_create_from_format_va (out_string, in_format, args); + va_end (args); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_string_create_from_format_va_retcode (kim_string_t *out_string, + kim_string_t in_format, + va_list in_args) +{ + kim_error_t err = KIM_NO_ERROR; + + int count = vasprintf ((char **) out_string, in_format, in_args); + if (count < 0) { err = os_error (ENOMEM); } + + return err; +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_string_create_from_format_va (kim_string_t *out_string, + kim_string_t in_format, + va_list in_args) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t string = NULL; + + if (!err && !out_string) { err = param_error (1, "out_string", "NULL"); } + if (!err && !in_format ) { err = param_error (2, "in_format", "NULL"); } + + if (!err) { + err = kim_string_create_from_format_va_retcode (&string, in_format, in_args); + } + + if (!err) { + *out_string = string; + string = NULL; + } + + if (string) { kim_string_free (&string); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_string_create_from_buffer (kim_string_t *out_string, + const char *in_buffer, + kim_count_t in_length) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t string = NULL; + + if (!err && !out_string) { err = param_error (1, "out_string", "NULL"); } + if (!err && !in_buffer ) { err = param_error (2, "in_buffer", "NULL"); } + + if (!err) { + err = kim_string_allocate (&string, in_length + 1); + } + + if (!err) { + memcpy ((char *) string, in_buffer, in_length * sizeof (char)); + *out_string = string; + string = NULL; + } + + kim_string_free (&string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_string_copy (kim_string_t *out_string, + kim_string_t in_string) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t string = NULL; + + if (!err && !out_string) { err = param_error (1, "out_string", "NULL"); } + if (!err && !in_string ) { err = param_error (2, "in_string", "NULL"); } + + if (!err) { + err = kim_string_allocate (&string, strlen (in_string) + 1); + } + + if (!err) { + strcpy ((char *) string, in_string); + *out_string = string; + string = NULL; + } + + kim_string_free (&string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_string_compare (kim_string_t in_string, + kim_string_t in_compare_to_string, + kim_comparison_t *out_comparison) +{ + return kim_os_string_compare (in_string, in_compare_to_string, out_comparison); +} + +/* ------------------------------------------------------------------------ */ + +void kim_string_free (kim_string_t *io_string) +{ + if (io_string && *io_string) { + free ((char *) *io_string); + *io_string = NULL; + } +} diff --git a/src/kim/lib/kim_string_private.h b/src/kim/lib/kim_string_private.h new file mode 100644 index 000000000..72204c2ed --- /dev/null +++ b/src/kim/lib/kim_string_private.h @@ -0,0 +1,60 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_STRING_PRIVATE_H +#define KIM_STRING_PRIVATE_H + +#include + + +kim_error_t kim_string_create_from_format (kim_string_t *out_string, + kim_string_t in_format, + ...); + +kim_error_t kim_string_create_from_format_va_retcode (kim_string_t *out_string, + kim_string_t in_format, + va_list in_args); + +kim_error_t kim_string_create_from_format_va (kim_string_t *out_string, + kim_string_t in_format, + va_list in_args); + +kim_error_t kim_string_create_from_buffer (kim_string_t *out_string, + const char *in_buffer, + kim_count_t in_length); + +kim_error_t kim_string_prepend (kim_string_t *io_string, + kim_string_t in_prefix); + +kim_error_t kim_string_append (kim_string_t *io_string, + kim_string_t in_suffix); + +/* OS-specific because it should use UTF8-safe sorting where possible */ +kim_error_t kim_os_string_compare (kim_string_t in_string, + kim_string_t in_compare_to_string, + kim_comparison_t *out_comparison); + +#endif /* KIM_STRING_PRIVATE_H */ diff --git a/src/kim/lib/mac/kim_os_identity.c b/src/kim/lib/mac/kim_os_identity.c new file mode 100644 index 000000000..ed351a55f --- /dev/null +++ b/src/kim/lib/mac/kim_os_identity.c @@ -0,0 +1,50 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include +#include + +#include "kim_os_private.h" + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_identity_create_for_username (kim_identity_t *out_identity) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !out_identity) { err = param_error (1, "out_identity", "NULL"); } + + if (!err) { + struct passwd *pw = getpwuid (kipc_session_get_session_uid ()); + if (pw) { + err = kim_identity_create_from_string (out_identity, pw->pw_name); + } else { + *out_identity = KIM_IDENTITY_ANY; + } + } + + return check_error (err); +} diff --git a/src/kim/lib/mac/kim_os_library.c b/src/kim/lib/mac/kim_os_library.c new file mode 100644 index 000000000..733f5c46c --- /dev/null +++ b/src/kim/lib/mac/kim_os_library.c @@ -0,0 +1,56 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include +#include + +#include "kim_os_private.h" + +/* ------------------------------------------------------------------------ */ + +void kim_os_library_debug_print (kim_string_t in_string) +{ + dprintf (in_string); +} + +/* ------------------------------------------------------------------------ */ + +kim_boolean_t kim_os_library_caller_is_server (void) +{ + CFBundleRef mainBundle = CFBundleGetMainBundle (); + if (mainBundle) { + CFStringRef mainBundleID = CFBundleGetIdentifier (mainBundle); + if (mainBundleID) { + CFComparisonResult result; + result = CFStringCompare (mainBundleID, CFSTR("edu.mit.Kerberos.KerberosAgent"), 0); + if (result == kCFCompareEqualTo) { + return TRUE; + } + } + } + + return FALSE; +} diff --git a/src/kim/lib/mac/kim_os_preferences.c b/src/kim/lib/mac/kim_os_preferences.c new file mode 100644 index 000000000..3cd2ab8e2 --- /dev/null +++ b/src/kim/lib/mac/kim_os_preferences.c @@ -0,0 +1,552 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include + +#include "kim_os_private.h" + +#define KIM_PREFERENCES_FILE CFSTR("edu.mit.Kerberos.IdentityManagement") +#define KLL_PREFERENCES_FILE CFSTR("edu.mit.Kerberos.KerberosLogin") + +#define kim_os_preference_any_identity "KIM_IDENTITY_ANY" + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_preferences_cfstring_for_key (kim_preference_key_t in_key, + CFStringRef *out_kim_string_key, + CFStringRef *out_kll_string_key) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !out_kim_string_key) { err = param_error (2, "out_kim_string_key", "NULL"); } + if (!err && !out_kll_string_key) { err = param_error (3, "out_kll_string_key", "NULL"); } + + if (!err) { + if (in_key == kim_preference_key_lifetime) { + *out_kim_string_key = CFSTR ("CredentialLifetime"); + *out_kll_string_key = CFSTR ("KLDefaultTicketLifetime"); + + } else if (in_key == kim_preference_key_renewable) { + *out_kim_string_key = CFSTR ("RenewableCredentials"); + *out_kll_string_key = CFSTR ("KLGetRenewableTickets"); + + } else if (in_key == kim_preference_key_renewal_lifetime) { + *out_kim_string_key = CFSTR ("CredentialRenewalLifetime"); + *out_kll_string_key = CFSTR ("KLDefaultRenewableLifetime"); + + } else if (in_key == kim_preference_key_forwardable) { + *out_kim_string_key = CFSTR ("ForwardableCredentials"); + *out_kll_string_key = CFSTR ("KLDefaultForwardableTicket"); + + } else if (in_key == kim_preference_key_proxiable) { + *out_kim_string_key = CFSTR ("ProxiableCredentials"); + *out_kll_string_key = CFSTR ("KLGetProxiableTickets"); + + } else if (in_key == kim_preference_key_addressless) { + *out_kim_string_key = CFSTR ("AddresslessCredentials"); + *out_kll_string_key = CFSTR ("KLGetAddresslessTickets"); + + } else if (in_key == kim_preference_key_remember_options) { + *out_kim_string_key = CFSTR ("RememberCredentialAttributes"); + *out_kll_string_key = CFSTR ("KLRememberExtras"); + + } else if (in_key == kim_preference_key_client_identity) { + *out_kim_string_key = CFSTR ("ClientIdentity"); + *out_kll_string_key = CFSTR ("KLName"); + + } else if (in_key == kim_preference_key_remember_client_identity) { + *out_kim_string_key = CFSTR ("RememberClientIdentity"); + *out_kll_string_key = CFSTR ("KLRememberPrincipal"); + + } else if (in_key == kim_preference_key_favorite_identities) { + *out_kim_string_key = CFSTR ("FavoriteIdentities"); + *out_kll_string_key = CFSTR ("KLFavoriteIdentities"); + + } else if (in_key == kim_preference_key_minimum_lifetime) { + *out_kim_string_key = CFSTR ("MinimumLifetime"); + *out_kll_string_key = CFSTR ("KLMinimumTicketLifetime"); + + } else if (in_key == kim_preference_key_maximum_lifetime) { + *out_kim_string_key = CFSTR ("MaximumLifetime"); + *out_kll_string_key = CFSTR ("KLMaximumTicketLifetime"); + + } else if (in_key == kim_preference_key_minimum_renewal_lifetime) { + *out_kim_string_key = CFSTR ("MinimumRenewalLifetime"); + *out_kll_string_key = CFSTR ("KLMinimumRenewableLifetime"); + + } else if (in_key == kim_preference_key_maximum_renewal_lifetime) { + *out_kim_string_key = CFSTR ("MaximumRenewalLifetime"); + *out_kll_string_key = CFSTR ("KLMaximumRenewableLifetime"); + + } else { + err = param_error (1, "in_key", "not in kim_preference_key_enum"); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_preferences_get_value (kim_preference_key_t in_key, + CFTypeID in_type, + CFPropertyListRef *out_value) +{ + + kim_error_t err = KIM_NO_ERROR; + CFPropertyListRef value = NULL; + CFStringRef users[] = { kCFPreferencesCurrentUser, kCFPreferencesAnyUser, NULL }; + CFStringRef files[] = { KIM_PREFERENCES_FILE, KLL_PREFERENCES_FILE, NULL }; + CFStringRef hosts[] = { kCFPreferencesCurrentHost, kCFPreferencesAnyHost, NULL }; + CFStringRef keys[] = { NULL, NULL, NULL }; + + if (!err && !out_value) { err = param_error (3, "out_value", "NULL"); } + + if (!err) { + /* Index must correspond to the appropriate file */ + err = kim_os_preferences_cfstring_for_key (in_key, &keys[0], &keys[1]); + } + + if (!err) { + kim_count_t u, f, h; + + if (!kim_library_allow_home_directory_access()) { + users[0] = kCFPreferencesAnyUser; + users[1] = NULL; + } + + for (u = 0; !value && users[u]; u++) { + for (f = 0; !value && files[f]; f++) { + for (h = 0; !value && hosts[h]; h++) { + value = CFPreferencesCopyValue (keys[f], files[f], users[u], hosts[h]); + } + } + } + + if (value && CFGetTypeID (value) != in_type) { + err = kim_error_create_from_code (KIM_PREFERENCES_READ_ECODE); + } + } + + + if (!err) { + *out_value = value; + value = NULL; + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_preferences_set_value (kim_preference_key_t in_key, + CFPropertyListRef in_value) +{ + kim_error_t err = KIM_NO_ERROR; + CFStringRef kim_key = NULL; + CFStringRef kll_key = NULL; + + if (!err && !in_value) { err = param_error (2, "in_value", "NULL"); } + + if (!err) { + err = kim_os_preferences_cfstring_for_key (in_key, &kim_key, &kll_key); + } + + if (!err) { + kim_boolean_t homedir_ok = kim_library_allow_home_directory_access(); + CFStringRef user = homedir_ok ? kCFPreferencesCurrentUser : kCFPreferencesAnyUser; + CFStringRef host = homedir_ok ? kCFPreferencesAnyHost : kCFPreferencesCurrentHost; + + CFPreferencesSetValue (kim_key, in_value, KIM_PREFERENCES_FILE, user, host); + if (!CFPreferencesSynchronize (KIM_PREFERENCES_FILE, user, host)) { + err = kim_error_create_from_code (KIM_PREFERENCES_WRITE_ECODE); + } + } + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_get_identity_for_key (kim_preference_key_t in_key, + kim_identity_t in_hardcoded_default, + kim_identity_t *out_identity) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t string = NULL; + CFStringRef value = NULL; + + if (!err && !out_identity) { err = param_error (2, "out_identity", "NULL"); } + + if (!err) { + err = kim_os_preferences_get_value (in_key, CFStringGetTypeID (), + (CFPropertyListRef *) &value); + } + + if (!err) { + if (value) { + err = kim_os_string_create_from_cfstring (&string, value); + + if (!err) { + if (!strcmp (kim_os_preference_any_identity, string)) { + *out_identity = KIM_IDENTITY_ANY; + + } else { + err = kim_identity_create_from_string (out_identity, string); + } + } + } else { + err = kim_identity_copy (out_identity, in_hardcoded_default); + } + } + + kim_string_free (&string); + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_set_identity_for_key (kim_preference_key_t in_key, + kim_identity_t in_identity) +{ + kim_error_t err = KIM_NO_ERROR; + CFStringRef value = NULL; + kim_string_t string = NULL; + + /* in_identity can be KIM_IDENTITY_ANY */ + + if (!err) { + if (in_identity) { + err = kim_identity_get_string (in_identity, &string); + + } else { + err = kim_string_copy (&string, kim_os_preference_any_identity); + } + } + + if (!err) { + err = kim_os_string_get_cfstring (string, &value); + } + + if (!err) { + err = kim_os_preferences_set_value (in_key, value); + } + + if (value) { CFRelease (value); } + kim_string_free (&string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_get_favorite_identities_for_key (kim_preference_key_t in_key, + kim_favorite_identities_t in_hardcoded_default, + kim_favorite_identities_t *out_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + CFArrayRef value = NULL; + + if (!err && !out_favorite_identities) { err = param_error (2, "out_favorite_identities", "NULL"); } + + if (!err) { + err = kim_os_preferences_get_value (in_key, CFArrayGetTypeID (), + (CFPropertyListRef *) &value); + } + + if (!err) { + if (!value || CFArrayGetCount (value) < 1) { + err = kim_favorite_identities_copy (out_favorite_identities, in_hardcoded_default); + + } else { + kim_favorite_identities_t favorite_identities = NULL; + CFIndex count = CFArrayGetCount (value); + CFIndex i; + + err = kim_favorite_identities_create (&favorite_identities); + + for (i = 0; !err && i < count; i++) { + CFStringRef cfstring = NULL; + kim_string_t string = NULL; + kim_identity_t identity = NULL; + + cfstring = (CFStringRef) CFArrayGetValueAtIndex (value, i); + if (!cfstring || CFGetTypeID (cfstring) != CFStringGetTypeID ()) { + err = kim_error_create_from_code (KIM_PREFERENCES_READ_ECODE); + } + + if (!err) { + err = kim_os_string_create_from_cfstring (&string, cfstring); + } + + if (!err) { + err = kim_identity_create_from_string (&identity, string); + } + + if (!err) { + err = kim_favorite_identities_add_identity (favorite_identities, identity); + } + + kim_identity_free (&identity); + kim_string_free (&string); + } + + if (!err) { + *out_favorite_identities = favorite_identities; + favorite_identities = NULL; + } + + kim_favorite_identities_free (&favorite_identities); + } + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_set_favorite_identities_for_key (kim_preference_key_t in_key, + kim_favorite_identities_t in_favorite_identities) +{ + kim_error_t err = KIM_NO_ERROR; + kim_count_t count = 0; + CFMutableArrayRef value = NULL; + + if (!err && !in_favorite_identities) { err = param_error (2, "in_favorite_identities", "NULL"); } + + if (!err) { + err = kim_favorite_identities_get_number_of_identities (in_favorite_identities, &count); + } + + if (!err) { + value = CFArrayCreateMutable (kCFAllocatorDefault, count, &kCFTypeArrayCallBacks); + if (!value) { err = os_error (ENOMEM); } + } + + if (!err) { + kim_count_t i; + + for (i = 0; !err && i < count; i++) { + kim_identity_t identity = NULL; + kim_string_t string = NULL; + CFStringRef cfstring = NULL; + + err = kim_favorite_identities_get_identity_at_index (in_favorite_identities, i, &identity); + + if (!err) { + err = kim_identity_get_string (identity, &string); + } + + if (!err) { + err = kim_os_string_get_cfstring (string, &cfstring); + } + + if (!err) { + CFArrayAppendValue (value, cfstring); + } + + if (cfstring) { CFRelease (cfstring); } + kim_string_free (&string); + kim_identity_free (&identity); + } + } + + if (!err) { + err = kim_os_preferences_set_value (in_key, value); + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_get_time_for_key (kim_preference_key_t in_key, + kim_time_t in_hardcoded_default, + kim_time_t *out_time) +{ + kim_error_t err = KIM_NO_ERROR; + CFNumberRef value = NULL; + + if (!err && !out_time) { err = param_error (2, "out_time", "NULL"); } + + if (!err) { + err = kim_os_preferences_get_value (in_key, CFNumberGetTypeID (), + (CFPropertyListRef *) &value); + } + + if (!err) { + if (value) { + SInt32 number; // CFNumbers are signed so we need to cast + if (CFNumberGetValue (value, kCFNumberSInt32Type, &number) != TRUE) { + err = os_error (ENOMEM); + } else { + *out_time = number; + } + } else { + *out_time = in_hardcoded_default; + } + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_set_time_for_key (kim_preference_key_t in_key, + kim_time_t in_time) +{ + kim_error_t err = KIM_NO_ERROR; + CFNumberRef value = NULL; + SInt32 number = (SInt32) in_time; + + if (!err) { + value = CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &number); + if (!value) { err = os_error (ENOMEM); } + } + + if (!err) { + err = kim_os_preferences_set_value (in_key, value); + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_get_lifetime_for_key (kim_preference_key_t in_key, + kim_lifetime_t in_hardcoded_default, + kim_lifetime_t *out_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + CFNumberRef value = NULL; + + if (!err && !out_lifetime) { err = param_error (2, "out_lifetime", "NULL"); } + + if (!err) { + err = kim_os_preferences_get_value (in_key, CFNumberGetTypeID (), + (CFPropertyListRef *) &value); + } + + if (!err) { + if (value) { + SInt32 number; // CFNumbers are signed so we need to cast + if (CFNumberGetValue (value, kCFNumberSInt32Type, &number) != TRUE) { + err = os_error (ENOMEM); + } else { + *out_lifetime = number; + } + } else { + *out_lifetime = in_hardcoded_default; + } + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_set_lifetime_for_key (kim_preference_key_t in_key, + kim_lifetime_t in_lifetime) +{ + kim_error_t err = KIM_NO_ERROR; + CFNumberRef value = NULL; + SInt32 number = (SInt32) in_lifetime; + + if (!err) { + value = CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &number); + if (!value) { err = os_error (ENOMEM); } + } + + if (!err) { + err = kim_os_preferences_set_value (in_key, value); + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_get_boolean_for_key (kim_preference_key_t in_key, + kim_boolean_t in_hardcoded_default, + kim_boolean_t *out_boolean) +{ + kim_error_t err = KIM_NO_ERROR; + CFBooleanRef value = NULL; + + if (!err && !out_boolean) { err = param_error (2, "out_boolean", "NULL"); } + + if (!err) { + err = kim_os_preferences_get_value (in_key, CFBooleanGetTypeID (), + (CFPropertyListRef *) &value); + } + + if (!err) { + if (value) { + *out_boolean = CFBooleanGetValue (value); + } else { + *out_boolean = in_hardcoded_default; + } + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_preferences_set_boolean_for_key (kim_preference_key_t in_key, + kim_boolean_t in_boolean) +{ + kim_error_t err = KIM_NO_ERROR; + CFBooleanRef value = in_boolean ? kCFBooleanTrue : kCFBooleanFalse; + + if (!err) { + err = kim_os_preferences_set_value (in_key, value); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ diff --git a/src/kim/lib/mac/kim_os_private.h b/src/kim/lib/mac/kim_os_private.h new file mode 100644 index 000000000..bebf98732 --- /dev/null +++ b/src/kim/lib/mac/kim_os_private.h @@ -0,0 +1,54 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 KIM_OS_PRIVATE_H +#define KIM_OS_PRIVATE_H + +#include +#include "kim_private.h" + + +CFStringEncoding kim_os_string_get_encoding (void); + +CFStringRef kim_os_string_get_cfstring_for_key_and_dictionary (CFStringRef in_key, + CFBundleRef in_bundle); + +CFStringRef kim_os_string_get_cfstring_for_key (kim_string_t in_key_string); + +kim_error_t kim_os_string_create_from_cfstring (kim_string_t *out_string, + CFStringRef in_cfstring); + +kim_error_t kim_os_string_create_for_key (kim_string_t *out_string, + kim_string_t in_key_string); + +kim_error_t kim_os_string_get_cfstring (kim_string_t in_string, + CFStringRef *out_cfstring); + +kim_error_t kim_os_string_compare_to_cfstring (kim_string_t in_string, + CFStringRef in_compare_to_cfstring, + kim_comparison_t *out_comparison); + +#endif /* KIM_PRIVATE_H */ diff --git a/src/kim/lib/mac/kim_os_selection_hints.c b/src/kim/lib/mac/kim_os_selection_hints.c new file mode 100644 index 000000000..e19ac777c --- /dev/null +++ b/src/kim/lib/mac/kim_os_selection_hints.c @@ -0,0 +1,538 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#define KIM_SELECTION_HINTS_FILE CFSTR("edu.mit.Kerberos.SelectionHints") + +#define KIM_SELECTION_HINTS_ARRAY CFSTR("Hints") + +#define KIM_SERVICE_IDENTITY_HINT CFSTR("KIMServiceIdentityHint") +#define KIM_APPLICATION_ID_HINT CFSTR("KIMApplicationIDHint") +#define KIM_USER_HINT CFSTR("KIMUserHint") +#define KIM_CLIENT_REALM_HINT CFSTR("KIMClientRealmHint") +#define KIM_SERVICE_HINT CFSTR("KIMServiceHint") +#define KIM_SERVICE_REALM_HINT CFSTR("KIMServiceRealmHint") +#define KIM_SERVER_HINT CFSTR("KIMServerHint") +#define KIM_IDENTITY_HINT CFSTR("KIMIdentityHint") + +#define KIM_MAX_HINTS 8 /* the number of hint types above */ + +#include "kim_os_private.h" + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_selection_hints_get_selection_hints_array (CFArrayRef *out_selection_hints_array) +{ + kim_error_t err = KIM_NO_ERROR; + CFPropertyListRef value = NULL; + CFStringRef users[] = { kCFPreferencesCurrentUser, kCFPreferencesAnyUser, NULL }; + CFStringRef hosts[] = { kCFPreferencesCurrentHost, kCFPreferencesAnyHost, NULL }; + + if (!err && !out_selection_hints_array) { err = param_error (1, "out_selection_hints_array", "NULL"); } + + if (!err) { + kim_count_t u, h; + + if (!kim_library_allow_home_directory_access()) { + users[0] = kCFPreferencesAnyUser; + users[1] = NULL; + } + + for (u = 0; !value && users[u]; u++) { + for (h = 0; !value && hosts[h]; h++) { + value = CFPreferencesCopyValue (KIM_SELECTION_HINTS_ARRAY, + KIM_SELECTION_HINTS_FILE, + users[u], hosts[h]); + } + } + + if (value && CFGetTypeID (value) != CFArrayGetTypeID ()) { + err = kim_error_create_from_code (KIM_PREFERENCES_READ_ECODE); + } + } + + if (!err) { + *out_selection_hints_array = value; + value = NULL; + } + + if (value) { CFRelease (value); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_selection_hints_set_selection_hints_array (CFArrayRef in_selection_hints_array) +{ + kim_error_t err = KIM_NO_ERROR; + + if (!err && !in_selection_hints_array) { err = param_error (1, "in_selection_hints_array", "NULL"); } + + if (!err) { + kim_boolean_t homedir_ok = kim_library_allow_home_directory_access(); + CFStringRef user = homedir_ok ? kCFPreferencesCurrentUser : kCFPreferencesAnyUser; + CFStringRef host = homedir_ok ? kCFPreferencesAnyHost : kCFPreferencesCurrentHost; + + CFPreferencesSetValue (KIM_SELECTION_HINTS_ARRAY, in_selection_hints_array, + KIM_SELECTION_HINTS_FILE, user, host); + if (!CFPreferencesSynchronize (KIM_SELECTION_HINTS_FILE, user, host)) { + err = kim_error_create_from_code (KIM_PREFERENCES_WRITE_ECODE); + } + } + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_selection_hints_create_dictionary (kim_selection_hints_t in_selection_hints, + kim_identity_t in_identity, + CFDictionaryRef *out_hints_dictionary) +{ + kim_error_t err = KIM_NO_ERROR; + kim_selection_hints_preference_strings preference_strings = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + kim_string_t identity_string = NULL; + CFStringRef keys[KIM_MAX_HINTS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + CFStringRef values[KIM_MAX_HINTS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + CFIndex i = 0; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !in_identity ) { err = param_error (2, "in_selection_hints", "NULL"); } + if (!err && !out_hints_dictionary) { err = param_error (3, "out_hints_dictionary", "NULL"); } + + if (!err) { + err = kim_selection_hints_get_preference_strings (in_selection_hints, &preference_strings); + } + + if (!err) { + err = kim_identity_get_string (in_identity, &identity_string); + } + + if (!err) { + keys[i] = KIM_APPLICATION_ID_HINT; + err = kim_os_string_get_cfstring (preference_strings.application_identifier, &values[i]); + } + + if (!err) { + keys[++i] = KIM_IDENTITY_HINT; + err = kim_os_string_get_cfstring (identity_string, &values[i]); + } + + if (!err && preference_strings.service_identity) { + keys[++i] = KIM_SERVICE_IDENTITY_HINT; + err = kim_os_string_get_cfstring (preference_strings.service_identity, &values[i]); + } + + if (!err && preference_strings.user) { + keys[++i] = KIM_USER_HINT; + err = kim_os_string_get_cfstring (preference_strings.user, &values[i]); + } + + if (!err && preference_strings.client_realm) { + keys[++i] = KIM_CLIENT_REALM_HINT; + err = kim_os_string_get_cfstring (preference_strings.client_realm, &values[i]); + } + + if (!err && preference_strings.service) { + keys[++i] = KIM_SERVICE_HINT; + err = kim_os_string_get_cfstring (preference_strings.service, &values[i]); + } + + if (!err && preference_strings.service_realm) { + keys[++i] = KIM_SERVICE_REALM_HINT; + err = kim_os_string_get_cfstring (preference_strings.service_realm, &values[i]); + } + + if (!err && preference_strings.server) { + keys[++i] = KIM_SERVER_HINT; + err = kim_os_string_get_cfstring (preference_strings.server, &values[i]); + } + + if (!err) { + *out_hints_dictionary = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) keys, + (const void **) values, + i+1, /* number of hints */ + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + } + + for (i = 0; i < KIM_MAX_HINTS; i++) { if (values[i]) { CFRelease (values[i]); } } + kim_string_free (&identity_string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static kim_boolean_t kim_os_selection_hints_compare_hint (kim_string_t in_string, + CFStringRef in_value) +{ + kim_boolean_t equal = 0; + + if (!in_string && !in_value) { + equal = 1; + + } else if (in_string && in_value) { + if (CFGetTypeID (in_value) == CFStringGetTypeID ()) { + kim_comparison_t comparison; + + kim_error_t err = kim_os_string_compare_to_cfstring (in_string, in_value, + &comparison); + + if (!err && kim_comparison_is_equal_to (comparison)) { + equal = 1; + } + + kim_error_free (&err); + } else { + kim_debug_printf ("%s: Malformed string in hints dictionary.", __FUNCTION__); + } + } + + return equal; +} + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_selection_hints_compare_to_dictionary (kim_selection_hints_t in_selection_hints, + CFDictionaryRef in_hints_dictionary, + kim_boolean_t *out_hints_equal) +{ + kim_error_t err = KIM_NO_ERROR; + kim_selection_hints_preference_strings preference_strings = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + kim_boolean_t hints_equal = 1; + + if (!err && !in_selection_hints ) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !in_hints_dictionary) { err = param_error (2, "in_hints_dictionary", "NULL"); } + if (!err && !out_hints_equal ) { err = param_error (3, "out_hints_equal", "NULL"); } + + if (!err) { + err = kim_selection_hints_get_preference_strings (in_selection_hints, &preference_strings); + } + + if (!err && hints_equal) { + hints_equal = kim_os_selection_hints_compare_hint (preference_strings.application_identifier, + CFDictionaryGetValue (in_hints_dictionary, + KIM_APPLICATION_ID_HINT)); + } + + if (!err && hints_equal) { + hints_equal = kim_os_selection_hints_compare_hint (preference_strings.service_identity, + CFDictionaryGetValue (in_hints_dictionary, + KIM_SERVICE_IDENTITY_HINT)); + } + + if (!err && hints_equal) { + hints_equal = kim_os_selection_hints_compare_hint (preference_strings.user, + CFDictionaryGetValue (in_hints_dictionary, + KIM_USER_HINT)); + } + + if (!err && hints_equal) { + hints_equal = kim_os_selection_hints_compare_hint (preference_strings.client_realm, + CFDictionaryGetValue (in_hints_dictionary, + KIM_CLIENT_REALM_HINT)); + } + + if (!err && hints_equal) { + hints_equal = kim_os_selection_hints_compare_hint (preference_strings.service, + CFDictionaryGetValue (in_hints_dictionary, + KIM_SERVICE_HINT)); + } + + if (!err && hints_equal) { + hints_equal = kim_os_selection_hints_compare_hint (preference_strings.service_realm, + CFDictionaryGetValue (in_hints_dictionary, + KIM_SERVICE_REALM_HINT)); + } + + if (!err && hints_equal) { + hints_equal = kim_os_selection_hints_compare_hint (preference_strings.server, + CFDictionaryGetValue (in_hints_dictionary, + KIM_SERVER_HINT)); + } + + if (!err) { + *out_hints_equal = hints_equal; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +static kim_error_t kim_os_selection_hints_get_dictionary_identity (CFDictionaryRef in_dictionary, + kim_identity_t *out_identity) +{ + kim_error_t err = KIM_NO_ERROR; + CFStringRef identity_cfstr = NULL; + kim_string_t identity_string = NULL; + + identity_cfstr = CFDictionaryGetValue (in_dictionary, KIM_IDENTITY_HINT); + if (!identity_cfstr || CFGetTypeID (identity_cfstr) != CFStringGetTypeID ()) { + kim_debug_printf ("%s: Malformed hints dictionary (invalid identity).", __FUNCTION__); + err = kim_error_create_from_code (KIM_PREFERENCES_READ_ECODE); + } + + if (!err) { + err = kim_os_string_create_from_cfstring (&identity_string, identity_cfstr); + } + + if (!err) { + err = kim_identity_create_from_string (out_identity, identity_string); + } + + kim_string_free (&identity_string); + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_selection_hints_lookup_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t *out_identity) +{ + kim_error_t err = KIM_NO_ERROR; + CFArrayRef hints_array = NULL; + CFIndex i = 0; + CFIndex count = 0; + kim_boolean_t found = 0; + CFDictionaryRef found_dictionary = NULL; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !out_identity ) { err = param_error (2, "out_identity", "NULL"); } + + if (!err) { + err = kim_os_selection_hints_get_selection_hints_array (&hints_array); + } + + if (!err) { + count = CFArrayGetCount (hints_array); + } + + for (i = 0; !err && !found && i < count; i++) { + CFDictionaryRef dictionary = NULL; + + dictionary = CFArrayGetValueAtIndex (hints_array, i); + if (!dictionary) { err = os_error (ENOMEM); } + + if (!err && CFGetTypeID (dictionary) != CFDictionaryGetTypeID ()) { + kim_debug_printf ("%s: Malformed entry in hints array.", __FUNCTION__); + continue; /* skip entries which aren't dictionaries */ + } + + if (!err) { + err = kim_os_selection_hints_compare_to_dictionary (in_selection_hints, + dictionary, + &found); + } + + if (!err && found) { + found_dictionary = dictionary; + } + } + + if (!err && found) { + err = kim_os_selection_hints_get_dictionary_identity (found_dictionary, + out_identity); + } + + if (hints_array) { CFRelease (hints_array); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_selection_hints_remember_identity (kim_selection_hints_t in_selection_hints, + kim_identity_t in_identity) +{ + kim_error_t err = KIM_NO_ERROR; + CFArrayRef old_hints_array = NULL; + CFMutableArrayRef new_hints_array = NULL; + CFIndex count = 0; + CFIndex i = 0; + kim_boolean_t hint_already_exists = 0; + kim_boolean_t hints_array_changed = 0; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + if (!err && !in_identity ) { err = param_error (2, "in_identity", "NULL"); } + + if (!err) { + err = kim_os_selection_hints_get_selection_hints_array (&old_hints_array); + } + + if (!err) { + if (old_hints_array) { + new_hints_array = CFArrayCreateMutableCopy (kCFAllocatorDefault, 0, + old_hints_array); + } else { + new_hints_array = CFArrayCreateMutable (kCFAllocatorDefault, 0, + &kCFTypeArrayCallBacks); + } + if (!new_hints_array) { err = os_error (ENOMEM); } + } + + if (!err) { + count = CFArrayGetCount (new_hints_array); + } + + for (i = 0; !err && i < count; i++) { + CFDictionaryRef dictionary = NULL; + kim_identity_t identity = NULL; + kim_boolean_t hints_equal = 0; + + dictionary = CFArrayGetValueAtIndex (new_hints_array, i); + if (!dictionary) { err = os_error (ENOMEM); } + + if (!err && CFGetTypeID (dictionary) != CFDictionaryGetTypeID ()) { + kim_debug_printf ("%s: Malformed entry in hints array.", __FUNCTION__); + continue; /* skip entries which aren't dictionaries */ + } + + if (!err) { + err = kim_os_selection_hints_compare_to_dictionary (in_selection_hints, + dictionary, + &hints_equal); + } + + if (!err && hints_equal) { + kim_comparison_t comparison; + + err = kim_os_selection_hints_get_dictionary_identity (dictionary, + &identity); + + if (!err) { + err = kim_identity_compare (in_identity, identity, &comparison); + } + + if (!err) { + if (kim_comparison_is_equal_to (comparison) && !hint_already_exists) { + hint_already_exists = 1; + } else { + CFArrayRemoveValueAtIndex (new_hints_array, i); + i--; /* back up one index so we don't skip */ + count = CFArrayGetCount (new_hints_array); /* count changed */ + hints_array_changed = 1; + } + } + + kim_identity_free (&identity); + } + } + + if (!err && !hint_already_exists) { + CFDictionaryRef new_hint_dictionary = NULL; + + err = kim_os_selection_hints_create_dictionary (in_selection_hints, + in_identity, + &new_hint_dictionary); + + if (!err) { + CFArrayInsertValueAtIndex (new_hints_array, 0, new_hint_dictionary); + hints_array_changed = 1; + } + + if (new_hint_dictionary) { CFRelease (new_hint_dictionary); } + } + + if (!err && hints_array_changed) { + err = kim_os_selection_hints_set_selection_hints_array (new_hints_array); + } + + if (new_hints_array ) { CFRelease (new_hints_array); } + if (old_hints_array ) { CFRelease (old_hints_array); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_selection_hints_forget_identity (kim_selection_hints_t in_selection_hints) +{ + kim_error_t err = KIM_NO_ERROR; + CFArrayRef old_hints_array = NULL; + CFMutableArrayRef new_hints_array = NULL; + CFIndex count = 0; + CFIndex i = 0; + + if (!err && !in_selection_hints) { err = param_error (1, "in_selection_hints", "NULL"); } + + if (!err) { + err = kim_os_selection_hints_get_selection_hints_array (&old_hints_array); + } + + if (!err) { + new_hints_array = CFArrayCreateMutableCopy (kCFAllocatorDefault, 0, + old_hints_array); + if (!new_hints_array) { err = os_error (ENOMEM); } + } + + if (!err) { + count = CFArrayGetCount (new_hints_array); + } + + for (i = 0; !err && i < count; i++) { + CFDictionaryRef dictionary = NULL; + kim_boolean_t hints_equal = 0; + + dictionary = CFArrayGetValueAtIndex (new_hints_array, i); + if (!dictionary) { err = os_error (ENOMEM); } + + if (!err && CFGetTypeID (dictionary) != CFDictionaryGetTypeID ()) { + kim_debug_printf ("%s: Malformed entry in hints array.", __FUNCTION__); + continue; /* skip entries which aren't dictionaries */ + } + + if (!err) { + err = kim_os_selection_hints_compare_to_dictionary (in_selection_hints, + dictionary, + &hints_equal); + } + + if (!err && hints_equal) { + CFArrayRemoveValueAtIndex (new_hints_array, i); + i--; /* back up one index so we don't skip */ + count = CFArrayGetCount (new_hints_array); /* count changed */ + } + } + + if (!err) { + err = kim_os_selection_hints_set_selection_hints_array (new_hints_array); + } + + if (new_hints_array) { CFRelease (new_hints_array); } + + return check_error (err); +} + diff --git a/src/kim/lib/mac/kim_os_string.c b/src/kim/lib/mac/kim_os_string.c new file mode 100644 index 000000000..a4c5699e6 --- /dev/null +++ b/src/kim/lib/mac/kim_os_string.c @@ -0,0 +1,266 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include + +#include "kim_os_private.h" + +/* ------------------------------------------------------------------------ */ +/* WARNING: DO NOT CALL check_error() -- it is called by error_message()!! */ + +CFStringEncoding kim_os_string_get_encoding (void) +{ + typedef TextEncoding (*GetApplicationTextEncodingProcPtr) (void); + GetApplicationTextEncodingProcPtr GetApplicationTextEncodingPtr = NULL; + + if (kim_os_library_caller_is_server ()) { + return kCFStringEncodingUTF8; /* server only does UTF8 */ + } + + CFBundleRef carbonBundle = CFBundleGetBundleWithIdentifier (CFSTR ("com.apple.Carbon")); + if (carbonBundle != NULL && CFBundleIsExecutableLoaded (carbonBundle)) { + GetApplicationTextEncodingPtr = (GetApplicationTextEncodingProcPtr) CFBundleGetFunctionPointerForName (carbonBundle, + CFSTR ("GetApplicationTextEncoding")); + } + + if (GetApplicationTextEncodingPtr) { + return (CFStringEncoding) (*GetApplicationTextEncodingPtr) (); + } + + return CFStringGetSystemEncoding (); +} + +/* ------------------------------------------------------------------------ */ +/* WARNING: DO NOT CALL check_error() -- it is called by error_message()!! */ + +CFStringRef kim_os_string_get_cfstring_for_key_and_dictionary (CFStringRef in_key, + CFBundleRef in_bundle) +{ + CFDictionaryRef dictionary = NULL; + CFStringRef value = NULL; + + if (kim_library_allow_home_directory_access ()) { + // Accesses user's homedir to get localization information + dictionary = CFBundleGetLocalInfoDictionary (in_bundle); + } else { + dictionary = CFBundleGetInfoDictionary (in_bundle); + } + + if (dictionary) { + value = (CFTypeRef) CFDictionaryGetValue (dictionary, in_key); + } + + if (value && (CFGetTypeID (value) != CFStringGetTypeID ())) { + value = NULL; // Only return CFStrings + } + + return value; +} + +/* ------------------------------------------------------------------------ */ +/* WARNING: DO NOT CALL check_error() -- it is called by error_message()!! */ + +CFStringRef kim_os_string_get_cfstring_for_key (kim_string_t in_key_string) +{ + CFStringRef key = NULL; + CFStringRef value = NULL; + + if (in_key_string) { + key = CFStringCreateWithCString (kCFAllocatorDefault, in_key_string, kCFStringEncodingASCII); + + if (key) { + // Try to find the key, first searching in the framework, then in the main bundle + CFBundleRef frameworkBundle = CFBundleGetBundleWithIdentifier (CFSTR ("edu.mit.Kerberos")); + if (frameworkBundle) { + value = kim_os_string_get_cfstring_for_key_and_dictionary (key, frameworkBundle); + } + + if (!value) { + CFBundleRef mainBundle = CFBundleGetMainBundle (); + + if (mainBundle) { + value = kim_os_string_get_cfstring_for_key_and_dictionary (key, mainBundle); + } + } + + if (value && (CFGetTypeID (value) != CFStringGetTypeID ())) { + value = NULL; // Only return CFStrings + } + + CFRelease (key); + } + } + + return value; +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_string_create_from_cfstring (kim_string_t *out_string, + CFStringRef in_cfstring) +{ + kim_error_t err = KIM_NO_ERROR; + kim_string_t string = NULL; + CFStringEncoding encoding = kim_os_string_get_encoding (); + CFIndex length = 0; + + if (!err && !out_string ) { err = param_error (1, "out_string", "NULL"); } + if (!err && !in_cfstring) { err = param_error (2, "in_cfstring", "NULL"); } + + if (!err) { + length = CFStringGetMaximumSizeForEncoding (CFStringGetLength (in_cfstring), encoding) + 1; + + string = (char *) calloc (length, sizeof (char)); + if (!string) { err = os_error (errno); } + } + + if (!err) { + if (!CFStringGetCString (in_cfstring, (char *) string, length, encoding)) { + err = os_error (ENOMEM); + } + } + + if (!err) { + *out_string = string; + string = NULL; + } + + kim_string_free (&string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_string_create_for_key (kim_string_t *out_string, + kim_string_t in_key_string) +{ + kim_error_t err = KIM_NO_ERROR; + CFStringRef value = NULL; + + if (!err && !out_string ) { err = param_error (1, "out_string", "NULL"); } + if (!err && !in_key_string) { err = param_error (2, "in_key_string", "NULL"); } + + if (!err) { + value = kim_os_string_get_cfstring_for_key (in_key_string); + if (value) { + err = kim_os_string_create_from_cfstring (out_string, value); + } else { + // We failed to look it up. Use the key so we return something. + err = kim_string_copy (out_string, in_key_string); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_string_get_cfstring (kim_string_t in_string, + CFStringRef *out_cfstring) +{ + kim_error_t err = KIM_NO_ERROR; + CFStringRef cfstring = NULL; + + if (!err && !in_string ) { err = param_error (1, "in_string", "NULL"); } + if (!err && !out_cfstring) { err = param_error (2, "out_cfstring", "NULL"); } + + if (!err) { + cfstring = CFStringCreateWithCString (kCFAllocatorDefault, in_string, kCFStringEncodingUTF8); + if (!cfstring) { err = os_error (ENOMEM); } + } + + if (!err) { + *out_cfstring = cfstring; + cfstring = NULL; + } + + if (cfstring) { CFRelease (cfstring); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_string_compare (kim_string_t in_string, + kim_string_t in_compare_to_string, + kim_comparison_t *out_comparison) +{ + kim_error_t err = KIM_NO_ERROR; + CFStringRef cfstring = NULL; + CFStringRef compare_to_cfstring = NULL; + + if (!err && !in_string ) { err = param_error (1, "in_string", "NULL"); } + if (!err && !in_compare_to_string) { err = param_error (2, "in_compare_to_string", "NULL"); } + if (!err && !out_comparison ) { err = param_error (3, "out_comparison", "NULL"); } + + if (!err) { + err = kim_os_string_get_cfstring (in_string, &cfstring); + } + + if (!err) { + err = kim_os_string_get_cfstring (in_compare_to_string, &compare_to_cfstring); + } + + if (!err) { + /* Returned CFComparisonResult is compatible with kim_comparison_t */ + *out_comparison = CFStringCompare (cfstring, compare_to_cfstring, 0); + } + + if (cfstring ) { CFRelease (cfstring); } + if (compare_to_cfstring) { CFRelease (compare_to_cfstring); } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + +kim_error_t kim_os_string_compare_to_cfstring (kim_string_t in_string, + CFStringRef in_compare_to_cfstring, + kim_comparison_t *out_comparison) +{ + kim_error_t err = KIM_NO_ERROR; + CFStringRef cfstring = NULL; + + if (!err && !in_string ) { err = param_error (1, "in_string", "NULL"); } + if (!err && !in_compare_to_cfstring) { err = param_error (2, "in_compare_to_cfstring", "NULL"); } + if (!err && !out_comparison ) { err = param_error (3, "out_comparison", "NULL"); } + + if (!err) { + err = kim_os_string_get_cfstring (in_string, &cfstring); + } + + if (!err) { + /* Returned CFComparisonResult is compatible with kim_comparison_t */ + *out_comparison = CFStringCompare (cfstring, in_compare_to_cfstring, 0); + } + + if (cfstring) { CFRelease (cfstring); } + + return check_error (err); +} diff --git a/src/kim/test/main.c b/src/kim/test/main.c new file mode 100644 index 000000000..c1c5bd19e --- /dev/null +++ b/src/kim/test/main.c @@ -0,0 +1,82 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "test_kim_identity.h" +#include "test_kim_preferences.h" +#include "test_kim_selection_hints.h" + +int main (int argc, const char * argv[]) +{ + kim_test_state_t state = NULL; + + if (test_init (&state)) { + return 1; + } + + test_kim_identity_create_from_krb5_principal (state); + + test_kim_identity_create_from_string (state); + + test_kim_identity_copy (state); + + test_kim_identity_compare (state); + + test_kim_identity_get_display_string (state); + + test_kim_identity_get_realm (state); + + test_kim_identity_get_number_of_components (state); + + test_kim_identity_get_component_at_index (state); + + test_kim_identity_get_krb5_principal (state); + + test_kim_preferences_create (state); + + test_kim_preferences_copy (state); + + test_kim_preferences_set_options (state); + + test_kim_preferences_set_remember_options (state); + + test_kim_preferences_set_client_identity (state); + + test_kim_selection_hints_set_service_identity_hint (state); + + test_kim_selection_hints_set_client_realm_hint (state); + + test_kim_selection_hints_set_user_hint (state); + + test_kim_selection_hints_set_service_realm_hint (state); + + test_kim_selection_hints_set_service_hint (state); + + test_kim_selection_hints_set_server_hint (state); + + test_kim_selection_hints_remember_identity (state); + + return test_cleanup (state); +} diff --git a/src/kim/test/test_kim_common.c b/src/kim/test/test_kim_common.c new file mode 100644 index 000000000..1e3b9673e --- /dev/null +++ b/src/kim/test/test_kim_common.c @@ -0,0 +1,159 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include + +const char *k_no_test_name = "No test name set"; + +/* ------------------------------------------------------------------------ */ + +int test_init (kim_test_state_t *out_state) +{ + kim_test_state_t state = NULL; + + printf ("Initializing tests... "); + + state = malloc (sizeof (*state)); + if (!state) { + printf ("out of memory.\n\n"); + return 1; + } + + state->test_name = k_no_test_name; + state->global_fail_count = 0; + state->test_fail_count = 0; + + *out_state = state; + + printf ("done.\n\n"); + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +int test_cleanup (kim_test_state_t io_state) +{ + int global_fail_count = io_state->global_fail_count; + + printf ("Exiting. %d total failures.", global_fail_count); + free (io_state); + + return global_fail_count; +} + +/* ------------------------------------------------------------------------ */ + +void start_test (kim_test_state_t in_state, + const char *in_test_name) +{ + in_state->test_name = in_test_name; + in_state->test_fail_count = 0; + + printf ("Testing %s...\n", in_state->test_name); +} + +/* ------------------------------------------------------------------------ */ + +void end_test (kim_test_state_t in_state) +{ + printf ("Finished testing %s. %d failures.\n\n", + in_state->test_name, in_state->test_fail_count); + + in_state->test_name = k_no_test_name; + in_state->global_fail_count += in_state->test_fail_count; + in_state->test_fail_count = 0; +} + +/* ------------------------------------------------------------------------ */ + +void fail_if_error (kim_test_state_t in_state, + const char *in_function, + kim_error_t in_err, + const char *in_format, + ...) +{ + if (in_err) { + va_list args; + + printf ("\tFAILURE: "); + printf ("%s() got %d (%s) ", + in_function, kim_error_get_code (in_err), + kim_error_get_display_string (in_err)); + + va_start (args, in_format); + vprintf (in_format, args); + va_end (args); + + printf ("\n"); + + in_state->test_fail_count++; + } +} + +/* ------------------------------------------------------------------------ */ + +void fail_if_error_code (kim_test_state_t in_state, + const char *in_function, + kim_error_code_t in_code, + const char *in_format, + ...) +{ + if (in_code) { + va_list args; + + printf ("\tFAILURE: "); + printf ("%s() got %d (%s) ", + in_function, in_code, error_message (in_code)); + + va_start (args, in_format); + vprintf (in_format, args); + va_end (args); + + printf ("\n"); + + in_state->test_fail_count++; + } +} + +/* ------------------------------------------------------------------------ */ + +void log_failure (kim_test_state_t in_state, + const char *in_format, + ...) +{ + va_list args; + + printf ("\tFAILURE: "); + + va_start (args, in_format); + vprintf (in_format, args); + va_end (args); + + printf ("\n"); + + in_state->test_fail_count++; +} diff --git a/src/kim/test/test_kim_common.h b/src/kim/test/test_kim_common.h new file mode 100644 index 000000000..161224239 --- /dev/null +++ b/src/kim/test/test_kim_common.h @@ -0,0 +1,78 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 TEST_KIM_COMMON_H +#define TEST_KIM_COMMON_H + +#include +#include +#include +#include + +typedef struct kim_test_state_d { + const char *test_name; + int global_fail_count; + int test_fail_count; +} *kim_test_state_t; + +int test_init (kim_test_state_t *out_state); + +int test_cleanup (kim_test_state_t io_state); + +void start_test (kim_test_state_t in_state, + const char *in_test_name); + +void end_test (kim_test_state_t in_state); + +void fail_if_error (kim_test_state_t in_state, + const char *in_function, + kim_error_t in_err, + const char *in_format, + ...) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +__attribute__ ((__format__ (__printf__, 4, 5))) +#endif +; + +void fail_if_error_code (kim_test_state_t in_state, + const char *in_function, + kim_error_code_t in_code, + const char *in_format, + ...) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +__attribute__ ((__format__ (__printf__, 4, 5))) +#endif +; + +void log_failure (kim_test_state_t in_state, + const char *in_format, + ...) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +__attribute__ ((__format__ (__printf__, 2, 3))) +#endif +; + +#endif /* TEST_KIM_COMMON_H */ diff --git a/src/kim/test/test_kim_identity.c b/src/kim/test/test_kim_identity.c new file mode 100644 index 000000000..be97726a7 --- /dev/null +++ b/src/kim/test/test_kim_identity.c @@ -0,0 +1,559 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "test_kim_identity.h" + +typedef struct test_identity_d { + const char *string; + const char *display_string; + kim_boolean_t is_tgt_service; + const char *realm; + kim_count_t component_count; + const char *components[5]; +} test_identity_t; + + +test_identity_t test_identities[] = { + {"a@B", "a@B", 0, "B", 1, { "a", NULL, NULL, NULL, NULL } }, + {"user@EXAMPLE.COM", "user@EXAMPLE.COM", 0, "EXAMPLE.COM", 1, { "user", NULL, NULL, NULL, NULL } }, + {"krbtgt@EXAMPLE.COM", "krbtgt@EXAMPLE.COM", 0, "EXAMPLE.COM", 1, { "krbtgt", NULL, NULL, NULL, NULL } }, + {"krbtgt/EXAMPLE.COM@EXAMPLE.COM", "krbtgt/EXAMPLE.COM@EXAMPLE.COM", 1, "EXAMPLE.COM", 2, { "krbtgt", "EXAMPLE.COM", NULL, NULL, NULL } }, + {"krbtgt/OTHER.COM@EXAMPLE.COM", "krbtgt/OTHER.COM@EXAMPLE.COM", 1, "EXAMPLE.COM", 2, { "krbtgt", "OTHER.COM", NULL, NULL, NULL } }, + {"a space@EXAMPLE.COM", "a space@EXAMPLE.COM", 0, "EXAMPLE.COM", 1, { "a space", NULL, NULL, NULL, NULL } }, + {"üñîçödé@EXAMPLE.COM", "üñîçödé@EXAMPLE.COM", 0, "EXAMPLE.COM", 1, { "üñîçödé", NULL, NULL, NULL, NULL } }, + {"user.name@EXAMPLE.COM", "user.name@EXAMPLE.COM", 0, "EXAMPLE.COM", 1, { "user.name", NULL, NULL, NULL, NULL } }, + {"user\\/instance@EXAMPLE.COM", "user/instance@EXAMPLE.COM", 0, "EXAMPLE.COM", 1, { "user\/instance", NULL, NULL, NULL, NULL } }, + {"user\\@instance@EXAMPLE.COM", "user@instance@EXAMPLE.COM", 0, "EXAMPLE.COM", 1, { "user\@instance", NULL, NULL, NULL, NULL } }, + {"user/instance@EXAMPLE.COM", "user/instance@EXAMPLE.COM", 0, "EXAMPLE.COM", 2, { "user", "instance", NULL, NULL, NULL } }, + {"user/i1/i2@EXAMPLE.COM", "user/i1/i2@EXAMPLE.COM", 0, "EXAMPLE.COM", 3, { "user", "i1", "i2", NULL, NULL } }, + {"user/i1/i2/i3/i4@EXAMPLE.COM", "user/i1/i2/i3/i4@EXAMPLE.COM", 0, "EXAMPLE.COM", 5, { "user", "i1", "i2", "i3", "i4" } }, + {"an insanely long principal for testing icky hex key principals/an insanely long instance for testing icky hex key principals@AN-INSANELY-LONG-REALM-NAME-FOR-TESTING-AUTOGENERATED-REALM-NAMES", + "an insanely long principal for testing icky hex key principals/an insanely long instance for testing icky hex key principals@AN-INSANELY-LONG-REALM-NAME-FOR-TESTING-AUTOGENERATED-REALM-NAMES", + 0, "AN-INSANELY-LONG-REALM-NAME-FOR-TESTING-AUTOGENERATED-REALM-NAMES", + 2, { "an insanely long principal for testing icky hex key principals", "an insanely long instance for testing icky hex key principals", NULL, NULL, NULL } }, + { NULL, NULL, 0, NULL, 0, { NULL, NULL, NULL, NULL, NULL } }, +}; + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_create_from_krb5_principal (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_create_from_krb5_principal"); + + for (i = 0; test_identities[i].string; i++) { + krb5_error_code code = 0; + kim_error_t err = NULL; + krb5_context context = NULL; + krb5_principal principal = NULL; + kim_identity_t identity = NULL; + kim_string_t string = NULL; + + printf ("."); + + code = krb5_init_context (&context); + fail_if_error_code (state, "krb5_init_context", code, + "while initializing context"); + + if (!code) { + code = krb5_parse_name (context, test_identities[i].string, &principal); + fail_if_error_code (state, "krb5_parse_name", code, + "while creating krb5_principal for %s", + test_identities[i].string); + } + + if (!code && !err) { + err = kim_identity_create_from_krb5_principal (&identity, context, principal); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!code && !err) { + err = kim_identity_get_string (identity, &string); + fail_if_error (state, "kim_identity_get_string", err, + "while getting the string for %s", + test_identities[i].string); + } + + if (!code && !err && strcmp (string, test_identities[i].string)) { + log_failure (state, "Unexpected string (got '%s', expected '%s')", + string, test_identities[i].string); + } + + kim_string_free (&string); + kim_identity_free (&identity); + if (principal) { krb5_free_principal (context, principal); } + if (context ) { krb5_free_context (context); } + + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_create_from_string (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_create_from_string"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + kim_string_t string = NULL; + + printf ("."); + + if (!err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!err) { + err = kim_identity_get_string (identity, &string); + fail_if_error (state, "kim_identity_get_string", err, + "while getting the string for %s", + test_identities[i].string); + } + + if (!err && strcmp (string, test_identities[i].string)) { + log_failure (state, "Unexpected string (got '%s', expected '%s')", + string, test_identities[i].string); + } + + kim_string_free (&string); + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_copy (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_copy"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + kim_identity_t identity_copy = NULL; + kim_string_t string = NULL; + + printf ("."); + + if (!err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!err) { + err = kim_identity_copy (&identity_copy, identity); + fail_if_error (state, "kim_identity_copy", err, + "while copying %s", test_identities[i].string); + } + + if (!err) { + err = kim_identity_get_string (identity_copy, &string); + fail_if_error (state, "kim_identity_get_string", err, + "while getting the string for the copy of %s", + test_identities[i].string); + } + + if (!err && strcmp (string, test_identities[i].string)) { + log_failure (state, "Unexpected string (got '%s', expected '%s')", + string, test_identities[i].string); + } + + kim_string_free (&string); + kim_identity_free (&identity_copy); + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_compare (kim_test_state_t state) +{ + kim_count_t i, j = 0; + + start_test (state, "kim_identity_create_from_string"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + + printf ("."); + + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + + for (j = 0; !err && test_identities[j].string; j++) { + kim_identity_t compare_to_identity = NULL; + kim_comparison_t comparison = 0; + + err = kim_identity_create_from_string (&compare_to_identity, test_identities[j].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[j].string); + + if (!err) { + err = kim_identity_compare (identity, compare_to_identity, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s and %s", + test_identities[i].string, test_identities[j].string); + } + + if (!err) { + if (i == j && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Expected %s and %s to be equal but kim_identity_compare returned %d", + test_identities[i].string, test_identities[j].string, comparison); + + } else if (i != j && kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Expected %s and %s to be NOT equal but kim_identity_compare returned %d", + test_identities[i].string, test_identities[j].string, comparison); + } + } + + kim_identity_free (&compare_to_identity); + } + + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_get_display_string (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_get_display_string"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + kim_string_t string = NULL; + + printf ("."); + + if (!err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!err) { + err = kim_identity_get_display_string (identity, &string); + fail_if_error (state, "kim_identity_get_display_string", err, + "while getting the display string for %s", + test_identities[i].string); + } + + if (!err && strcmp (string, test_identities[i].display_string)) { + log_failure (state, "Unexpected display string for %s (got '%s', expected '%s')", + test_identities[i].string, string, test_identities[i].display_string); + } + + kim_string_free (&string); + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_get_realm (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_get_realm"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + kim_string_t realm = NULL; + + printf ("."); + + if (!err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!err) { + err = kim_identity_get_realm (identity, &realm); + fail_if_error (state, "kim_identity_get_realm", err, + "while getting the realm for %s", test_identities[i].string); + } + + if (!err && strcmp (realm, test_identities[i].realm)) { + log_failure (state, "Unexpected realm string (got '%s', expected '%s')", + realm, test_identities[i].realm); + } + + kim_string_free (&realm); + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_get_number_of_components (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_get_number_of_components"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + kim_count_t count = 0; + + printf ("."); + + if (!err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!err) { + err = kim_identity_get_number_of_components (identity, &count); + fail_if_error (state, "kim_identity_get_number_of_components", err, + "while getting number of components of %s", + test_identities[i].string); + } + + if (!err && (count != test_identities[i].component_count)) { + log_failure (state, "Unexpected component count of %s (got %d, expected %d)", + test_identities[i].string, (int) count, (int) test_identities[i].component_count); + } + + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_get_component_at_index (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_get_component_at_index"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + kim_count_t c = 0; + + printf ("."); + + if (!err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + for (c = 0; !err && c < test_identities[i].component_count; c++) { + kim_string_t component = NULL; + + err = kim_identity_get_component_at_index (identity, c, &component); + fail_if_error (state, "kim_identity_get_component_at_index", err, + "while getting component %d of %s", (int) c, + test_identities[i].string); + + if (!err && strcmp (component, test_identities[i].components[c])) { + log_failure (state, "Unexpected component %d of %s (got '%s', expected '%s')", + (int) c, test_identities[i].string, + component, test_identities[i].components[c]); + } + + kim_string_free (&component); + } + + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_identity_get_krb5_principal (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_get_krb5_principal"); + + for (i = 0; test_identities[i].string; i++) { + krb5_error_code code = 0; + kim_error_t err = NULL; + krb5_context context = NULL; + krb5_principal principal = NULL; + krb5_principal identity_principal = NULL; + kim_identity_t identity = NULL; + + printf ("."); + + code = krb5_init_context (&context); + fail_if_error_code (state, "krb5_init_context", code, + "while initializing context"); + + if (!code) { + code = krb5_parse_name (context, test_identities[i].string, &principal); + fail_if_error_code (state, "krb5_parse_name", code, + "while creating krb5_principal for %s", + test_identities[i].string); + } + + if (!code && !err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!code && !err) { + err = kim_identity_get_krb5_principal (identity, context, &identity_principal); + fail_if_error (state, "kim_identity_get_krb5_principal", err, + "while getting the krb5_principal for %s", + test_identities[i].string); + } + + if (!code && !err) { + if (!krb5_principal_compare (context, principal, identity_principal)) { + log_failure (state, "Principal and identity principal for %s do not match", + test_identities[i].string); + } + } + + kim_identity_free (&identity); + if (identity_principal) { krb5_free_principal (context, identity_principal); } + if (principal ) { krb5_free_principal (context, principal); } + if (context ) { krb5_free_context (context); } + + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ +/* +void test_kim_identity_is_tgt_service (kim_test_state_t state) +{ + kim_count_t i = 0; + + start_test (state, "kim_identity_is_tgt_service"); + + for (i = 0; test_identities[i].string; i++) { + kim_error_t err = NULL; + kim_identity_t identity = NULL; + kim_boolean_t is_tgt_service = 0; + + printf ("."); + + if (!err) { + err = kim_identity_create_from_string (&identity, test_identities[i].string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", + test_identities[i].string); + } + + if (!err) { + err = kim_identity_is_tgt_service (identity, &is_tgt_service); + fail_if_error (state, "kim_identity_is_tgt_service", err, + "while determining if %s is a tgt service", + test_identities[i].string); + } + + if (!err && (is_tgt_service != test_identities[i].is_tgt_service)) { + log_failure (state, "Unexpected result from kim_identity_is_tgt_service for %s (got %d, expected %d)", + test_identities[i].string, is_tgt_service, test_identities[i].is_tgt_service); + } + + kim_identity_free (&identity); + kim_error_free (&err); + } + + printf ("\n"); + + end_test (state); +} +*/ diff --git a/src/kim/test/test_kim_identity.h b/src/kim/test/test_kim_identity.h new file mode 100644 index 000000000..a294c2c3d --- /dev/null +++ b/src/kim/test/test_kim_identity.h @@ -0,0 +1,50 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 TEST_KIM_IDENTITY_H +#define TEST_KIM_IDENTITY_H + +#include "test_kim_common.h" + +void test_kim_identity_create_from_krb5_principal (kim_test_state_t state); + +void test_kim_identity_create_from_string (kim_test_state_t state); + +void test_kim_identity_copy (kim_test_state_t state); + +void test_kim_identity_compare (kim_test_state_t state); + +void test_kim_identity_get_display_string (kim_test_state_t state); + +void test_kim_identity_get_realm (kim_test_state_t state); + +void test_kim_identity_get_number_of_components (kim_test_state_t state); + +void test_kim_identity_get_component_at_index (kim_test_state_t state); + +void test_kim_identity_get_krb5_principal (kim_test_state_t state); + +#endif /* TEST_KIM_IDENTITY_H */ diff --git a/src/kim/test/test_kim_preferences.c b/src/kim/test/test_kim_preferences.c new file mode 100644 index 000000000..6cf0ba76d --- /dev/null +++ b/src/kim/test/test_kim_preferences.c @@ -0,0 +1,298 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "test_kim_preferences.h" + +/* ------------------------------------------------------------------------ */ + +void test_kim_preferences_create (kim_test_state_t state) +{ + + start_test (state, "kim_preferences_create"); + + { + kim_error_t err = NULL; + kim_preferences_t prefs = NULL; + + err = kim_preferences_create (&prefs); + fail_if_error (state, "kim_preferences_create", err, + "while creating preferences"); + + kim_preferences_free (&prefs); + kim_error_free (&err); + } + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_preferences_copy (kim_test_state_t state) +{ + + start_test (state, "test_kim_preferences_copy"); + + { + kim_error_t err = NULL; + kim_preferences_t prefs = NULL; + kim_preferences_t prefs_copy = NULL; + + err = kim_preferences_create (&prefs); + fail_if_error (state, "kim_preferences_create", err, + "while creating preferences"); + + if (!err) { + err = kim_preferences_copy (&prefs_copy, prefs); + fail_if_error (state, "kim_preferences_copy", err, + "while copying preferences"); + } + + kim_preferences_free (&prefs_copy); + kim_preferences_free (&prefs); + kim_error_free (&err); + } + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_preferences_set_options (kim_test_state_t state) +{ + + start_test (state, "kim_preferences_set_options"); + + { + kim_error_t err = NULL; + kim_preferences_t prefs = NULL; + kim_options_t old_options = NULL; + kim_options_t new_options = NULL; + kim_options_t verify_options = NULL; + const char *custom_data = "Some custom data"; + const char *verify_custom_data = NULL; + + err = kim_preferences_create (&prefs); + fail_if_error (state, "kim_preferences_create", err, + "while creating preferences"); + + if (!err) { + err = kim_preferences_get_options (prefs, &old_options); + fail_if_error (state, "kim_preferences_get_options", err, + "while getting old options"); + } + + if (!err) { + err = kim_options_create (&new_options); + fail_if_error (state, "kim_options_create", err, + "while creating options"); + } + + if (!err) { + err = kim_options_set_data (new_options, custom_data); + fail_if_error (state, "kim_options_set_data", err, + "while setting the custom data to %s", custom_data); + } + + if (!err) { + err = kim_preferences_set_options (prefs, new_options); + fail_if_error (state, "kim_preferences_set_options", err, + "while setting the new options"); + } + + if (!err) { + err = kim_preferences_get_options (prefs, &verify_options); + fail_if_error (state, "kim_preferences_get_options", err, + "while getting options for verification"); + } + + if (!err) { + err = kim_options_get_data (verify_options, (const void **)&verify_custom_data); + fail_if_error (state, "kim_options_get_data", err, + "while getting the custom data of the verify options"); + } + + if (!err && custom_data != verify_custom_data) { + log_failure (state, "Unexpected custom data in options (got %p, expected %p)", + verify_custom_data, custom_data); + } + + if (!err) { + err = kim_preferences_set_options (prefs, old_options); + fail_if_error (state, "kim_preferences_set_options", err, + "while restoring the options"); + } + + kim_options_free (&old_options); + kim_options_free (&new_options); + kim_options_free (&verify_options); + kim_preferences_free (&prefs); + kim_error_free (&err); + } + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_preferences_set_remember_options (kim_test_state_t state) +{ + + start_test (state, "kim_preferences_set_remember_options"); + + { + kim_error_t err = NULL; + kim_preferences_t prefs = NULL; + kim_boolean_t remember_options = FALSE; + + err = kim_preferences_create (&prefs); + fail_if_error (state, "kim_preferences_create", err, + "while creating preferences"); + + if (!err) { + err = kim_preferences_set_remember_options (prefs, TRUE); + fail_if_error (state, "kim_preferences_set_remember_options", err, + "while setting the preference to remember options"); + } + + if (!err) { + err = kim_preferences_get_remember_options (prefs, &remember_options); + fail_if_error (state, "kim_preferences_get_remember_options", err, + "while getting the preference to remember options"); + } + + if (!err && !remember_options) { + log_failure (state, "Unexpected remember options preference (got %d, expected TRUE)", + remember_options); + } + + if (!err) { + err = kim_preferences_set_remember_options (prefs, FALSE); + fail_if_error (state, "kim_preferences_set_remember_options", err, + "while setting the preference to remember options"); + } + + if (!err) { + err = kim_preferences_get_remember_options (prefs, &remember_options); + fail_if_error (state, "kim_preferences_get_remember_options", err, + "while getting the preference to remember options"); + } + + if (!err && remember_options) { + log_failure (state, "Unexpected remember options preference (got %d, expected 0)", + remember_options); + } + + kim_preferences_free (&prefs); + kim_error_free (&err); + } + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_preferences_set_client_identity (kim_test_state_t state) +{ + + start_test (state, "kim_preferences_set_client_identity"); + + { + kim_error_t err = NULL; + kim_preferences_t prefs = NULL; + kim_string_t test_string = "user@EXAMPLE.COM"; + kim_identity_t test_identity = KIM_IDENTITY_ANY; + kim_string_t string = NULL; + kim_identity_t identity = KIM_IDENTITY_ANY; + kim_comparison_t comparison = 0; + + err = kim_preferences_create (&prefs); + fail_if_error (state, "kim_preferences_create", err, + "while creating preferences"); + + if (!err) { + err = kim_identity_create_from_string (&test_identity, test_string); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating the identity for %s", test_string); + } + + if (!err) { + err = kim_preferences_set_client_identity (prefs, KIM_IDENTITY_ANY); + fail_if_error (state, "kim_preferences_set_client_identity", err, + "while setting the identity to KIM_IDENTITY_ANY"); + } + + if (!err) { + err = kim_preferences_get_client_identity (prefs, &identity); + fail_if_error (state, "kim_preferences_get_client_identity", err, + "while getting the client identity preference"); + } + + if (!err && identity != KIM_IDENTITY_ANY) { + log_failure (state, "Unexpected client identity preference (got %p, expected %p)", + identity, KIM_IDENTITY_ANY); + kim_identity_free (&identity); + } + + if (!err) { + err = kim_preferences_set_client_identity (prefs, test_identity); + fail_if_error (state, "kim_preferences_set_client_identity", err, + "while setting the identity to %s", test_string); + } + + if (!err) { + err = kim_preferences_get_client_identity (prefs, &identity); + fail_if_error (state, "kim_preferences_get_client_identity", err, + "while getting the client identity preference"); + } + + if (!err && identity) { + err = kim_identity_get_string (identity, &string); + fail_if_error (state, "kim_identity_get_string", err, + "while getting the string for client identity preference"); + } + + if (!err) { + err = kim_identity_compare (identity, test_identity, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the identity preference %s", + test_string, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected client identity preference (got %s, expected %s)", + string ? string : "NULL", test_string); + kim_identity_free (&identity); + } + + kim_string_free (&string); + kim_identity_free (&identity); + kim_identity_free (&test_identity); + kim_preferences_free (&prefs); + kim_error_free (&err); + } + + end_test (state); +} diff --git a/src/kim/test/test_kim_preferences.h b/src/kim/test/test_kim_preferences.h new file mode 100644 index 000000000..e86f11f2f --- /dev/null +++ b/src/kim/test/test_kim_preferences.h @@ -0,0 +1,42 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 TEST_KIM_PREFERENCES_H +#define TEST_KIM_PREFERENCES_H + +#include "test_kim_common.h" + +void test_kim_preferences_create (kim_test_state_t state); + +void test_kim_preferences_copy (kim_test_state_t state); + +void test_kim_preferences_set_options (kim_test_state_t state); + +void test_kim_preferences_set_remember_options (kim_test_state_t state); + +void test_kim_preferences_set_client_identity (kim_test_state_t state); + +#endif /* TEST_KIM_PREFERENCES_H */ diff --git a/src/kim/test/test_kim_selection_hints.c b/src/kim/test/test_kim_selection_hints.c new file mode 100644 index 000000000..d2bc51151 --- /dev/null +++ b/src/kim/test/test_kim_selection_hints.c @@ -0,0 +1,469 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + */ + +#include "test_kim_selection_hints.h" + +#define KSH_TEST_ID "edu.mit.Kerberos.test_kim" +#define KSH_SERVICE_IDENTITY "service/server.example.com@EXAMPLE.COM" +#define KSH_SERVICE "service" +#define KSH_SERVER "server.example.com" +#define KSH_SERVICE_REALM "EXAMPLE.COM" +#define KSH_USER "jdoe" +#define KSH_CLIENT_REALM "USERS.EXAMPLE.COM" + +#define KSH_IDENTITY "jdoe@USERS.EXAMPLE.COM" + + +/* ------------------------------------------------------------------------ */ + +void test_kim_selection_hints_set_service_identity_hint (kim_test_state_t state) +{ + kim_error_t err = NULL; + kim_selection_hints_t hints = NULL; + kim_identity_t service_identity = NULL; + kim_identity_t identity = KIM_IDENTITY_ANY; + kim_string_t string = NULL; + kim_comparison_t comparison = 0; + + start_test (state, "kim_selection_hints_set_service_identity_hint"); + + if (!err) { + err = kim_selection_hints_create (&hints, KSH_TEST_ID); + fail_if_error (state, "kim_selection_hints_create", err, + "while creating selection hints for %s", KSH_TEST_ID); + } + + if (!err) { + err = kim_identity_create_from_string (&service_identity, + KSH_SERVICE_IDENTITY); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating an identity for %s", + KSH_SERVICE_IDENTITY); + } + + if (!err) { + err = kim_selection_hints_set_service_identity_hint (hints, service_identity); + fail_if_error (state, "kim_selection_hints_set_service_identity_hint", + err, "while setting service identity to %s", + KSH_SERVICE_IDENTITY); + } + + if (!err) { + err = kim_selection_hints_get_service_identity_hint (hints, &identity); + fail_if_error (state, "kim_selection_hints_get_service_identity_hint", + err, "while getting service identity %s", + KSH_SERVICE_IDENTITY); + } + + if (!err && identity) { + err = kim_identity_get_string (identity, &string); + fail_if_error (state, "kim_identity_get_string", err, + "while getting the string for the service identity hint"); + } + + if (!err) { + err = kim_identity_compare (service_identity, identity, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the identity hint %s", + KSH_SERVICE_IDENTITY, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected service identity hint (got %s, expected %s)", + string ? string : "NULL", KSH_SERVICE_IDENTITY); + kim_identity_free (&identity); + } + + kim_string_free (&string); + kim_identity_free (&service_identity); + kim_selection_hints_free (&hints); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_selection_hints_set_client_realm_hint (kim_test_state_t state) +{ + kim_error_t err = NULL; + kim_selection_hints_t hints = NULL; + kim_string_t string = NULL; + kim_comparison_t comparison = 0; + + start_test (state, "kim_selection_hints_set_client_realm_hint"); + + if (!err) { + err = kim_selection_hints_create (&hints, KSH_TEST_ID); + fail_if_error (state, "kim_selection_hints_create", err, + "while creating selection hints for %s", KSH_TEST_ID); + } + + if (!err) { + err = kim_selection_hints_set_client_realm_hint (hints, KSH_CLIENT_REALM); + fail_if_error (state, "kim_selection_hints_set_client_realm_hint", + err, "while setting client realm hint to %s", + KSH_CLIENT_REALM); + } + + if (!err) { + err = kim_selection_hints_get_client_realm_hint (hints, &string); + fail_if_error (state, "kim_selection_hints_get_client_realm_hint", + err, "while getting the client realm %s", + KSH_CLIENT_REALM); + } + + if (!err) { + err = kim_string_compare (KSH_CLIENT_REALM, string, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the client realm hint %s", + KSH_CLIENT_REALM, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected client realm hint (got %s, expected %s)", + string ? string : "NULL", KSH_CLIENT_REALM); + } + + kim_string_free (&string); + kim_selection_hints_free (&hints); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_selection_hints_set_user_hint (kim_test_state_t state) +{ + kim_error_t err = NULL; + kim_selection_hints_t hints = NULL; + kim_string_t string = NULL; + kim_comparison_t comparison = 0; + + start_test (state, "kim_selection_hints_set_user_hint"); + + if (!err) { + err = kim_selection_hints_create (&hints, KSH_TEST_ID); + fail_if_error (state, "kim_selection_hints_create", err, + "while creating selection hints for %s", KSH_TEST_ID); + } + + if (!err) { + err = kim_selection_hints_set_user_hint (hints, KSH_USER); + fail_if_error (state, "kim_selection_hints_set_user_hint", + err, "while setting user hint to %s", + KSH_USER); + } + + if (!err) { + err = kim_selection_hints_get_user_hint (hints, &string); + fail_if_error (state, "kim_selection_hints_get_user_hint", + err, "while getting the user hint %s", + KSH_USER); + } + + if (!err) { + err = kim_string_compare (KSH_USER, string, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the user hint %s", + KSH_USER, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected user hint (got %s, expected %s)", + string ? string : "NULL", KSH_USER); + } + + kim_string_free (&string); + kim_selection_hints_free (&hints); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_selection_hints_set_service_realm_hint (kim_test_state_t state) +{ + kim_error_t err = NULL; + kim_selection_hints_t hints = NULL; + kim_string_t string = NULL; + kim_comparison_t comparison = 0; + + start_test (state, "kim_selection_hints_set_service_realm_hint"); + + if (!err) { + err = kim_selection_hints_create (&hints, KSH_TEST_ID); + fail_if_error (state, "kim_selection_hints_create", err, + "while creating selection hints for %s", KSH_TEST_ID); + } + + if (!err) { + err = kim_selection_hints_set_service_realm_hint (hints, KSH_SERVICE_REALM); + fail_if_error (state, "kim_selection_hints_set_service_realm_hint", + err, "while setting service realm to %s", + KSH_SERVICE_REALM); + } + + if (!err) { + err = kim_selection_hints_get_service_realm_hint (hints, &string); + fail_if_error (state, "kim_selection_hints_get_service_realm_hint", + err, "while getting the service realm hint %s", + KSH_SERVICE_REALM); + } + + if (!err) { + err = kim_string_compare (KSH_SERVICE_REALM, string, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the service realm hint %s", + KSH_SERVICE_REALM, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected service realm hint (got %s, expected %s)", + string ? string : "NULL", KSH_SERVICE_REALM); + } + + kim_string_free (&string); + kim_selection_hints_free (&hints); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_selection_hints_set_service_hint (kim_test_state_t state) +{ + kim_error_t err = NULL; + kim_selection_hints_t hints = NULL; + kim_string_t string = NULL; + kim_comparison_t comparison = 0; + + start_test (state, "kim_selection_hints_set_service_hint"); + + if (!err) { + err = kim_selection_hints_create (&hints, KSH_TEST_ID); + fail_if_error (state, "kim_selection_hints_create", err, + "while creating selection hints for %s", KSH_TEST_ID); + } + + if (!err) { + err = kim_selection_hints_set_service_hint (hints, KSH_SERVICE); + fail_if_error (state, "kim_selection_hints_set_service_hint", + err, "while setting service hint to %s", + KSH_SERVICE); + } + + if (!err) { + err = kim_selection_hints_get_service_hint (hints, &string); + fail_if_error (state, "kim_selection_hints_get_service_hint", + err, "while getting the service hint %s", + KSH_SERVICE); + } + + if (!err) { + err = kim_string_compare (KSH_SERVICE, string, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the service hint %s", + KSH_SERVICE, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected service hint (got %s, expected %s)", + string ? string : "NULL", KSH_SERVICE); + } + + kim_string_free (&string); + kim_selection_hints_free (&hints); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_selection_hints_set_server_hint (kim_test_state_t state) +{ + kim_error_t err = NULL; + kim_selection_hints_t hints = NULL; + kim_string_t string = NULL; + kim_comparison_t comparison = 0; + + start_test (state, "kim_selection_hints_set_server_hint"); + + if (!err) { + err = kim_selection_hints_create (&hints, KSH_TEST_ID); + fail_if_error (state, "kim_selection_hints_create", err, + "while creating selection hints for %s", KSH_TEST_ID); + } + + if (!err) { + err = kim_selection_hints_set_server_hint (hints, KSH_SERVER); + fail_if_error (state, "kim_selection_hints_set_server_hint", + err, "while setting server hint to %s", + KSH_SERVER); + } + + if (!err) { + err = kim_selection_hints_get_server_hint (hints, &string); + fail_if_error (state, "kim_selection_hints_get_server_hint", + err, "while getting the server hint %s", + KSH_SERVER); + } + + if (!err) { + err = kim_string_compare (KSH_SERVER, string, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the server hint %s", + KSH_SERVER, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected server hint (got %s, expected %s)", + string ? string : "NULL", KSH_SERVER); + } + + kim_string_free (&string); + kim_selection_hints_free (&hints); + + end_test (state); +} + +/* ------------------------------------------------------------------------ */ + +void test_kim_selection_hints_remember_identity (kim_test_state_t state) +{ + kim_error_t err = NULL; + kim_selection_hints_t hints = NULL; + kim_identity_t service_identity = NULL; + kim_identity_t client_identity = NULL; + kim_string_t string = NULL; + kim_identity_t identity = KIM_IDENTITY_ANY; + kim_comparison_t comparison = 0; + + start_test (state, "kim_selection_hints_remember_identity"); + + if (!err) { + err = kim_selection_hints_create (&hints, KSH_TEST_ID); + fail_if_error (state, "kim_selection_hints_create", err, + "while creating selection hints for %s", KSH_TEST_ID); + } + + if (!err) { + err = kim_identity_create_from_string (&service_identity, + KSH_SERVICE_IDENTITY); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating an identity for %s", + KSH_SERVICE_IDENTITY); + } + + if (!err) { + err = kim_identity_create_from_string (&client_identity, + KSH_IDENTITY); + fail_if_error (state, "kim_identity_create_from_string", err, + "while creating an identity for %s", + KSH_IDENTITY); + } + + if (!err) { + err = kim_selection_hints_set_service_identity_hint (hints, service_identity); + fail_if_error (state, "kim_selection_hints_set_service_identity_hint", + err, "while setting service identity to %s", + KSH_SERVICE_IDENTITY); + } + + if (!err) { + err = kim_selection_hints_set_client_realm_hint (hints, KSH_CLIENT_REALM); + fail_if_error (state, "kim_selection_hints_set_client_realm_hint", + err, "while setting client realm to %s", + KSH_CLIENT_REALM); + } + + if (!err) { + err = kim_selection_hints_set_user_hint (hints, KSH_USER); + fail_if_error (state, "kim_selection_hints_set_user_hint", + err, "while setting user to %s", + KSH_USER); + } + + if (!err) { + err = kim_selection_hints_set_service_realm_hint (hints, KSH_SERVICE_REALM); + fail_if_error (state, "kim_selection_hints_set_service_realm_hint", + err, "while setting service realm to %s", + KSH_SERVICE_REALM); + } + + if (!err) { + err = kim_selection_hints_set_service_hint (hints, KSH_SERVICE); + fail_if_error (state, "kim_selection_hints_set_service_hint", + err, "while setting service to %s", + KSH_SERVICE); + } + + if (!err) { + err = kim_selection_hints_set_server_hint (hints, KSH_SERVER); + fail_if_error (state, "kim_selection_hints_set_server_hint", + err, "while setting server to %s", + KSH_SERVER); + } + + if (!err) { + err = kim_selection_hints_remember_identity (hints, client_identity); + fail_if_error (state, "kim_selection_hints_remember_identity", + err, "while remembering identity %s", + KSH_IDENTITY); + } + + if (!err) { + err = kim_selection_hints_get_identity (hints, &identity); + fail_if_error (state, "kim_selection_hints_get_identity", + err, "while checking if identity is %s", + KSH_IDENTITY); + } + + if (!err && identity) { + err = kim_identity_get_string (identity, &string); + fail_if_error (state, "kim_identity_get_string", err, + "while getting the string for the client identity hint"); + } + + if (!err) { + err = kim_identity_compare (client_identity, identity, &comparison); + fail_if_error (state, "kim_identity_compare", err, + "while comparing %s to the identity hint %s", + KSH_IDENTITY, string ? string : "NULL"); + } + + if (!err && !kim_comparison_is_equal_to (comparison)) { + log_failure (state, "Unexpected client identity hint (got %s, expected %s)", + string ? string : "NULL", KSH_IDENTITY); + } + + kim_string_free (&string); + kim_identity_free (&identity); + kim_identity_free (&client_identity); + kim_identity_free (&service_identity); + kim_selection_hints_free (&hints); + + end_test (state); +} diff --git a/src/kim/test/test_kim_selection_hints.h b/src/kim/test/test_kim_selection_hints.h new file mode 100644 index 000000000..fac9a3d5d --- /dev/null +++ b/src/kim/test/test_kim_selection_hints.h @@ -0,0 +1,46 @@ +/* + * $Header$ + * + * Copyright 2006 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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 TEST_KIM_SELECTION_HINTS_H +#define TEST_KIM_SELECTION_HINTS_H + +#include "test_kim_common.h" + +void test_kim_selection_hints_set_service_identity_hint (kim_test_state_t state); + +void test_kim_selection_hints_set_client_realm_hint (kim_test_state_t state); + +void test_kim_selection_hints_set_user_hint (kim_test_state_t state); + +void test_kim_selection_hints_set_service_realm_hint (kim_test_state_t state); + +void test_kim_selection_hints_set_service_hint (kim_test_state_t state); + +void test_kim_selection_hints_set_server_hint (kim_test_state_t state); + +void test_kim_selection_hints_remember_identity (kim_test_state_t state); + +#endif /* TEST_KIM_SELECTION_HINTS_H */ -- 2.26.2