#include #include #include #include #include #include #include #include "npassd/collection.h" #include "npassd/common.h" #include "npassd/util.h" #include "util.h" #define COLLECTION_IFACE "org.freedesktop.Secret.Collection" #define DBUS_ROOT_PREFIX "dbus" static int collection_alloc(sd_bus *bus, struct collection **p, const char *root, const char *alias, const char *label, uint64_t created, uint64_t modified); static int collection_db_root_read(sd_bus *bus, struct sqlite3 *db, struct collection **p, const char *alias); static int collection_db_write(struct sqlite3 *db, struct collection *c); static int handle_create_item(__attribute__((unused)) sd_bus_message *msg, __attribute__((unused)) void *data, __attribute__((unused)) sd_bus_error *ret_error); static const sd_bus_vtable collection_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("CreateItem", "a{sv}(oayays)b", "oo", handle_create_item, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; static int handle_create_item(__attribute__((unused)) sd_bus_message *msg, __attribute__((unused)) void *data, __attribute__((unused)) sd_bus_error *ret_error) { return 0; } void collection_free(struct collection *c) { if (c == NULL) return; sd_bus_slot_unref(c->slot); sd_bus_slot_unref(c->slot); free(c->path); free(c->alias); free(c->label); free(c->root); free(c); } static int collection_db_root_read(sd_bus *bus, struct sqlite3 *db, struct collection **p, const char *root) { const char *query, *alias, *label; uint64_t created, modified; sqlite3_stmt *stmt; int ret; query = "SELECT alias, label, created, modified " "FROM collections " "WHERE collections.root = ? "; ret = sqlite3_prepare_v2(db, query, -1, &stmt, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to prepare sql"); return -EPERM; } ret = sqlite3_bind_text(stmt, 1, root, -1, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to bind sql"); ret = -EPERM; goto out_stmt_finalize; } ret = sqlite3_step(stmt); if (ret == SQLITE_DONE) { ret = -ENOENT; goto out_stmt_finalize; } if (ret != SQLITE_ROW) { print_err("%s", "Failed to step sql"); ret = -EPERM; goto out_stmt_finalize; } alias = (char *)sqlite3_column_text(stmt, 0); label = (char *)sqlite3_column_text(stmt, 1); created = sqlite3_column_int64(stmt, 2); modified = sqlite3_column_int64(stmt, 3); if (alias == NULL || label == NULL || created == 0 || modified == 0) { print_err("%s", "Found null value in column"); ret = -EINVAL; goto out_stmt_finalize; } ret = collection_alloc(bus, p, root, alias, label, created, modified); out_stmt_finalize: sqlite3_finalize(stmt); return ret; } int collection_db_alias_read(sd_bus *bus, struct sqlite3 *db, struct collection **p, const char *alias) { const char *query, *root, *label; uint64_t created, modified; sqlite3_stmt *stmt; int ret; query = "SELECT root, label, created, modified " "FROM collections " "WHERE collections.alias = ? "; ret = sqlite3_prepare_v2(db, query, -1, &stmt, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to prepare sql"); return -EPERM; } ret = sqlite3_bind_text(stmt, 1, alias, -1, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to bind sql"); ret = -EPERM; goto out_stmt_finalize; } ret = sqlite3_step(stmt); if (ret == SQLITE_DONE) { ret = -ENOENT; goto out_stmt_finalize; } if (ret != SQLITE_ROW) { print_err("%s", "Failed to step sql"); ret = -EPERM; goto out_stmt_finalize; } root = (char *)sqlite3_column_text(stmt, 0); label = (char *)sqlite3_column_text(stmt, 1); created = sqlite3_column_int64(stmt, 2); modified = sqlite3_column_int64(stmt, 3); if (root == NULL || label == NULL || created == 0 || modified == 0) { print_err("%s", "Found null value in column"); ret = -EINVAL; goto out_stmt_finalize; } ret = collection_alloc(bus, p, root, alias, label, created, modified); out_stmt_finalize: sqlite3_finalize(stmt); return ret; } static int collection_db_write(struct sqlite3 *db, struct collection *c) { sqlite3_stmt *stmt; const char *query; int ret; query = "INSERT INTO " "collections(root, label, alias, created, modified) " "VALUES (?, ?, ?, ?, ?) "; ret = sqlite3_prepare_v2(db, query, -1, &stmt, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to prepare sql"); return -EPERM; } ret = sqlite3_bind_text(stmt, 1, c->root, -1, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to bind sql"); ret = -EPERM; goto out_stmt_finalize; } ret = sqlite3_bind_text(stmt, 2, c->label, -1, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to bind sql"); ret = -EPERM; goto out_stmt_finalize; } ret = sqlite3_bind_text(stmt, 3, c->alias, -1, NULL); if (ret != SQLITE_OK) { print_err("%s", "Failed to bind sql"); ret = -EPERM; goto out_stmt_finalize; } ret = sqlite3_bind_int64(stmt, 4, c->created); if (ret != SQLITE_OK) { print_err("%s", "Failed to bind sql"); ret = -EPERM; goto out_stmt_finalize; } ret = sqlite3_bind_int64(stmt, 5, c->modified); if (ret != SQLITE_OK) { print_err("%s", "Failed to bind sql"); ret = -EPERM; goto out_stmt_finalize; } ret = sqlite3_step(stmt); if (ret != SQLITE_DONE) { print_err("%s", "No such alias"); ret = -EPERM; goto out_stmt_finalize; } out_stmt_finalize: sqlite3_finalize(stmt); return ret; } static int collection_alloc(sd_bus *bus, struct collection **p, const char *root, const char *alias, const char *label, uint64_t created, uint64_t modified) { struct collection *collection; char *alias_path; int ret; *p = malloc(sizeof(**p)); if (*p == NULL) { print_err("Failed to make collection: %s", strerror(errno)); return -errno; } collection = *p; collection->slot = NULL; collection->root = NULL; collection->alias = NULL; collection->label = NULL; collection->locked = 0; collection->created = created; collection->modified = modified; collection->root = strdup(root); if (collection->root == NULL) { collection_free(collection); print_err("%s", strerror(errno)); return -errno; } collection->label = strdup(label); if (collection->label == NULL) { collection_free(collection); print_err("%s", strerror(errno)); return -errno; } collection->alias = strdup(alias); if (collection->alias == NULL) { collection_free(collection); print_err("%s", strerror(errno)); return -errno; } ret = asprintf(&collection->path, DBUS_OBJECT_PATH "/collection/%s", collection->root); if (ret < 0) { collection_free(collection); print_err("%s", "Failed to build collection object path"); return -ENOMEM; } ret = dbus_objpath_alnumify(collection->path); if (ret < 0) { collection_free(collection); print_err("%s", strerror(-ret)); return ret; } ret = sd_bus_add_object_vtable(bus, &collection->slot, collection->path, COLLECTION_IFACE, collection_vtable, collection); if (ret < 0) { collection_free(collection); print_err("Failed to connect to bus: %s", strerror(-ret)); return ret; } else if (alias == NULL) { return 0; } ret = asprintf(&alias_path, DBUS_OBJECT_PATH "/aliases/%s", alias); if (ret < 0) { collection_free(collection); print_err("%s", "Failed to build collection alias path"); return -ENOMEM; } ret = sd_bus_add_object_vtable(bus, &collection->slot, alias_path, COLLECTION_IFACE, collection_vtable, collection); free(alias_path); if (ret < 0) { collection_free(collection); print_err("Failed to connect to bus: %s", strerror(-ret)); return ret; } return 0; } int collection_alias_search(struct collection_dlist *collections, const char *alias, struct collection **collection) { struct collection *c; int ret; LIST_FOREACH (c, collections, dlist) { ret = strcmp(c->alias, alias); if (ret == 0) { *collection = c; return 0; } } return -ENOENT; } int collection_root_make(const char *label, const char *alias, char **root) { int ret; if (label == NULL && alias == NULL) { print_err("%s", strerror(EINVAL)); return -EINVAL; } ret = asprintf(root, DBUS_ROOT_PREFIX " %s keyring", alias ? alias : label); if (ret < 0) { print_err("%s", "Failed to build collection root path"); return -EPERM; } return 0; } int collection_new(sd_bus *bus, struct sqlite3 *db, struct collection **p, const char *alias, const char *label, const char *root) { uint64_t epoch; int ret; ret = collection_db_root_read(bus, db, p, root); if (ret != -ENOENT) return ret; errno = 0; epoch = time(NULL); if (errno != 0) { print_err("Failed to get time: %s", strerror(errno)); return -errno; } ret = collection_alloc(bus, p, root, alias, label, epoch, epoch); if (ret < 0) return ret; return collection_db_write(db, *p); }