aboutsummaryrefslogblamecommitdiff
path: root/src/npassd/collection.c
blob: 48167bf880de24481fd221a338129d31e0dc732d (plain) (tree)
































































































































































































































































































                                                                                
#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);
}