GnuCash 2.4.99
gnc-keyring.c
00001 /*
00002  * gnc-keyring.c -- utility functions to store and retrieve passwords.
00003  *
00004  * Copyright (C) 2010 Geert Janssens <janssens.geert@telenet.be>
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License as
00008  * published by the Free Software Foundation; either version 2 of
00009  * the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, contact:
00018  *
00019  * Free Software Foundation           Voice:  +1-617-542-5942
00020  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
00021  * Boston, MA  02110-1301,  USA       gnu@gnu.org
00022  */
00023 
00024 #include "config.h"
00025 #include <glib/gi18n.h>
00026 #include "qof.h"
00027 #include "gnc-ui.h"
00028 #include "gnc-keyring.h"
00029 #ifdef HAVE_GNOME_KEYRING
00030 #include <gnome-keyring.h>
00031 #endif
00032 #ifdef HAVE_OSX_KEYCHAIN
00033 #include <Security/Security.h>
00034 #include <CoreFoundation/CoreFoundation.h>
00035 #include <Carbon/Carbon.h>
00036 #endif
00037 
00038 /* This static indicates the debugging module that this .o belongs to. */
00039 static QofLogModule log_module = GNC_MOD_GUI;
00040 
00041 void gnc_keyring_set_password (const gchar *access_method,
00042                                const gchar *server,
00043                                guint32 port,
00044                                const gchar *service,
00045                                const gchar *user,
00046                                const gchar* password)
00047 {
00048 
00049 #ifdef HAVE_GNOME_KEYRING
00050     GnomeKeyringResult  gkr_result;
00051     guint32 item_id = 0;
00052 
00053     gkr_result = gnome_keyring_set_network_password_sync
00054                  (NULL, user, NULL, server, service,
00055                   access_method, NULL, port, password, &item_id);
00056 
00057     if (gkr_result != GNOME_KEYRING_RESULT_OK)
00058     {
00059         PWARN ("Gnome-keyring error: %s",
00060                gnome_keyring_result_to_message(gkr_result));
00061         PWARN ("The user will be prompted for a password again next time.");
00062     }
00063 #endif /* HAVE_GNOME_KEYRING */
00064 #ifdef HAVE_OSX_KEYCHAIN
00065     OSStatus status;
00066     SecKeychainItemRef *itemRef = NULL;
00067 
00068     /* mysql and postgres aren't valid protocols on Mac OS X.
00069      * So we use the security domain parameter to allow us to
00070      * distinguish between these two.
00071      */
00072     // FIXME I'm not sure this works if a password was already in the keychain
00073     //       I may have to do a lookup first and if it exists, run some update
00074     //       update function instead
00075     status = SecKeychainAddInternetPassword ( NULL, /* keychain */
00076              strlen(server), server,                /* servername */
00077              strlen(access_method), access_method,  /* securitydomain */
00078              strlen(user), user,                    /* acountname */
00079              strlen(service), service,              /* path */
00080              port,                                  /* port */
00081              kSecProtocolTypeAny,                   /* protocol */
00082              kSecAuthenticationTypeDefault,         /* auth type */
00083              strlen(password), password,            /* passworddata */
00084              itemRef );
00085 
00086     if ( status != noErr )
00087     {
00088         CFStringRef osx_resultstring = SecCopyErrorMessageString( status, NULL );
00089         const gchar *resultstring = CFStringGetCStringPtr(osx_resultstring,
00090                                     GetApplicationTextEncoding());
00091         PWARN ( "OS X keychain error: %s", resultstring );
00092         PWARN ( "The user will be prompted for a password again next time." );
00093         CFRelease ( osx_resultstring );
00094     }
00095 #endif /* HAVE_OSX_KEYCHAIN */
00096 }
00097 
00098 
00099 gboolean gnc_keyring_get_password ( GtkWidget *parent,
00100                                     const gchar *access_method,
00101                                     const gchar *server,
00102                                     guint32 port,
00103                                     const gchar *service,
00104                                     gchar **user,
00105                                     gchar **password)
00106 {
00107     gboolean password_found = FALSE;
00108 #ifdef HAVE_GNOME_KEYRING
00109     GnomeKeyringResult  gkr_result;
00110     GList *found_list = NULL;
00111     GnomeKeyringNetworkPasswordData *found;
00112 #endif
00113 #ifdef HAVE_OSX_KEYCHAIN
00114     void *password_data;
00115     UInt32 password_length;
00116     OSStatus status;
00117 #endif
00118 
00119     g_return_val_if_fail (user != NULL, FALSE);
00120     g_return_val_if_fail (password != NULL, FALSE);
00121 
00122     *password = NULL;
00123 
00124 #ifdef HAVE_GNOME_KEYRING
00125     gkr_result = gnome_keyring_find_network_password_sync
00126                  ( *user, NULL, server, service,
00127                    access_method, NULL, port, &found_list );
00128 
00129     if (gkr_result == GNOME_KEYRING_RESULT_OK)
00130     {
00131         found = (GnomeKeyringNetworkPasswordData *) found_list->data;
00132         if (found->password)
00133             *password = g_strdup(found->password);
00134         password_found = TRUE;
00135     }
00136     else
00137         PWARN ("Gnome-keyring access failed: %s.",
00138                gnome_keyring_result_to_message(gkr_result));
00139 
00140     gnome_keyring_network_password_list_free(found_list);
00141 #endif /* HAVE_GNOME_KEYRING */
00142 
00143 #ifdef HAVE_OSX_KEYCHAIN
00144     /* mysql and postgres aren't valid protocols on Mac OS X.
00145      * So we use the security domain parameter to allow us to
00146      * distinguish between these two.
00147      */
00148     if (*user != NULL)
00149     {
00150         status = SecKeychainFindInternetPassword( NULL,
00151                  strlen(server), server,
00152                  strlen(access_method), access_method,
00153                  strlen(*user), *user,
00154                  strlen(service), service,
00155                  port,
00156                  kSecProtocolTypeAny,
00157                  kSecAuthenticationTypeDefault,
00158                  &password_length, &password_data,
00159                  NULL);
00160 
00161         if ( status == noErr )
00162         {
00163             *password = g_strndup(password_data, password_length);
00164             password_found = TRUE;
00165             SecKeychainItemFreeContent(NULL, password_data);
00166         }
00167         else
00168         {
00169             CFStringRef osx_resultstring = SecCopyErrorMessageString( status, NULL );
00170             const gchar *resultstring = CFStringGetCStringPtr(osx_resultstring,
00171                                         GetApplicationTextEncoding());
00172             PWARN ( "OS X keychain error: %s", resultstring );
00173             CFRelease ( osx_resultstring );
00174         }
00175     }
00176 #endif /* HAVE_OSX_KEYCHAIN */
00177 
00178     if ( !password_found )
00179     {
00180         /* If we got here, either no proper password store is
00181          * available on this system, or we couldn't retrieve
00182          * a password from it. In both cases, just ask the user
00183          * to enter one
00184          */
00185         gchar *db_path, *heading;
00186 
00187         if ( port == 0 )
00188             db_path = g_strdup_printf ( "%s://%s/%s", access_method, server, service );
00189         else
00190             db_path = g_strdup_printf ( "%s://%s:%d/%s", access_method, server, port, service );
00191         heading = g_strdup_printf ( /* Translators: %s is a path to a database or any other url,
00192                  like mysql://user@server.somewhere/somedb, http://www.somequotes.com/thequotes */
00193                       _("Enter a user name and password to connect to: %s"),
00194                       db_path );
00195 
00196         password_found = gnc_get_username_password ( parent, heading,
00197                          *user, NULL,
00198                          user, password );
00199         g_free ( db_path );
00200         g_free ( heading );
00201 
00202         if ( password_found )
00203         {
00204             /* User entered new user/password information
00205              * Let's try to add it to a password store.
00206              */
00207             gchar *newuser = g_strdup( *user );
00208             gchar *newpassword = g_strdup( *password );
00209             gnc_keyring_set_password ( access_method,
00210                                        server,
00211                                        port,
00212                                        service,
00213                                        newuser,
00214                                        newpassword );
00215             g_free ( newuser );
00216             g_free ( newpassword );
00217         }
00218     }
00219 
00220     return password_found;
00221 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines