| 1 | #include <libsecret/secret.h> |
| 2 | #include <stdio.h> |
| 3 | |
| 4 | /* Exit codes are consumed by ChromiumKeys.ts. Stdout contains only the secret, |
| 5 | * with no newline or other framing that could change the derived cookie key. */ |
| 6 | enum { KEY_MISSING = 2, KEY_LOCKED = 3, READ_FAILED = 4 }; |
| 7 | |
| 8 | /* Chromium looks its key up by attributes alone: items written by older |
| 9 | * libgnome-keyring builds carry no schema name, and SECRET_SCHEMA_NONE would |
| 10 | * refuse them. DONT_MATCH_NAME keeps the attribute match and drops the name. */ |
| 11 | static const SecretSchema chromium_schema = { |
| 12 | .name = "chrome_libsecret_os_crypt_password_v2", |
| 13 | .flags = SECRET_SCHEMA_DONT_MATCH_NAME, |
| 14 | .attributes = {{"application", SECRET_SCHEMA_ATTRIBUTE_STRING}, {NULL, 0}}, |
| 15 | }; |
| 16 | |
| 17 | int main(int argc, char **argv) { |
| 18 | if (argc != 2 || argv[1][0] == '\0') return 64; |
| 19 | g_set_application_name("The Knife"); |
| 20 | |
| 21 | GHashTable *attributes = secret_attributes_build(&chromium_schema, "application", argv[1], NULL); |
| 22 | GError *error = NULL; |
| 23 | /* libsecret keeps the normal desktop unlock prompt. A cancelled unlock can |
| 24 | * still return a locked item, so check the item before reading its value. */ |
| 25 | GList *items = secret_service_search_sync( |
| 26 | NULL, &chromium_schema, attributes, |
| 27 | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, NULL, &error); |
| 28 | g_hash_table_unref(attributes); |
| 29 | |
| 30 | int status = READ_FAILED; |
| 31 | if (error != NULL) { |
| 32 | if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || |
| 33 | g_error_matches(error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED) || |
| 34 | g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED) || |
| 35 | g_error_matches(error, SECRET_ERROR, SECRET_ERROR_IS_LOCKED)) { |
| 36 | status = KEY_LOCKED; |
| 37 | } |
| 38 | g_error_free(error); |
| 39 | } else if (items == NULL) { |
| 40 | status = KEY_MISSING; |
| 41 | } else { |
| 42 | SecretItem *item = SECRET_ITEM(items->data); |
| 43 | if (secret_item_get_locked(item)) { |
| 44 | status = KEY_LOCKED; |
| 45 | } else { |
| 46 | SecretValue *secret = secret_item_get_secret(item); |
| 47 | if (secret != NULL) { |
| 48 | gsize length = 0; |
| 49 | const gchar *value = secret_value_get(secret, &length); |
| 50 | status = length == 0 ? KEY_MISSING : READ_FAILED; |
| 51 | if (length > 0 && fwrite(value, 1, length, stdout) == length && fflush(stdout) == 0) { |
| 52 | status = 0; |
| 53 | } |
| 54 | secret_value_unref(secret); |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | g_list_free_full(items, g_object_unref); |
| 59 | return status; |
| 60 | } |