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 (;;) {

Reply via email to