GnuCash 2.3.0
Files | Functions
GUIUtility
GUI

Files

file  gnc-keyring.h
 

Functions to save and retrieve passwords.


Functions

void gnc_keyring_set_password (const gchar *access_method, const gchar *server, guint32 port, const gchar *service, const gchar *user, const gchar *password)
gboolean gnc_keyring_get_password (GtkWidget *parent, const gchar *access_method, const gchar *server, guint32 port, const gchar *service, gchar **user, gchar **password)

Function Documentation

gboolean gnc_keyring_get_password ( GtkWidget *  parent,
const gchar *  access_method,
const gchar *  server,
guint32  port,
const gchar *  service,
gchar **  user,
gchar **  password 
)

Attempt to retrieve a password to connect to a remote service. This is deliberately generic: the remote service can be a database, website, anything.

If a trusted keystore infrastructure is found (such as the Gnome's keyring or Mac OS X' keychain) this infrastructure will be queried first.

If no such infrastructure is available or the query didn't return a valid result, the user will be prompted for his password.

Warning:
When the user is prompted for a password, he can also change the username. So whenever you call this function, read both the username and password values before you continue !
Parameters:
parentUsed to transition from in case the user is prompted for a password.
access_methodService type the user attempts to access. Can things like 'mysql', 'postgres' and so on.
serverServer the user wishes to connect to.
portPort the service listens on. If set to 0, it will be ignored in the search for a password.
serviceThe service the user wishes to access on the server. This can be a database name or a path.
userThe user name to access the service. Remember, although you pass it to search for the password, it can have changed when the function returns.
passwordThe password to access the service.
Returns:
a boolean indicating whether or not a valid password has been retrieved. The function will return FALSE when the user explicitly cancels the password dialog or if it wasn't called properly. Otherwise it wil return TRUE.

access_method, server, port, service and user will be the parameters passed to the trusted keystore (if available) to find the unique password for this service.

Definition at line 99 of file gnc-keyring.c.

{
    gboolean password_found = FALSE;
#ifdef HAVE_GNOME_KEYRING
    GnomeKeyringResult  gkr_result;
    GList *found_list = NULL;
    GnomeKeyringNetworkPasswordData *found;
#endif
#ifdef HAVE_OSX_KEYCHAIN
    void *password_data;
    UInt32 password_length;
    OSStatus status;
#endif

    g_return_val_if_fail (user != NULL, FALSE);
    g_return_val_if_fail (password != NULL, FALSE);

    *password = NULL;

#ifdef HAVE_GNOME_KEYRING
    gkr_result = gnome_keyring_find_network_password_sync
                 ( *user, NULL, server, service,
                   access_method, NULL, port, &found_list );

    if (gkr_result == GNOME_KEYRING_RESULT_OK)
    {
        found = (GnomeKeyringNetworkPasswordData *) found_list->data;
        if (found->password)
            *password = g_strdup(found->password);
        password_found = TRUE;
    }
    else
        PWARN ("Gnome-keyring access failed: %s.",
               gnome_keyring_result_to_message(gkr_result));

    gnome_keyring_network_password_list_free(found_list);
#endif /* HAVE_GNOME_KEYRING */

#ifdef HAVE_OSX_KEYCHAIN
    /* mysql and postgres aren't valid protocols on Mac OS X.
     * So we use the security domain parameter to allow us to
     * distinguish between these two.
     */
    if (*user != NULL)
    {
        status = SecKeychainFindInternetPassword( NULL,
                 strlen(server), server,
                 strlen(access_method), access_method,
                 strlen(*user), *user,
                 strlen(service), service,
                 port,
                 kSecProtocolTypeAny,
                 kSecAuthenticationTypeDefault,
                 &password_length, &password_data,
                 NULL);

        if ( status == noErr )
        {
            *password = g_strndup(password_data, password_length);
            password_found = TRUE;
            SecKeychainItemFreeContent(NULL, password_data);
        }
        else
        {
            CFStringRef osx_resultstring = SecCopyErrorMessageString( status, NULL );
            const gchar *resultstring = CFStringGetCStringPtr(osx_resultstring,
                                        GetApplicationTextEncoding());
            PWARN ( "OS X keychain error: %s", resultstring );
            CFRelease ( osx_resultstring );
        }
    }
#endif /* HAVE_OSX_KEYCHAIN */

    if ( !password_found )
    {
        /* If we got here, either no proper password store is
         * available on this system, or we couldn't retrieve
         * a password from it. In both cases, just ask the user
         * to enter one
         */
        gchar *db_path, *heading;

        if ( port == 0 )
            db_path = g_strdup_printf ( "%s://%s/%s", access_method, server, service );
        else
            db_path = g_strdup_printf ( "%s://%s:%d/%s", access_method, server, port, service );
        heading = g_strdup_printf ( /* Translators: %s is a path to a database or any other url,
                 like mysql://user@server.somewhere/somedb, http://www.somequotes.com/thequotes */
                      _("Enter a user name and password to connect to: %s"),
                      db_path );

        password_found = gnc_get_username_password ( parent, heading,
                         *user, NULL,
                         user, password );
        g_free ( db_path );
        g_free ( heading );

        if ( password_found )
        {
            /* User entered new user/password information
             * Let's try to add it to a password store.
             */
            gchar *newuser = g_strdup( *user );
            gchar *newpassword = g_strdup( *password );
            gnc_keyring_set_password ( access_method,
                                       server,
                                       port,
                                       service,
                                       newuser,
                                       newpassword );
            g_free ( newuser );
            g_free ( newpassword );
        }
    }

    return password_found;
}
void gnc_keyring_set_password ( const gchar *  access_method,
const gchar *  server,
guint32  port,
const gchar *  service,
const gchar *  user,
const gchar *  password 
)

