On Fri, Nov 26, 2021 at 11:37:37PM +0100, Alexandr Nedvedicky wrote:
> Hello,
> 
> On Fri, Nov 26, 2021 at 01:01:40PM +0100, Claudio Jeker wrote:
> > 
> > One more thing to consider, I think the following test in pfi_set_flags():
> > 
> > > +                 if ((p->pfik_flags_new != p->pfik_flags) &&
> > > +                     (p->pfik_flagrefs == 0))
> > > +                         pfi_kif_ref(p, PFI_KIF_REF_FLAG);
> > 
> > should actually check for the PFI_IFLAG_SKIP flag and not any flag.
> > 
> >                     if (ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
> >                         p->pfik_flagrefs == 0)
> >                             pfi_kif_ref(p, PFI_KIF_REF_FLAG);
> > 
> > Same goes for pfi_clear_flags() just in reverse:
> > 
> > > +         if ((p->pfik_flags_new != p->pfik_flags) &&
> > > +             (p->pfik_flagrefs == 1))
> > > +                 pfi_kif_unref(p, PFI_KIF_REF_FLAG);
> > 
> > Should be changed to:
> >             if (!ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
> >                 p->pfik_flagrefs == 1)
> >                     pfi_kif_unref(p, PFI_KIF_REF_FLAG);
> > 
> > We only want to track the PFI_IFLAG_SKIP flag but not any other flag like
> > PFI_IFLAG_ANY. At least I think we want to do that, but then I guess
> > pfi_set_flags() should only add a kif
> >     if (found == 0 && ISSET(flags, PFI_IFLAG_SKIP))
> > 
> 
>     yes it makes sense.
> 
> > I don't really like pfi_set_flags() and pfi_clear_flags()
> > and their ioctls DIOCSETIFFLAG and DIOCCLRIFFLAG. There are no checks for
> > valid flag combinations. So anything goes for these functions.
> > Also should the name check not happen in the ioctl handler and return
> > EINVAL for bad input?
> > 
> 
>     the thing is that empty string acts as a kind of wildcard pfi_skip_if()
>     matches anything if 'name' is NULL or empty string. So I'm keeping sanity
>     check in pfi_set_flags().
> 
>     But it it still worth to add test for io == NULL to DIOCSETIFFLAG
>     and to DIOCCLRIFFLAG to avoid NULL pointer dereference (NULL->pfiio_name)
> 
> updated diff is below.

See comments below.


> +void
> +pfi_group_delmember(const char *group, struct ifnet *ifp)
> +{
> +     struct pfi_kif          *gkif, *ikif;
> +
> +     if ((gkif = pfi_kif_get(group, NULL)) == NULL ||
> +         (ikif = pfi_kif_get(ifp->if_xname, NULL)) == NULL)
> +             panic("%s: pfi_kif_get failed", __func__);
> +     ikif->pfik_flags_new = ikif->pfik_flags & ~gkif->pfik_flags;
> +
> +     pfi_group_change(group);
> +}
> +

This function is not quite right. For example:

ifconfig vio0 group foo group bar
pfctl -f - <<EOF
set skip on { foo bar }
block return
EOF
ping -qc2 100.64.1.2

PING 100.64.1.2 (100.64.1.2): 56 data bytes

--- 100.64.1.2 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.153/0.178/0.202/0.024 ms


Now lets remove just group bar from the interface.

ifconfig vio0 -group bar          
ping -qc2 100.64.1.2      

PING 100.64.1.2 (100.64.1.2): 56 data bytes
ping: sendmsg: Permission denied
ping: wrote 100.64.1.2 64 chars, ret=-1
ping: sendmsg: Permission denied
ping: wrote 100.64.1.2 64 chars, ret=-1
 
pfi_group_delmember() does not take into consideration other groups
(including the interface itself) that may still allow PFI_IFLAG_SKIP.
It just clears the flag.

I think on delete the flag needs to be recalculated after the group has
been removed. Not sure if this is easy possible though.

> --- a/sys/net/pf_ioctl.c
> +++ b/sys/net/pf_ioctl.c
> @@ -2892,6 +2892,9 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, 
> struct proc *p)
>       case DIOCSETIFFLAG: {
>               struct pfioc_iface *io = (struct pfioc_iface *)addr;
>  
> +             if (io == NULL)
> +                     return (EINVAL);

I would extend this to also make sure that io->pfiio_flags only sets
PFI_IFLAG_SKIP.
                if (io == NULL || io->pfiio_flags & ~PFI_IFLAG_SKIP)
                        return (EINVAL);

Userland is not allowed to touch PFI_IFLAG_ANY (at least that is my
understanding of the code).

> +
>               NET_LOCK();
>               PF_LOCK();
>               error = pfi_set_flags(io->pfiio_name, io->pfiio_flags);
> @@ -2903,6 +2906,9 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, 
> struct proc *p)
>       case DIOCCLRIFFLAG: {
>               struct pfioc_iface *io = (struct pfioc_iface *)addr;
>  
> +             if (io == NULL)
> +                     return (EINVAL);

Same here.

> +
>               NET_LOCK();
>               PF_LOCK();
>               error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags);

-- 
:wq Claudio

Reply via email to