GnuCash fec5643+
Loading...
Searching...
No Matches
Files | Functions
GUIUtility

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)
 Attempt to store a password in some trusted keystore.
 
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.
 

Detailed Description

Function Documentation

◆ gnc_keyring_get_password()

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 will 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 180 of file gnc-keyring.c.

187{
188 gboolean password_found = FALSE;
189 gchar *db_path, *heading;
190#ifdef HAVE_LIBSECRET
191 GError* error = NULL;
192 char* libsecret_password;
193#elif HAVE_GNOME_KEYRING
194 GnomeKeyringResult gkr_result;
195 GList *found_list = NULL;
196 GnomeKeyringNetworkPasswordData *found;
197#endif
198#ifdef HAVE_OSX_KEYCHAIN
199 void *password_data;
200 UInt32 password_length;
201 OSStatus status;
202#endif
203
204 g_return_val_if_fail (user != NULL, FALSE);
205 g_return_val_if_fail (password != NULL, FALSE);
206
207 *password = NULL;
208
209#ifdef HAVE_LIBSECRET
210 /* Workaround for https://bugs.gnucash.org/show_bug.cgi?id=746873
211 * and by extension for https://bugs.gnucash.org/show_bug.cgi?id=748625
212 * Store a dummy password and delete it again. This forces libsecret
213 * to open the keychain, where only a call to secret_password_lookup_sync
214 * sometimes fails to do so. More details can be found in the bug reports
215 * referenced above. */
216 secret_password_store_sync (SECRET_SCHEMA_GNUCASH, SECRET_COLLECTION_DEFAULT,
217 "Dummy password", "dummy", NULL, &error,
218 "protocol", PROJECT_NAME,
219 "server", PROJECT_NAME,
220 "user", PROJECT_NAME,
221 NULL);
222 secret_password_clear_sync (SECRET_SCHEMA_GNUCASH, NULL, &error,
223 "protocol", PROJECT_NAME,
224 "server", PROJECT_NAME,
225 "user", PROJECT_NAME,
226 NULL);
227
228 /* Note: only use the port attribute if it was set by the user. */
229 if (port == 0)
230 libsecret_password = secret_password_lookup_sync (SECRET_SCHEMA_GNUCASH, NULL, &error,
231 "protocol", access_method,
232 "server", server,
233 "user", *user,
234 NULL);
235 else
236 libsecret_password = secret_password_lookup_sync (SECRET_SCHEMA_GNUCASH, NULL, &error,
237 "protocol", access_method,
238 "server", server,
239 "port", port,
240 "user", *user,
241 NULL);
242
243 if (libsecret_password != NULL) {
244 *password = g_strdup (libsecret_password);
245 secret_password_free (libsecret_password);
246 return TRUE;
247 }
248
249 /* No password found yet. Perhaps it was written with a port equal to 0.
250 * Gnucash versions prior to 2.6.7 did this unfortunately... */
251 libsecret_password = secret_password_lookup_sync (SECRET_SCHEMA_GNUCASH, NULL, &error,
252 "protocol", access_method,
253 "server", server,
254 "port", 0,
255 "user", *user,
256 NULL);
257
258 if (libsecret_password != NULL) {
259 *password = g_strdup (libsecret_password);
260 secret_password_free (libsecret_password);
261
262 /* Ok, got an password with 0 port.
263 Store a copy in a more recent gnucash style. */
264 gnc_keyring_set_password(access_method, server, port, service, *user, *password);
265 return TRUE;
266 }
267
268 /* No password was found while querying libsecret using the gnucash schema,
269 Look for a password stored via gnome-keyring instead */
270 if (port == 0)
271 libsecret_password = secret_password_lookup_sync (SECRET_SCHEMA_COMPAT_NETWORK, NULL, &error,
272 "protocol", access_method,
273 "server", server,
274 "object", service,
275 "user", *user,
276 NULL);
277 else
278 libsecret_password = secret_password_lookup_sync (SECRET_SCHEMA_COMPAT_NETWORK, NULL, &error,
279 "protocol", access_method,
280 "server", server,
281 "port", port,
282 "object", service,
283 "user", *user,
284 NULL);
285
286 if (libsecret_password != NULL) {
287 *password = g_strdup (libsecret_password);
288 secret_password_free (libsecret_password);
289
290 /* Ok, got an old gnome-keyring password.
291 * Store a copy of it in a libsecret compatible format. */
292 gnc_keyring_set_password(access_method, server, port, service, *user, *password);
293 return TRUE;
294 }
295
296 /* Something went wrong while attempting to access libsecret
297 * Log the error message and carry on... */
298 if (error != NULL) {
299 PWARN ("libsecret access failed: %s.", error->message);
300 g_error_free(error);
301 }
302
303#elif HAVE_GNOME_KEYRING
304 gkr_result = gnome_keyring_find_network_password_sync
305 ( *user, NULL, server, service,
306 access_method, NULL, port, &found_list );
307
308 if (gkr_result == GNOME_KEYRING_RESULT_OK)
309 {
310 found = (GnomeKeyringNetworkPasswordData *) found_list->data;
311 if (found->password)
312 *password = g_strdup(found->password);
313 gnome_keyring_network_password_list_free(found_list);
314 return TRUE;
315 }
316
317 /* Something went wrong while attempting to access libsecret
318 * Log the error message and carry on... */
319 PWARN ("Gnome-keyring access failed: %s.",
320 gnome_keyring_result_to_message(gkr_result));
321 gnome_keyring_network_password_list_free(found_list);
322#endif /* HAVE_LIBSECRET or HAVE_GNOME_KEYRING */
323
324#ifdef HAVE_OSX_KEYCHAIN
325 /* mysql and postgres aren't valid protocols on Mac OS X.
326 * So we use the security domain parameter to allow us to
327 * distinguish between these two.
328 */
329 if (*user != NULL)
330 {
331 status = SecKeychainFindInternetPassword( NULL,
332 strlen(server), server,
333 strlen(access_method), access_method,
334 strlen(*user), *user,
335 strlen(service), service,
336 port,
337 kSecProtocolTypeAny,
338 kSecAuthenticationTypeDefault,
339 &password_length, &password_data,
340 NULL);
341
342 if ( status == noErr )
343 {
344 *password = g_strndup(password_data, password_length);
345 SecKeychainItemFreeContent(NULL, password_data);
346 return TRUE;
347 }
348 else
349 {
350 gnc_log_osx_keychain_error ("gnc_keyring_get_password", status);
351 }
352 }
353#endif /* HAVE_OSX_KEYCHAIN */
354
355 /* If we got here, either no proper password store is
356 * available on this system, or we couldn't retrieve
357 * a password from it. In both cases, just ask the user
358 * to enter one
359 */
360
361 if ( port == 0 )
362 db_path = g_strdup_printf ( "%s://%s/%s", access_method, server, service );
363 else
364 db_path = g_strdup_printf ( "%s://%s:%d/%s", access_method, server, port, service );
365 heading = g_strdup_printf ( /* Translators: %s is a path to a database or any other url,
366 like mysql://user@server.somewhere/somedb, https://www.somequotes.com/thequotes */
367 _("Enter a user name and password to connect to: %s"),
368 db_path );
369
370 password_found = gnc_get_username_password ( parent, heading,
371 *user, NULL,
372 user, password );
373 g_free ( db_path );
374 g_free ( heading );
375
376 if ( password_found )
377 {
378 /* User entered new user/password information
379 * Let's try to add it to a password store.
380 */
381 gchar *newuser = g_strdup( *user );
382 gchar *newpassword = g_strdup( *password );
383 gnc_keyring_set_password ( access_method,
384 server,
385 port,
386 service,
387 newuser,
388 newpassword );
389 g_free ( newuser );
390 g_free ( newpassword );
391 }
392
393 return password_found;
394}
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.
Definition gnc-keyring.c:86
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250

