Re: [Qemu-devel] [PATCH v2 2/4] Add access control support to qemu bridge helper

2011-10-24 Thread Corey Bryant



On 10/23/2011 09:10 AM, Blue Swirl wrote:

On Fri, Oct 21, 2011 at 15:07, Corey Bryantcor...@linux.vnet.ibm.com  wrote:

  We go to great lengths to restrict ourselves to just cap_net_admin as an OS
  enforced security mechanism.  However, we further restrict what we allow 
users
  to do to simply adding a tap device to a bridge interface by virtue of the 
fact
  that this is the only functionality we expose.

  This is not good enough though.  An administrator is likely to want to 
restrict
  the bridges that an unprivileged user can access, in particular, to restrict
  an unprivileged user from putting a guest on what should be isolated 
networks.

  This patch implements an ACL mechanism that is enforced by 
qemu-bridge-helper.
  The ACLs are fairly simple whitelist/blacklist mechanisms with a wildcard of
  'all'.  All users are blacklisted by default, and deny takes precedence over
  allow.

  An interesting feature of this ACL mechanism is that you can include external
  ACL files.  The main reason to support this is so that you can set different
  file system permissions on those external ACL files.  This allows an
  administrator to implement rather sophisicated ACL policies based on 
user/group

sophisticated



Yep, thanks.


  policies via the file system.

  As an example:

  /etc/qemu/bridge.conf root:qemu 0640

allow br0
include /etc/qemu/alice.conf
include /etc/qemu/bob.conf
include /etc/qemu/charlie.conf

  /etc/qemu/alice.conf root:alice 0640
allow br1

  /etc/qemu/bob.conf root:bob 0640
allow br2

  /etc/qemu/charlie.conf root:charlie 0640
deny all

I think syntax 'include/etc/qemu/user.d/*.conf' or 'includedir
/etc/qemu/user.d' could be also useful.



That could be useful, though I'm not sure it's necessary right now.


  This ACL pattern allows any user in the qemu group to get a tap device
  connected to br0 (which is bridged to the physical network).

  Users in the alice group can additionally get a tap device connected to br1.
  This allows br1 to act as a private bridge for the alice group.

  Users in the bob group can additionally get a tap device connected to br2.
  This allows br2 to act as a private bridge for the bob group.

  Users in the charlie group cannot get a tap device connected to any bridge.

  Under no circumstance can the bob group get access to br1 or can the alice
  group get access to br2.  And under no cicumstance can the charlie group
  get access to any bridge.

  Signed-off-by: Anthony Liguorialigu...@us.ibm.com
  Signed-off-by: Richa Marwaharmar...@linux.vnet.ibm.com
  Signed-off-by: Corey Bryantcor...@linux.vnet.ibm.com
  ---
qemu-bridge-helper.c |  141 
++
1 files changed, 141 insertions(+), 0 deletions(-)

  diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c
  index 2ce82fb..db257d5 100644
  --- a/qemu-bridge-helper.c
  +++ b/qemu-bridge-helper.c
  @@ -33,6 +33,105 @@

#include net/tap-linux.h

  +#define MAX_ACLS (128)

If all users (or groups) in the system have an ACL, this number could
be way too low. Please use a list instead.



