aboutsummaryrefslogtreecommitdiff
path: root/src/npassd/collection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/npassd/collection.c')
-rw-r--r--src/npassd/collection.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/npassd/collection.c b/src/npassd/collection.c
new file mode 100644
index 0000000..48167bf
--- /dev/null
+++ b/src/npassd/collection.c
@@ -0,0 +1,289 @@
+#include <errno.h>
+#include <linux/limits.h>
+#include <sqlite3.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <systemd/sd-bus.h>
+#include <time.h>
+
+#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);
+}