From 202aab4366ed4611c6b252e04a8146925a05301c Mon Sep 17 00:00:00 2001 From: tobhe Date: Mon, 9 Oct 2023 15:32:14 +0000 Subject: [PATCH] Add pledge("stdio") before parsing pfkey messages. This applies to ipsecctl -m and ipsecctl -s. Refactor ipsecctl_show_*() to setup all sysctls first before dropping privileges and finally parsing and printing IPsec SAs and flows. feedback and ok mbuhl@ ok deraadt@ --- sbin/ipsecctl/ipsecctl.c | 236 +++++++++++++++++++++------------------ sbin/ipsecctl/ipsecctl.h | 5 +- sbin/ipsecctl/pfkey.c | 5 +- 3 files changed, 134 insertions(+), 112 deletions(-) diff --git a/sbin/ipsecctl/ipsecctl.c b/sbin/ipsecctl/ipsecctl.c index 51858d1a404..ef58ad94ce3 100644 --- a/sbin/ipsecctl/ipsecctl.c +++ b/sbin/ipsecctl/ipsecctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsecctl.c,v 1.85 2023/03/07 17:43:59 guenther Exp $ */ +/* $OpenBSD: ipsecctl.c,v 1.86 2023/10/09 15:32:14 tobhe Exp $ */ /* * Copyright (c) 2004, 2005 Hans-Joerg Hoexer * @@ -57,10 +57,10 @@ void ipsecctl_print_flow(struct ipsec_rule *, int); void ipsecctl_print_sa(struct ipsec_rule *, int); void ipsecctl_print_sabundle(struct ipsec_rule *, int); int ipsecctl_flush(int); -void ipsecctl_get_rules(struct ipsecctl *); +char *ipsecctl_get_rules(struct ipsecctl *, size_t *); +void ipsecctl_parse_rules(struct ipsecctl *, char *, size_t); void ipsecctl_print_title(char *); -void ipsecctl_show_flows(int); -void ipsecctl_show_sas(int); +void ipsecctl_show(int); int ipsecctl_monitor(int); void usage(void); const char *ipsecctl_lookup_option(char *, const char **); @@ -595,30 +595,37 @@ ipsecctl_flush(int opts) return (0); } -void -ipsecctl_get_rules(struct ipsecctl *ipsec) +char * +ipsecctl_get_rules(struct ipsecctl *ipsec, size_t *need) { - struct sadb_msg *msg; - struct ipsec_rule *rule, *last = NULL; int mib[4]; - size_t need; - char *buf, *lim, *next; + char *buf; mib[0] = CTL_NET; mib[1] = PF_KEY; mib[2] = PF_KEY_V2; mib[3] = NET_KEY_SPD_DUMP; - if (sysctl(mib, 4, NULL, &need, NULL, 0) == -1) + if (sysctl(mib, 4, NULL, need, NULL, 0) == -1) err(1, "ipsecctl_get_rules: sysctl"); - if (need == 0) - return; - if ((buf = malloc(need)) == NULL) + if (*need == 0) + return NULL; + if ((buf = malloc(*need)) == NULL) err(1, "ipsecctl_get_rules: malloc"); - if (sysctl(mib, 4, buf, &need, NULL, 0) == -1) + if (sysctl(mib, 4, buf, need, NULL, 0) == -1) err(1, "ipsecctl_get_rules: sysctl"); - lim = buf + need; + return buf; +} + +void +ipsecctl_parse_rules(struct ipsecctl *ipsec, char *buf, size_t need) +{ + struct sadb_msg *msg; + struct ipsec_rule *rule, *last = NULL; + char *lim, *next; + + lim = buf + need; for (next = buf; next < lim; next += msg->sadb_msg_len * PFKEYV2_CHUNK) { msg = (struct sadb_msg *)next; @@ -627,13 +634,13 @@ ipsecctl_get_rules(struct ipsecctl *ipsec) rule = calloc(1, sizeof(struct ipsec_rule)); if (rule == NULL) - err(1, "ipsecctl_get_rules: calloc"); + err(1, "ipsecctl_parse_rules: calloc"); rule->nr = ipsec->rule_nr++; rule->type |= RULE_FLOW; TAILQ_INIT(&rule->collapsed_rules); if (pfkey_parse(msg, rule)) - errx(1, "ipsecctl_get_rules: " + errx(1, "ipsecctl_parse_rules: " "failed to parse PF_KEY message"); /* @@ -663,108 +670,117 @@ ipsecctl_print_title(char *title) } void -ipsecctl_show_flows(int opts) +ipsecctl_show(int opts) { struct ipsecctl ipsec; struct ipsec_rule *rp; + struct sadb_msg *msg; + struct sad *sad; + int mib[5], sacount, i; + size_t need = 0, rlen; + char *sbuf = NULL, *rbuf = NULL, *lim, *next; + + if (opts & IPSECCTL_OPT_SHOWFLOWS) { + bzero(&ipsec, sizeof(ipsec)); + ipsec.opts = opts; + TAILQ_INIT(&ipsec.rule_queue); + rbuf = ipsecctl_get_rules(&ipsec, &rlen); + } + + if (opts & IPSECCTL_OPT_SHOWSAS) { + mib[0] = CTL_NET; + mib[1] = PF_KEY; + mib[2] = PF_KEY_V2; + mib[3] = NET_KEY_SADB_DUMP; + mib[4] = SADB_SATYPE_UNSPEC; + + /* When the SAD is empty we get ENOENT, no need to err(). */ + if (sysctl(mib, 5, NULL, &need, NULL, 0) == -1 && + errno != ENOENT) + err(1, "ipsecctl_show: sysctl"); + if (need > 0) { + if ((sbuf = malloc(need)) == NULL) + err(1, "ipsecctl_show: malloc"); + if (sysctl(mib, 5, sbuf, &need, NULL, 0) == -1) + err(1, "ipsecctl_show: sysctl"); + } + } - bzero(&ipsec, sizeof(ipsec)); - ipsec.opts = opts; - TAILQ_INIT(&ipsec.rule_queue); + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); - ipsecctl_get_rules(&ipsec); + if (rbuf != NULL) { + ipsecctl_parse_rules(&ipsec, rbuf, rlen); - if (opts & IPSECCTL_OPT_SHOWALL) + if (opts & IPSECCTL_OPT_SHOWALL) + ipsecctl_print_title("FLOWS:"); + + if (TAILQ_FIRST(&ipsec.rule_queue) != NULL) { + while ((rp = TAILQ_FIRST(&ipsec.rule_queue))) { + TAILQ_REMOVE(&ipsec.rule_queue, rp, rule_entry); + + ipsecctl_print_rule(rp, ipsec.opts); + + free(rp->src->name); + free(rp->src); + free(rp->dst->name); + free(rp->dst); + if (rp->local) { + free(rp->local->name); + free(rp->local); + } + if (rp->peer) { + free(rp->peer->name); + free(rp->peer); + } + if (rp->auth) { + free(rp->auth->srcid); + free(rp->auth->dstid); + free(rp->auth); + } + free(rp); + } + } + } else if (opts & IPSECCTL_OPT_SHOWALL) { ipsecctl_print_title("FLOWS:"); - - if (TAILQ_FIRST(&ipsec.rule_queue) == 0) { if (opts & IPSECCTL_OPT_SHOWALL) printf("No flows\n"); - return; } - while ((rp = TAILQ_FIRST(&ipsec.rule_queue))) { - TAILQ_REMOVE(&ipsec.rule_queue, rp, rule_entry); - - ipsecctl_print_rule(rp, ipsec.opts); - - free(rp->src->name); - free(rp->src); - free(rp->dst->name); - free(rp->dst); - if (rp->local) { - free(rp->local->name); - free(rp->local); - } - if (rp->peer) { - free(rp->peer->name); - free(rp->peer); + if (sbuf != NULL) { + if (opts & IPSECCTL_OPT_SHOWALL) + ipsecctl_print_title("SAD:"); + + sacount = 0; + lim = sbuf + need; + for (next = sbuf; next < lim; + next += msg->sadb_msg_len * PFKEYV2_CHUNK) { + msg = (struct sadb_msg *)next; + if (msg->sadb_msg_len == 0) + break; + sacount++; } - if (rp->auth) { - free(rp->auth->srcid); - free(rp->auth->dstid); - free(rp->auth); + if ((sad = calloc(sacount, sizeof(*sad))) == NULL) + err(1, "ipsecctl_show: calloc"); + i = 0; + for (next = sbuf; next < lim; + next += msg->sadb_msg_len * PFKEYV2_CHUNK) { + msg = (struct sadb_msg *)next; + if (msg->sadb_msg_len == 0) + break; + sad[i].sad_spi = pfkey_get_spi(msg); + sad[i].sad_msg = msg; + i++; } - free(rp); - } -} - -void -ipsecctl_show_sas(int opts) -{ - struct sadb_msg *msg; - struct sad *sad; - int mib[5], sacount, i; - size_t need = 0; - char *buf, *lim, *next; - - mib[0] = CTL_NET; - mib[1] = PF_KEY; - mib[2] = PF_KEY_V2; - mib[3] = NET_KEY_SADB_DUMP; - mib[4] = SADB_SATYPE_UNSPEC; - - if (opts & IPSECCTL_OPT_SHOWALL) + qsort(sad, sacount, sizeof(*sad), sacompare); + for (i = 0; i < sacount; i++) + pfkey_print_sa(sad[i].sad_msg, opts); + free(sad); + free(sbuf); + } else if (opts & IPSECCTL_OPT_SHOWALL) { ipsecctl_print_title("SAD:"); - - /* When the SAD is empty we get ENOENT, no need to err(). */ - if (sysctl(mib, 5, NULL, &need, NULL, 0) == -1 && errno != ENOENT) - err(1, "ipsecctl_show_sas: sysctl"); - if (need == 0) { - if (opts & IPSECCTL_OPT_SHOWALL) - printf("No entries\n"); - return; + printf("No entries\n"); } - if ((buf = malloc(need)) == NULL) - err(1, "ipsecctl_show_sas: malloc"); - if (sysctl(mib, 5, buf, &need, NULL, 0) == -1) - err(1, "ipsecctl_show_sas: sysctl"); - sacount = 0; - lim = buf + need; - for (next = buf; next < lim; - next += msg->sadb_msg_len * PFKEYV2_CHUNK) { - msg = (struct sadb_msg *)next; - if (msg->sadb_msg_len == 0) - break; - sacount++; - } - if ((sad = calloc(sacount, sizeof(*sad))) == NULL) - err(1, "ipsecctl_show_sas: calloc"); - i = 0; - for (next = buf; next < lim; - next += msg->sadb_msg_len * PFKEYV2_CHUNK) { - msg = (struct sadb_msg *)next; - if (msg->sadb_msg_len == 0) - break; - sad[i].sad_spi = pfkey_get_spi(msg); - sad[i].sad_msg = msg; - i++; - } - qsort(sad, sacount, sizeof(*sad), sacompare); - for (i = 0; i < sacount; i++) - pfkey_print_sa(sad[i].sad_msg, opts); - free(sad); - free(buf); } int @@ -882,16 +898,18 @@ main(int argc, char *argv[]) if (showopt != NULL) { switch (*showopt) { case 'f': - ipsecctl_show_flows(opts); + opts |= IPSECCTL_OPT_SHOWFLOWS; break; case 's': - ipsecctl_show_sas(opts); + opts |= IPSECCTL_OPT_SHOWSAS; break; case 'a': + opts |= IPSECCTL_OPT_SHOWFLOWS; + opts |= IPSECCTL_OPT_SHOWSAS; opts |= IPSECCTL_OPT_SHOWALL; - ipsecctl_show_flows(opts); - ipsecctl_show_sas(opts); + break; } + ipsecctl_show(opts); } if (opts & IPSECCTL_OPT_MONITOR) diff --git a/sbin/ipsecctl/ipsecctl.h b/sbin/ipsecctl/ipsecctl.h index c7f92d7d41c..29c200f943e 100644 --- a/sbin/ipsecctl/ipsecctl.h +++ b/sbin/ipsecctl/ipsecctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsecctl.h,v 1.76 2023/08/07 04:10:08 dlg Exp $ */ +/* $OpenBSD: ipsecctl.h,v 1.77 2023/10/09 15:32:14 tobhe Exp $ */ /* * Copyright (c) 2004, 2005 Hans-Joerg Hoexer * @@ -30,6 +30,8 @@ #define IPSECCTL_OPT_MONITOR 0x0400 #define IPSECCTL_OPT_SHOWKEY 0x0800 #define IPSECCTL_OPT_COLLAPSE 0x1000 +#define IPSECCTL_OPT_SHOWFLOWS 0x2000 +#define IPSECCTL_OPT_SHOWSAS 0x4000 enum { ACTION_ADD, ACTION_DELETE @@ -244,7 +246,6 @@ int parse_rules(const char *, struct ipsecctl *); int cmdline_symset(char *); int ipsecctl_add_rule(struct ipsecctl *, struct ipsec_rule *); void ipsecctl_free_rule(struct ipsec_rule *); -void ipsecctl_get_rules(struct ipsecctl *); void ipsecctl_print_rule(struct ipsec_rule *, int); int ike_print_config(struct ipsec_rule *, int); int ike_ipsec_establish(int, struct ipsec_rule *, const char *); diff --git a/sbin/ipsecctl/pfkey.c b/sbin/ipsecctl/pfkey.c index 388b3663e3b..fb12f329f3d 100644 --- a/sbin/ipsecctl/pfkey.c +++ b/sbin/ipsecctl/pfkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfkey.c,v 1.63 2021/10/22 12:30:54 bluhm Exp $ */ +/* $OpenBSD: pfkey.c,v 1.64 2023/10/09 15:32:14 tobhe Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * Copyright (c) 2003, 2004 Markus Friedl @@ -1324,6 +1324,9 @@ pfkey_monitor(int opts) if (pfkey_promisc() < 0) return -1; + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + pfd[0].fd = fd; pfd[0].events = POLLIN; for (;;) { -- 2.20.1