Attempt to store a password in some trusted keystore. At this point that can be Gnome's keyring or Mac OS X' keychain. If no such keystore is available, this function does nothing.

All the parameters passed (except for the password) will be used to create a unique key, so the password can later be retrieved again with the same parameters.

Parameters:
access_methodService type the user attempts to access. Can things like 'mysql', 'postgres' and so on.
serverServer the user wishes to connect to.
portPort the service listens on. If set to 0, it will be ignored in the search for a password.
serviceThe service the user wishes to access on the server. This can be a database name or a path.
userThe username to access the service.
passwordThe password to access the service.

Definition at line 41 of file gnc-keyring.c.

{

#ifdef HAVE_GNOME_KEYRING
    GnomeKeyringResult  gkr_result;
    guint32 item_id = 0;

    gkr_result = gnome_keyring_set_network_password_sync
                 (NULL, user, NULL, server, service,
                  access_method, NULL, port, password, &item_id);

    if (gkr_result != GNOME_KEYRING_RESULT_OK)
    {
        PWARN ("Gnome-keyring error: %s",
               gnome_keyring_result_to_message(gkr_result));
        PWARN ("The user will be prompted for a password again next time.");
    }
#endif /* HAVE_GNOME_KEYRING */
#ifdef HAVE_OSX_KEYCHAIN
    OSStatus status;
    SecKeychainItemRef *itemRef = NULL;

    /* mysql and postgres aren't valid protocols on Mac OS X.
     * So we use the security domain parameter to allow us to
     * distinguish between these two.
     */
    // FIXME I'm not sure this works if a password was already in the keychain
    //       I may have to do a lookup first and if it exists, run some update
    //       update function instead
    status = SecKeychainAddInternetPassword ( NULL, /* keychain */
             strlen(server), server,                /* servername */
             strlen(access_method), access_method,  /* securitydomain */
             strlen(user), user,                    /* acountname */
             strlen(service), service,              /* path */
             port,                                  /* port */
             kSecProtocolTypeAny,                   /* protocol */
             kSecAuthenticationTypeDefault,         /* auth type */
             strlen(password), password,            /* passworddata */
             itemRef );

    if ( status != noErr )
    {
        CFStringRef osx_resultstring = SecCopyErrorMessageString( status, NULL );
        const gchar *resultstring = CFStringGetCStringPtr(osx_resultstring,
                                    GetApplicationTextEncoding());
        PWARN ( "OS X keychain error: %s", resultstring );
        PWARN ( "The user will be prompted for a password again next time." );
        CFRelease ( osx_resultstring );
    }
#endif /* HAVE_OSX_KEYCHAIN */
}
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines