pfctl should check pfctl.astack is not overrun

2019-04-17 Thread Petr Hoffmann

Hi,

I noticed pfctl crashes on segfault when anchors go too deep:

--8<---
$ cat ~/pf.conf | head -5
anchor foo {
anchor foo {
anchor foo {
anchor foo {
anchor foo {

$ grep anchor ~/pf.conf | wc -l
  66
$ /sbin/pfctl -nf ~/pf.conf
Segmentation fault (core dumped)
--->8--

It seems there is no check we fit into pfctl.astack[]. The attached
patch resolves this issue:

--8<---
$ ./pfctl -nf ~/pf.conf
pfctl: pfa_anchor: anchors too deep

$ grep anchor ~/pf2.conf | wc -l
  63
$ ./pfctl -nf ~/pf2.conf
$
--->8--

Petr
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 1e7ce21..5e19c5f39da 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -846,6 +846,8 @@ pfa_anchor  : '{'
 
/* steping into a brace anchor */
pf->asd++;
+   if (pf->asd >= PFCTL_ANCHOR_STACK_DEPTH)
+   errx(1, "pfa_anchor: anchors too deep");
pf->bn++;
pf->brace = 1;
 


Re: introduce 'pfctl -FR' to reset settings to defaults

2019-04-02 Thread Petr Hoffmann

On 02.04.2019 12:06, Klemens Nanni wrote:

On Tue, Apr 02, 2019 at 11:28:43AM +0200, Petr Hoffmann wrote:

would make me believe everything mentioned as OPTIONS in pf.conf(5) is about
to be reset. I see e.g. the debug level is reset, but what about the other
stuff like fingerprints, 'skip on' and other options set via the 'set'
command? Maybe the manpage should be more precise here?

Seems fine to me, given that a) some options are not persisted in the
driver but only effective during ruleset parsing and b) stuff like
fingerprints is already flushed separately, see pfctl(8) `-F osfp'.
For me, forcing the user to think what is meant by 'options' is not very 
friendly, though I understand the idea behind *some* options being used 
only while parsing. Let's assume I'm the smart user who is able to 
distinguish them. But then, 'set skip on' is the persistent one, right, 
but still not reset, I guess.


Petr


On 02.04.2019 12:06, Klemens Nanni wrote:

On Tue, Apr 02, 2019 at 11:28:43AM +0200, Petr Hoffmann wrote:

would make me believe everything mentioned as OPTIONS in pf.conf(5) is about
to be reset. I see e.g. the debug level is reset, but what about the other
stuff like fingerprints, 'skip on' and other options set via the 'set'
command? Maybe the manpage should be more precise here?

Seems fine to me, given that a) some options are not persisted in the
driver but only effective during ruleset parsing and b) stuff like
fingerprints is already flushed separately, see pfctl(8) `-F osfp'.





Re: introduce 'pfctl -FR' to reset settings to defaults

2019-04-02 Thread Petr Hoffmann

Hi,

seeing this in the manpage
--8<--
+.It Fl F Cm Reset
+Reset limits, timeouts and options back to default settings.
-->8--
would make me believe everything mentioned as OPTIONS in pf.conf(5) is 
about to be reset. I see e.g. the debug level is reset, but what about 
the other stuff like fingerprints, 'skip on' and other options set via 
the 'set' command? Maybe the manpage should be more precise here?


PH

On 02.04.2019 9:40, Alexandr Nedvedicky wrote:

Hello,

below is diff I plan to commit. I did add a comment to pfctl_reset()
and wording in manpage.

thanks and
regards
sashan

8<---8<---8<---8<---
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index 48b2893cfcd..00bd27c200a 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -197,6 +197,8 @@ Flush the filter information (statistics that are not bound 
to rules).
  Flush the tables.
  .It Fl F Cm osfp
  Flush the passive operating system fingerprints.
+.It Fl F Cm Reset
+Reset limits, timeouts and options back to default settings.
  .It Fl F Cm all
  Flush all of the above.
  .El
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 493ff47af2f..40929d90530 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -105,6 +105,7 @@ int  pfctl_load_rule(struct pfctl *, char *, struct pf_rule 
*, int);
  const char*pfctl_lookup_option(char *, const char **);
  void  pfctl_state_store(int, const char *);
  void  pfctl_state_load(int, const char *);
+void   pfctl_reset(int, int);
  
  const char	*clearopt;

  char  *rulesopt;
@@ -205,7 +206,8 @@ static const struct {
  };
  
  static const char *clearopt_list[] = {

-   "rules", "Sources", "states", "info", "Tables", "osfp", "all", NULL
+   "rules", "Sources", "states", "info", "Tables", "osfp", "Reset",
+   "all", NULL
  };
  
  static const char *showopt_list[] = {

@@ -2232,6 +2234,41 @@ pfctl_state_load(int dev, const char *file)
fclose(f);
  }
  
+void

+pfctl_reset(int dev, int opts)
+{
+   struct pfctlpf;
+   struct pfr_buffer t;
+   int i;
+
+   pf.dev = dev;
+   pfctl_init_options(&pf);
+
+   /* Force reset upon pfctl_load_options() */
+   pf.debug_set = 1;
+   pf.reass_set = 1;
+   pf.syncookieswat_set = 1;
+   pf.ifname = strdup("none");
+   pf.ifname_set = 1;
+
+   memset(&t, 0, sizeof(t));
+   t.pfrb_type = PFRB_TRANS;
+   if (pfctl_trans(dev, &t, DIOCXBEGIN, 0))
+   warn("%s, DIOCXBEGIN", __func__);
+
+
+   for (i = 0; pf_limits[i].name; i++)
+   pf.limit_set[pf_limits[i].index] = 1;
+
+   for (i = 0; pf_timeouts[i].name; i++)
+   pf.timeout_set[pf_timeouts[i].timeout] = 1;
+
+   pfctl_load_options(&pf);
+
+   if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+   warn("%s, DIOCXCOMMIT", __func__);
+}
+
  int
  main(int argc, char *argv[])
  {
@@ -2558,6 +2595,7 @@ main(int argc, char *argv[])
pfctl_clear_stats(dev, ifaceopt, opts);
pfctl_clear_fingerprints(dev, opts);
pfctl_clear_interface_flags(dev, opts);
+   pfctl_reset(dev, opts);
}
break;
case 'o':
@@ -2566,6 +2604,9 @@ main(int argc, char *argv[])
case 'T':
pfctl_clear_tables(anchorname, opts);
break;
+   case 'R':
+   pfctl_reset(dev, opts);
+   break;
}
}
if (state_killers) {





invalid netmasks should be reported

2019-03-27 Thread Petr Hoffmann

Hi,

I noticed it is possible to specify an invalid netmask,
e.g. 1.1.1.1/10/20 and still get the address loaded into a table. I
conjecture this was introduced by the following change:

a7ede25358dad545e0342d2a9f8ef6ce68c6df66
Zap bits in host_v4(), use mask parameter

It looks like the author missed the path addresses are loaded by pfctl's 
'-T add' command. I guess the '/20' is dropped in host() and then '/10' 
is processed within host_ip() by inet_net_pton() so no error is reported.


The proposed patch is attached. For me it works:

    OLD:
    # pfctl -t tableta -T add 1.1.1.1/10/20
    1 table created.
    1/1 addresses added.

    NEW:
    # $PFCTL -t tableta -T add
    1.1.1.1/10/20
    netmask is invalid: /10/20

Petr

diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index ee3c2926f1a..5737846123d 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1627,7 +1627,7 @@ host(const char *s, int opts)
if_name++;
}
 
-   if ((p = strrchr(ps, '/')) != NULL) {
+   if ((p = strchr(ps, '/')) != NULL) {
mask = strtonum(p+1, 0, 128, &errstr);
if (errstr) {
fprintf(stderr, "netmask is %s: %s\n", errstr, p);


Re: once rules fix

2019-03-05 Thread petr . hoffmann
Klemens Nanni  writes:

> Thanks! Diff makes sense, see comments inline.  I confirm that this
> restores intended behaviour and regress is fine as well.
>
> With those addressed OK kn;  or I take care of it after getting an OK.
> sashan?

Thanks for pointing to the details. Fixed now:

diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index e8dd97f6222..ceca208ab71 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -354,7 +354,7 @@ struct pfctl_watermarks  syncookie_opts;
 int disallow_table(struct node_host *, const char *);
 int disallow_urpf_failed(struct node_host *, const char *);
 int disallow_alias(struct node_host *, const char *);
-int rule_consistent(struct pf_rule *, int);
+int rule_consistent(struct pf_rule *);
 int process_tabledef(char *, struct table_opts *, int);
 voidexpand_label_str(char *, size_t, const char *, const char *);
 voidexpand_label_if(const char *, char *, size_t, const char *);
@@ -377,8 +377,7 @@ void expand_rule(struct pf_rule *, int, 
struct node_if *,
struct node_proto *,
struct node_os *, struct node_host *, struct node_port *,
struct node_host *, struct node_port *, struct node_uid *,
-   struct node_gid *, struct node_if *, struct node_icmp *,
-   const char *);
+   struct node_gid *, struct node_if *, struct node_icmp *);
 int expand_queue(char *, struct node_if *, struct queue_opts *);
 int expand_skip_interface(struct node_if *);
 
@@ -876,6 +875,7 @@ anchorrule  : ANCHOR anchorname dir quick interface af 
proto fromto
{
struct pf_rule  r;
struct node_proto   *proto;
+   char*p;
 
memset(&r, 0, sizeof(r));
if (pf->astack[pf->asd + 1]) {
@@ -913,7 +913,33 @@ anchorrule : ANCHOR anchorname dir quick interface af 
proto fromto
"rules must specify a name");
YYERROR;
}
+
+   /*
+* Don't make non-brace anchors part of the 
main anchor pool.
+*/
+   if ((r.anchor = calloc(1, sizeof(*r.anchor))) 
== NULL) {
+   err(1, "anchorrule: calloc");
+   }
+   pf_init_ruleset(&r.anchor->ruleset);
+   r.anchor->ruleset.anchor = r.anchor;
+   if (strlcpy(r.anchor->path, $2,
+   sizeof(r.anchor->path)) >= 
sizeof(r.anchor->path)) {
+   errx(1, "anchorrule: strlcpy");
+   }
+   if ((p = strrchr($2, '/')) != NULL) {
+   if (strlen(p) == 1) {
+   yyerror("anchorrule: bad anchor 
name %s",
+   $2);
+   YYERROR;
+   }
+   } else
+   p = $2;
+   if (strlcpy(r.anchor->name, p,
+   sizeof(r.anchor->name)) >= 
sizeof(r.anchor->name)) {
+   errx(1, "anchorrule: strlcpy");
+   }
}
+
r.direction = $3;
r.quick = $4.quick;
r.af = $6;
@@ -955,8 +981,7 @@ anchorrule  : ANCHOR anchorname dir quick interface af 
proto fromto
 
expand_rule(&r, 0, $5, NULL, NULL, NULL, $7, $8.src_os,
$8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
-   $9.uid, $9.gid, $9.rcv, $9.icmpspec,
-   pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+   $9.uid, $9.gid, $9.rcv, $9.icmpspec);
free($2);
pf->astack[pf->asd + 1] = NULL;
}
@@ -1110,7 +1135,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af 
antispoof_opts {
if (h != NULL)
expand_rule(&r, 0, j, NULL, NULL, NULL,
NULL, NULL, h, NULL, NULL, NULL,
-   NULL, NULL, NULL, NULL, "");
+   NULL, NULL, NULL, NULL);
 
if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
   

Re: once rules fix

2019-03-05 Thread petr . hoffmann
Sorry, my MUA replaced tabs with spaces in the patch I sent
previously. Find the correct one below:

diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index e8dd97f6222..e55b2893069 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -354,7 +354,7 @@ struct pfctl_watermarks  syncookie_opts;
 int disallow_table(struct node_host *, const char *);
 int disallow_urpf_failed(struct node_host *, const char *);
 int disallow_alias(struct node_host *, const char *);
-int rule_consistent(struct pf_rule *, int);
+int rule_consistent(struct pf_rule *);
 int process_tabledef(char *, struct table_opts *, int);
 voidexpand_label_str(char *, size_t, const char *, const char *);
 voidexpand_label_if(const char *, char *, size_t, const char *);
@@ -377,8 +377,7 @@ void expand_rule(struct pf_rule *, int, 
struct node_if *,
struct node_proto *,
struct node_os *, struct node_host *, struct node_port *,
struct node_host *, struct node_port *, struct node_uid *,
-   struct node_gid *, struct node_if *, struct node_icmp *,
-   const char *);
+   struct node_gid *, struct node_if *, struct node_icmp *);
 int expand_queue(char *, struct node_if *, struct queue_opts *);
 int expand_skip_interface(struct node_if *);
 
@@ -876,6 +875,7 @@ anchorrule  : ANCHOR anchorname dir quick interface af 
proto fromto
{
struct pf_rule  r;
struct node_proto   *proto;
+   char*p;
 
memset(&r, 0, sizeof(r));
if (pf->astack[pf->asd + 1]) {
@@ -913,7 +913,33 @@ anchorrule : ANCHOR anchorname dir quick interface af 
proto fromto
"rules must specify a name");
YYERROR;
}
+
+   /*
+* Don't make non-brace anchors part of the 
main anchor pool.
+*/
+   if ((r.anchor = calloc(1, sizeof(*r.anchor))) 
== NULL) {
+   err(1, "anchorrule: calloc");
+   }
+   pf_init_ruleset(&r.anchor->ruleset);
+   r.anchor->ruleset.anchor = r.anchor;
+   if (strlcpy(r.anchor->path, $2,
+   sizeof(r.anchor->path)) >= 
sizeof(r.anchor->path)) {
+   errx(1, "anchorrule: strlcpy");
+   }
+   if ((p = strrchr($2, '/')) != NULL) {
+   if (strlen(p) == 1) {
+   yyerror("anchorrule: bad anchor 
name %s",
+   $2);
+   YYERROR;
+   }
+   } else
+   p = (char *)$2;
+   if (strlcpy(r.anchor->name, p,
+   sizeof(r.anchor->name)) >= 
sizeof(r.anchor->name)) {
+   errx(1, "anchorrule: strlcpy");
+   }
}
+
r.direction = $3;
r.quick = $4.quick;
r.af = $6;
@@ -955,8 +981,7 @@ anchorrule  : ANCHOR anchorname dir quick interface af 
proto fromto
 
