]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
vfs: add files to access the varlink keybridge API
authorJohn Mulligan <jmulligan@redhat.com>
Thu, 5 Jun 2025 20:47:10 +0000 (16:47 -0400)
committerAnoop C S <anoopcs@samba.org>
Tue, 20 Jan 2026 05:49:35 +0000 (05:49 +0000)
Add a pair of helper files that will allow vfs modules to make use of
the keybridge - a varlink API and server that is defined by the sambacc
project. The keybridge server exists to act as a proxy between smbd and
various possible "secrets management" backends. Currently, the sambacc
keybridge server implements a "mem" backend, for testing only, and a
KMIP backend.

Using a local RPC protocol, like varlink + keybridge allows the smbd
side to be very simple and only know how to talk the keybridge API,
versus having to teach it about various other APIs that may need to
make use of things like mTLS.

Furthermore, samba already has an (currently optional) dependency on
libvarlink so adding another use of varlink seems like a fairly
minimal change to samba's set of dependencies. This feature will
not be built on if libvarlink is not enabled. The plan is to add this
to vfs_ceph_new in a future patch.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
Reviewed-by: Gunther Deschner <gd@samba.org>
Reviewed-by: Anoop C S <anoopcs@samba.org>
source3/modules/varlink_keybridge.c [new file with mode: 0644]
source3/modules/varlink_keybridge.h [new file with mode: 0644]

