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. */
6enum { 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. */
11static 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
17int 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}