GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-keyring.c
1 /*
2  * gnc-keyring.c -- utility functions to store and retrieve passwords.
3  *
4  * Copyright (C) 2010 Geert Janssens <[email protected]>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, contact:
18  *
19  * Free Software Foundation Voice: +1-617-542-5942
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
21  * Boston, MA 02110-1301, USA [email protected]
22  */
23 
24 #include "config.h"
25 #include <glib/gi18n.h>
26 #include "qof.h"
27 #include "gnc-ui.h"
28 #include "gnc-keyring.h"
29 #ifdef HAVE_LIBSECRET
30 #include <libsecret/secret.h>
31 #endif
32 #if HAVE_GNOME_KEYRING
33 #define GNOME_KEYRING_DEPRECATED
34 #define GNOME_KEYRING_DEPRECATED_FOR(x)
35 #include <gnome-keyring.h>
36 #endif
37 #ifdef HAVE_OSX_KEYCHAIN
38 #include <Security/Security.h>
39 #include <CoreFoundation/CoreFoundation.h>
40 #include <Carbon/Carbon.h>
41 #endif
42 
43 /* This static indicates the debugging module that this .o belongs to. */
44 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI;
45 
46 #ifdef HAVE_LIBSECRET
47 const SecretSchema* gnucash_get_secret_schema(void) G_GNUC_CONST;
48 const SecretSchema* gnucash_get_secret_schema(void)
49 {
50  static const SecretSchema secret_schema = {
51  "org.gnucash.password", SECRET_SCHEMA_NONE,
52  {
53  { "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING },
54  { "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
55  { "port", SECRET_SCHEMA_ATTRIBUTE_INTEGER },
56  { "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
57  { "NULL", 0 },
58  }
59  };
60 
61  return &secret_schema;
62 }
63 
64 #define SECRET_SCHEMA_GNUCASH gnucash_get_secret_schema()
65 #endif
66 
67 void gnc_keyring_set_password (const gchar *access_method,
68  const gchar *server,
69  guint32 port,
70  const gchar *service,
71  const gchar *user,
72  const gchar* password)
73 {
74 #ifdef HAVE_LIBSECRET
75  GError* error = NULL;
76  gchar* label = NULL;
77 
78  label = g_strdup_printf("GnuCash password for %s://%s@%s", access_method, user, server);
79 
80  secret_password_store_sync (SECRET_SCHEMA_GNUCASH, SECRET_COLLECTION_DEFAULT,
81  label, password, NULL, &error,
82  "protocol", access_method,
83  "server", server,
84  "port", port,
85  "user", user,
86  NULL);
87 
88  g_free(label);
89 
90  if (error != NULL)
91  {
92  PWARN ("libsecret error: %s", error->message);
93  PWARN ("The user will be prompted for a password again next time.");
94  g_error_free(error);
95  }
96 #elif HAVE_GNOME_KEYRING
97  GnomeKeyringResult gkr_result;
98  guint32 item_id = 0;
99 
100  gkr_result = gnome_keyring_set_network_password_sync
101  (NULL, user, NULL, server, service,
102  access_method, NULL, port, password, &item_id);
103 
104  if (gkr_result != GNOME_KEYRING_RESULT_OK)
105  {
106  PWARN ("Gnome-keyring error: %s",
107  gnome_keyring_result_to_message(gkr_result));
108  PWARN ("The user will be prompted for a password again next time.");
109  }
110 #endif /* HAVE_GNOME_KEYRING */
111 #ifdef HAVE_OSX_KEYCHAIN
112  OSStatus status;
113  SecKeychainItemRef *itemRef = NULL;
114 
115  /* mysql and postgres aren't valid protocols on Mac OS X.
116  * So we use the security domain parameter to allow us to
117  * distinguish between these two.
118  */
119  // FIXME I'm not sure this works if a password was already in the keychain
120  // I may have to do a lookup first and if it exists, run some update
121  // update function instead
122  status = SecKeychainAddInternetPassword ( NULL, /* keychain */
123  strlen(server), server, /* servername */
124  strlen(access_method), access_method, /* securitydomain */
125  strlen(user), user, /* acountname */
126  strlen(service), service, /* path */
127  port, /* port */
128  kSecProtocolTypeAny, /* protocol */
129  kSecAuthenticationTypeDefault, /* auth type */
130  strlen(password), password, /* passworddata */
131  itemRef );
132 
133  if ( status != noErr )
134  {
135  CFStringRef osx_resultstring = SecCopyErrorMessageString( status, NULL );
136  const gchar *resultstring = CFStringGetCStringPtr(osx_resultstring,
137  GetApplicationTextEncoding());
138  PWARN ( "OS X keychain error: %s", resultstring );
139  PWARN ( "The user will be prompted for a password again next time." );
140  CFRelease ( osx_resultstring );
141  }
142 #endif /* HAVE_OSX_KEYCHAIN */
143 }
144 
145 
146 gboolean gnc_keyring_get_password ( GtkWidget *parent,
147  const gchar *access_method,
148  const gchar *server,
149  guint32 port,
150  const gchar *service,
151  gchar **user,
152  gchar **password)
153 {
154  gboolean password_found = FALSE;
155 #ifdef HAVE_LIBSECRET
156  GError* error = NULL;
157  char* libsecret_password;
158 #endif
159 #if HAVE_GNOME_KEYRING
160  GnomeKeyringResult gkr_result;
161  GList *found_list = NULL;
162  GnomeKeyringNetworkPasswordData *found;
163 #endif
164 #ifdef HAVE_OSX_KEYCHAIN
165  void *password_data;
166  UInt32 password_length;
167  OSStatus status;
168 #endif
169 
170  g_return_val_if_fail (user != NULL, FALSE);
171  g_return_val_if_fail (password != NULL, FALSE);
172 
173  *password = NULL;
174 
175 #ifdef HAVE_LIBSECRET
176  libsecret_password = secret_password_lookup_sync (SECRET_SCHEMA_GNUCASH, NULL, &error,
177  "protocol", access_method,
178  "server", server,
179  "port", port,
180  "user", *user,
181  NULL);
182 
183  if (libsecret_password == NULL) {
184  if (error != NULL) {
185  PWARN ("libsecret access failed: %s.", error->message);
186  g_error_free(error);
187  }
188  } else {
189  password_found = TRUE;
190  *password = g_strdup (libsecret_password);
191  secret_password_free (libsecret_password);
192  }
193 #endif /* HAVE_LIBSECRET */
194 
195 #if HAVE_GNOME_KEYRING
196  if (password_found == FALSE) {
197  gkr_result = gnome_keyring_find_network_password_sync
198  ( *user, NULL, server, service,
199  access_method, NULL, port, &found_list );
200 
201  if (gkr_result == GNOME_KEYRING_RESULT_OK)
202  {
203  found = (GnomeKeyringNetworkPasswordData *) found_list->data;
204  if (found->password)
205  *password = g_strdup(found->password);
206  password_found = TRUE;
207  }
208  else
209  PWARN ("Gnome-keyring access failed: %s.",
210  gnome_keyring_result_to_message(gkr_result));
211 
212  gnome_keyring_network_password_list_free(found_list);
213  }
214 #endif /* HAVE_GNOME_KEYRING */
215 
216 #if defined(HAVE_LIBSECRET) && defined(HAVE_GNOME_KEYRING)
217  /* If we were not able to retrieve the password with libsecret and the new
218  * schema and libgnome-keyring was successful to retrieve the password using
219  * the old schema, we immediatly store it in the new schema.
220  */
221  if (libsecret_password == NULL && password_found == TRUE) {
222  gnc_keyring_set_password(access_method, server, port, service, *user, *password);
223  }
224 #endif /* HAVE_LIBSECRET && HAVE_GNOME_KEYRING */
225 
226 #ifdef HAVE_OSX_KEYCHAIN
227  /* mysql and postgres aren't valid protocols on Mac OS X.
228  * So we use the security domain parameter to allow us to
229  * distinguish between these two.
230  */
231  if (*user != NULL)
232  {
233  status = SecKeychainFindInternetPassword( NULL,
234  strlen(server), server,
235  strlen(access_method), access_method,
236  strlen(*user), *user,
237  strlen(service), service,
238  port,
239  kSecProtocolTypeAny,
240  kSecAuthenticationTypeDefault,
241  &password_length, &password_data,
242  NULL);
243 
244  if ( status == noErr )
245  {
246  *password = g_strndup(password_data, password_length);
247  password_found = TRUE;
248  SecKeychainItemFreeContent(NULL, password_data);
249  }
250  else
251  {
252  CFStringRef osx_resultstring = SecCopyErrorMessageString( status, NULL );
253  const gchar *resultstring = CFStringGetCStringPtr(osx_resultstring,
254  GetApplicationTextEncoding());
255  PWARN ( "OS X keychain error: %s", resultstring );
256  CFRelease ( osx_resultstring );
257  }
258  }
259 #endif /* HAVE_OSX_KEYCHAIN */
260 
261  if ( !password_found )
262  {
263  /* If we got here, either no proper password store is
264  * available on this system, or we couldn't retrieve
265  * a password from it. In both cases, just ask the user
266  * to enter one
267  */
268  gchar *db_path, *heading;
269 
270  if ( port == 0 )
271  db_path = g_strdup_printf ( "%s://%s/%s", access_method, server, service );
272  else
273  db_path = g_strdup_printf ( "%s://%s:%d/%s", access_method, server, port, service );
274  heading = g_strdup_printf ( /* Translators: %s is a path to a database or any other url,
275  like mysql://[email protected]/somedb, http://www.somequotes.com/thequotes */
276  _("Enter a user name and password to connect to: %s"),
277  db_path );
278 
279  password_found = gnc_get_username_password ( parent, heading,
280  *user, NULL,
281  user, password );
282  g_free ( db_path );
283  g_free ( heading );
284 
285  if ( password_found )
286  {
287  /* User entered new user/password information
288  * Let's try to add it to a password store.
289  */
290  gchar *newuser = g_strdup( *user );
291  gchar *newpassword = g_strdup( *password );
292  gnc_keyring_set_password ( access_method,
293  server,
294  port,
295  service,
296  newuser,
297  newpassword );
298  g_free ( newuser );
299  g_free ( newpassword );
300  }
301  }
302 
303  return password_found;
304 }
Functions to save and retrieve passwords.
#define PWARN(format, args...)
Definition: qoflog.h:243
void gnc_keyring_set_password(const gchar *access_method, const gchar *server, guint32 port, const gchar *service, const gchar *user, const gchar *password)
Definition: gnc-keyring.c:67
gboolean gnc_keyring_get_password(GtkWidget *parent, const gchar *access_method, const gchar *server, guint32 port, const gchar *service, gchar **user, gchar **password)
Definition: gnc-keyring.c:146
const gchar * QofLogModule
Definition: qofid.h:89