aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsinanmohd <sinan@sinanmohd.com>2024-04-27 12:31:12 +0530
committersinanmohd <sinan@sinanmohd.com>2024-04-28 15:43:50 +0530
commitda21dd57634aebffe0f5833b598e1128fafc0def (patch)
tree71d8e510eb788e0df695052325a1fe4fec6bf5a7
parent06e3f1885fc112093f867cd5d7a8b163ef28650c (diff)
npassd/collection: init
-rw-r--r--README.md2
-rw-r--r--include/npassd/collection.h21
-rw-r--r--include/npassd/service.h4
-rw-r--r--include/npassd/util.h1
-rw-r--r--src/npassd/collection.c289
-rw-r--r--src/npassd/db.c29
-rw-r--r--src/npassd/meson.build2
-rw-r--r--src/npassd/service.c91
-rw-r--r--src/npassd/session.c8
-rw-r--r--src/npassd/util.c15
10 files changed, 444 insertions, 18 deletions
diff --git a/README.md b/README.md
index 7ef87eb..1860026 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Todo
- [ ] npassd (dbus)
- [ ] org.freedesktop.Secret.Service
- [x] OpenSession
- - [ ] CreateCollection
+ - [x] CreateCollection
- [ ] SearchItems
- [ ] Unlock
- [ ] Lock
diff --git a/include/npassd/collection.h b/include/npassd/collection.h
new file mode 100644
index 0000000..36d6b72
--- /dev/null
+++ b/include/npassd/collection.h
@@ -0,0 +1,21 @@
+#include <linux/limits.h>
+#include <sqlite3.h>
+#include <sys/queue.h>
+#include <systemd/sd-bus.h>
+
+struct collection {
+ sd_bus_slot *slot;
+ char *root, *path;
+
+ int locked;
+ char *alias, *label;
+ uint64_t created, modified;
+
+ LIST_ENTRY(collection) dlist;
+};
+
+LIST_HEAD(collection_dlist, collection);
+
+int collection_new(sd_bus *bus, struct sqlite3 *db, struct collection **p,
+ const char *alias, const char *label, const char *root);
+int collection_root_make(const char *label, const char *alias, char **root);
diff --git a/include/npassd/service.h b/include/npassd/service.h
index b7c71af..92a833e 100644
--- a/include/npassd/service.h
+++ b/include/npassd/service.h
@@ -2,6 +2,7 @@
#include <sys/queue.h>
#include <systemd/sd-bus.h>
+#include "npassd/collection.h"
#include "npassd/session.h"
#define MAX_SESSION 128
@@ -9,9 +10,10 @@
struct service {
sd_bus *bus;
sd_bus_slot *slot;
-
struct sqlite3 *db;
+
struct session_dlist sessions;
+ struct collection_dlist collections;
};
int service_init(sd_bus *bus, struct service *service);
diff --git a/include/npassd/util.h b/include/npassd/util.h
new file mode 100644
index 0000000..44efb9b
--- /dev/null
+++ b/include/npassd/util.h
@@ -0,0 +1 @@
+int dbus_objpath_alnumify(char *path);
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);
+}
diff --git a/src/npassd/db.c b/src/npassd/db.c
index 5afa45e..f55bede 100644
--- a/src/npassd/db.c
+++ b/src/npassd/db.c
@@ -7,17 +7,23 @@
#define DB_NAME "secret_service.sqlite"
-const char sql_stmt_init[] = "CREATE TABLE IF NOT EXISTS store ("
- " storeid INTEGER PRIMARY KEY,"
- " name TEXT NOT NULL UNIQUE"
- ");"
- "CREATE TABLE IF NOT EXISTS lookup ("
- " key TEXT NOT NULL,"
- " value TEXT NOT NULL,"
- " storeid INTEGER,"
- " UNIQUE(storeid, key),"
- " FOREIGN KEY(storeid) REFERENCES store(storeid)"
- ");";
+const char sql_stmt_init[] =
+ "CREATE TABLE IF NOT EXISTS collections ("
+ " id INTEGER PRIMARY KEY,"
+ " root TEXT NOT NULL UNIQUE,"
+ " label TEXT UNIQUE,"
+ " alias TEXT UNIQUE,"
+ " created INTEGER,"
+ " modified INTEGER"
+ ");"
+ "CREATE TABLE IF NOT EXISTS collection_attrs ("
+ " id INTEGER PRIMARY KEY,"
+ " key TEXT NOT NULL,"
+ " value TEXT NOT NULL,"
+ " collectionid INTEGER,"
+ " UNIQUE(key, collectionid)"
+ " FOREIGN KEY(collectionid) REFERENCES collections(id)"
+ ");";
int db_init(const char *store_path, struct sqlite3 **db)
{
@@ -41,6 +47,7 @@ int db_init(const char *store_path, struct sqlite3 **db)
ret = sqlite3_exec(*db, sql_stmt_init, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
+ sqlite3_close(*db);
print_err("Failed to create tables: %s", sqlite3_errmsg(*db));
return -EPERM;
}
diff --git a/src/npassd/meson.build b/src/npassd/meson.build
index e73c28f..5f2c4ad 100644
--- a/src/npassd/meson.build
+++ b/src/npassd/meson.build
@@ -4,7 +4,9 @@ e = executable(
'npassd.c',
'service.c',
'session.c',
+ 'collection.c',
'db.c',
+ 'util.c',
],
include_directories: npass_inc,
diff --git a/src/npassd/service.c b/src/npassd/service.c
index b179735..e57fefe 100644
--- a/src/npassd/service.c
+++ b/src/npassd/service.c
@@ -13,6 +13,8 @@ static int handle_open_session(sd_bus_message *msg, void *data,
sd_bus_error *ret_error);
static int handle_search_items(sd_bus_message *msg, void *data,
sd_bus_error *ret_error);
+static int handle_create_collection(sd_bus_message *msg, void *data,
+ sd_bus_error *ret_error);
static const sd_bus_vtable service_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -20,6 +22,8 @@ static const sd_bus_vtable service_vtable[] = {
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SearchItems", "a{ss}", "aoao", handle_search_items,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CreateCollection", "a{sv}s", "oo",
+ handle_create_collection, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
@@ -30,6 +34,84 @@ static int handle_search_items(__attribute__((unused)) sd_bus_message *msg,
return sd_bus_reply_method_return(msg, "aoao", 0, 0);
}
+static int handle_create_collection(sd_bus_message *msg, void *data,
+ __attribute__((unused))
+ sd_bus_error *ret_error)
+{
+ const char *label, *alias, *key;
+ struct service *service = data;
+ struct collection *collection;
+ char *root;
+ int ret;
+
+ ret = sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (ret <= 0) {
+ print_err("%s", strerror(ret < 0 ? -ret : EINVAL));
+ return ret < 0 ? ret : -EINVAL;
+ }
+
+ ret = sd_bus_message_enter_container(msg, SD_BUS_TYPE_DICT_ENTRY, "sv");
+ if (ret <= 0) {
+ print_err("%s", strerror(ret < 0 ? -ret : EINVAL));
+ return ret < 0 ? ret : -EINVAL;
+ }
+
+ ret = sd_bus_message_read(msg, "s", &key);
+ if (ret < 0) {
+ print_err("%s", strerror(-ret));
+ return ret;
+ }
+
+ ret = strcmp("org.freedesktop.Secret.Collection.Label", key);
+ if (ret) {
+ print_err("Unsupported property key: %s", key);
+ return -EINVAL;
+ }
+
+ ret = sd_bus_message_read(msg, "v", "s", &label);
+ if (ret < 0) {
+ print_err("%s", strerror(-ret));
+ return ret;
+ }
+
+ ret = sd_bus_message_exit_container(msg);
+ if (ret < 0) {
+ print_err("%s", strerror(-ret));
+ return ret;
+ }
+
+ ret = sd_bus_message_exit_container(msg);
+ if (ret < 0) {
+ print_err("%s", strerror(-ret));
+ return ret;
+ }
+
+ ret = sd_bus_message_read(msg, "s", &alias);
+ if (ret < 0) {
+ print_err("%s", strerror(-ret));
+ return ret;
+ }
+
+ ret = collection_root_make(label, alias, &root);
+ if (ret < 0)
+ return ret;
+
+ ret = collection_new(service->bus, service->db, &collection, alias,
+ label, root);
+ free(root);
+ if (ret < 0)
+ return ret;
+
+ ret = sd_bus_reply_method_return(msg, "oo", collection->path, "/");
+ if (ret < 0) {
+ print_err("%s", strerror(-ret));
+ return ret;
+ }
+
+ LIST_INSERT_HEAD(&service->collections, collection, dlist);
+ return ret;
+}
+
static int handle_open_session(sd_bus_message *msg, void *data,
sd_bus_error *ret_error)
{
@@ -55,8 +137,14 @@ static int handle_open_session(sd_bus_message *msg, void *data,
if (ret < 0)
return ret;
+ ret = sd_bus_reply_method_return(msg, "vo", "s", NULL, session->path);
+ if (ret < 0) {
+ print_err("%s", strerror(-ret));
+ return ret;
+ }
+
LIST_INSERT_HEAD(&service->sessions, session, dlist);
- return sd_bus_reply_method_return(msg, "vo", "s", NULL, session->path);
+ return ret;
}
void service_free(struct service *service)
@@ -75,6 +163,7 @@ int service_init(sd_bus *bus, struct service *service)
service->bus = bus;
LIST_INIT(&service->sessions);
+ LIST_INIT(&service->collections);
ret = sd_bus_add_object_vtable(service->bus, &service->slot,
DBUS_OBJECT_PATH, SERVICE_IFACE,
diff --git a/src/npassd/session.c b/src/npassd/session.c
index 1807ea1..52f8476 100644
--- a/src/npassd/session.c
+++ b/src/npassd/session.c
@@ -141,10 +141,10 @@ int session_new(sd_bus *bus, struct session **p, const char *owner)
return ret;
}
- ret = sd_bus_match_signal(bus, &session->slot_singal, "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus", "NameOwnerChanged",
- handle_nameownerchanged, session);
+ ret = sd_bus_match_signal(
+ bus, &session->slot_singal, "org.freedesktop.DBus",
+ "/org/freedesktop/DBus", "org.freedesktop.DBus",
+ "NameOwnerChanged", handle_nameownerchanged, session);
if (ret < 0) {
session_free(session);
print_err("%s", strerror(-ret));
diff --git a/src/npassd/util.c b/src/npassd/util.c
new file mode 100644
index 0000000..9b1fdac
--- /dev/null
+++ b/src/npassd/util.c
@@ -0,0 +1,15 @@
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+int dbus_objpath_alnumify(char *path)
+{
+ for (size_t i = 0; path[i]; i++) {
+ if (!isalnum(path[i]) && path[i] != '/')
+ path[i] = '_';
+ else if (i > 0 && path[i] == '/' && path[i - 1] == '/')
+ return -EINVAL;
+ }
+
+ return 0;
+}