Skip to content

Commit

Permalink
port: add security processing to bc_event()
Browse files Browse the repository at this point in the history
add sad_process_auth() to bc_event() to check for authentication tlvs on
incoming messages. This processing happens after msg_post_recv().
However, if security is active, a duplicate message is allocated and
kept in network byte order to be used for icv calculation.

The standard proposes a security parameters database (SPD) to specify
policy limiting attributes as to which messages should authenticated but
this is a lot of overhead for a something that isn't too helpful. For
this patch, the only policy limiting attribute is the port. That is to
say, you can specify a spp (and corresponding Security Association) for
each port. When spp is set to -1, no security processing is done.

Signed-off-by: Clay Kaiser <Clay.Kaiser@ibm.com>
Reviewed-by: Erez Geva <ErezGeva2@gmail.com>
Reviewed-by: Miroslav Lichvar <mlichvar@redhat.com>
  • Loading branch information
Clay Kaiser (via linuxptp-devel Mailing List) authored and richardcochran committed Jun 4, 2024
1 parent d9511f7 commit 60980bb
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 2 deletions.
54 changes: 54 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ enum config_type {
CFG_TYPE_DOUBLE,
CFG_TYPE_ENUM,
CFG_TYPE_STRING,
CFG_TYPE_UINT,
};

