summaryrefslogtreecommitdiff
path: root/src/ipcpd/common/enroll.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipcpd/common/enroll.c')
-rw-r--r--src/ipcpd/common/enroll.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/src/ipcpd/common/enroll.c b/src/ipcpd/common/enroll.c
new file mode 100644
index 00000000..5e35ce37
--- /dev/null
+++ b/src/ipcpd/common/enroll.c
@@ -0,0 +1,337 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Enrollment Task
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 199309L
+#endif
+
+#define OUROBOROS_PREFIX "enrollment"
+
+#include <ouroboros/dev.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/serdes-oep.h>
+#include <ouroboros/time.h>
+
+#include "common/connmgr.h"
+#include "common/enroll.h"
+#include "ipcp.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#define ENROLL_COMP "Enrollment"
+#define ENROLL_PROTO "OEP" /* Ouroboros enrollment protocol */
+#define ENROLL_WARN_TIME_OFFSET 20
+#define ENROLL_BUF_LEN 1024
+
+enum enroll_state {
+ ENROLL_NULL = 0,
+ ENROLL_INIT,
+ ENROLL_RUNNING
+};
+
+struct {
+ struct ipcp_config conf;
+ enum enroll_state state;
+ pthread_t listener;
+} enroll;
+
+static void * enroll_handle(void * o)
+{
+ struct enroll_req req;
+ struct enroll_resp resp;
+ struct enroll_ack ack;
+ struct conn conn;
+ uint8_t __buf[ENROLL_BUF_LEN];
+ buffer_t buf;
+ ssize_t len;
+
+ (void) o;
+
+ buf.data = __buf;
+ buf.len = sizeof(__buf);
+
+ resp.response = 0;
+ resp.conf = enroll.conf;
+
+ while (true) {
+ buffer_t msg;
+ int fd;
+
+ if (connmgr_wait(COMPID_ENROLL, &conn)) {
+ log_err("Failed to get next connection.");
+ continue;
+ }
+
+ fd = conn.flow_info.fd;
+
+ log_info("Incoming enrollment connection on flow %d.", fd);
+
+ len = flow_read(fd, buf.data, buf.len);
+ if (len < 0) {
+ log_err("Failed to read from flow %d.", fd);
+ goto finish_flow;
+ }
+
+ msg.data = buf.data;
+ msg.len = (size_t) len;
+
+ if (enroll_req_des(&req, msg) < 0) {
+ log_err("Failed to unpack request message.");
+ goto finish_flow;
+ }
+
+ log_info_id(req.id, "Handling incoming enrollment.");
+
+ /* TODO: authentication, timezone handling (UTC). */
+
+ ack.result = -100;
+
+ clock_gettime(CLOCK_REALTIME, &resp.t);
+
+ memcpy(resp.id, req.id, ENROLL_ID_LEN);
+
+ len = enroll_resp_ser(&resp, buf);
+ if (len < 0) {
+ log_err_id(req.id, "Failed to pack reply.");
+ goto finish_enroll;
+ }
+
+ log_dbg_id(req.id, "Sending enrollment info (%zd bytes).", len);
+
+ if (flow_write(conn.flow_info.fd, buf.data, len) < 0) {
+ log_err_id(req.id, "Failed te send response.");
+ goto finish_enroll;
+ }
+
+ len = flow_read(conn.flow_info.fd, buf.data, buf.len);
+ if (len < 0) {
+ log_err_id(req.id, "Failed to read from flow.");
+ goto finish_enroll;
+ }
+
+ msg.data = buf.data;
+ msg.len = (size_t) len;
+
+ if (enroll_ack_des(&ack, msg) < 0) {
+ log_err_id(req.id, "Failed to unpack ack.");
+ goto finish_enroll;
+ }
+
+ if (memcmp(req.id, ack.id, ENROLL_ID_LEN) != 0)
+ log_warn_id(req.id, "Enrollment ID mismatch.");
+
+ finish_enroll:
+ switch(ack.result) {
+ case 0:
+ log_info_id(req.id, "Enrollment completed.");
+ break;
+ case -100:
+ log_warn_id(req.id, "Enrollment failed.");
+ break;
+ default:
+ log_warn_id(req.id, "Enrollment failed at remote.");
+ }
+ finish_flow:
+ connmgr_dealloc(COMPID_ENROLL, &conn);
+
+ log_info("Enrollment flow %d closed.", fd);
+ }
+
+ return 0;
+}
+
+int enroll_boot(struct conn * conn,
+ const uint8_t * id)
+{
+ uint8_t __buf[ENROLL_BUF_LEN];
+ buffer_t buf;
+ buffer_t msg;
+ ssize_t len;
+ ssize_t delta_t;
+ struct timespec t0;
+ struct timespec rtt;
+ int fd;
+ int ret;
+ struct enroll_req req;
+ struct enroll_resp resp;
+
+ fd = conn->flow_info.fd;
+
+ buf.data = __buf;
+ buf.len = sizeof(__buf);
+
+ memcpy(req.id, id, ENROLL_ID_LEN);
+
+ len = enroll_req_ser(&req, buf);
+ if (len < 0) {
+ log_err_id(id, "Failed to pack request message.");
+ return -1;
+ }
+
+ clock_gettime(CLOCK_REALTIME, &t0);
+
+ if (flow_write(fd, buf.data, len) < 0) {
+ log_err_id(id, "Failed to send request message.");
+ return -1;
+ }
+
+ len = flow_read(fd, buf.data, buf.len);
+ if (len < 0) {
+ log_err_id(id, "No reply received.");
+ return -1;
+ }
+
+ log_dbg_id(id, "Received configuration info (%zd bytes).", len);
+
+ msg.data = buf.data;
+ msg.len = len;
+
+ ret = enroll_resp_des(&resp, msg);
+ if (ret < 0) {
+ log_err_id(id, "Failed to unpack response message.");
+ return -1;
+ }
+
+ if (memcmp(resp.id, id, ENROLL_ID_LEN) != 0) {
+ log_err_id(id, "Enrollment ID mismatch.");
+ return -1;
+ }
+
+ if (resp.response < 0) {
+ log_warn_id(id, "Remote denied request: %d.", resp.response);
+ return -1;
+ }
+
+ if (resp.conf.type != ipcpi.type) {
+ log_err_id(id, "Wrong type in enrollment response %d (%d).",
+ resp.conf.type, ipcpi.type);
+ return -1;
+ }
+
+ clock_gettime(CLOCK_REALTIME, &rtt);
+
+ delta_t = ts_diff_ms(&t0, &rtt);
+
+ rtt.tv_sec = resp.t.tv_sec;
+ rtt.tv_nsec = resp.t.tv_nsec;
+
+ if (labs(ts_diff_ms(&t0, &rtt)) - delta_t > ENROLL_WARN_TIME_OFFSET)
+ log_warn_id(id, "Clock offset above threshold.");
+
+ enroll.conf = resp.conf;
+
+ return 0;
+}
+
+int enroll_ack(struct conn * conn,
+ const uint8_t * id,
+ const int result)
+{
+ struct enroll_ack ack;
+ uint8_t __buf[ENROLL_BUF_LEN];
+ buffer_t buf;
+ ssize_t len;
+
+ buf.data = __buf;
+ buf.len = sizeof(__buf);
+
+ ack.result = result;
+
+ memcpy(ack.id, id, ENROLL_ID_LEN);
+
+ len = enroll_ack_ser(&ack, buf);
+ if (len < 0) {
+ log_err_id(id, "Failed to pack acknowledgement.");
+ return -1;
+ }
+
+ if (flow_write(conn->flow_info.fd, buf.data, len) < 0) {
+ log_err_id(id, "Failed to send acknowledgment.");
+ return -1;
+ }
+
+ return 0;
+}
+
+void enroll_bootstrap(const struct ipcp_config * conf)
+{
+ assert(conf);
+
+ memcpy(&enroll.conf, conf, sizeof(enroll.conf));
+}
+
+struct ipcp_config * enroll_get_conf(void)
+{
+ return &enroll.conf;
+}
+
+int enroll_init(void)
+{
+ struct conn_info info;
+
+ memset(&info, 0, sizeof(info));
+
+ strcpy(info.comp_name, ENROLL_COMP);
+ strcpy(info.protocol, ENROLL_PROTO);
+ info.pref_version = 1;
+ info.pref_syntax = PROTO_GPB;
+ info.addr = 0;
+
+ if (connmgr_comp_init(COMPID_ENROLL, &info)) {
+ log_err("Failed to register with connmgr.");
+ return -1;
+ }
+
+ enroll.state = ENROLL_INIT;
+
+ return 0;
+}
+
+void enroll_fini(void)
+{
+ if (enroll.state == ENROLL_RUNNING)
+ pthread_join(enroll.listener, NULL);
+
+ connmgr_comp_fini(COMPID_ENROLL);
+}
+
+int enroll_start(void)
+{
+ if (pthread_create(&enroll.listener, NULL, enroll_handle, NULL))
+ return -1;
+
+ enroll.state = ENROLL_RUNNING;
+
+ return 0;
+}
+
+void enroll_stop(void)
+{
+ if (enroll.state == ENROLL_RUNNING)
+ pthread_cancel(enroll.listener);
+}