#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_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", "", "", 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; } static void collection_free(struct collection *c) { if (c == NULL) return; if (c->slot != NULL) sd_bus_slot_unref(c->slot); if (c->path != NULL) free(c->path); if (c->alias != NULL) free(c->alias); if (c->label != NULL) free(c->label); if (c->root != NULL) free(c->root); free(c); } static int collection_db_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 (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; 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 create 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; } return 0; } 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_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); }