struct config_enum {
Expand All @@ -65,6 +66,7 @@ typedef union {
int i;
double d;
char *s;
uint32_t u;
} any_t;

#define CONFIG_LABEL_SIZE 64
Expand Down Expand Up @@ -109,6 +111,14 @@ struct config_item {
.min.i = _min, \
.max.i = _max, \
}
#define CONFIG_ITEM_UINT(_label, _port, _default, _min, _max) { \
.label = _label, \
.type = CFG_TYPE_UINT, \
.flags = _port ? CFG_ITEM_PORT : 0, \
.val.u = _default, \
.min.u = _min, \
.max.u = _max, \
}
#define CONFIG_ITEM_STRING(_label, _port, _default) { \
.label = _label, \
.type = CFG_TYPE_STRING, \
Expand All @@ -125,6 +135,9 @@ struct config_item {
#define GLOB_ITEM_INT(label, _default, min, max) \
CONFIG_ITEM_INT(label, 0, _default, min, max)

#define GLOB_ITEM_UIN(label, _default, min, max) \
CONFIG_ITEM_UINT(label, 0, _default, min, max)

#define GLOB_ITEM_STR(label, _default) \
CONFIG_ITEM_STRING(label, 0, _default)

Expand All @@ -137,6 +150,9 @@ struct config_item {
#define PORT_ITEM_INT(label, _default, min, max) \
CONFIG_ITEM_INT(label, 1, _default, min, max)

#define PORT_ITEM_UIN(label, _default, min, max) \
CONFIG_ITEM_UINT(label, 1, _default, min, max)

#define PORT_ITEM_STR(label, _default) \
CONFIG_ITEM_STRING(label, 1, _default)

Expand Down Expand Up @@ -236,6 +252,7 @@ static struct config_enum bmca_enu[] = {
};

struct config_item config_tab[] = {
PORT_ITEM_UIN("active_key_id", 0, 0, UINT32_MAX),
PORT_ITEM_INT("allowedLostResponses", 3, 1, 255),
PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX),
PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu),
Expand Down Expand Up @@ -340,6 +357,7 @@ struct config_item config_tab[] = {
GLOB_ITEM_STR("slave_event_monitor", ""),
GLOB_ITEM_INT("slaveOnly", 0, 0, 1), /*deprecated*/
GLOB_ITEM_INT("socket_priority", 0, 0, 15),
PORT_ITEM_INT("spp", -1, -1, UINT8_MAX),
GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX),
GLOB_ITEM_INT("step_window", 0, 0, INT_MAX),
GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX),
Expand Down Expand Up @@ -574,6 +592,7 @@ static enum parser_result parse_item(struct config *cfg,
struct config_enum *cte;
double df;
int val;
uint32_t uval;

r = parse_fault_interval(cfg, section, option, value);
if (r != NOT_PARSED)
Expand Down Expand Up @@ -606,6 +625,9 @@ static enum parser_result parse_item(struct config *cfg,
case CFG_TYPE_STRING:
r = PARSED_OK;
break;
case CFG_TYPE_UINT:
r = get_ranged_uint(value, &uval, cgi->min.u, cgi->max.u);
break;
}
if (r != PARSED_OK) {
return r;
Expand Down Expand Up @@ -650,6 +672,9 @@ static enum parser_result parse_item(struct config *cfg,
}
dst->flags |= CFG_ITEM_DYNSTR;
break;
case CFG_TYPE_UINT:
dst->val.u = uval;
break;
}

if (commandline) {
Expand Down Expand Up @@ -1029,6 +1054,7 @@ int config_get_int(struct config *cfg, const char *section, const char *option)
switch (ci->type) {
case CFG_TYPE_DOUBLE:
case CFG_TYPE_STRING:
case CFG_TYPE_UINT:
pr_err("bug: config option %s type mismatch!", option);
exit(-1);
case CFG_TYPE_INT:
Expand All @@ -1039,6 +1065,19 @@ int config_get_int(struct config *cfg, const char *section, const char *option)
return ci->val.i;
}

uint32_t config_get_uint(struct config *cfg, const char *section,
const char *option)
{
struct config_item *ci = config_find_item(cfg, section, option);

if (!ci || ci->type != CFG_TYPE_UINT) {
pr_err("bug: config option %s missing or invalid!", option);
exit(-1);
}
pr_debug("config item %s.%s is %u", section, option, ci->val.u);
return ci->val.u;
}

char *config_get_string(struct config *cfg, const char *section,
const char *option)
{
Expand Down Expand Up @@ -1146,6 +1185,7 @@ int config_set_section_int(struct config *cfg, const char *section,
switch (cgi->type) {
case CFG_TYPE_DOUBLE:
case CFG_TYPE_STRING:
case CFG_TYPE_UINT:
pr_err("bug: config option %s type mismatch!", option);
return -1;
case CFG_TYPE_INT:
Expand All @@ -1171,6 +1211,20 @@ int config_set_section_int(struct config *cfg, const char *section,
return 0;
}

int config_set_uint(struct config *cfg, const char *option, uint32_t val)
{
struct config_item *ci = config_find_item(cfg, NULL, option);

if (!ci || ci->type != CFG_TYPE_UINT) {
pr_err("bug: config option %s missing or invalid!", option);
return -1;
}
ci->flags |= CFG_ITEM_LOCKED;
ci->val.u = val;
pr_debug("locked item global.%s as %u", option, ci->val.u);
return 0;
}

int config_set_string(struct config *cfg, const char *option,
const char *val)
{
Expand Down
5 changes: 5 additions & 0 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ double config_get_double(struct config *cfg, const char *section,
int config_get_int(struct config *cfg, const char *section,
const char *option);

uint32_t config_get_uint(struct config *cfg, const char *section,
const char *option);

char *config_get_string(struct config *cfg, const char *section,
const char *option);

Expand All @@ -87,6 +90,8 @@ static inline int config_set_int(struct config *cfg,
return config_set_section_int(cfg, NULL, option, val);
}

int config_set_uint(struct config *cfg, const char *option, uint32_t val);

int config_set_string(struct config *cfg, const char *option,
const char *val);

Expand Down
2 changes: 2 additions & 0 deletions configs/default.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ power_profile.2017.totalTimeInaccuracy -1
power_profile.grandmasterID 0
power_profile.version none
ptp_minor_version 1
spp -1
active_key_id 0
#
# Run time options
#
Expand Down
4 changes: 4 additions & 0 deletions msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ struct ptp_message *msg_duplicate(struct ptp_message *msg, int cnt)
dup->refcnt = 1;
TAILQ_INIT(&dup->tlv_list);

if (!cnt) {
return dup;
}

err = msg_post_recv(dup, cnt);
if (err) {
switch (err) {
Expand Down
3 changes: 2 additions & 1 deletion msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ void msg_cleanup(void);
* @param msg A message obtained using @ref msg_allocate().
* The passed message must be in network byte order, not
* having been passed to @ref msg_post_recv().
*
* @param cnt The size of 'msg' in bytes. set to zero when
* @ref msg_post_recv() is not required (icv calculation)
* @return Pointer to a message on success, NULL otherwise.
* The returned message will be in host byte order, having
* been passed to @ref msg_post_recv().
Expand Down
59 changes: 58 additions & 1 deletion port.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "port_private.h"
#include "print.h"
#include "rtnl.h"
#include "sad.h"
#include "sk.h"
#include "tc.h"
#include "tlv.h"
Expand All @@ -58,6 +59,7 @@ enum syfu_event {

static int port_is_ieee8021as(struct port *p);
static int port_is_uds(struct port *p);
static int port_has_security(struct port *p);
static void port_nrate_initialize(struct port *p);

static int announce_compare(struct ptp_message *m1, struct ptp_message *m2)
Expand Down Expand Up @@ -867,6 +869,11 @@ static int port_is_uds(struct port *p)
return transport_type(p->trp) == TRANS_UDS;
}

static int port_has_security(struct port *p)
{
return p->spp >= 0;
}

static void port_management_send_error(struct port *p, struct port *ingress,
struct ptp_message *msg, int error_id)
{
Expand Down Expand Up @@ -3014,7 +3021,7 @@ enum fsm_event port_event(struct port *p, int fd_index)
static enum fsm_event bc_event(struct port *p, int fd_index)
{
enum fsm_event event = EV_NONE;
struct ptp_message *msg;
struct ptp_message *msg, *dup = NULL;
int cnt, fd = p->fda.fd[fd_index], err;

switch (fd_index) {
Expand Down Expand Up @@ -3138,6 +3145,13 @@ static enum fsm_event bc_event(struct port *p, int fd_index)
msg_put(msg);
return EV_FAULT_DETECTED;
}
if (port_has_security(p)) {
dup = msg_duplicate(msg, 0);
if (!dup) {
msg_put(msg);
return EV_NONE;
}
}
err = msg_post_recv(msg, cnt);
if (err) {
switch (err) {
Expand All @@ -3149,18 +3163,43 @@ static enum fsm_event bc_event(struct port *p, int fd_index)
break;
}
msg_put(msg);
if (dup) {
msg_put(dup);
}
return EV_NONE;
}
port_stats_inc_rx(p, msg);
if (port_ignore(p, msg)) {
msg_put(msg);
if (dup) {
msg_put(dup);
}
return EV_NONE;
}
if (msg_sots_missing(msg) &&
!(p->timestamping == TS_P2P1STEP && msg_type(msg) == PDELAY_REQ)) {
pr_err("%s: received %s without timestamp",
p->log_name, msg_type_string(msg_type(msg)));
msg_put(msg);
if (dup) {
msg_put(dup);
}
return EV_NONE;
}
err = sad_process_auth(clock_config(p->clock), p->spp, msg, dup);
if (err) {
switch (err) {
case -EBADMSG:
pr_err("%s: auth: bad message", p->log_name);
break;
case -EPROTO:
pr_debug("%s: auth: ignoring message", p->log_name);
break;
}
msg_put(msg);
if (dup) {
msg_put(dup);
}
return EV_NONE;
}
if (msg_sots_valid(msg)) {
Expand Down Expand Up @@ -3212,6 +3251,9 @@ static enum fsm_event bc_event(struct port *p, int fd_index)
}

msg_put(msg);
if (dup) {
msg_put(dup);
}
return event;
}

Expand Down Expand Up @@ -3586,6 +3628,8 @@ struct port *port_open(const char *phc_device,
config_get_int(cfg, p->name, "power_profile.2017.totalTimeInaccuracy");
p->slave_event_monitor = clock_slave_monitor(clock);
p->allowedLostResponses = config_get_int(cfg, p->name, "allowedLostResponses");
p->spp = config_get_int(cfg, p->name, "spp");
p->active_key_id = config_get_uint(cfg, p->name, "active_key_id");

if (!port_is_uds(p) && unicast_client_initialize(p)) {
goto err_transport;
Expand Down Expand Up @@ -3615,6 +3659,19 @@ struct port *port_open(const char *phc_device,
if (p->net_sync_monitor && !p->hybrid_e2e) {
pr_warning("%s: net_sync_monitor needs hybrid_e2e", p->log_name);
}
if (sad_readiness_check(p->spp, p->active_key_id, clock_config(p->clock))) {
pr_err("%s: security readiness check failed", p->log_name);
goto err_uc_service;
}
if (port_has_security(p) && (p->timestamping == TS_ONESTEP ||
p->timestamping == TS_P2P1STEP)) {
pr_err("%s: spp not supported on one-step ports", p->log_name);
goto err_uc_service;
}
if (port_has_security(p) && (config_get_int(cfg, NULL, "ptp_minor_version") < 1)) {
pr_err("%s: spp needs at least PTPv2.1", p->log_name);
goto err_uc_service;
}

/* Set fault timeouts to a default value */
for (i = 0; i < FT_CNT; i++) {
Expand Down
2 changes: 2 additions & 0 deletions port_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ struct port {
/* slave event monitoring */
struct monitor *slave_event_monitor;
bool unicast_state_dirty;
int spp;
UInteger32 active_key_id;
struct {
unsigned int timer_count;
time_t last_renewal;
Expand Down
46 changes: 46 additions & 0 deletions sad.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,3 +577,49 @@ int sad_create(struct config *cfg)
fclose(fp);
return 0;
}

int sad_readiness_check(int spp, size_t active_key_id, struct config *cfg)
{
struct security_association *sa;
if (spp < 0 && active_key_id < 1) {
return 0;
}
#if !defined (HAVE_NETTLE) && !defined (HAVE_GNUTLS) && \
!defined (HAVE_GNUPG) && !defined (HAVE_OPENSSL)
if (spp >= 0 || active_key_id > 0) {
pr_err("spp or active_key_id set but security not supported");
return -1;
}
#endif
if (spp >= 0) {
if (!config_get_string(cfg, NULL, "sa_file")) {
pr_err("sa_file required when spp set");
return -1;
}
if (STAILQ_EMPTY(&cfg->security_association_database)) {
pr_err("spp set but sad is empty");
return -1;
}
sa = sad_get_association(cfg, spp);
if (!sa) {
pr_err("spp set but sa %u not defined", spp);
return -1;
}
if (active_key_id < 1) {
pr_err("active_key_id required when spp set");
return -1;
} else {
if (!sad_get_key(sa, active_key_id)) {
pr_err("sa %u: active_key_id set but key %zu"
" not defined", spp, active_key_id);
return -1;
}
}
} else {
if (active_key_id > 0) {
pr_err("spp required when active_key_id set");
return -1;
}
}
return 0;
}
Loading

0 comments on commit 60980bb

Please sign in to comment.