◆ gnc_keyring_set_password()

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 86 of file gnc-keyring.c.

92{
93#ifdef HAVE_LIBSECRET
94 GError* error = NULL;
95 gchar* label = NULL;
96
97 g_return_if_fail(access_method != NULL && server != NULL &&
98 service != NULL && user != NULL && password != NULL);
99
100 label = g_strdup_printf("GnuCash password for %s://%s@%s", access_method, user, server);
101
102 if (port == 0)
103 secret_password_store_sync (SECRET_SCHEMA_GNUCASH, SECRET_COLLECTION_DEFAULT,
104 label, password, NULL, &error,
105 "protocol", access_method,
106 "server", server,
107 "user", user,
108 NULL);
109 else
110 secret_password_store_sync (SECRET_SCHEMA_GNUCASH, SECRET_COLLECTION_DEFAULT,
111 label, password, NULL, &error,
112 "protocol", access_method,
113 "server", server,
114 "port", port,
115 "user", user,
116 NULL);
117
118 g_free(label);
119
120 if (error != NULL)
121 {
122 PWARN ("libsecret error: %s", error->message);
123 PWARN ("The user will be prompted for a password again next time.");
124 g_error_free(error);
125 }
126#elif HAVE_GNOME_KEYRING
127 GnomeKeyringResult gkr_result;
128 guint32 item_id = 0;
129
130 g_return_if_fail(access_method != NULL && server != NULL &&
131 service != NULL && user != NULL && password != NULL);
132
133 gkr_result = gnome_keyring_set_network_password_sync
134 (NULL, user, NULL, server, service,
135 access_method, NULL, port, password, &item_id);
136
137 if (gkr_result != GNOME_KEYRING_RESULT_OK)
138 {
139 PWARN ("Gnome-keyring error: %s",
140 gnome_keyring_result_to_message(gkr_result));
141 PWARN ("The user will be prompted for a password again next time.");
142 }
143#endif /* HAVE_GNOME_KEYRING */
144#ifdef HAVE_OSX_KEYCHAIN
145 OSStatus status;
146 SecKeychainItemRef *itemRef = NULL;
147
148 g_return_if_fail(access_method != NULL && server != NULL &&
149 service != NULL && user != NULL && password != NULL);
150 /* mysql and postgres aren't valid protocols on Mac OS X.
151 * So we use the security domain parameter to allow us to
152 * distinguish between these two.
153 */
154 // FIXME I'm not sure this works if a password was already in the keychain
155 // I may have to do a lookup first and if it exists, run some
156 // update function instead
157 status =
158 SecKeychainAddInternetPassword (NULL, /* keychain */
159 strlen(server), server, /* servername */
160 strlen(access_method),
161 access_method, /* securitydomain */
162 strlen(user), user, /* accountname */
163 strlen(service), service, /* path */
164 port, /* port */
165 kSecProtocolTypeAny, /* protocol */
166 kSecAuthenticationTypeDefault, /* auth type */
167 strlen(password),
168 password, /* passworddata */
169 itemRef );
170
171 if ( status != noErr )
172 {
173 gnc_log_osx_keychain_error ("gnc_keyring_set_password", status);
174 PWARN ( "The user will be prompted for a password again next time." );
175 }
176#endif /* HAVE_OSX_KEYCHAIN */
177}