diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/kim/kim_credential.h | 1004 | ||||
-rw-r--r-- | src/include/kim/kim_identity.h | 21 | ||||
-rw-r--r-- | src/include/kim/kim_options.h | 147 | ||||
-rw-r--r-- | src/include/kim/kim_ui_plugin.h | 55 | ||||
-rw-r--r-- | src/kim/lib/kim-lite.exports | 12 | ||||
-rw-r--r-- | src/kim/lib/kim.exports | 8 | ||||
-rw-r--r-- | src/kim/lib/kim_credential.c | 336 | ||||
-rw-r--r-- | src/kim/lib/kim_identity.c | 116 | ||||
-rw-r--r-- | src/kim/lib/kim_options.c | 176 | ||||
-rw-r--r-- | src/kim/lib/kim_selection_hints.c | 14 | ||||
-rw-r--r-- | src/kim/lib/kim_string.c | 1 | ||||
-rw-r--r-- | src/kim/lib/kim_string_private.h | 4 | ||||
-rw-r--r-- | src/kim/lib/kim_ui.c | 212 | ||||
-rw-r--r-- | src/kim/lib/kim_ui_cli.c | 324 | ||||
-rw-r--r-- | src/kim/lib/kim_ui_cli_private.h | 24 | ||||
-rw-r--r-- | src/kim/lib/kim_ui_gui.c | 44 | ||||
-rw-r--r-- | src/kim/lib/kim_ui_gui_private.h | 14 | ||||
-rw-r--r-- | src/kim/lib/kim_ui_plugin.c | 44 | ||||
-rw-r--r-- | src/kim/lib/kim_ui_plugin_private.h | 18 | ||||
-rw-r--r-- | src/kim/lib/kim_ui_private.h | 39 | ||||
-rw-r--r-- | src/kim/lib/mac/kim_os_library.c | 2 | ||||
-rw-r--r-- | src/kim/lib/mac/kim_os_string.c | 49 |
22 files changed, 1707 insertions, 957 deletions
diff --git a/src/include/kim/kim_credential.h b/src/include/kim/kim_credential.h index ed58a72ae..88ccc5dce 100644 --- a/src/include/kim/kim_credential.h +++ b/src/include/kim/kim_credential.h @@ -32,484 +32,532 @@ extern "C" { #include <kim/kim_types.h> #include <krb5.h> - /*! - * \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; - - /*! @} */ - - /*! - * \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 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 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 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/<REALM>@<REALM>". - * 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 code representing the failure. - * \brief Get a credential iterator to enumerate credentials in a ccache. - */ - - kim_error kim_credential_iterator_create (kim_credential_iterator *out_credential_iterator, - kim_ccache 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 code representing the failure. - * \brief Get the next credential in a ccache. - */ - - kim_error kim_credential_iterator_next (kim_credential_iterator in_credential_iterator, - kim_credential *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 *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 code representing the failure. - * \brief Acquire a new initial credential. - * \sa kim_ccache_create_new - */ - kim_error kim_credential_create_new (kim_credential *out_credential, - kim_identity in_client_identity, - kim_options 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 code representing the failure. - * \brief Acquire a new initial credential from a keytab. - * \sa kim_ccache_create_from_keytab - */ - kim_error kim_credential_create_from_keytab (kim_credential *out_credential, - kim_identity in_identity, - kim_options in_options, - kim_string 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 code representing the failure. - * \brief Copy a credential from a krb5 credential object. - */ - kim_error kim_credential_create_from_krb5_creds (kim_credential *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 code representing the failure. - * \brief Copy a credential object. - */ - kim_error kim_credential_copy (kim_credential *out_credential, - kim_credential 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 code representing the failure. - * \brief Get a krb5 credentials object for a credential object. - */ - kim_error kim_credential_get_krb5_creds (kim_credential 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 code representing the failure. - * \brief Get the client identity of a credential object. - */ - kim_error kim_credential_get_client_identity (kim_credential in_credential, - kim_identity *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 code representing the failure. - * \brief Get the service identity of a credential object. - */ - kim_error kim_credential_get_service_identity (kim_credential in_credential, - kim_identity *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 code representing the failure. - * \brief Check if a credential is a ticket granting ticket. - */ - kim_error kim_credential_is_tgt (kim_credential in_credential, - kim_boolean *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 code representing the failure. - * \brief Check the state of a credential (valid, expired, postdated, etc). - */ - kim_error kim_credential_get_state (kim_credential in_credential, - kim_credential_state *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 code representing the failure. - * \brief Get the time when the credentials become valid. - * \sa kim_ccache_get_start_time - */ - kim_error kim_credential_get_start_time (kim_credential in_credential, - kim_time *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 code representing the failure. - * \brief Get the time when the credentials will expire. - * \sa kim_ccache_get_expiration_time - */ - kim_error kim_credential_get_expiration_time (kim_credential in_credential, - kim_time *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 code representing the failure. - * \brief Get the time when the credentials will no longer be renewable. - * \sa kim_ccache_get_renewal_expiration_time - */ - kim_error kim_credential_get_renewal_expiration_time (kim_credential in_credential, - kim_time *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 code representing the failure. - * \brief Store a credential in a ccache in the cache collection. - */ - kim_error kim_credential_store (kim_credential in_credential, - kim_identity in_client_identity, - kim_ccache *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/<host's FQDN>@<host's local realm>). - * \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 code representing the failure. - * \brief Verify a TGT credential. - * \sa kim_ccache_verify - */ - kim_error kim_credential_verify (kim_credential in_credential, - kim_identity in_service_identity, - kim_string in_keytab, - kim_boolean 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 code representing the failure. - * \brief Renew a TGT credential. - * \sa kim_ccache_renew - */ - kim_error kim_credential_renew (kim_credential *io_credential, - kim_options 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 code representing the failure. - * \brief Validate a TGT credential. - * \sa kim_ccache_validate - */ - kim_error kim_credential_validate (kim_credential *io_credential, - kim_options 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 *io_credential); - - /*!@}*/ +/*! + * \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; + +/*! @} */ + +/*! + * \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 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 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 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/<REALM>@<REALM>". + * 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 code representing the failure. + * \brief Get a credential iterator to enumerate credentials in a ccache. + */ + +kim_error kim_credential_iterator_create (kim_credential_iterator *out_credential_iterator, + kim_ccache 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 code representing the failure. + * \brief Get the next credential in a ccache. + */ + +kim_error kim_credential_iterator_next (kim_credential_iterator in_credential_iterator, + kim_credential *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 *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 code representing the failure. + * \brief Acquire a new initial credential. + * \sa kim_ccache_create_new + */ +kim_error kim_credential_create_new (kim_credential *out_credential, + kim_identity in_client_identity, + kim_options 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 code representing the failure. + * \brief Acquire a new initial credential from a keytab. + * \sa kim_ccache_create_from_keytab + */ +kim_error kim_credential_create_from_keytab (kim_credential *out_credential, + kim_identity in_identity, + kim_options in_options, + kim_string 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 code representing the failure. + * \brief Copy a credential from a krb5 credential object. + */ +kim_error kim_credential_create_from_krb5_creds (kim_credential *out_credential, + krb5_context in_krb5_context, + krb5_creds *in_krb5_creds); + +/*! + * \param out_credential on exit, a new credential object containing a change + * password credential for \a in_identity. + * Must be freed with kim_credential_free(). + * \param in_identity a client identity to obtain a change password credential for. + * \param in_old_password the current password for \a in_identity. May be + * an expired password. + * \return On success, #KIM_NO_ERROR. On failure, an error code representing the failure. + * \brief Obtain a credential for changing an identity's password. + * \sa kim_credential_change_password + */ +kim_error kim_credential_create_for_change_password (kim_credential *out_credential, + kim_identity in_identity, + kim_string in_old_password); + +/*! + * \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 code representing the failure. + * \brief Copy a credential object. + */ +kim_error kim_credential_copy (kim_credential *out_credential, + kim_credential 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 code representing the failure. + * \brief Get a krb5 credentials object for a credential object. + */ +kim_error kim_credential_get_krb5_creds (kim_credential 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 code representing the failure. + * \brief Get the client identity of a credential object. + */ +kim_error kim_credential_get_client_identity (kim_credential in_credential, + kim_identity *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 code representing the failure. + * \brief Get the service identity of a credential object. + */ +kim_error kim_credential_get_service_identity (kim_credential in_credential, + kim_identity *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 code representing the failure. + * \brief Check if a credential is a ticket granting ticket. + */ +kim_error kim_credential_is_tgt (kim_credential in_credential, + kim_boolean *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 code representing the failure. + * \brief Check the state of a credential (valid, expired, postdated, etc). + */ +kim_error kim_credential_get_state (kim_credential in_credential, + kim_credential_state *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 code representing the failure. + * \brief Get the time when the credentials become valid. + * \sa kim_ccache_get_start_time + */ +kim_error kim_credential_get_start_time (kim_credential in_credential, + kim_time *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 code representing the failure. + * \brief Get the time when the credentials will expire. + * \sa kim_ccache_get_expiration_time + */ +kim_error kim_credential_get_expiration_time (kim_credential in_credential, + kim_time *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 code representing the failure. + * \brief Get the time when the credentials will no longer be renewable. + * \sa kim_ccache_get_renewal_expiration_time + */ +kim_error kim_credential_get_renewal_expiration_time (kim_credential in_credential, + kim_time *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 code representing the failure. + * \brief Store a credential in a ccache in the cache collection. + */ +kim_error kim_credential_store (kim_credential in_credential, + kim_identity in_client_identity, + kim_ccache *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/<host's FQDN>@<host's local realm>). + * \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 code representing the failure. + * \brief Verify a TGT credential. + * \sa kim_ccache_verify + */ +kim_error kim_credential_verify (kim_credential in_credential, + kim_identity in_service_identity, + kim_string in_keytab, + kim_boolean 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 code representing the failure. + * \brief Renew a TGT credential. + * \sa kim_ccache_renew + */ +kim_error kim_credential_renew (kim_credential *io_credential, + kim_options 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 code representing the failure. + * \brief Validate a TGT credential. + * \sa kim_ccache_validate + */ +kim_error kim_credential_validate (kim_credential *io_credential, + kim_options in_options); + +/*! + * \param in_credential a credential object containing a change + * password credential. Use + * #kim_credential_change_password to obtain + * a change password credential. + * \param in_identity an identity to change the password for. May + * be different than the identity the credential + * is for. + * \param in_new_password the password to change the identity to. + * \param out_rejected_err on exit, 0 if the password change was + * successful or an error describing why the + * new password was rejected. + * \param out_rejected_message on exit, if \a out_rejected_err is non-zero + * this argument will contain an error message + * for \a out_rejected_err. Pass NULL if you + * do not want this error string. Must be + * freed with #kim_string_free(); + * \param out_rejected_description on exit, if \a out_rejected_err is non-zero + * this argument will contain an string describing + * why \a in_new_password was rejected. Pass NULL + * if you do not want this error string. Must be + * freed with #kim_string_free(); + * \return On success, #KIM_NO_ERROR. On failure, an error code representing the failure. + * \brief Change an identity's password. + * \sa kim_credential_create_for_change_password + */ +kim_error kim_credential_change_password (kim_credential in_credential, + kim_identity in_identity, + kim_string in_new_password, + kim_error *out_rejected_err, + kim_string *out_rejected_message, + kim_string *out_rejected_description); + +/*! + * \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 *io_credential); + +/*!@}*/ #ifdef __cplusplus diff --git a/src/include/kim/kim_identity.h b/src/include/kim/kim_identity.h index 4461c3163..f09c24aa7 100644 --- a/src/include/kim/kim_identity.h +++ b/src/include/kim/kim_identity.h @@ -256,29 +256,12 @@ kim_error kim_identity_get_krb5_principal (kim_identity in_identity, /*! * \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 code 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. + * the password. */ -kim_error kim_identity_change_password (kim_identity in_identity, - kim_options 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 code 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 kim_identity_change_password_to_password (kim_identity in_identity, - kim_options in_options, - kim_string in_new_password); +kim_error kim_identity_change_password (kim_identity in_identity); /*! * \param io_identity the identity object to be freed. Set to NULL on exit. diff --git a/src/include/kim/kim_options.h b/src/include/kim/kim_options.h index f5b975cf3..2c82b3ef5 100644 --- a/src/include/kim/kim_options.h +++ b/src/include/kim/kim_options.h @@ -46,70 +46,6 @@ extern "C" { */ #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; - -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 (*kim_prompt_callback) (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply); - -/*! - * The default prompt callback. - * See \ref kim_options_custom_prompt_callback for more information. - */ -kim_error kim_prompt_callback_default (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply); - -/*! - * The graphical prompt callback. - * See \ref kim_options_custom_prompt_callback for more information. - */ -kim_error kim_prompt_callback_gui (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply); - -/*! - * The command line prompt callback. - * See \ref kim_options_custom_prompt_callback for more information. - */ -kim_error kim_prompt_callback_cli (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **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 kim_prompt_callback_none (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply); - /*! @} */ /*! @@ -126,34 +62,6 @@ kim_error kim_prompt_callback_none (kim_prompt_type in_type, * 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 a 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. - * * \section kim_options_credential_properties Options for Controlling Credential Properties * * Kerberos credentials have a number of different properties which can be requested @@ -286,61 +194,6 @@ kim_error kim_options_copy (kim_options *out_options, kim_options 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 code 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 kim_options_set_prompt_callback (kim_options io_options, - kim_prompt_callback 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 code 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 kim_options_get_prompt_callback (kim_options in_options, - kim_prompt_callback *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 code 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 kim_options_set_data (kim_options 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 code 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 kim_options_get_data (kim_options in_options, - const void **out_data); - -/*! * \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 diff --git a/src/include/kim/kim_ui_plugin.h b/src/include/kim/kim_ui_plugin.h index d7fd6e1b7..b7d48ad89 100644 --- a/src/include/kim/kim_ui_plugin.h +++ b/src/include/kim/kim_ui_plugin.h @@ -29,7 +29,28 @@ extern "C" { #endif +/*! + * 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; + +enum kim_prompt_type_enum { + kim_prompt_type_password = 0, + kim_prompt_type_preauth = 1 +}; + /* + * Plugins for Controlling Identity Selection and 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 plugin mechanism for replacing the default prompt ui with your own. + * * The function table / structure which a KIM ui plugin module must export * as "kim_ui_0". If the interfaces work correctly, future versions of the * table will add either more callbacks or more arguments to callbacks, and @@ -49,9 +70,19 @@ typedef struct kim_ui_plugin_ftable_v0 { * this ui. */ kim_error (*init) (void **out_context); + /* Present UI which allows the user to enter a new identity. + * This is typically called when the user selects a "new tickets" + * control or menu item from a ticket management utility. + * If this UI calls into KIM to get new credentials it may + * call auth_prompt below. */ + kim_error (*enter_identity) (void *in_context, + kim_identity *out_identity); + /* Present UI to select which identity to use. + * This is typically called the first time an application tries to use + * Kerberos and is used to establish a hints preference for the application. * If this UI calls into KIM to get new credentials it may - * call acquire_new_credentials below. */ + * call auth_prompt below. */ kim_error (*select_identity) (void *in_context, kim_selection_hints in_hints, kim_identity *out_identity); @@ -60,13 +91,14 @@ typedef struct kim_ui_plugin_ftable_v0 { kim_error (*auth_prompt) (void *in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, char **out_reply); /* Prompt to change the identity's password. - * May be combined with an auth prompt if additional auth is required, + * May be combined with an auth_prompt if additional auth is required, * eg: SecurID pin. * If in_old_password_expired is true, this callback is in response * to an expired password error. If this is the case the same context @@ -79,15 +111,18 @@ typedef struct kim_ui_plugin_ftable_v0 { char **out_verify_password); /* Display an error to the user; may be called after any of the prompts */ - kim_error (*display_error) (void *in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description); + kim_error (*handle_error) (void *in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description); - /* Free strings returned by the UI */ - void (*free_string) (void *in_context, - char *io_string); + /* Free strings returned by the UI. Will be called once for each string + * returned from a plugin callback. If you have returned a string twice + * just make sure your free function checks for NULL and sets the pointer + * to NULL when done freeing memory. */ + void (*free_string) (void *in_context, + char **io_string); /* Called after the last prompt (even on error) to allow the UI to * free allocated resources associated with its context. */ diff --git a/src/kim/lib/kim-lite.exports b/src/kim/lib/kim-lite.exports index ebad7bf94..1b8466a56 100644 --- a/src/kim/lib/kim-lite.exports +++ b/src/kim/lib/kim-lite.exports @@ -16,20 +16,10 @@ kim_identity_get_number_of_components kim_identity_get_component_at_index kim_identity_get_krb5_principal kim_identity_change_password -kim_identity_change_password_to_password kim_identity_free -kim_prompt_callback_default -kim_prompt_callback_gui -kim_prompt_callback_cli -kim_prompt_callback_none - kim_options_create kim_options_copy -kim_options_set_prompt_callback -kim_options_get_prompt_callback -kim_options_set_data -kim_options_get_data kim_options_set_start_time kim_options_get_start_time kim_options_set_lifetime @@ -91,6 +81,7 @@ kim_credential_iterator_free kim_credential_create_new kim_credential_create_from_krb5_creds +kim_credential_create_for_change_password kim_credential_copy kim_credential_get_krb5_creds kim_credential_get_client_identity @@ -103,6 +94,7 @@ kim_credential_get_renewal_expiration_time kim_credential_store kim_credential_renew kim_credential_validate +kim_credential_change_password kim_credential_free kim_ccache_iterator_create diff --git a/src/kim/lib/kim.exports b/src/kim/lib/kim.exports index a408815b6..d2f2e27e7 100644 --- a/src/kim/lib/kim.exports +++ b/src/kim/lib/kim.exports @@ -16,14 +16,8 @@ kim_identity_get_number_of_components kim_identity_get_component_at_index kim_identity_get_krb5_principal kim_identity_change_password -kim_identity_change_password_to_password kim_identity_free -kim_prompt_callback_default -kim_prompt_callback_gui -kim_prompt_callback_cli -kim_prompt_callback_none - kim_options_create kim_options_copy kim_options_set_start_time @@ -88,6 +82,7 @@ kim_credential_iterator_free kim_credential_create_new kim_credential_create_from_keytab kim_credential_create_from_krb5_creds +kim_credential_create_for_change_password kim_credential_copy kim_credential_get_krb5_creds kim_credential_get_client_identity @@ -101,6 +96,7 @@ kim_credential_store kim_credential_verify kim_credential_renew kim_credential_validate +kim_credential_change_password kim_credential_free kim_ccache_iterator_create diff --git a/src/kim/lib/kim_credential.c b/src/kim/lib/kim_credential.c index 9952a9c72..f81b50f12 100644 --- a/src/kim/lib/kim_credential.c +++ b/src/kim/lib/kim_credential.c @@ -179,12 +179,19 @@ static inline kim_error kim_credential_allocate (kim_credential *out_credential) /* ------------------------------------------------------------------------ */ kim_error kim_credential_create_new (kim_credential *out_credential, - kim_identity in_client_identity, + kim_identity in_identity, kim_options in_options) { kim_error err = KIM_NO_ERROR; kim_credential credential = NULL; - + kim_options options = NULL; + kim_ui_context context; + kim_string service = NULL; + krb5_principal principal = NULL; + krb5_get_init_creds_opt *init_cred_options = NULL; + kim_boolean ui_inited = 0; + kim_boolean done = 0; + if (!err && !out_credential) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { @@ -196,7 +203,82 @@ kim_error kim_credential_create_new (kim_credential *out_credential, } if (!err) { -#warning Get tickets here + if (in_options) { + options = in_options; + } else { + err = kim_options_create (&options); + } + } + + if (!err) { + err = kim_options_get_init_cred_options (options, + credential->context, + &init_cred_options); + } + + if (!err) { + kim_options_get_service_name (options, &service); + } + + if (!err) { + err = kim_identity_get_krb5_principal (in_identity, + credential->context, + &principal); + } + + if (!err) { + err = kim_ui_init (&context); + if (!err) { + context.identity = in_identity; /* used by kim_ui_prompter */ + ui_inited = 1; + } + } + + while (!err && !done) { + krb5_creds creds; + kim_boolean free_creds = 0; + + err = krb5_error (credential->context, + krb5_get_init_creds_password (credential->context, + &creds, + principal, + NULL, + kim_ui_prompter, + &context, 0, + (char *) service, + init_cred_options)); + + if (!err) { free_creds = 1; } + + if (!err) { + err = krb5_error (credential->context, + krb5_copy_creds (credential->context, + &creds, + &credential->creds)); + } + + if (!err || err == KIM_USER_CANCELED_ERR) { + /* new creds obtained or the user gave up */ + done = 1; + + } else { + /* new creds failed, report error to user */ + err = kim_ui_handle_kim_error (&context, in_identity, + kim_ui_error_type_authentication, + err); + } + + if (free_creds) { krb5_free_cred_contents (credential->context, &creds); } + } + + if (ui_inited) { + kim_error fini_err = kim_ui_fini (&context); + if (!err) { err = check_error (fini_err); } + } + + /* free before credential is passed back to caller */ + if (credential && init_cred_options) { + kim_options_free_init_cred_options (credential->context, &init_cred_options); } if (!err) { @@ -204,6 +286,9 @@ kim_error kim_credential_create_new (kim_credential *out_credential, credential = NULL; } + if (principal ) { krb5_free_principal (credential->context, principal); } + if (!in_options) { kim_options_free (&options); } + kim_string_free (&service); kim_credential_free (&credential); return check_error (err); @@ -362,7 +447,9 @@ kim_error kim_credential_create_from_krb5_creds (kim_credential *out_credential, } if (!err) { - err = krb5_error (NULL, krb5_init_context (&credential->context)); + err = krb5_error (in_krb5_context, + krb5_copy_context (in_krb5_context, + &credential->context)); } if (!err) { @@ -382,6 +469,117 @@ kim_error kim_credential_create_from_krb5_creds (kim_credential *out_credential, /* ------------------------------------------------------------------------ */ +kim_error kim_credential_create_for_change_password (kim_credential *out_credential, + kim_identity in_identity, + kim_string in_old_password) +{ + kim_error err = KIM_NO_ERROR; + kim_credential credential = NULL; + kim_string realm = NULL; + kim_string service = NULL; + kim_ui_context context; + krb5_principal principal = NULL; + kim_string service_format = "kadmin/changepw@%s"; + kim_boolean ui_inited = 0; + kim_boolean done = 0; + + if (!err && !out_credential ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !in_old_password) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + err = kim_credential_allocate (&credential); + } + + if (!err) { + err = krb5_error (NULL, krb5_init_context (&credential->context)); + } + + if (!err) { + err = kim_identity_get_krb5_principal (in_identity, + credential->context, + &principal); + } + + if (!err) { + err = kim_identity_get_realm (in_identity, &realm); + } + + if (!err) { + err = kim_string_create_from_format (&service, service_format, realm); + } + + if (!err) { + err = kim_ui_init (&context); + if (!err) { + context.identity = in_identity; /* used by kim_ui_prompter */ + ui_inited = 1; + } + } + + while (!err && !done) { + krb5_creds creds; + kim_boolean free_creds = 0; + krb5_get_init_creds_opt opts; + + krb5_get_init_creds_opt_init (&opts); + krb5_get_init_creds_opt_set_tkt_life (&opts, 5*60); + krb5_get_init_creds_opt_set_renew_life (&opts, 0); + krb5_get_init_creds_opt_set_forwardable (&opts, 0); + krb5_get_init_creds_opt_set_proxiable (&opts, 0); + + err = krb5_error (credential->context, + krb5_get_init_creds_password (credential->context, + &creds, + principal, + (char *) in_old_password, + kim_ui_prompter, + &context, 0, (char *) service, + &opts)); + if (!err) { free_creds = 1; } + + if (!err) { + err = krb5_error (credential->context, + krb5_copy_creds (credential->context, + &creds, + &credential->creds)); + } + + if (!err || err == KIM_USER_CANCELED_ERR) { + /* new creds obtained or the user gave up */ + done = 1; + + } else { + /* new creds failed, report error to user */ + err = kim_ui_handle_kim_error (&context, in_identity, + kim_ui_error_type_change_password, + err); + } + + if (free_creds) { krb5_free_cred_contents (credential->context, &creds); } + } + + if (ui_inited) { + kim_error fini_err = kim_ui_fini (&context); + if (!err) { err = check_error (fini_err); } + } + + if (!err) { + *out_credential = credential; + credential = NULL; + } + + if (principal ) { krb5_free_principal (credential->context, principal); } + + kim_string_free (&realm); + kim_string_free (&service); + kim_credential_free (&credential); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + kim_error kim_credential_copy (kim_credential *out_credential, kim_credential in_credential) { @@ -396,7 +594,9 @@ kim_error kim_credential_copy (kim_credential *out_credential, } if (!err) { - err = krb5_error (NULL, krb5_init_context (&credential->context)); + err = krb5_error (in_credential->context, + krb5_copy_context (in_credential->context, + &credential->context)); } if (!err) { @@ -690,8 +890,6 @@ kim_error kim_credential_store (kim_credential in_credential, k5ccache, in_credential->creds)); } -#warning Call plugins here - if (!err && out_ccache) { err = kim_ccache_create_from_krb5_ccache (out_ccache, context, k5ccache); } @@ -966,6 +1164,130 @@ kim_error kim_credential_validate (kim_credential *io_credential, /* ------------------------------------------------------------------------ */ +kim_error kim_credential_change_password (kim_credential in_credential, + kim_identity in_identity, + kim_string in_new_password, + kim_error *out_rejected_err, + kim_string *out_rejected_message, + kim_string *out_rejected_description) +{ + kim_error err = KIM_NO_ERROR; + krb5_principal principal = NULL; + int rejected_code = 0; + krb5_data message_data; + krb5_data description_data; + + if (!err && !in_credential ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !in_new_password ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !out_rejected_err) { err = check_error (KIM_NULL_PARAMETER_ERR); } + /* out_rejected_message and out_rejected_description may be NULL */ + + if (!err) { + err = kim_identity_get_krb5_principal (in_identity, + in_credential->context, + &principal); + } + + if (!err) { + err = krb5_error (in_credential->context, + krb5_principal_compare (in_credential->context, + in_credential->creds->client, + principal)); + } + + if (!err) { + if (krb5_principal_compare (in_credential->context, + in_credential->creds->client, + principal)) { + /* Same principal, change the password normally */ + err = krb5_error (in_credential->context, + krb5_change_password (in_credential->context, + in_credential->creds, + (char *) in_new_password, + &rejected_code, + &message_data, + &description_data)); + } else { + /* Different principal, use set change password protocol */ + err = krb5_error (in_credential->context, + krb5_set_password (in_credential->context, + in_credential->creds, + (char *) in_new_password, + principal, + &rejected_code, + &message_data, + &description_data)); + } + + } + + if (!err && rejected_code) { + kim_string rejected_message = NULL; + kim_string rejected_description = NULL; + + if (!err) { + if (message_data.data && message_data.length > 0) { + err = kim_string_create_from_buffer (&rejected_message, + message_data.data, + message_data.length); + } else { + err = kim_os_string_create_localized (&rejected_message, + "KLStringChangePasswordFailed"); + } + } + + if (!err) { + if (description_data.data && description_data.length > 0) { + err = kim_string_create_from_buffer (&rejected_description, + description_data.data, + description_data.length); + } else { + err = kim_os_string_create_localized (&rejected_description, + "KLStringPasswordRejected"); + } + } + + if (!err) { + char *c; + + // replace all \n and \r characters with spaces + for (c = (char *) rejected_message; *c != '\0'; c++) { + if ((*c == '\n') || (*c == '\r')) { *c = ' '; } + } + + for (c = (char *) rejected_description; *c != '\0'; c++) { + if ((*c == '\n') || (*c == '\r')) { *c = ' '; } + } + } + + if (!err) { + if (out_rejected_message) { + *out_rejected_message = rejected_message; + rejected_message = NULL; + } + + if (out_rejected_description) { + *out_rejected_description = rejected_description; + rejected_description = NULL; + } + } + + kim_string_free (&rejected_message); + kim_string_free (&rejected_description); + + krb5_free_data_contents (in_credential->context, &message_data); + krb5_free_data_contents (in_credential->context, &description_data); + } + + if (!err) { + *out_rejected_err = rejected_code; + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + void kim_credential_free (kim_credential *io_credential) { if (io_credential && *io_credential) { diff --git a/src/kim/lib/kim_identity.c b/src/kim/lib/kim_identity.c index e427a2a1b..7a5b68a9f 100644 --- a/src/kim/lib/kim_identity.c +++ b/src/kim/lib/kim_identity.c @@ -92,10 +92,6 @@ kim_error kim_identity_create_from_string (kim_identity *out_identity, } if (!err) { -#warning Run translator here - } - - if (!err) { *out_identity = identity; identity = NULL; } @@ -175,10 +171,6 @@ kim_error kim_identity_create_from_components (kim_identity *out_identity, } if (!err) { -#warning Run translator here - } - - if (!err) { *out_identity = identity; identity = NULL; } @@ -226,10 +218,6 @@ kim_error kim_identity_create_from_krb5_principal (kim_identity *out_identity, } if (!err) { -#warning Run translator here - } - - if (!err) { *out_identity = identity; identity = NULL; } @@ -253,7 +241,9 @@ kim_error kim_identity_copy (kim_identity *out_identity, err = kim_identity_allocate (&identity); if (!err) { - err = krb5_error (NULL, krb5_init_context (&identity->context)); + err = krb5_error (in_identity->context, + krb5_copy_context (in_identity->context, + &identity->context)); } if (!err) { @@ -546,33 +536,97 @@ kim_error kim_identity_is_tgt_service (kim_identity in_identity, /* ------------------------------------------------------------------------ */ -kim_error kim_identity_change_password (kim_identity in_identity, - kim_options in_options) +kim_error kim_identity_change_password (kim_identity in_identity) { kim_error err = KIM_NO_ERROR; + kim_ui_context context; + kim_boolean ui_inited = 0; + kim_boolean done = 0; if (!err && !in_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { -#warning Implement change password GUI support + err = kim_ui_init (&context); + if (!err) { ui_inited = 1; } } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - -kim_error kim_identity_change_password_to_password (kim_identity in_identity, - kim_options in_options, - kim_string in_new_password) -{ - kim_error err = KIM_NO_ERROR; - if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); } - if (!err && !in_new_password) { err = check_error (KIM_NULL_PARAMETER_ERR); } + while (!err && !done) { + char *old_password = NULL; + char *new_password = NULL; + char *verify_password = NULL; + kim_error rejected_err = KIM_NO_ERROR; + kim_string rejected_message = NULL; + kim_string rejected_description = NULL; + + err = kim_ui_change_password (&context, + in_identity, + 0 /* old password not expired */, + &old_password, + &new_password, + &verify_password); + + if (!err) { + kim_comparison comparison; + + err = kim_string_compare (new_password, verify_password, &comparison); + if (!err && !kim_comparison_is_equal_to (comparison)) { + err = check_error (KIM_PASSWORD_MISMATCH_ERR); + } + } + + if (!err) { + kim_credential credential = NULL; + + if (context.type == kim_ui_type_cli && context.tcontext) { + /* command line has already gotten the credentials for us */ + credential = (kim_credential) context.tcontext; + } else { + err = kim_credential_create_for_change_password (&credential, + in_identity, + old_password); + } + + if (!err) { + err = kim_credential_change_password (credential, + in_identity, + new_password, + &rejected_err, + &rejected_message, + &rejected_description); + + } + + kim_credential_free (&credential); + } + + if (!err || err == KIM_USER_CANCELED_ERR) { + /* password change succeeded or the user gave up */ + done = 1; + + } else if (!err && rejected_err) { + /* Password rejected, report it to the user */ + err = kim_ui_handle_error (&context, in_identity, + rejected_err, + rejected_message, + rejected_description); + + } else { + /* Password change failed, report error to user */ + err = kim_ui_handle_kim_error (&context, in_identity, + kim_ui_error_type_change_password, + err); + } + + kim_string_free (&rejected_message); + kim_string_free (&rejected_description); + kim_ui_free_string (&context, &old_password); + kim_ui_free_string (&context, &new_password); + kim_ui_free_string (&context, &verify_password); + } - if (!err) { -#warning Implement change password support + if (ui_inited) { + kim_error fini_err = kim_ui_fini (&context); + if (!err) { err = check_error (fini_err); } } return check_error (err); diff --git a/src/kim/lib/kim_options.c b/src/kim/lib/kim_options.c index dbb024c2d..102fa9644 100644 --- a/src/kim/lib/kim_options.c +++ b/src/kim/lib/kim_options.c @@ -29,8 +29,6 @@ /* ------------------------------------------------------------------------ */ struct kim_options_opaque { - kim_prompt_callback prompt_callback; - const void *prompt_callback_data; kim_time start_time; kim_lifetime lifetime; kim_boolean renewable; @@ -42,7 +40,6 @@ struct kim_options_opaque { }; struct kim_options_opaque kim_options_initializer = { -NULL, NULL, 0, kim_default_lifetime, kim_default_renewable, @@ -121,11 +118,6 @@ kim_error kim_options_copy (kim_options *out_options, err = kim_options_allocate (&options); if (!err) { - options->prompt_callback = in_options->prompt_callback; - options->prompt_callback_data = in_options->prompt_callback_data; - } - - if (!err) { options->start_time = in_options->start_time; options->lifetime = in_options->lifetime; options->renewable = in_options->renewable; @@ -153,73 +145,6 @@ kim_error kim_options_copy (kim_options *out_options, /* ------------------------------------------------------------------------ */ -kim_error kim_options_set_prompt_callback (kim_options io_options, - kim_prompt_callback in_prompt_callback) -{ - kim_error err = KIM_NO_ERROR; - - if (!err && !io_options) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - io_options->prompt_callback = in_prompt_callback; - } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - -kim_error kim_options_get_prompt_callback (kim_options in_options, - kim_prompt_callback *out_prompt_callback) -{ - kim_error err = KIM_NO_ERROR; - - if (!err && !in_options ) { err = check_error (KIM_NULL_PARAMETER_ERR); } - if (!err && !out_prompt_callback) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - *out_prompt_callback = in_options->prompt_callback; - } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - -kim_error kim_options_set_data (kim_options io_options, - const void *in_data) - -{ - kim_error err = KIM_NO_ERROR; - - if (!err && !io_options) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - io_options->prompt_callback_data = in_data; - } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - -kim_error kim_options_get_data (kim_options in_options, - const void **out_data) -{ - kim_error err = KIM_NO_ERROR; - - if (!err && !in_options) { err = check_error (KIM_NULL_PARAMETER_ERR); } - if (!err && !out_data ) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - *out_data = in_options->prompt_callback_data; - } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - kim_error kim_options_set_start_time (kim_options io_options, kim_time in_start_time) { @@ -507,22 +432,29 @@ kim_error kim_options_get_init_cred_options (kim_options in_option if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err && !out_init_cred_options) { err = check_error (KIM_NULL_PARAMETER_ERR); } - if (!err && !in_options->addressless) { - err = krb5_error (in_context, - 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); + } + + if (!err && in_options) { + if (!in_options->addressless) { + err = krb5_error (in_context, + krb5_os_localaddr (in_context, &addresses)); + } - *out_init_cred_options = init_cred_options; - init_cred_options = NULL; - addresses = NULL; + if (!err) { + 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); + addresses = NULL; + } + } + + if (!err) { + *out_init_cred_options = init_cred_options; + init_cred_options = NULL; } if (init_cred_options) { krb5_get_init_creds_opt_free (in_context, init_cred_options); } @@ -543,6 +475,7 @@ kim_error kim_options_free_init_cred_options (krb5_context in_conte 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); + (*io_init_cred_options)->address_list = NULL; } krb5_get_init_creds_opt_free (in_context, *io_init_cred_options); *io_init_cred_options = NULL; @@ -561,70 +494,3 @@ void kim_options_free (kim_options *io_options) *io_options = NULL; } } - -#pragma mark - - -/* ------------------------------------------------------------------------ */ - -kim_error kim_prompt_callback_default (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply) -{ - kim_error err = KIM_NO_ERROR; - - if (!err && !out_reply) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - -kim_error kim_prompt_callback_gui (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply) -{ - kim_error err = KIM_NO_ERROR; - - if (!err && !out_reply) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - -kim_error kim_prompt_callback_cli (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply) -{ - kim_error err = KIM_NO_ERROR; - - if (!err && !out_reply) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - } - - return check_error (err); -} - -/* ------------------------------------------------------------------------ */ - -kim_error kim_prompt_callback_none (kim_prompt_type in_type, - kim_string in_title, - kim_string in_message, - kim_string in_description, - char **out_reply) -{ - return KIM_USER_CANCELED_ERR; -} diff --git a/src/kim/lib/kim_selection_hints.c b/src/kim/lib/kim_selection_hints.c index 78a4c03e8..1aa8555d8 100644 --- a/src/kim/lib/kim_selection_hints.c +++ b/src/kim/lib/kim_selection_hints.c @@ -478,7 +478,19 @@ kim_error kim_selection_hints_get_identity (kim_selection_hints in_selection_hi } if (!err && !identity && in_selection_hints->allow_user_interaction) { -#warning GUI to let user pick identity here + kim_ui_context context; + + err = kim_ui_init (&context); + + if (!err) { + err = kim_ui_select_identity (&context, + in_selection_hints, + &identity); + } + + if (!err) { + err = kim_ui_fini (&context); + } } if (!err) { diff --git a/src/kim/lib/kim_string.c b/src/kim/lib/kim_string.c index 94bdb7a89..6968f19d9 100644 --- a/src/kim/lib/kim_string.c +++ b/src/kim/lib/kim_string.c @@ -147,6 +147,7 @@ kim_error kim_string_compare (kim_string in_string, { return kim_os_string_compare (in_string, in_compare_to_string, + 0, /* case sensitive */ out_comparison); } diff --git a/src/kim/lib/kim_string_private.h b/src/kim/lib/kim_string_private.h index 8998ac7f3..4b1cc1839 100644 --- a/src/kim/lib/kim_string_private.h +++ b/src/kim/lib/kim_string_private.h @@ -55,6 +55,10 @@ kim_error kim_string_append (kim_string *io_string, /* OS-specific because it should use UTF8-safe sorting where possible */ kim_error kim_os_string_compare (kim_string in_string, kim_string in_compare_to_string, + kim_boolean in_case_insensitive, kim_comparison *out_comparison); +kim_error kim_os_string_create_localized (kim_string *out_string, + kim_string in_string); + #endif /* KIM_STRING_PRIVATE_H */ diff --git a/src/kim/lib/kim_ui.c b/src/kim/lib/kim_ui.c index 1ef364771..36920144e 100644 --- a/src/kim/lib/kim_ui.c +++ b/src/kim/lib/kim_ui.c @@ -29,6 +29,78 @@ /* ------------------------------------------------------------------------ */ +static kim_prompt_type kim_ui_ptype2ktype (krb5_prompt_type type) +{ + switch (type) { + case KRB5_PROMPT_TYPE_PASSWORD: + return kim_prompt_type_password; + + case KRB5_PROMPT_TYPE_PREAUTH: + return kim_prompt_type_preauth; + } + return kim_prompt_type_preauth; +} + +/* ------------------------------------------------------------------------ */ +/* Set the identity field in your context and pass the context as the data */ + +krb5_error_code kim_ui_prompter (krb5_context in_krb5_context, + void *in_context, + const char *in_name, + const char *in_banner, + int in_num_prompts, + krb5_prompt in_prompts[]) +{ + kim_error err = KIM_NO_ERROR; + krb5_prompt_type *types = NULL; + kim_ui_context *context = (kim_ui_context *) in_context; + int i; + + if (!err && !in_krb5_context) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !in_prompts ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + types = krb5_get_prompt_types (in_krb5_context); + if (!types) { err = check_error (KIM_NULL_PARAMETER_ERR); } + } + + for (i = 0; !err && i < in_num_prompts; i++) { + char *reply = NULL; + + err = kim_ui_auth_prompt (context, + context->identity, + kim_ui_ptype2ktype (types[i]), + in_prompts[i].hidden, + in_name, + in_banner, + in_prompts[i].prompt, + &reply); + + if (!err) { + uint32_t reply_len = strlen (reply); + + if ((reply_len + 1) > in_prompts[i].reply->length) { + kim_debug_printf ("%s(): reply %d is too long (is %d, should be %d)\n", + __FUNCTION__, i, + reply_len, in_prompts[i].reply->length); + reply_len = in_prompts[i].reply->length; + } + + memmove (in_prompts[i].reply->data, reply, reply_len + 1); + in_prompts[i].reply->length = reply_len; + } + + kim_ui_free_string (context, &reply); + } + + return check_error (err); +} + +#pragma mark - + +/* ------------------------------------------------------------------------ */ + kim_error kim_ui_init (kim_ui_context *io_context) { kim_error err = KIM_NO_ERROR; @@ -36,13 +108,15 @@ kim_error kim_ui_init (kim_ui_context *io_context) if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { +#ifndef LEAN_CLIENT kim_ui_environment environment = kim_library_ui_environment (); if (environment == KIM_UI_ENVIRONMENT_GUI) { +#endif /* LEAN_CLIENT */ io_context->type = kim_ui_type_gui_plugin; err = kim_ui_plugin_init ((kim_ui_plugin_context *) &io_context->tcontext); - +#ifndef LEAN_CLIENT if (err) { io_context->type = kim_ui_type_gui_builtin; @@ -59,6 +133,7 @@ kim_error kim_ui_init (kim_ui_context *io_context) err = check_error (KIM_NO_UI_ERR); } +#endif /* LEAN_CLIENT */ } return check_error (err); @@ -66,6 +141,40 @@ kim_error kim_ui_init (kim_ui_context *io_context) /* ------------------------------------------------------------------------ */ +kim_error kim_ui_enter_identity (kim_ui_context *in_context, + kim_identity *out_identity) +{ + kim_error err = KIM_NO_ERROR; + + if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + if (in_context->type == kim_ui_type_gui_plugin) { + err = kim_ui_plugin_enter_identity ((kim_ui_plugin_context) in_context->tcontext, + out_identity); + +#ifndef LEAN_CLIENT + } else if (in_context->type == kim_ui_type_gui_builtin) { + err = kim_ui_gui_enter_identity ((kim_ui_gui_context) in_context->tcontext, + out_identity); + + } else if (in_context->type == kim_ui_type_cli) { + err = kim_ui_cli_enter_identity ((kim_ui_cli_context) in_context->tcontext, + out_identity); + +#endif /* LEAN_CLIENT */ + + } else { + err = check_error (KIM_NO_UI_ERR); + } + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + kim_error kim_ui_select_identity (kim_ui_context *in_context, kim_selection_hints in_hints, kim_identity *out_identity) @@ -82,6 +191,7 @@ kim_error kim_ui_select_identity (kim_ui_context *in_context, in_hints, out_identity); +#ifndef LEAN_CLIENT } else if (in_context->type == kim_ui_type_gui_builtin) { err = kim_ui_gui_select_identity ((kim_ui_gui_context) in_context->tcontext, in_hints, @@ -92,6 +202,8 @@ kim_error kim_ui_select_identity (kim_ui_context *in_context, in_hints, out_identity); +#endif /* LEAN_CLIENT */ + } else { err = check_error (KIM_NO_UI_ERR); } @@ -105,6 +217,7 @@ kim_error kim_ui_select_identity (kim_ui_context *in_context, kim_error kim_ui_auth_prompt (kim_ui_context *in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -122,15 +235,18 @@ kim_error kim_ui_auth_prompt (kim_ui_context *in_context, err = kim_ui_plugin_auth_prompt ((kim_ui_plugin_context) in_context->tcontext, in_identity, in_type, + in_hide_reply, in_title, in_message, in_description, out_reply); +#ifndef LEAN_CLIENT } else if (in_context->type == kim_ui_type_gui_builtin) { err = kim_ui_gui_auth_prompt ((kim_ui_gui_context) in_context->tcontext, in_identity, in_type, + in_hide_reply, in_title, in_message, in_description, @@ -140,10 +256,12 @@ kim_error kim_ui_auth_prompt (kim_ui_context *in_context, err = kim_ui_cli_auth_prompt ((kim_ui_cli_context) in_context->tcontext, in_identity, in_type, + in_hide_reply, in_title, in_message, in_description, out_reply); +#endif /* LEAN_CLIENT */ } else { err = check_error (KIM_NO_UI_ERR); @@ -179,6 +297,7 @@ kim_error kim_ui_change_password (kim_ui_context *in_context, out_new_password, out_verify_password); +#ifndef LEAN_CLIENT } else if (in_context->type == kim_ui_type_gui_builtin) { err = kim_ui_gui_change_password ((kim_ui_gui_context) in_context->tcontext, in_identity, @@ -195,6 +314,8 @@ kim_error kim_ui_change_password (kim_ui_context *in_context, out_new_password, out_verify_password); +#endif /* LEAN_CLIENT */ + } else { err = check_error (KIM_NO_UI_ERR); } @@ -204,12 +325,64 @@ kim_error kim_ui_change_password (kim_ui_context *in_context, } /* ------------------------------------------------------------------------ */ +/* Helper function */ + +kim_error kim_ui_handle_kim_error (kim_ui_context *in_context, + kim_identity in_identity, + enum kim_ui_error_type in_type, + kim_error in_error) +{ + kim_error err = KIM_NO_ERROR; + kim_string message = NULL; + kim_string description = NULL; + + if (!err) { + /* Do this first so last error doesn't get overwritten */ + err = kim_string_get_last_error_message (&description, in_error); + } + + if (!err && !in_context) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + kim_string key = NULL; + + switch (in_type) { + case kim_ui_error_type_authentication: + key = "KLStringLoginFailed"; + break; + + case kim_ui_error_type_change_password: + key = "KLStringChangePasswordFailed"; + break; + + case kim_ui_error_type_selection: + case kim_ui_error_type_generic: + default: + key = "KLStringKerberosOperationFailed"; + break; + } + + err = kim_os_string_create_localized (&message, key); + } + + if (!err) { + err = kim_ui_handle_error (in_context, in_identity, + in_error, message, description); + } + + kim_string_free (&description); + kim_string_free (&message); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ -kim_error kim_ui_display_error (kim_ui_context *in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description) +kim_error kim_ui_handle_error (kim_ui_context *in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description) { kim_error err = KIM_NO_ERROR; @@ -219,25 +392,27 @@ kim_error kim_ui_display_error (kim_ui_context *in_context, if (!err) { if (in_context->type == kim_ui_type_gui_plugin) { - err = kim_ui_plugin_display_error ((kim_ui_plugin_context) in_context->tcontext, + err = kim_ui_plugin_handle_error ((kim_ui_plugin_context) in_context->tcontext, in_identity, in_error, in_error_message, in_error_description); +#ifndef LEAN_CLIENT } else if (in_context->type == kim_ui_type_gui_builtin) { - err = kim_ui_gui_display_error ((kim_ui_gui_context) in_context->tcontext, + err = kim_ui_gui_handle_error ((kim_ui_gui_context) in_context->tcontext, in_identity, in_error, in_error_message, in_error_description); } else if (in_context->type == kim_ui_type_cli) { - err = kim_ui_cli_display_error ((kim_ui_cli_context) in_context->tcontext, + err = kim_ui_cli_handle_error ((kim_ui_cli_context) in_context->tcontext, in_identity, in_error, in_error_message, in_error_description); +#endif /* LEAN_CLIENT */ } else { err = check_error (KIM_NO_UI_ERR); @@ -249,19 +424,15 @@ kim_error kim_ui_display_error (kim_ui_context *in_context, /* ------------------------------------------------------------------------ */ -void kim_ui_free_string (kim_ui_context *in_context, - char *io_string) +void kim_ui_free_string (kim_ui_context *in_context, + char **io_string) { - kim_error err = KIM_NO_ERROR; - - if (!err && !in_context) { err = check_error (KIM_NULL_PARAMETER_ERR); } - if (!err && !io_string ) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { + if (in_context && io_string && *io_string) { if (in_context->type == kim_ui_type_gui_plugin) { kim_ui_plugin_free_string ((kim_ui_plugin_context) in_context->tcontext, io_string); +#ifndef LEAN_CLIENT } else if (in_context->type == kim_ui_type_gui_builtin) { kim_ui_gui_free_string ((kim_ui_gui_context) in_context->tcontext, io_string); @@ -269,9 +440,8 @@ void kim_ui_free_string (kim_ui_context *in_context, } else if (in_context->type == kim_ui_type_cli) { kim_ui_cli_free_string ((kim_ui_cli_context) in_context->tcontext, io_string); +#endif /* LEAN_CLIENT */ - } else { - err = check_error (KIM_NO_UI_ERR); } } } @@ -285,14 +455,18 @@ kim_error kim_ui_fini (kim_ui_context *io_context) if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { + kim_identity_free (&io_context->identity); + if (io_context->type == kim_ui_type_gui_plugin) { err = kim_ui_plugin_fini ((kim_ui_plugin_context *) &io_context->tcontext); +#ifndef LEAN_CLIENT } else if (io_context->type == kim_ui_type_gui_builtin) { err = kim_ui_gui_fini ((kim_ui_gui_context *) &io_context->tcontext); } else if (io_context->type == kim_ui_type_cli) { err = kim_ui_cli_fini ((kim_ui_cli_context *) &io_context->tcontext); +#endif /* LEAN_CLIENT */ } else { err = check_error (KIM_NO_UI_ERR); diff --git a/src/kim/lib/kim_ui_cli.c b/src/kim/lib/kim_ui_cli.c index d4ba76f58..898b58086 100644 --- a/src/kim/lib/kim_ui_cli.c +++ b/src/kim/lib/kim_ui_cli.c @@ -24,19 +24,112 @@ * or implied warranty. */ +#ifndef LEAN_CLIENT + #include "kim_private.h" +// --------------------------------------------------------------------------- +static kim_error kim_ui_cli_read_string (kim_string *out_string, + kim_boolean in_hide_reply, + const char *in_format, ...) +{ + kim_error err = KIM_NO_ERROR; + krb5_context k5context = NULL; + krb5_prompt prompts[1]; + char prompt_string [BUFSIZ]; + krb5_data reply_data; + char reply_string [BUFSIZ]; + + if (!err && !out_string) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !in_format ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + err = krb5_init_context (&k5context); + } + + if (!err) { + unsigned int count; + va_list args; + + va_start (args, in_format); + count = vsnprintf (prompt_string, sizeof (prompt_string), + in_format, args); + va_end (args); + + if (count > sizeof (prompt_string)) { + kim_debug_printf ("%s(): WARNING! Prompt should be %d characters\n", + __FUNCTION__, count); + prompt_string [sizeof (prompt_string) - 1] = '\0'; + } + } + + if (!err) { + /* Build the prompt structures */ + prompts[0].prompt = prompt_string; + prompts[0].hidden = in_hide_reply; + prompts[0].reply = &reply_data; + prompts[0].reply->data = reply_string; + prompts[0].reply->length = sizeof (reply_string); + + err = krb5_prompter_posix (k5context, NULL, NULL, NULL, 1, prompts); + if (err == KRB5_LIBOS_PWDINTR) { err = check_error (KIM_USER_CANCELED_ERR); } + } + + if (!err) { + err = kim_string_create_from_buffer (out_string, + prompts[0].reply->data, + prompts[0].reply->length); + } + + if (k5context) { krb5_free_context (k5context); } + + return check_error (err); +} /* ------------------------------------------------------------------------ */ kim_error kim_ui_cli_init (kim_ui_cli_context *out_context) { + *out_context = NULL; + return KIM_NO_ERROR; } /* ------------------------------------------------------------------------ */ +kim_error kim_ui_cli_enter_identity (kim_ui_cli_context in_context, + kim_identity *out_identity) +{ + kim_error err = KIM_NO_ERROR; + kim_string enter_identity_string = NULL; + kim_string identity_string = NULL; + + if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + err = kim_os_string_create_localized (&enter_identity_string, + "KLStringEnterPrincipal"); + } + + if (!err) { + err = kim_ui_cli_read_string (&identity_string, + 0, enter_identity_string); + } + + if (!err) { + err = kim_identity_create_from_string (out_identity, identity_string); + } + + kim_string_free (&identity_string); + kim_string_free (&enter_identity_string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + kim_error kim_ui_cli_select_identity (kim_ui_cli_context in_context, kim_selection_hints in_hints, kim_identity *out_identity) @@ -48,6 +141,7 @@ kim_error kim_ui_cli_select_identity (kim_ui_cli_context in_context, if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { + err = kim_ui_cli_enter_identity (in_context, out_identity); } return check_error (err); @@ -58,6 +152,7 @@ kim_error kim_ui_cli_select_identity (kim_ui_cli_context in_context, kim_error kim_ui_cli_auth_prompt (kim_ui_cli_context in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -71,6 +166,55 @@ kim_error kim_ui_cli_auth_prompt (kim_ui_cli_context in_context, /* in_title, in_message or in_description may be NULL */ if (!err) { + if (in_type == kim_prompt_type_password) { + kim_string enter_password_format = NULL; + kim_string identity_string = NULL; + + err = kim_os_string_create_localized (&enter_password_format, + "KLStringEnterPassword"); + + if (!err) { + err = kim_identity_get_display_string (in_identity, + &identity_string); + } + + if (!err) { + err = kim_ui_cli_read_string ((kim_string *) out_reply, + 1, enter_password_format, + identity_string); + } + + kim_string_free (&identity_string); + kim_string_free (&enter_password_format); + + } else { + krb5_context k5context = NULL; + krb5_prompt prompts[1]; + krb5_data reply_data; + char reply_string [BUFSIZ]; + + prompts[0].prompt = (char *) in_description; + prompts[0].hidden = in_hide_reply; + prompts[0].reply = &reply_data; + prompts[0].reply->data = reply_string; + prompts[0].reply->length = sizeof (reply_string); + + err = krb5_init_context (&k5context); + + if (!err) { + err = krb5_prompter_posix (k5context, in_context, in_title, + in_message, 1, prompts); + if (err == KRB5_LIBOS_PWDINTR) { err = check_error (KIM_USER_CANCELED_ERR); } + } + + if (!err) { + err = kim_string_create_from_buffer ((kim_string *) out_reply, + prompts[0].reply->data, + prompts[0].reply->length); + } + + if (k5context) { krb5_free_context (k5context); } + } } return check_error (err); @@ -78,6 +222,88 @@ kim_error kim_ui_cli_auth_prompt (kim_ui_cli_context in_context, /* ------------------------------------------------------------------------ */ +static kim_error kim_ui_cli_ask_change_password (kim_string in_identity_string) +{ + kim_error err = KIM_NO_ERROR; + kim_string ask_change_password = NULL; + kim_string answer_options = NULL; + kim_string yes = NULL; + kim_string no = NULL; + kim_string unknown_response = NULL; + kim_boolean done = 0; + kim_comparison no_comparison, yes_comparison; + + if (!err) { + err = kim_os_string_create_localized (&ask_change_password, + "KLStringPasswordExpired"); + } + + if (!err) { + err = kim_os_string_create_localized (&answer_options, + "KLStringYesOrNoAnswerOptions"); + } + + if (!err) { + err = kim_os_string_create_localized (&yes, + "KLStringYes"); + } + + if (!err) { + err = kim_os_string_create_localized (&no, + "KLStringNo"); + } + + if (!err) { + err = kim_os_string_create_localized (&unknown_response, + "KLStringUnknownResponse"); + } + + while (!err && !done) { + kim_string answer = NULL; + + err = kim_ui_cli_read_string (&answer, + 0, "%s %s", + ask_change_password, answer_options); + + if (!err) { + err = kim_os_string_compare (answer, no, + 1 /* case insensitive */, + &no_comparison); + } + + if (!err && kim_comparison_is_equal_to (no_comparison)) { + err = check_error (KIM_USER_CANCELED_ERR); + } + + if (!err) { + err = kim_os_string_compare (answer, yes, + 1 /* case insensitive */, + &yes_comparison); + } + + if (!err) { + if (kim_comparison_is_equal_to (yes_comparison)) { + done = 1; + } else { + fprintf (stdout, unknown_response, answer); + fprintf (stdout, "\n"); + } + } + + kim_string_free (&answer); + } + + kim_string_free (&ask_change_password); + kim_string_free (&answer_options); + kim_string_free (&yes); + kim_string_free (&no); + kim_string_free (&unknown_response); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + kim_error kim_ui_cli_change_password (kim_ui_cli_context in_context, kim_identity in_identity, kim_boolean in_old_password_expired, @@ -86,6 +312,13 @@ kim_error kim_ui_cli_change_password (kim_ui_cli_context in_context, char **out_verify_password) { kim_error err = KIM_NO_ERROR; + kim_string enter_old_password_format = NULL; + kim_string enter_new_password_format = NULL; + kim_string enter_verify_password_format = NULL; + kim_string identity_string = NULL; + kim_string old_password = NULL; + kim_string new_password = NULL; + kim_string verify_password = NULL; if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); } @@ -94,18 +327,79 @@ kim_error kim_ui_cli_change_password (kim_ui_cli_context in_context, if (!err && !out_verify_password) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { + err = kim_identity_get_display_string (in_identity, &identity_string); + } + + if (!err && in_old_password_expired) { + err = kim_ui_cli_ask_change_password (identity_string); + } + + if (!err) { + err = kim_os_string_create_localized (&enter_old_password_format, + "KLStringEnterOldPassword"); + } + + if (!err) { + err = kim_os_string_create_localized (&enter_new_password_format, + "KLStringEnterNewPassword"); + } + + if (!err) { + err = kim_os_string_create_localized (&enter_verify_password_format, + "KLStringEnterVerifyPassword"); + } + + if (!err) { + err = kim_ui_cli_read_string (&old_password, + 1, enter_old_password_format, + identity_string); + } + + if (!err) { + err = kim_credential_create_for_change_password (&in_context, + in_identity, + old_password); + } + + if (!err) { + err = kim_ui_cli_read_string (&new_password, + 1, enter_new_password_format, + identity_string); + } + + if (!err) { + err = kim_ui_cli_read_string (&verify_password, + 1, enter_new_password_format, + identity_string); + } + + if (!err) { + *out_old_password = (char *) old_password; + old_password = NULL; + *out_new_password = (char *) new_password; + new_password = NULL; + *out_verify_password = (char *) verify_password; + verify_password = NULL; } + kim_string_free (&old_password); + kim_string_free (&new_password); + kim_string_free (&verify_password); + kim_string_free (&identity_string); + kim_string_free (&enter_old_password_format); + kim_string_free (&enter_new_password_format); + kim_string_free (&enter_verify_password_format); + return check_error (err); } /* ------------------------------------------------------------------------ */ -kim_error kim_ui_cli_display_error (kim_ui_cli_context in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description) +kim_error kim_ui_cli_handle_error (kim_ui_cli_context in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description) { kim_error err = KIM_NO_ERROR; @@ -114,6 +408,7 @@ kim_error kim_ui_cli_display_error (kim_ui_cli_context in_context, if (!err && !in_error_description) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { + fprintf (stdout, "%s: %s\n", in_error_message, in_error_description); } return check_error (err); @@ -121,22 +416,21 @@ kim_error kim_ui_cli_display_error (kim_ui_cli_context in_context, /* ------------------------------------------------------------------------ */ -void kim_ui_cli_free_string (kim_ui_cli_context in_context, - char *io_string) +void kim_ui_cli_free_string (kim_ui_cli_context in_context, + char **io_string) { - kim_error err = KIM_NO_ERROR; - - if (!err && !in_context) { err = check_error (KIM_NULL_PARAMETER_ERR); } - if (!err && !io_string ) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - kim_string_free ((kim_string *) io_string); - } + kim_string_free ((kim_string *) io_string); } /* ------------------------------------------------------------------------ */ kim_error kim_ui_cli_fini (kim_ui_cli_context *io_context) { + if (io_context && *io_context) { + kim_credential_free (io_context); + } + return KIM_NO_ERROR; } + +#endif /* LEAN_CLIENT */ diff --git a/src/kim/lib/kim_ui_cli_private.h b/src/kim/lib/kim_ui_cli_private.h index cc5ff7f0c..89011aa3a 100644 --- a/src/kim/lib/kim_ui_cli_private.h +++ b/src/kim/lib/kim_ui_cli_private.h @@ -27,13 +27,18 @@ #ifndef KIM_UI_CLI_PRIVATE_H #define KIM_UI_CLI_PRIVATE_H +#ifndef LEAN_CLIENT + #include <kim/kim.h> -typedef void *kim_ui_cli_context; +typedef kim_credential kim_ui_cli_context; kim_error kim_ui_cli_init (kim_ui_cli_context *out_context); +kim_error kim_ui_cli_enter_identity (kim_ui_cli_context in_context, + kim_identity *out_identity); + kim_error kim_ui_cli_select_identity (kim_ui_cli_context in_context, kim_selection_hints in_hints, kim_identity *out_identity); @@ -41,6 +46,7 @@ kim_error kim_ui_cli_select_identity (kim_ui_cli_context in_context, kim_error kim_ui_cli_auth_prompt (kim_ui_cli_context in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -53,15 +59,17 @@ kim_error kim_ui_cli_change_password (kim_ui_cli_context in_context, char **out_new_password, char **out_verify_password); -kim_error kim_ui_cli_display_error (kim_ui_cli_context in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description); +kim_error kim_ui_cli_handle_error (kim_ui_cli_context in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description); -void kim_ui_cli_free_string (kim_ui_cli_context in_context, - char *io_string); +void kim_ui_cli_free_string (kim_ui_cli_context in_context, + char **io_string); kim_error kim_ui_cli_fini (kim_ui_cli_context *io_context); +#endif /* LEAN_CLIENT */ + #endif /* KIM_UI_CLI_PRIVATE_H */ diff --git a/src/kim/lib/kim_ui_gui.c b/src/kim/lib/kim_ui_gui.c index 56d0401fa..9eb41e457 100644 --- a/src/kim/lib/kim_ui_gui.c +++ b/src/kim/lib/kim_ui_gui.c @@ -24,6 +24,8 @@ * or implied warranty. */ +#ifndef LEAN_CLIENT + #include "kim_private.h" @@ -95,6 +97,22 @@ kim_error kim_ui_gui_init (kim_ui_gui_context *out_context) /* ------------------------------------------------------------------------ */ +kim_error kim_ui_gui_enter_identity (kim_ui_gui_context in_context, + kim_identity *out_identity) +{ + kim_error err = KIM_NO_ERROR; + + if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + kim_error kim_ui_gui_select_identity (kim_ui_gui_context in_context, kim_selection_hints in_hints, kim_identity *out_identity) @@ -116,6 +134,7 @@ kim_error kim_ui_gui_select_identity (kim_ui_gui_context in_context, kim_error kim_ui_gui_auth_prompt (kim_ui_gui_context in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -159,11 +178,11 @@ kim_error kim_ui_gui_change_password (kim_ui_gui_context in_context, /* ------------------------------------------------------------------------ */ -kim_error kim_ui_gui_display_error (kim_ui_gui_context in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description) +kim_error kim_ui_gui_handle_error (kim_ui_gui_context in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description) { kim_error err = KIM_NO_ERROR; @@ -179,17 +198,10 @@ kim_error kim_ui_gui_display_error (kim_ui_gui_context in_context, /* ------------------------------------------------------------------------ */ -void kim_ui_gui_free_string (kim_ui_gui_context in_context, - char *io_string) +void kim_ui_gui_free_string (kim_ui_gui_context in_context, + char **io_string) { - kim_error err = KIM_NO_ERROR; - - if (!err && !in_context) { err = check_error (KIM_NULL_PARAMETER_ERR); } - if (!err && !io_string ) { err = check_error (KIM_NULL_PARAMETER_ERR); } - - if (!err) { - kim_string_free ((kim_string *) io_string); - } + kim_string_free ((kim_string *) io_string); } /* ------------------------------------------------------------------------ */ @@ -210,3 +222,5 @@ kim_error kim_ui_gui_fini (kim_ui_gui_context *io_context) return check_error (err); } + +#endif /* LEAN_CLIENT */ diff --git a/src/kim/lib/kim_ui_gui_private.h b/src/kim/lib/kim_ui_gui_private.h index 9ddffb19a..b6d2ebd52 100644 --- a/src/kim/lib/kim_ui_gui_private.h +++ b/src/kim/lib/kim_ui_gui_private.h @@ -27,6 +27,8 @@ #ifndef KIM_UI_GUI_PRIVATE_H #define KIM_UI_GUI_PRIVATE_H +#ifndef LEAN_CLIENT + #include <kim/kim.h> struct kim_ui_gui_context; @@ -35,6 +37,9 @@ typedef struct kim_ui_gui_context *kim_ui_gui_context; kim_error kim_ui_gui_init (kim_ui_gui_context *out_context); +kim_error kim_ui_gui_enter_identity (kim_ui_gui_context in_context, + kim_identity *out_identity); + kim_error kim_ui_gui_select_identity (kim_ui_gui_context in_context, kim_selection_hints in_hints, kim_identity *out_identity); @@ -42,6 +47,7 @@ kim_error kim_ui_gui_select_identity (kim_ui_gui_context in_context, kim_error kim_ui_gui_auth_prompt (kim_ui_gui_context in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -54,15 +60,17 @@ kim_error kim_ui_gui_change_password (kim_ui_gui_context in_context, char **out_new_password, char **out_verify_password); -kim_error kim_ui_gui_display_error (kim_ui_gui_context in_context, +kim_error kim_ui_gui_handle_error (kim_ui_gui_context in_context, kim_identity in_identity, kim_error in_error, kim_string in_error_message, kim_string in_error_description); -void kim_ui_gui_free_string (kim_ui_gui_context in_context, - char *io_string); +void kim_ui_gui_free_string (kim_ui_gui_context in_context, + char **io_string); kim_error kim_ui_gui_fini (kim_ui_gui_context *io_context); +#endif /* LEAN_CLIENT */ + #endif /* KIM_UI_GUI_PRIVATE_H */ diff --git a/src/kim/lib/kim_ui_plugin.c b/src/kim/lib/kim_ui_plugin.c index 5c5cc26eb..f1b5db923 100644 --- a/src/kim/lib/kim_ui_plugin.c +++ b/src/kim/lib/kim_ui_plugin.c @@ -156,6 +156,24 @@ kim_error kim_ui_plugin_init (kim_ui_plugin_context *out_context) /* ------------------------------------------------------------------------ */ +kim_error kim_ui_plugin_enter_identity (kim_ui_plugin_context in_context, + kim_identity *out_identity) +{ + kim_error err = KIM_NO_ERROR; + + if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + err = in_context->ftable->enter_identity (in_context->plugin_context, + out_identity); + } + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + kim_error kim_ui_plugin_select_identity (kim_ui_plugin_context in_context, kim_selection_hints in_hints, kim_identity *out_identity) @@ -180,6 +198,7 @@ kim_error kim_ui_plugin_select_identity (kim_ui_plugin_context in_context, kim_error kim_ui_plugin_auth_prompt (kim_ui_plugin_context in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -196,6 +215,7 @@ kim_error kim_ui_plugin_auth_prompt (kim_ui_plugin_context in_context, err = in_context->ftable->auth_prompt (in_context->plugin_context, in_identity, in_type, + in_hide_reply, in_title, in_message, in_description, @@ -236,11 +256,11 @@ kim_error kim_ui_plugin_change_password (kim_ui_plugin_context in_context, /* ------------------------------------------------------------------------ */ -kim_error kim_ui_plugin_display_error (kim_ui_plugin_context in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description) +kim_error kim_ui_plugin_handle_error (kim_ui_plugin_context in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description) { kim_error err = KIM_NO_ERROR; @@ -249,11 +269,11 @@ kim_error kim_ui_plugin_display_error (kim_ui_plugin_context in_context, if (!err && !in_error_description) { err = check_error (KIM_NULL_PARAMETER_ERR); } if (!err) { - err = in_context->ftable->display_error (in_context->plugin_context, - in_identity, - in_error, - in_error_message, - in_error_description); + err = in_context->ftable->handle_error (in_context->plugin_context, + in_identity, + in_error, + in_error_message, + in_error_description); } return check_error (err); @@ -261,8 +281,8 @@ kim_error kim_ui_plugin_display_error (kim_ui_plugin_context in_context, /* ------------------------------------------------------------------------ */ -void kim_ui_plugin_free_string (kim_ui_plugin_context in_context, - char *io_string) +void kim_ui_plugin_free_string (kim_ui_plugin_context in_context, + char **io_string) { kim_error err = KIM_NO_ERROR; diff --git a/src/kim/lib/kim_ui_plugin_private.h b/src/kim/lib/kim_ui_plugin_private.h index b48166516..c39447df0 100644 --- a/src/kim/lib/kim_ui_plugin_private.h +++ b/src/kim/lib/kim_ui_plugin_private.h @@ -35,6 +35,9 @@ typedef struct kim_ui_plugin_context *kim_ui_plugin_context; kim_error kim_ui_plugin_init (kim_ui_plugin_context *out_context); +kim_error kim_ui_plugin_enter_identity (kim_ui_plugin_context in_context, + kim_identity *out_identity); + kim_error kim_ui_plugin_select_identity (kim_ui_plugin_context in_context, kim_selection_hints in_hints, kim_identity *out_identity); @@ -42,6 +45,7 @@ kim_error kim_ui_plugin_select_identity (kim_ui_plugin_context in_context, kim_error kim_ui_plugin_auth_prompt (kim_ui_plugin_context in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -54,14 +58,14 @@ kim_error kim_ui_plugin_change_password (kim_ui_plugin_context in_context, char **out_new_password, char **out_verify_password); -kim_error kim_ui_plugin_display_error (kim_ui_plugin_context in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description); +kim_error kim_ui_plugin_handle_error (kim_ui_plugin_context in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description); -void kim_ui_plugin_free_string (kim_ui_plugin_context in_context, - char *io_string); +void kim_ui_plugin_free_string (kim_ui_plugin_context in_context, + char **io_string); kim_error kim_ui_plugin_fini (kim_ui_plugin_context *io_context); diff --git a/src/kim/lib/kim_ui_private.h b/src/kim/lib/kim_ui_private.h index 21183ef2b..817a4b29f 100644 --- a/src/kim/lib/kim_ui_private.h +++ b/src/kim/lib/kim_ui_private.h @@ -36,22 +36,41 @@ enum kim_ui_type { kim_ui_type_none }; +enum kim_ui_error_type { + kim_ui_error_type_authentication, + kim_ui_error_type_change_password, + kim_ui_error_type_selection, + kim_ui_error_type_generic +}; + /* declare struct on stack. Deep contents will be freed by kim_ui_fini. */ typedef struct kim_ui_context { enum kim_ui_type type; void *tcontext; + kim_identity identity; } kim_ui_context; +krb5_error_code kim_ui_prompter (krb5_context in_krb5_context, + void *in_context, + const char *in_name, + const char *in_banner, + int in_num_prompts, + krb5_prompt in_prompts[]); + kim_error kim_ui_init (kim_ui_context *io_context); +kim_error kim_ui_enter_identity (kim_ui_context *in_context, + kim_identity *out_identity); + kim_error kim_ui_select_identity (kim_ui_context *in_context, kim_selection_hints in_hints, - kim_identity *out_identity); + kim_identity *out_identity); kim_error kim_ui_auth_prompt (kim_ui_context *in_context, kim_identity in_identity, kim_prompt_type in_type, + kim_boolean in_hide_reply, kim_string in_title, kim_string in_message, kim_string in_description, @@ -64,14 +83,20 @@ kim_error kim_ui_change_password (kim_ui_context *in_context, char **out_new_password, char **out_verify_password); -kim_error kim_ui_display_error (kim_ui_context *in_context, - kim_identity in_identity, - kim_error in_error, - kim_string in_error_message, - kim_string in_error_description); +/* Helper function */ +kim_error kim_ui_handle_kim_error (kim_ui_context *in_context, + kim_identity in_identity, + enum kim_ui_error_type in_type, + kim_error in_error); + +kim_error kim_ui_handle_error (kim_ui_context *in_context, + kim_identity in_identity, + kim_error in_error, + kim_string in_error_message, + kim_string in_error_description); void kim_ui_free_string (kim_ui_context *in_context, - char *io_string); + char **io_string); kim_error kim_ui_fini (kim_ui_context *io_context); diff --git a/src/kim/lib/mac/kim_os_library.c b/src/kim/lib/mac/kim_os_library.c index 43dd3d5c7..4abe13e20 100644 --- a/src/kim/lib/mac/kim_os_library.c +++ b/src/kim/lib/mac/kim_os_library.c @@ -95,6 +95,7 @@ kim_error kim_os_library_unlock_for_bundle_lookup (void) kim_ui_environment kim_os_library_get_ui_environment (void) { +#ifndef LEAN_CLIENT kipc_session_attributes_t attributes = kipc_session_get_attributes (); if (attributes & kkipc_session_caller_uses_gui) { @@ -106,6 +107,7 @@ kim_ui_environment kim_os_library_get_ui_environment (void) } kim_debug_printf ("kim_os_library_get_ui_environment(): no way to talk to the user."); +#endif return KIM_UI_ENVIRONMENT_NONE; } diff --git a/src/kim/lib/mac/kim_os_string.c b/src/kim/lib/mac/kim_os_string.c index 9cd8e0559..fab5ed8fe 100644 --- a/src/kim/lib/mac/kim_os_string.c +++ b/src/kim/lib/mac/kim_os_string.c @@ -33,8 +33,7 @@ static kim_error kim_os_string_for_key_in_bundle (CFBundleRef in_bundle, CFStringRef in_key, kim_string *out_string) { - kim_error lock_err = kim_os_library_lock_for_bundle_lookup (); - kim_error err = lock_err; + kim_error err = KIM_NO_ERROR; kim_string string = NULL; if (!err && !in_bundle ) { err = check_error (KIM_NULL_PARAMETER_ERR); } @@ -100,9 +99,7 @@ static kim_error kim_os_string_for_key_in_bundle (CFBundleRef in_bundle, } kim_string_free (&string); - - if (!lock_err) { kim_os_library_unlock_for_bundle_lookup (); } - + return check_error (err); } @@ -110,10 +107,40 @@ static kim_error kim_os_string_for_key_in_bundle (CFBundleRef in_bundle, /* ------------------------------------------------------------------------ */ +kim_error kim_os_string_create_localized (kim_string *out_string, + kim_string in_string) +{ + kim_error err = KIM_NO_ERROR; + kim_string string = NULL; + + if (!err && !out_string) { err = check_error (KIM_NULL_PARAMETER_ERR); } + if (!err && !in_string ) { err = check_error (KIM_NULL_PARAMETER_ERR); } + + if (!err) { + err = kim_os_string_create_for_key (&string, in_string); + } + + if (!err && !string) { + err = kim_string_copy (&string, in_string); + } + + if (!err) { + *out_string = string; + string = NULL; + } + + kim_string_free (&string); + + return check_error (err); +} + +/* ------------------------------------------------------------------------ */ + kim_error kim_os_string_create_for_key (kim_string *out_string, kim_string in_key_string) { - kim_error err = KIM_NO_ERROR; + kim_error lock_err = kim_os_library_lock_for_bundle_lookup (); + kim_error err = lock_err; CFStringRef key = NULL; kim_string string = NULL; @@ -148,6 +175,8 @@ kim_error kim_os_string_create_for_key (kim_string *out_string, kim_string_free (&string); if (key) { CFRelease (key); } + if (!lock_err) { kim_os_library_unlock_for_bundle_lookup (); } + return check_error (err); } @@ -222,6 +251,7 @@ kim_error kim_os_string_get_cfstring (kim_string in_string, kim_error kim_os_string_compare (kim_string in_string, kim_string in_compare_to_string, + kim_boolean in_case_insensitive, kim_comparison *out_comparison) { kim_error err = KIM_NO_ERROR; @@ -243,8 +273,13 @@ kim_error kim_os_string_compare (kim_string in_string, } if (!err) { + CFOptionFlags options = (in_case_insensitive ? + 1 : kCFCompareCaseInsensitive); + /* Returned CFComparisonResult is compatible with kim_comparison_t */ - *out_comparison = CFStringCompare (cfstring, compare_to_cfstring, 0); + *out_comparison = CFStringCompare (cfstring, + compare_to_cfstring, + options); } if (cfstring ) { CFRelease (cfstring); } |