|
GnuCash 2.4.99
|
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 }
1.7.4