diff --git a/source3/modules/varlink_keybridge.c b/source3/modules/varlink_keybridge.c
new file mode 100644 (file)
index 0000000..38b1616
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (c) 2025      John Mulligan <jmulligan@samba.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "varlink_keybridge.h"
+
+#include <varlink.h>
+
+#define VLKB_PREFIX "[varlink_keybridge] "
+#define KB_DBG_T(...) DEBUG(8, (VLKB_PREFIX __VA_ARGS__))
+#define KB_DBG_I(...) DEBUG(5, (VLKB_PREFIX __VA_ARGS__))
+
+#define DBG_ERR_VLKB(msg, verror) \
+       DBG_ERR(VLKB_PREFIX "%s: %s\n", (msg), (varlink_error_string(verror)))
+
+#define KB_GET "org.samba.containers.keybridge.Get"
+#define KB_F_NAME "name"
+#define KB_F_SCOPE "scope"
+#define KB_F_KIND "kind"
+#define KB_F_ENTRY "entry"
+#define KB_F_DATA "data"
+#define KB_KIND_B64 "B64"
+#define KB_KIND_VALUE "VALUE"
+
+static const char *vlkb_kind_string(enum varlink_keybridge_kind kind)
+{
+       switch (kind) {
+       case VARLINK_KEYBRIDGE_KIND_B64:
+               return KB_KIND_B64;
+       default:
+               return KB_KIND_VALUE;
+       }
+}
+
+static void vlkb_error(struct varlink_keybridge_result *result,
+                      const char *msg,
+                      VarlinkObject *obj)
+{
+       char *json;
+       long vret = varlink_object_to_json(obj, &json);
+       if (vret < 0) {
+               result->status = VARLINK_KEYBRIDGE_STATUS_FAILURE;
+               result->data = talloc_strdup(result,
+                                            "varlink_object_to_json failed");
+               return;
+       }
+
+       result->status = VARLINK_KEYBRIDGE_STATUS_ERROR;
+       result->data = talloc_asprintf(result, "%s, object: %s", msg, json);
+       free(json);
+}
+
+static long vlkb_get(VarlinkConnection *conn,
+                    const char *error,
+                    VarlinkObject *parameters,
+                    uint64_t flags,
+                    void *userdata)
+{
+       struct varlink_keybridge_result *result = userdata;
+       const char *tmp = NULL;
+       VarlinkObject *entry;
+       enum varlink_keybridge_kind kind = VARLINK_KEYBRIDGE_KIND_DEFAULT;
+       int vret;
+
+       if (error) {
+               vlkb_error(result, error, parameters);
+               goto done;
+       }
+
+       vret = varlink_object_get_object(parameters, KB_F_ENTRY, &entry);
+       if (vret < 0) {
+               vlkb_error(result, "invalid field: " KB_F_ENTRY, parameters);
+               goto done;
+       }
+
+       vret = varlink_object_get_string(entry, KB_F_KIND, &tmp);
+       if (vret < 0) {
+               vlkb_error(result, "invalid field: " KB_F_KIND, entry);
+               goto done;
+       }
+       if (strcmp(KB_KIND_B64, tmp) == 0) {
+               kind = VARLINK_KEYBRIDGE_KIND_B64;
+       } else if (strcmp(KB_KIND_VALUE, tmp) == 0) {
+               kind = VARLINK_KEYBRIDGE_KIND_VALUE;
+       }
+
+       vret = varlink_object_get_string(entry, KB_F_DATA, &tmp);
+       if (vret < 0) {
+               vlkb_error(result, "invalid field: " KB_F_DATA, entry);
+               goto done;
+       }
+       result->data = talloc_strdup(result, tmp);
+       if (result->data == NULL) {
+               DBG_ERR(VLKB_PREFIX "talloc_strdup failed\n");
+               goto done;
+       }
+       result->status = VARLINK_KEYBRIDGE_STATUS_OK;
+       result->kind = kind;
+
+done:
+       return varlink_connection_close(conn);
+}
+
+static long vlkb_wait_for_response(VarlinkConnection *conn)
+{
+       struct timeval tv = {.tv_sec = 5, .tv_usec = 0};
+       int fd = varlink_connection_get_fd(conn);
+       int ret;
+       fd_set rfds;
+
+       FD_ZERO(&rfds);
+       FD_SET(fd, &rfds);
+
+       ret = select(fd + 1, &rfds, NULL, NULL, &tv);
+       if (ret == -1) {
+               DBG_ERR(VLKB_PREFIX "select() error: %s\n", strerror(errno));
+               return -VARLINK_ERROR_INVALID_CALL;
+       } else if (ret == 0) {
+               DBG_ERR(VLKB_PREFIX "select() timed out\n");
+               return -VARLINK_ERROR_INVALID_CALL;
+       }
+       return varlink_connection_process_events(conn, 0);
+}
+
+static bool vlkb_entry_get(TALLOC_CTX *mem_ctx,
+                          const struct varlink_keybridge_config *kbc,
+                          struct varlink_keybridge_result **resp)
+{
+       bool completed = false;
+       VarlinkConnection *conn = NULL;
+       VarlinkObject *params = NULL;
+       struct varlink_keybridge_result *result;
+       long vret;
+
+       KB_DBG_I("calling varlink keybridge get method\n");
+       KB_DBG_T("creating %s arguments object\n", KB_GET);
+       vret = varlink_object_new(&params);
+       if (vret < 0) {
+               DBG_ERR_VLKB("varlink_object_new failed", -vret);
+               goto done;
+       }
+       vret = varlink_object_set_string(params, KB_F_NAME, kbc->name);
+       if (vret < 0) {
+               DBG_ERR_VLKB("varlink_object_set_string '" KB_F_NAME "' failed",
+                            -vret);
+               goto done;
+       }
+       vret = varlink_object_set_string(params, KB_F_SCOPE, kbc->scope);
+       if (vret < 0) {
+               DBG_ERR_VLKB("varlink_object_set_string '" KB_F_SCOPE
+                            "' failed",
+                            -vret);
+               goto done;
+       }
+       vret = varlink_object_set_string(params,
+                                        KB_F_KIND,
+                                        vlkb_kind_string(kbc->kind));
+       if (vret < 0) {
+               DBG_ERR_VLKB("varlink_object_set_string '" KB_F_KIND "' failed",
+                            -vret);
+               goto done;
+       }
+
+       /* set up the varlink connection */
+       KB_DBG_T("creating %s connection\n", KB_GET);
+       vret = varlink_connection_new(&conn, kbc->path);
+       if (vret < 0) {
+               DBG_ERR_VLKB("varlink_connection_new failed", -vret);
+               goto done;
+       }
+
+       KB_DBG_T("creating %s result object\n", KB_GET);
+       result = talloc_zero(mem_ctx, struct varlink_keybridge_result);
+       if (result == NULL) {
+               DBG_ERR(VLKB_PREFIX "talloc_zero failed\n");
+               goto done;
+       }
+       *resp = result;
+
+       KB_DBG_T("performing %s call\n", KB_GET);
+       vret = varlink_connection_call(
+               conn, KB_GET, params, 0, vlkb_get, result);
+       if (vret < 0) {
+               DBG_ERR_VLKB("varlink_connection_call failed", -vret);
+               goto done;
+       }
+       KB_DBG_T("waiting for %s response\n", KB_GET);
+       vret = vlkb_wait_for_response(conn);
+       if (vret < 0) {
+               DBG_ERR_VLKB("vlkb_wait_for_response failed", -vret);
+               goto done;
+       }
+       completed = true;
+
+done:
+       if (params) {
+               varlink_object_unref(params);
+       }
+       if (conn) {
+               varlink_connection_free(conn);
+       }
+       KB_DBG_I("varlink keybridge get method: %s\n",
+                (completed) ? "success" : "failure");
+       return completed;
+}
+
+bool varlink_keybridge_entry_get(TALLOC_CTX *mem_ctx,
+                                const struct varlink_keybridge_config *kbc,
+                                struct varlink_keybridge_result **resp)
+{
+       return vlkb_entry_get(mem_ctx, kbc, resp);
+}
diff --git a/source3/modules/varlink_keybridge.h b/source3/modules/varlink_keybridge.h
new file mode 100644 (file)
index 0000000..5bcd058
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (c) 2025      John Mulligan <jmulligan@samba.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _VARLINK_KEYBRIDGE_H
+#define _VARLINK_KEYBRIDGE_H
+/*
+ * The keybridge is a simple varlink based protocol used to fetch
+ * configuration data, in particular key material for encrypted
+ * cephfs, that samba can use when setting up a share.
+ * The keybridge server may or may not be a proxy for more complex
+ * remote protocols that use (m)TLS HTTPS APIs to remote servers.
+ * keybridge aims to hide all that from samba and use a simple
+ * local API that is an existing samba dependency.
+ */
+
+/* kind describes how the data is stored:
+ * DEFAULT - unspecified - typically VALUE
+ * B64 - base64 encoded binary data
+ * VALUE - plain (UTF8) string
+ */
+enum varlink_keybridge_kind {
+       VARLINK_KEYBRIDGE_KIND_DEFAULT = 0,
+       VARLINK_KEYBRIDGE_KIND_B64,
+       VARLINK_KEYBRIDGE_KIND_VALUE
+};
+
+/* status of a keybridge api call */
+enum varlink_keybridge_status {
+       /* protocol/connection error */
+       VARLINK_KEYBRIDGE_STATUS_FAILURE = 0,
+       /* result is successful */
+       VARLINK_KEYBRIDGE_STATUS_OK,
+       /* server returned an error message */
+       VARLINK_KEYBRIDGE_STATUS_ERROR,
+};
+
+/* parameters for an outgoing api Get entry call */
+struct varlink_keybridge_config {
+       /* path to socket with unix: prefix */
+       char *path;
+       /* keybridge scope */
+       char *scope;
+       /* keybridge entry name */
+       char *name;
+       /* keybridge entry kind (data format) */
+       enum varlink_keybridge_kind kind;
+};
+
+/* Get entry call results */
+struct varlink_keybridge_result {
+       enum varlink_keybridge_status status;
+       /* data kind */
+       enum varlink_keybridge_kind kind;
+       /* result data or error string */
+       char *data;
+};
+
+/* Get a requested entry.
+ * returns true if result was populated
+ * result will be assigned a newly allocated result (from mem_ctx)
+ */
+bool varlink_keybridge_entry_get(TALLOC_CTX *mem_ctx,
+                                const struct varlink_keybridge_config *kbc,
+                                struct varlink_keybridge_result **resp);
+
+#endif /* _VARLINK_KEYBRIDGE_H */