I agree, we shouldn't be hard-coding the limit here.  I'll update this.


  +#define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR /bridge.conf
  +
  +enum {
  +ACL_ALLOW = 0,
  +ACL_ALLOW_ALL,
  +ACL_DENY,
  +ACL_DENY_ALL,
  +};
  +
  +typedef struct ACLRule {
  +int type;
  +char iface[IFNAMSIZ];
  +} ACLRule;
  +
  +static int parse_acl_file(const char *filename, ACLRule *acls, int 
*pacl_count)
  +{
  +int acl_count = *pacl_count;
  +FILE *f;
  +char line[4096];
  +
  +f = fopen(filename, r);
  +if (f == NULL) {
  +return -1;
  +}
  +
  +while (acl_count != MAX_ACLS
  +fgets(line, sizeof(line), f) != NULL) {
  +char *ptr = line;
  +char *cmd, *arg, *argend;
  +
  +while (isspace(*ptr)) {
  +ptr++;
  +}
  +
  +/* skip comments and empty lines */
  +if (*ptr == '#' || *ptr == 0) {
  +continue;
  +}
  +
  +cmd = ptr;
  +arg = strchr(cmd, ' ');
  +if (arg == NULL) {
  +arg = strchr(cmd, '\t');
  +}
  +
  +if (arg == NULL) {
  +fprintf(stderr, Invalid config line:\n  %s\n, line);
  +fclose(f);
  +errno = EINVAL;
  +return -1;
  +}
  +
  +*arg = 0;
  +arg++;
  +while (isspace(*arg)) {
  +arg++;
  +}
  +
  +argend = arg + strlen(arg);
  +while (arg != argend  isspace(*(argend - 1))) {
  +argend--;
  +}

These while loops to skip spaces are repeated, but the comment
skipping part is not, so it is not possible to have comments after
rules or split rules to several lines. I'd add a simple state variable
to track at which stage we are in parsing instead.



That could be useful too, but again not sure it's 

Re: [Qemu-devel] [PATCH v2 2/4] Add access control support to qemu bridge helper

2011-10-24 Thread Blue Swirl
On Mon, Oct 24, 2011 at 13:44, Corey Bryant cor...@linux.vnet.ibm.com wrote:


 On 10/23/2011 09:10 AM, Blue Swirl wrote:

 On Fri, Oct 21, 2011 at 15:07, Corey Bryantcor...@linux.vnet.ibm.com
  wrote:

   We go to great lengths to restrict ourselves to just cap_net_admin as
  an OS
   enforced security mechanism.  However, we further restrict what we
  allow users
   to do to simply adding a tap device to a bridge interface by virtue of
  the fact
   that this is the only functionality we expose.
 
   This is not good enough though.  An administrator is likely to want to
  restrict
   the bridges that an unprivileged user can access, in particular, to
  restrict
   an unprivileged user from putting a guest on what should be isolated
  networks.
 
   This patch implements an ACL mechanism that is enforced by
  qemu-bridge-helper.
   The ACLs are fairly simple whitelist/blacklist mechanisms with a
  wildcard of
   'all'.  All users are blacklisted by default, and deny takes
  precedence over
   allow.
 
   An interesting feature of this ACL mechanism is that you can include
  external
   ACL files.  The main reason to support this is so that you can set
  different
   file system permissions on those external ACL files.  This allows an
   administrator to implement rather sophisicated ACL policies based on
  user/group

 sophisticated


 Yep, thanks.

   policies via the file system.
 
   As an example:
 
   /etc/qemu/bridge.conf root:qemu 0640
 
     allow br0
     include /etc/qemu/alice.conf
     include /etc/qemu/bob.conf
     include /etc/qemu/charlie.conf
 
   /etc/qemu/alice.conf root:alice 0640
     allow br1
 
   /etc/qemu/bob.conf root:bob 0640
     allow br2
 
   /etc/qemu/charlie.conf root:charlie 0640
     deny all

 I think syntax 'include/etc/qemu/user.d/*.conf' or 'includedir
 /etc/qemu/user.d' could be also useful.


 That could be useful, though I'm not sure it's necessary right now.

It can be added later.

   This ACL pattern allows any user in the qemu group to get a tap device
   connected to br0 (which is bridged to the physical network).
 
   Users in the alice group can additionally get a tap device connected
  to br1.
   This allows br1 to act as a private bridge for the alice group.
 
   Users in the bob group can additionally get a tap device connected to
  br2.
   This allows br2 to act as a private bridge for the bob group.
 
   Users in the charlie group cannot get a tap device connected to any
  bridge.
 
   Under no circumstance can the bob group get access to br1 or can the
  alice
   group get access to br2.  And under no cicumstance can the charlie
  group
   get access to any bridge.
 
   Signed-off-by: Anthony Liguorialigu...@us.ibm.com
   Signed-off-by: Richa Marwaharmar...@linux.vnet.ibm.com
   Signed-off-by: Corey Bryantcor...@linux.vnet.ibm.com
   ---
     qemu-bridge-helper.c |  141
  ++
     1 files changed, 141 insertions(+), 0 deletions(-)
 
   diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c
   index 2ce82fb..db257d5 100644
   --- a/qemu-bridge-helper.c
   +++ b/qemu-bridge-helper.c
   @@ -33,6 +33,105 @@
 
     #include net/tap-linux.h
 
   +#define MAX_ACLS (128)

 If all users (or groups) in the system have an ACL, this number could
 be way too low. Please use a list instead.


 I agree, we shouldn't be hard-coding the limit here.  I'll update this.

   +#define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR /bridge.conf
   +
   +enum {
   +    ACL_ALLOW = 0,
   +    ACL_ALLOW_ALL,
   +    ACL_DENY,
   +    ACL_DENY_ALL,
   +};
   +
   +typedef struct ACLRule {
   +    int type;
   +    char iface[IFNAMSIZ];
   +} ACLRule;
   +
   +static int parse_acl_file(const char *filename, ACLRule *acls, int
  *pacl_count)
   +{
   +    int acl_count = *pacl_count;
   +    FILE *f;
   +    char line[4096];
   +
   +    f = fopen(filename, r);
   +    if (f == NULL) {
   +        return -1;
   +    }
   +
   +    while (acl_count != MAX_ACLS
   +            fgets(line, sizeof(line), f) != NULL) {
   +        char *ptr = line;
   +        char *cmd, *arg, *argend;
   +
   +        while (isspace(*ptr)) {
   +            ptr++;
   +        }
   +
   +        /* skip comments and empty lines */
   +        if (*ptr == '#' || *ptr == 0) {
   +            continue;
   +        }
   +
   +        cmd = ptr;
   +        arg = strchr(cmd, ' ');
   +        if (arg == NULL) {
   +            arg = strchr(cmd, '\t');
   +        }
   +
   +        if (arg == NULL) {
   +            fprintf(stderr, Invalid config line:\n  %s\n, line);
   +            fclose(f);
   +            errno = EINVAL;
   +            return -1;
   +        }
   +
   +        *arg = 0;
   +        arg++;
   +        while (isspace(*arg)) {
   +            arg++;
   +        }
   +
   +        argend = arg + strlen(arg);
   +        while (arg != argend  isspace(*(argend - 1))) {
   +            argend--;
   +        }

 These while loops to skip spaces are 

Re: [Qemu-devel] [PATCH v2 2/4] Add access control support to qemu bridge helper

2011-10-23 Thread Blue Swirl
On Fri, Oct 21, 2011 at 15:07, Corey Bryant cor...@linux.vnet.ibm.com wrote:
 We go to great lengths to restrict ourselves to just cap_net_admin as an OS
 enforced security mechanism.  However, we further restrict what we allow users
 to do to simply adding a tap device to a bridge interface by virtue of the 
 fact
 that this is the only functionality we expose.

 This is not good enough though.  An administrator is likely to want to 
 restrict
 the bridges that an unprivileged user can access, in particular, to restrict
 an unprivileged user from putting a guest on what should be isolated networks.

 This patch implements an ACL mechanism that is enforced by qemu-bridge-helper.
 The ACLs are fairly simple whitelist/blacklist mechanisms with a wildcard of
 'all'.  All users are blacklisted by default, and deny takes precedence over
 allow.

 An interesting feature of this ACL mechanism is that you can include external
 ACL files.  The main reason to support this is so that you can set different
 file system permissions on those external ACL files.  This allows an
 administrator to implement rather sophisicated ACL policies based on 
 user/group

sophisticated

 policies via the file system.

 As an example:

 /etc/qemu/bridge.conf root:qemu 0640

  allow br0
  include /etc/qemu/alice.conf
  include /etc/qemu/bob.conf
  include /etc/qemu/charlie.conf

 /etc/qemu/alice.conf root:alice 0640
  allow br1

 /etc/qemu/bob.conf root:bob 0640
  allow br2

 /etc/qemu/charlie.conf root:charlie 0640
  deny all

I think syntax 'include /etc/qemu/user.d/*.conf' or 'includedir
/etc/qemu/user.d' could be also useful.

 This ACL pattern allows any user in the qemu group to get a tap device
 connected to br0 (which is bridged to the physical network).

 Users in the alice group can additionally get a tap device connected to br1.
 This allows br1 to act as a private bridge for the alice group.

 Users in the bob group can additionally get a tap device connected to br2.
 This allows br2 to act as a private bridge for the bob group.

 Users in the charlie group cannot get a tap device connected to any bridge.

 Under no circumstance can the bob group get access to br1 or can the alice
 group get access to br2.  And under no cicumstance can the charlie group
 get access to any bridge.

 Signed-off-by: Anthony Liguori aligu...@us.ibm.com
 Signed-off-by: Richa Marwaha rmar...@linux.vnet.ibm.com
 Signed-off-by: Corey Bryant cor...@linux.vnet.ibm.com
 ---
  qemu-bridge-helper.c |  141 
 ++
  1 files changed, 141 insertions(+), 0 deletions(-)

 diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c
 index 2ce82fb..db257d5 100644
 --- a/qemu-bridge-helper.c
 +++ b/qemu-bridge-helper.c
 @@ -33,6 +33,105 @@

  #include net/tap-linux.h

 +#define MAX_ACLS (128)

If all users (or groups) in the system have an ACL, this number could
be way too low. Please use a list instead.

 +#define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR /bridge.conf
 +
 +enum {
 +    ACL_ALLOW = 0,
 +    ACL_ALLOW_ALL,
 +    ACL_DENY,
 +    ACL_DENY_ALL,
 +};
 +
 +typedef struct ACLRule {
 +    int type;
 +    char iface[IFNAMSIZ];
 +} ACLRule;
 +
 +static int parse_acl_file(const char *filename, ACLRule *acls, int 
 *pacl_count)
 +{
 +    int acl_count = *pacl_count;
 +    FILE *f;
 +    char line[4096];
 +
 +    f = fopen(filename, r);
 +    if (f == NULL) {
 +        return -1;
 +    }
 +
 +    while (acl_count != MAX_ACLS 
 +            fgets(line, sizeof(line), f) != NULL) {
 +        char *ptr = line;
 +        char *cmd, *arg, *argend;
 +
 +        while (isspace(*ptr)) {
 +            ptr++;
 +        }
 +
 +        /* skip comments and empty lines */
 +        if (*ptr == '#' || *ptr == 0) {
 +            continue;
 +        }
 +
 +        cmd = ptr;
 +        arg = strchr(cmd, ' ');
 +        if (arg == NULL) {
 +            arg = strchr(cmd, '\t');
 +        }
 +
 +        if (arg == NULL) {
 +            fprintf(stderr, Invalid config line:\n  %s\n, line);
 +            fclose(f);
 +            errno = EINVAL;
 +            return -1;
 +        }
 +
 +        *arg = 0;
 +        arg++;
 +        while (isspace(*arg)) {
 +            arg++;
 +        }
 +
 +        argend = arg + strlen(arg);
 +        while (arg != argend  isspace(*(argend - 1))) {
 +            argend--;
 +        }

These while loops to skip spaces are repeated, but the comment
skipping part is not, so it is not possible to have comments after
rules or split rules to several lines. I'd add a simple state variable
to track at which stage we are in parsing instead.

 +        *argend = 0;
 +
 +        if (strcmp(cmd, deny) == 0) {
 +            if (strcmp(arg, all) == 0) {
 +                acls[acl_count].type = ACL_DENY_ALL;
 +            } else {
 +                acls[acl_count].type = ACL_DENY;
 +                snprintf(acls[acl_count].iface, IFNAMSIZ, %s, arg);
 +            }
 +            acl_count++;
 +        } else if 

[Qemu-devel] [PATCH v2 2/4] Add access control support to qemu bridge helper

2011-10-21 Thread Corey Bryant
We go to great lengths to restrict ourselves to just cap_net_admin as an OS
enforced security mechanism.  However, we further restrict what we allow users
to do to simply adding a tap device to a bridge interface by virtue of the fact
that this is the only functionality we expose.

This is not good enough though.  An administrator is likely to want to restrict
the bridges that an unprivileged user can access, in particular, to restrict
an unprivileged user from putting a guest on what should be isolated networks.

This patch implements an ACL mechanism that is enforced by qemu-bridge-helper.
The ACLs are fairly simple whitelist/blacklist mechanisms with a wildcard of
'all'.  All users are blacklisted by default, and deny takes precedence over
allow.

An interesting feature of this ACL mechanism is that you can include external
ACL files.  The main reason to support this is so that you can set different
file system permissions on those external ACL files.  This allows an
administrator to implement rather sophisicated ACL policies based on user/group
policies via the file system.

As an example:

/etc/qemu/bridge.conf root:qemu 0640

 allow br0
 include /etc/qemu/alice.conf
 include /etc/qemu/bob.conf
 include /etc/qemu/charlie.conf

/etc/qemu/alice.conf root:alice 0640
 allow br1

/etc/qemu/bob.conf root:bob 0640
 allow br2

/etc/qemu/charlie.conf root:charlie 0640
 deny all

This ACL pattern allows any user in the qemu group to get a tap device
connected to br0 (which is bridged to the physical network).

Users in the alice group can additionally get a tap device connected to br1.
This allows br1 to act as a private bridge for the alice group.

Users in the bob group can additionally get a tap device connected to br2.
This allows br2 to act as a private bridge for the bob group.

Users in the charlie group cannot get a tap device connected to any bridge.

Under no circumstance can the bob group get access to br1 or can the alice
group get access to br2.  And under no cicumstance can the charlie group
get access to any bridge.

Signed-off-by: Anthony Liguori aligu...@us.ibm.com
Signed-off-by: Richa Marwaha rmar...@linux.vnet.ibm.com
Signed-off-by: Corey Bryant cor...@linux.vnet.ibm.com
---
 qemu-bridge-helper.c |  141 ++
 1 files changed, 141 insertions(+), 0 deletions(-)

diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c
index 2ce82fb..db257d5 100644
--- a/qemu-bridge-helper.c
+++ b/qemu-bridge-helper.c
@@ -33,6 +33,105 @@
 
 #include net/tap-linux.h
 
+#define MAX_ACLS (128)
+#define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR /bridge.conf
+
+enum {
+ACL_ALLOW = 0,
+ACL_ALLOW_ALL,
+ACL_DENY,
+ACL_DENY_ALL,
+};
+
+typedef struct ACLRule {
+int type;
+char iface[IFNAMSIZ];
+} ACLRule;
+
+static int parse_acl_file(const char *filename, ACLRule *acls, int *pacl_count)
+{
+int acl_count = *pacl_count;
+FILE *f;
+char line[4096];
+
+f = fopen(filename, r);
+if (f == NULL) {
+return -1;
+}
+
+while (acl_count != MAX_ACLS 
+fgets(line, sizeof(line), f) != NULL) {
+char *ptr = line;
+char *cmd, *arg, *argend;
+
+while (isspace(*ptr)) {
+ptr++;
+}
+
+/* skip comments and empty lines */
+if (*ptr == '#' || *ptr == 0) {
+continue;
+}
+
+cmd = ptr;
+arg = strchr(cmd, ' ');
+if (arg == NULL) {
+arg = strchr(cmd, '\t');
+}
+
+if (arg == NULL) {
+fprintf(stderr, Invalid config line:\n  %s\n, line);
+fclose(f);
+errno = EINVAL;
+return -1;
+}
+
+*arg = 0;
+arg++;
+while (isspace(*arg)) {
+arg++;
+}
+
+argend = arg + strlen(arg);
+while (arg != argend  isspace(*(argend - 1))) {
+argend--;
+}
+*argend = 0;
+
+if (strcmp(cmd, deny) == 0) {
+if (strcmp(arg, all) == 0) {
+acls[acl_count].type = ACL_DENY_ALL;
+} else {
+acls[acl_count].type = ACL_DENY;
+snprintf(acls[acl_count].iface, IFNAMSIZ, %s, arg);
+}
+acl_count++;
+} else if (strcmp(cmd, allow) == 0) {
+if (strcmp(arg, all) == 0) {
+acls[acl_count].type = ACL_ALLOW_ALL;
+} else {
+acls[acl_count].type = ACL_ALLOW;
+snprintf(acls[acl_count].iface, IFNAMSIZ, %s, arg);
+}
+acl_count++;
+} else if (strcmp(cmd, include) == 0) {
+/* ignore errors */
+parse_acl_file(arg, acls, acl_count);
+} else {
+fprintf(stderr, Unknown command `%s'\n, cmd);
+fclose(f);
+errno = EINVAL;
+return -1;
+}
+}
+
+*pacl_count = acl_count;
+
+fclose(f);
+
+return 0;
+}
+
 static int