On Mon, Oct 09, 2023 at 12:29:43AM +0200, Tobias Heider wrote: > The diff below adds pledge("stdio") calls for the pfkey dump subset > of ipsecctl commands. > > In particular ipsecctl -s which prints all SAs or flows in the kernel > and more importantly ipsecctl -m which contiously parses and prints every > pfkey message forwarded by the kernel don't seem to need any additional > privileges after setting up pfkey sockets and sysctls. > > ok?
New diff after some contstructive feedback from mbuhl@. ipsecctl -m stays the same, ipsecctl -s is a bit more complicated. I moved SA and flow printing to the new common function ipsecctl_show() which first does all the setup, then drops priviledges and finally parses and prints the messages. With this all of -sa -ssa and -sf should work properly. diff /home/user/got/co/src commit - 6d4ae77d67512ff88b0f7db1a1f3f79dda95dead path + /home/user/got/co/src blob - 51858d1a404c26e8c23259910dc399905909ef22 file + sbin/ipsecctl/ipsecctl.c --- sbin/ipsecctl/ipsecctl.c +++ sbin/ipsecctl/ipsecctl.c @@ -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"); + + 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,116 @@ ipsecctl_print_title(char *title) } void -ipsecctl_show_flows(int opts) +ipsecctl_show(int opts) { struct ipsecctl ipsec; struct ipsec_rule *rp; - - bzero(&ipsec, sizeof(ipsec)); - ipsec.opts = opts; - TAILQ_INIT(&ipsec.rule_queue); - - ipsecctl_get_rules(&ipsec); - - 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 (rp->auth) { - free(rp->auth->srcid); - free(rp->auth->dstid); - free(rp->auth); - } - 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; + size_t need = 0, rlen; + char *sbuf = NULL, *rbuf = NULL, *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_SHOWFLOWS) { + bzero(&ipsec, sizeof(ipsec)); + ipsec.opts = opts; + TAILQ_INIT(&ipsec.rule_queue); + rbuf = ipsecctl_get_rules(&ipsec, &rlen); + } - if (opts & IPSECCTL_OPT_SHOWALL) - ipsecctl_print_title("SAD:"); + 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_sas: sysctl"); - if (need == 0) { + /* 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"); + } else { + if (opts & IPSECCTL_OPT_SHOWALL) + printf("No entries\n"); + } + } + + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + + if (rbuf != NULL) { + ipsecctl_parse_rules(&ipsec, rbuf, rlen); + if (opts & IPSECCTL_OPT_SHOWALL) - printf("No entries\n"); - return; + 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) + printf("No flows\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 (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 ((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++; + } + qsort(sad, sacount, sizeof(*sad), sacompare); + for (i = 0; i < sacount; i++) + pfkey_print_sa(sad[i].sad_msg, opts); + free(sad); + free(sbuf); } - 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 +897,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) blob - c7f92d7d41c3db71a6be686413c46a1b3d304892 file + sbin/ipsecctl/ipsecctl.h --- sbin/ipsecctl/ipsecctl.h +++ sbin/ipsecctl/ipsecctl.h @@ -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 *); blob - 388b3663e3b4762acbcea9afb83a8a0998d49a3b file + sbin/ipsecctl/pfkey.c --- sbin/ipsecctl/pfkey.c +++ sbin/ipsecctl/pfkey.c @@ -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 (;;) {