/* * Ouroboros - Copyright (C) 2016 - 2023 * * Enrollment Task * * Dimitri Staessens * Sander Vrijders * * 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 #include #include #include #include #include #include #include "common/connmgr.h" #include "common/enroll.h" #include "ipcp.h" #include #include #include #include #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 int send_rcv_enroll_msg(int fd) { 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 ret; struct enroll_resp resp; buf.data = __buf; buf.len = sizeof(__buf); len = enroll_req_ser(buf); if (len < 0) { log_dbg("Failed to pack request message."); return -1; } clock_gettime(CLOCK_REALTIME, &t0); log_dbg("Sending request message."); if (flow_write(fd, buf.data, len) < 0) { log_dbg("Failed to send request message."); return -1; } log_dbg("Waiting for reply message."); len = flow_read(fd, buf.data, buf.len); if (len < 0) { log_dbg("No reply received."); return -1; } log_dbg("Received configuration info (%zd bytes).", len); msg.data = buf.data; msg.len = len; ret = enroll_resp_des(&resp, msg); if (ret < 0) { log_dbg("Failed to unpack response message."); return -1; } if (resp.response < 0) { log_dbg("Remote denied request: %d.", resp.response); return -1; } if (resp.conf.type != ipcpi.type) { log_dbg("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("Clock offset above threshold."); enroll.conf = resp.conf; return 0; } static void * enroll_handle(void * o) { struct enroll_resp resp; struct conn conn; uint8_t __buf[ENROLL_BUF_LEN]; buffer_t buf; ssize_t len; int response; (void) o; buf.data = __buf; buf.len = sizeof(__buf); resp.conf = enroll.conf; while (true) { buffer_t msg; if (connmgr_wait(COMPID_ENROLL, &conn)) { log_err("Failed to get next connection."); continue; } log_info("New enrollment connection."); len = flow_read(conn.flow_info.fd, buf.data, buf.len); if (len < 0) { log_err("Failed to read from flow."); connmgr_dealloc(COMPID_ENROLL, &conn); continue; } log_dbg("Read request from flow (%zd bytes).", len); msg.data = buf.data; msg.len = (size_t) len; if (enroll_req_des(msg) < 0) { log_err("Failed to unpack request message."); connmgr_dealloc(COMPID_ENROLL, &conn); continue; } /* TODO: authentication */ log_dbg("Enrolling a new neighbor."); clock_gettime(CLOCK_REALTIME, &resp.t); resp.response = 0; len = enroll_resp_ser(&resp, buf); if (len < 0) { log_err("Failed to pack reply."); connmgr_dealloc(COMPID_ENROLL, &conn); continue; } log_dbg("Sending enrollment info (%zd bytes).", len); if (flow_write(conn.flow_info.fd, buf.data, len) < 0) { log_err("Failed respond to request."); connmgr_dealloc(COMPID_ENROLL, &conn); continue; } len = flow_read(conn.flow_info.fd, buf.data, buf.len); if (len < 0) { log_err("Failed to read from flow."); connmgr_dealloc(COMPID_ENROLL, &conn); continue; } msg.data = buf.data; msg.len = (size_t) len; if (enroll_ack_des(&response, msg) < 0) { log_err("Failed to unpack acknowledgment."); connmgr_dealloc(COMPID_ENROLL, &conn); continue; } if (response == 0) log_info("Neighbor enrollment successful."); else log_info("Neigbor enrolment failed at remote."); connmgr_dealloc(COMPID_ENROLL, &conn); log_info("Enrollment connection closed."); } return 0; } int enroll_boot(struct conn * conn) { log_dbg("Starting enrollment."); if (send_rcv_enroll_msg(conn->flow_info.fd)) { log_err("Failed to enroll."); return -1; } log_dbg("Enrollment complete."); return 0; } int enroll_ack(struct conn * conn, int result) { uint8_t __buf[ENROLL_BUF_LEN]; buffer_t buf; ssize_t len; buf.data = __buf; buf.len = sizeof(__buf); len = enroll_ack_ser(result, buf); if (len < 0) { log_err("Failed to pack acknowledgement."); return -1; } if (flow_write(conn->flow_info.fd, buf.data, len) < 0) { log_dbg("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); }