expand_rule(&r, 0, $5, NULL, NULL, NULL, $7, $8.src_os,
$8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
-   $9.uid, $9.gid, $9.rcv, $9.icmpspec,
-   pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+   $9.uid, $9.gid, $9.rcv, $9.icmpspec);
free($2);
pf->astack[pf->asd + 1] = NULL;
}
@@ -1110,7 +1135,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af 
antispoof_opts {
if (h != NULL)
expand_rule(&r, 0, j, NULL, NULL, NULL,
NULL, NULL, h, NULL, NULL, NULL,
-   NULL, NULL, NULL, NULL, "");
+   NULL, NULL, NULL, NULL);
 
if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
bzero(&r, sizeof(r));
@@ -1132,7 +1157,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af 
antispoof_opts {
expand_rule(&

once rules fix

2019-03-03 Thread Petr Hoffmann

Hi,

I noticed that pfctl says 'once' can be used only with pass/block rules,
but it is not true - it can't for block but can for anchor rules:

--8<---
# echo 'block once' | pfctl -f -
stdin:1: 'once' only applies to pass/block rules
pfctl: Syntax error in config file: pf rules not loaded

# echo 'anchor xxx once' | pfctl -f -
# pfctl -sr
anchor "xxx" all once
--->8--

The patch below resolves the issue. Note that pf_rule.action has no
special constant for anchors. The patch uses pf_rule.anchor to recognize
an anchor rule. To do this, I moved a piece of code from pfctl_add_rule()
to parse.y so that the anchor is created early and pf_rule.anchor becomes
non-NULL. This further allowed tosimplify expand_rule() and
pfctl_add_rule() by shortening their parameterlists.

The patched pfctl responds as expected:

--8<---
# echo 'block once' | ./pfctl -f -
# pfctl -sr
block drop all once
# echo 'anchor xxx once' | ./pfctl -f -
stdin:1: 'once' only applies to pass/block rules
pfctl: Syntax error in config file: pf rules not loaded
--->8--


diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index e8dd97f6222..ce866883b04 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -354,7 +354,7 @@ struct pfctl_watermarks  syncookie_opts;
 int         disallow_table(struct node_host *, const char *);
 int         disallow_urpf_failed(struct node_host *, const char *);
 int         disallow_alias(struct node_host *, const char *);
-int         rule_consistent(struct pf_rule *, int);
+int         rule_consistent(struct pf_rule *);
 int         process_tabledef(char *, struct table_opts *, int);
 void         expand_label_str(char *, size_t, const char *, const char *);
 void         expand_label_if(const char *, char *, size_t, const char *);
@@ -377,8 +377,7 @@ void         expand_rule(struct pf_rule *, int, 
struct node_if *,

         struct node_proto *,
         struct node_os *, struct node_host *, struct node_port *,
         struct node_host *, struct node_port *, struct node_uid *,
-            struct node_gid *, struct node_if *, struct node_icmp *,
-            const char *);
+            struct node_gid *, struct node_if *, struct node_icmp *);
 int         expand_queue(char *, struct node_if *, struct queue_opts *);
 int         expand_skip_interface(struct node_if *);

@@ -876,6 +875,7 @@ anchorrule    : ANCHOR anchorname dir quick 
interface af proto fromto

     {
         struct pf_rule    r;
         struct node_proto    *proto;
+            char    *p;

         memset(&r, 0, sizeof(r));
         if (pf->astack[pf->asd + 1]) {
@@ -913,7 +913,33 @@ anchorrule    : ANCHOR anchorname dir quick 
interface af proto fromto

                     "rules must specify a name");
                 YYERROR;
             }
+
+                /*
+                 * Don't make non-brace anchors part of the main anchor 
pool.

+                 */
+                if ((r.anchor = calloc(1, sizeof(*r.anchor))) == NULL) {
+                    err(1, "anchorrule: calloc");
+                }
+ pf_init_ruleset(&r.anchor->ruleset);
+                r.anchor->ruleset.anchor = r.anchor;
+                if (strlcpy(r.anchor->path, $2,
+                    sizeof(r.anchor->path)) >= sizeof(r.anchor->path)) {
+                   errx(1, "anchorrule: strlcpy");
+                }
+                if ((p = strrchr($2, '/')) != NULL) {
+                    if (strlen(p) == 1) {
+                        yyerror("anchorrule: bad anchor name %s",
+                            $2);
+                        YYERROR;
+                    }
+                } else
+                    p = (char *)$2;
+                if (strlcpy(r.anchor->name, p,
+                    sizeof(r.anchor->name)) >= sizeof(r.anchor->name)) {
+                   errx(1, "anchorrule: strlcpy");
+                }
         }
+
         r.direction = $3;
         r.quick = $4.quick;
         r.af = $6;
@@ -955,8 +981,7 @@ anchorrule    : ANCHOR anchorname dir quick 
interface af proto fromto


         expand_rule(&r, 0, $5, NULL, NULL, NULL, $7, $8.src_os,
             $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
-                $9.uid, $9.gid, $9.rcv, $9.icmpspec,
-                pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+                $9.uid, $9.gid, $9.rcv, $9.icmpspec);
         free($2);
         pf->astack[pf->asd + 1] = NULL;
     }
@@ -1110,7 +1135,7 @@ antispoof    : ANTISPOOF logquick antispoof_ifspc 
af antispoof_opts {

             if (h != NULL)
                 expand_rule(&r, 0, j, NULL, NULL, NULL,
                     NULL, NULL, h, NULL, NULL, NULL,
-