GnuCash c935c2f+
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 167 of file gnc-keyring.c.

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

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