This patch adds a queue to the config structure to house the security association database and the necessary functions to copy information (spps, keys, etc) from a security associations file into the security association database. Although these functions are similar to those found in config.c, it is important the keys are stored in a separate file from the rest of the config, thus all processing is done in sad.c
Signed-off-by: Clay Kaiser <clay.kai...@ibm.com> --- config.c | 2 + config.h | 3 + makefile | 7 +- ptp4l.c | 6 + sad.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++ sad.h | 53 +++++++ sad_private.h | 69 +++++++++ 7 files changed, 514 insertions(+), 3 deletions(-) create mode 100644 sad.c create mode 100644 sad.h create mode 100644 sad_private.h diff --git a/config.c b/config.c index b104f1b..8150dc7 100644 --- a/config.c +++ b/config.c @@ -364,6 +364,7 @@ struct config_item config_tab[] = { GLOB_ITEM_INT("utc_offset", CURRENT_UTC_OFFSET, 0, INT_MAX), GLOB_ITEM_INT("verbose", 0, 0, 1), GLOB_ITEM_INT("write_phase_mode", 0, 0, 1), + GLOB_ITEM_STR("sa_file", NULL), }; static struct unicast_master_table *current_uc_mtab; @@ -918,6 +919,7 @@ struct config *config_create(void) } STAILQ_INIT(&cfg->interfaces); STAILQ_INIT(&cfg->unicast_master_tables); + STAILQ_INIT(&cfg->security_association_database); cfg->opts = config_alloc_longopts(); if (!cfg->opts) { diff --git a/config.h b/config.h index 14d2f64..6915c85 100644 --- a/config.h +++ b/config.h @@ -45,6 +45,9 @@ struct config { /* unicast master tables */ STAILQ_HEAD(ucmtab_head, unicast_master_table) unicast_master_tables; + + /* security association database */ + STAILQ_HEAD(sa_head, security_association) security_association_database; }; int config_read(const char *name, struct config *cfg); diff --git a/makefile b/makefile index 3e3b8b3..581cca4 100644 --- a/makefile +++ b/makefile @@ -23,6 +23,7 @@ VER = -DVER=$(version) CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) LDLIBS = -lm -lrt -pthread $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc tz2alt +SECURITY = sad.o FILTERS = filter.o mave.o mmedian.o SERVOS = linreg.o ntpshm.o nullf.o pi.o refclock_sock.o servo.o TRANSP = raw.o transport.o udp.o udp6.o uds.o @@ -30,9 +31,9 @@ TS2PHC = ts2phc.o lstab.o nmea.o serial.o sock.o ts2phc_generic_pps_source.o \ ts2phc_nmea_pps_source.o ts2phc_phc_pps_source.o ts2phc_pps_sink.o ts2phc_pps_source.o OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \ e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o monitor.o msg.o phc.o \ - port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) \ - sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \ - unicast_fsm.o unicast_service.o util.o version.o + port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SECURITY) \ + $(SERVOS) sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \ + unicast_fsm.o unicast_service.o util.o version.o OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_agent.o \ pmc_common.o sysoff.o timemaster.o $(TS2PHC) tz2alt.o diff --git a/ptp4l.c b/ptp4l.c index c61175b..58c26b7 100644 --- a/ptp4l.c +++ b/ptp4l.c @@ -29,6 +29,7 @@ #include "pi.h" #include "print.h" #include "raw.h" +#include "sad.h" #include "sk.h" #include "transport.h" #include "udp6.h" @@ -192,6 +193,10 @@ int main(int argc, char *argv[]) sk_tx_timeout = config_get_int(cfg, NULL, "tx_timestamp_timeout"); sk_hwts_filter_mode = config_get_int(cfg, NULL, "hwts_filter"); + if (sad_create(cfg)) { + goto out; + } + if (config_get_int(cfg, NULL, "clock_servo") == CLOCK_SERVO_NTPSHM) { config_set_int(cfg, "kernel_leap", 0); config_set_int(cfg, "sanity_freq_limit", 0); @@ -255,6 +260,7 @@ int main(int argc, char *argv[]) out: if (clock) clock_destroy(clock); + sad_destroy(cfg); config_destroy(cfg); return err; } diff --git a/sad.c b/sad.c new file mode 100644 index 0000000..b6ee416 --- /dev/null +++ b/sad.c @@ -0,0 +1,377 @@ +/** + * @file sad.c + * @brief Security Association Database + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> + +#include "config.h" +#include "msg.h" +#include "print.h" +#include "sad.h" +#include "sad_private.h" +#include "tlv.h" + +static struct security_association *current_sa; +static struct integrity_alg_info supported_algorithms [] = { + { "SHA256-128", HMAC_SHA256_128, 16 }, + { "SHA256", HMAC_SHA256, 32 }, + { "AES128", CMAC_AES128, 16 }, + { "AES256", CMAC_AES256, 16 }, + { NULL, 0, 0 }, +}; + +static void sad_destroy_association(struct security_association *sa) +{ + struct security_association_key *key; + while ((key = STAILQ_FIRST(&sa->keys))) { + STAILQ_REMOVE_HEAD(&sa->keys, list); + sad_deinit_mac(key->data); + free(key); + } +} + +void sad_destroy(struct config *cfg) +{ + struct security_association *sa; + while ((sa = STAILQ_FIRST(&cfg->security_association_database))) { + sad_destroy_association(sa); + STAILQ_REMOVE_HEAD(&cfg->security_association_database, list); + free(sa); + } +} + +static int sad_config_switch_security_association(struct config *cfg, + int spp, int line_num) +{ + struct security_association *sa; + current_sa = NULL; + + if (spp < 0 || spp > UINT8_MAX) { + pr_err("sa_file: line %d: spp %d is out of range. " + "Must be in the range %d to %d - ignoring", + line_num, spp, 0, UINT8_MAX); + return -1; + } + STAILQ_FOREACH(sa, &cfg->security_association_database, list) { + if (sa->spp == spp) { + pr_err("line %d: sa %d already taken" + " - ignoring", line_num, spp); + return -1; + } + } + sa = calloc(1, sizeof(*sa)); + if (!sa) { + pr_err("low memory"); + return -1; + } + STAILQ_INIT(&sa->keys); + sa->spp = spp; + /* set defaults */ + sa->seqnum_ind = 0; + sa->seqnum_len = 0; + sa->seqid_window = 3; + sa->immediate_ind = 1; + sa->res_ind = 0; + sa->res_len = 0; + sa->mutable = 0; + sa->last_seqid = 0; + + STAILQ_INSERT_TAIL(&cfg->security_association_database, sa, list); + current_sa = sa; + + return 0; +} + +static int sad_config_sa_seqnum_len(int seqnum_len, int line_num) +{ + if (!current_sa) { + pr_err("sa_file: line %d: missing spp - ignoring", + line_num); + return -1; + } + + if (seqnum_len > 0) { + pr_err("sa_file: line %d: seqnum field not supported (yet)" + " - ignoring", line_num); + return -1; + } + + current_sa->seqnum_len = seqnum_len; + current_sa->seqnum_ind = (seqnum_len > 0) ? 1 : 0; + + return 0; +} + +static int sad_config_sa_seqid_window(int seqid_window, int line_num) +{ + if (!current_sa) { + pr_err("sa_file: line %d: missing spp - ignoring", + line_num); + return -1; + } + + if (seqid_window < 0 || seqid_window > UINT16_MAX / 2) { + pr_err("sa_file: line %d: seqid_window %d out of range" + " - ignoring", line_num, seqid_window); + return -1; + } + + current_sa->seqid_window = seqid_window; + + return 0; +} + +static int sad_config_sa_res_len(int res_len, int line_num) +{ + if (!current_sa) { + pr_err("sa_file: line %d: missing spp - ignoring", + line_num); + return -1; + } + + if (res_len > 0) { + pr_err("sa_file: line %d: res field not supported (yet)" + " - ignoring", line_num); + return -1; + } + + current_sa->res_len = res_len; + current_sa->res_ind = (res_len > 0) ? 1 : 0; + + return 0; +} + +static int sad_config_sa_mutable(int mutable, int line_num) +{ + if (!current_sa) { + pr_err("sa_file: line %d: missing spp - ignoring", + line_num); + return -1; + } + + if (mutable < 0 || mutable > 1) { + pr_err("sa_file: line %d: allow_mutable must be 0 or 1" + " - ignoring", line_num); + return -1; + } + + current_sa->mutable = (mutable > 0) ? 1 : 0; + + return 0; +} + +static int sad_config_parse_key(char *line, UInteger32 *id, + const char **type, char **key) +{ + char *token, *tokens[4]; + int count = 0; + + token = strtok(line, " "); + while (token != NULL && count < 4) { + tokens[count] = token; + count++; + token = strtok(NULL, " "); + } + if (count < 2 || count > 3) { + return 0; + } + + if (sscanf(tokens[0], "%u", id) != 1) { + return 0; + } + if (count == 2) { + *type = "SHA256-128"; + *key = tokens[1]; + } else { + *type = tokens[1]; + *key = tokens[2]; + } + + return 1; +} + +static int sad_config_security_association_key(UInteger32 key_id, const char *icv_str, + char *key_str, int line_num) +{ + struct security_association_key *key; + struct integrity_alg_info *icv; + int key_len = 0; + + if (!current_sa) { + pr_err("sa_file: line %d: missing spp - ignoring", + line_num); + return -1; + } + STAILQ_FOREACH(key, ¤t_sa->keys, list) { + if (key->key_id == key_id) { + pr_err("sa_file: line %d: key_id %u already taken" + " - ignoring", line_num, key_id); + return -1; + } + } + key = calloc(1, sizeof(*key)); + if (!key) { + pr_err("low memory"); + free(key); + return -1; + } + /* process key_id */ + if (key_id < 1 || key_id >= UINT32_MAX) { + pr_err("sa_file: line %d: key_id %u out of range" + " - ignoring", line_num, key_id); + free(key); + return -1; + } + key->key_id = key_id; + /* process icv_str */ + for (icv = supported_algorithms; icv->label; icv++) { + if (!strcasecmp(icv->label, icv_str)) { + key->icv = *icv; + break; + } + } + if (icv->label == NULL) { + pr_err("sa_file: line %d: unsupported algorithm: %s" + " - ignoring", line_num, icv_str); + free(key); + return -1; + } + /* process key_str */ + key_len = strlen(key_str); + if (strncmp(key_str, "ASCII:", 6) == 0) { + memmove(key_str, key_str + 6, key_len - 6); + key_len = key_len - 6; + } else if (strncmp(key_str, "HEX:", 4) == 0) { + memmove(key_str, key_str + 4, key_len - 4); + key_len = key_len - 4; + if (key_len % 2) { + pr_err("sa_file: line %d: invalid key length" + " - ignoring", line_num); + free(key); + return -1; + } + for (int i = 0; i < key_len; i += 2) { + char hex_pair[3] = { key_str[i], key_str[i + 1], '\0' }; + long value = strtol(hex_pair, NULL, 16); + key_str[i / 2] = (char)value; + } + key_len = key_len / 2; + memset(&key_str[key_len], 0, key_len); + key_str[key_len] = '\0'; + } + /* initialize mac function */ + key->data = sad_init_mac(icv->type, (unsigned char *) key_str, key_len); + if (key->data == NULL) { + pr_err("sa_file: line %d: key %u init failed" + " - ignoring", line_num, key_id); + free(key); + return -1; + } + memset(&key_str, 0, key_len); + + STAILQ_INSERT_TAIL(¤t_sa->keys, key, list); + + return 0; +} + +static int sad_parse_security_association_line(struct config *cfg, + char *line, int line_num) +{ + int spp, seqnum_len, seqid_window, res_len, mutable; + UInteger32 key_id; + char *key_value; + const char *key_type; + + if (sscanf(line, " spp %d", &spp) == 1) + return sad_config_switch_security_association(cfg, spp, line_num); + + if (sscanf(line, " seqnum_length %d", &seqnum_len) == 1) + return sad_config_sa_seqnum_len(seqnum_len, line_num); + + if (sscanf(line, " seqid_window %d", &seqid_window) == 1) + return sad_config_sa_seqid_window(seqid_window, line_num); + + if (sscanf(line, " res_length %d", &res_len) == 1) + return sad_config_sa_res_len(res_len, line_num); + + if (sscanf(line, " allow_mutable %d", &mutable) == 1) + return sad_config_sa_mutable(mutable, line_num); + + if (sad_config_parse_key(line, &key_id, &key_type, &key_value)) + return sad_config_security_association_key(key_id, key_type, + key_value, line_num); + + return 0; +} + +int sad_create(struct config *cfg) +{ + char buf[1024], *line, *c; + int line_num; + + const char *sa_file = config_get_string(cfg, NULL, "sa_file"); + if (sa_file == NULL || strlen(sa_file) == 0) { + return 0; + } + + FILE *fp = fopen(sa_file, "r"); + if (!fp) { + pr_err("failed to open sa_file %s: %m", sa_file); + return -1; + } + + /* destroy current sad if already configured */ + sad_destroy(cfg); + + for (line_num = 1; fgets(buf, sizeof(buf), fp); line_num++) { + c = buf; + /* skip whitespace characters */ + while (isspace(*c)) + c++; + /* ignore empty lines and comments */ + if (*c == '#' || *c == '\n' || *c == '\0') + continue; + + line = c; + /* remove trailing whitespace characters and \n */ + c += strlen(line) - 1; + while (c > line && (*c == '\n' || isspace(*c))) { + *c-- = '\0'; + } + if (!strcasecmp(line, "[security_association]")) { + current_sa = NULL; + continue; + } + /* remove associated sa and continue if a config line fails */ + if (sad_parse_security_association_line(cfg, line, line_num)) { + if (current_sa != NULL) { + pr_debug("discarding sa %u", current_sa->spp); + sad_destroy_association(current_sa); + STAILQ_REMOVE(&cfg->security_association_database, + current_sa, security_association, list); + free(current_sa); + current_sa = NULL; + } + continue; + } + } + + fclose(fp); + return 0; +} diff --git a/sad.h b/sad.h new file mode 100644 index 0000000..a6b647d --- /dev/null +++ b/sad.h @@ -0,0 +1,53 @@ +/** + * @file sad.h + * @brief Security Association Database + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef HAVE_SAD_H +#define HAVE_SAD_H + +#include <sys/queue.h> + +#include "pdt.h" + +struct security_association { + STAILQ_ENTRY(security_association) list; + UInteger8 spp; /* negotiated by key management (see 3.1.68). */ + STAILQ_HEAD(keys_head, security_association_key) keys; + Boolean seqnum_ind; /* not supported in 1588-2019 */ + UInteger16 seqnum_len; /* value of “S” in Table 131 */ + UInteger16 seqid_window; /* sequenceID window for anti-replay */ + Boolean immediate_ind; /* immediate or delayed boolean */ + Boolean res_ind; /* not supported in 1588-2019 */ + UInteger16 res_len; /* value of “R” in Table 131 */ + Boolean mutable; /* allow for mutable correction */ + UInteger16 last_seqid; +}; + +/** + * Read the defined security association file and append to config. + * @param cfg config where security association database should be stored + * @return -1 if the read failed, 0 otherwise + */ +int sad_create(struct config *cfg); + +/** + * Free current security association database + * @param cfg config where security association database should is stored + */ +void sad_destroy(struct config *cfg); + +#endif diff --git a/sad_private.h b/sad_private.h new file mode 100644 index 0000000..141970c --- /dev/null +++ b/sad_private.h @@ -0,0 +1,69 @@ +/** + * @file sad_private.h + * @brief Security Association Database + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef HAVE_SAD_PRIVATE_H +#define HAVE_SAD_PRIVATE_H + +#include <sys/queue.h> + +#include "pdt.h" + +typedef enum { + INVALID = 0, + HMAC_SHA256_128, + HMAC_SHA256, + CMAC_AES128, + CMAC_AES256, +} integrity_alg_type; + +struct integrity_alg_info { + const char *label; + integrity_alg_type type; /* algorithm type - minimum HMAC-SHA256-128 */ + UInteger16 len; /* length of icv */ +}; + +struct mac_data; +struct security_association_key { + STAILQ_ENTRY(security_association_key) list; + struct integrity_alg_info icv; + struct mac_data *data; /* data for mac function */ + UInteger32 key_id; /* symmetric key ID */ +}; + +int sad_hash(struct mac_data *mac_data, + const void *data, int data_len, + unsigned char *mac, int mac_len) +{ + pr_debug("security configured but not supported"); + return 0; +} + +struct mac_data *sad_init_mac(integrity_alg_type algorithm, + const unsigned char *key, int key_len) +{ + pr_debug("security configured but not supported"); + return NULL; +} + +void sad_deinit_mac(struct mac_data *mac_data) +{ + pr_debug("security configured but not supported"); + return; +} + +#endif -- 2.42.1 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel