From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Le7U1-0000FO-5W for qemu-devel@nongnu.org; Mon, 02 Mar 2009 07:42:57 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Le7U0-0000Ex-ND for qemu-devel@nongnu.org; Mon, 02 Mar 2009 07:42:56 -0500 Received: from [199.232.76.173] (port=54303 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Le7U0-0000Ep-7X for qemu-devel@nongnu.org; Mon, 02 Mar 2009 07:42:56 -0500 Received: from mx1.redhat.com ([66.187.233.31]:39493) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1Le7Tz-0006xS-Mv for qemu-devel@nongnu.org; Mon, 02 Mar 2009 07:42:55 -0500 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id n22Cgtkr012821 for ; Mon, 2 Mar 2009 07:42:55 -0500 Received: from file.fab.redhat.com (file.fab.redhat.com [10.33.63.6]) by int-mx1.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n22CgXOs011660 for ; Mon, 2 Mar 2009 07:42:57 -0500 Received: from file.fab.redhat.com (localhost.localdomain [127.0.0.1]) by file.fab.redhat.com (8.13.1/8.13.1) with ESMTP id n22CgVd9020474 for ; Mon, 2 Mar 2009 12:42:31 GMT Received: (from berrange@localhost) by file.fab.redhat.com (8.13.1/8.13.1/Submit) id n22CgVfv020470 for qemu-devel@nongnu.org; Mon, 2 Mar 2009 12:42:31 GMT Date: Mon, 2 Mar 2009 12:42:31 +0000 From: "Daniel P. Berrange" Subject: Re: [Qemu-devel] PATCH: 9/9: Persist ACLs in external files Message-ID: <20090302124231.GI2131@redhat.com> References: <20090302123121.GH15108@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20090302123121.GH15108@redhat.com> Reply-To: "Daniel P. Berrange" , qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org This patch introduces a simple access control file capability for authorizing clients of QEMU's various network services. The file is designed such that it can be shared amongst multiple QEMU instances. The style of commands is similar to that used in the monitor ACL commands. It is a line oriented format, with comments indicated by leading '#'.Each non-comment line consists of 4 fields, 'scope', 'aclname', 'action' and 'value'. The scope allows control over what VMs the rule applies to. This is a glob, so '*' matches any VM. An explicit value can be match against the VM name, as given by the '-name' argument. The aclname is one of the ACLs defined by QEMU, either vnc.username or vnc.x509dname for now. More later perhaps. The action can be one of 'policy' 'allow', or 'deny'. The policy sets the default allow/deny state for the ACL, if no rules match. Finally the 'value' is another glob matching against the client name being checked. An example showing use of both SASL username ACLs, and x509 client certificate distinguished name ACLs. # Default deny all for all SASL authenticated users in all VMs * vnc.username policy deny # Allow bob access to all VMs * vnc.username allow bob # Allow fred and test access to the VM named 'demo' demo vnc.username allow fred demo vnc.username allow test # Deny all x509 client certificates on all VMs * vnc.x509dname policy deny # Allow all users from the ACME, London office to all VMs * vnc.x509dname allow "C=GB,O=ACME,L=London,CN=*" # Allow Joe from Boston, access to VM 'demo' demo vnc.x509dname allow "C=GB,O=ACME,L=Boston,CN=joe" Save this to a file, and give it to QEMU at startup with '-acl demo.acl' The code for parsing the file is a little crude because I didn't want to spend too much time writing this. Its more of a proof of concept of how it might be possible to store ACL rules in a simple way. acl.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ acl.h | 3 + vl.c | 12 +++++++ 3 files changed, 128 insertions(+) Signed-off-by: Daniel P. Berrange diff -r 4afd17da1465 acl.c --- a/acl.c Mon Mar 02 12:11:04 2009 +0000 +++ b/acl.c Mon Mar 02 12:12:48 2009 +0000 @@ -73,6 +73,119 @@ qemu_acl *qemu_acl_init(const char *acln return acl; } +#define SKIP_SPACES(buf) while (*buf == ' ') { buf++; } + +int qemu_acl_load_file(const char *path, + const char *name) +{ + FILE *f = fopen(path, "r"); + char line[1024]; + int ret = -1; + int linenum = 0; + + if (!f) + return -1; + + /* * vnc.username policy deny */ + /* * vnc.username allow fred */ + /* somevm vnc.username allow joe */ + /* somevm vnc.username deny bob*/ + while (fgets(line, sizeof(line), f)) { + char *scope, *aclname, *action, *value; + char *tmp = line; + qemu_acl *acl; + int quoted = 0; + + linenum++; + + SKIP_SPACES(tmp); + if (line[0] == '#' || + line[0] == '\n' || + line[0] == '\0') + continue; + + scope = tmp; + + tmp = strchr(tmp, ' '); + if (!tmp) { + fprintf(stderr, "missing aclname data at line %d\n", linenum); + continue; + } + *tmp++ = '\0'; + SKIP_SPACES(tmp); + aclname = tmp; + + tmp = strchr(tmp, ' '); + if (!tmp) { + fprintf(stderr, "missing action data at line %d\n", linenum); + continue; + } + *tmp++ = '\0'; + SKIP_SPACES(tmp); + action = tmp; + + tmp = strchr(tmp, ' '); + if (!tmp) { + fprintf(stderr, "missing value data at line %d\n", linenum); + continue; + } + *tmp++ = '\0'; + SKIP_SPACES(tmp); + value = tmp; + + + if (*tmp == '"') { + quoted = 1; + value++; + tmp++; + } + while (*tmp != '\0' && *tmp != '\n') { + if ((quoted && *tmp == '"') || + (!quoted && *tmp == ' ')) + break; + tmp++; + } + if (tmp) + *tmp = '\0'; + +#ifdef HAVE_FNMATCH_H + if (fnmatch(scope, name ? name : "", 0) != 0) { + continue; + } +#else + if (name && + strcmp(scope, name) != 0) { + continue; + } +#endif + + acl = qemu_acl_init(aclname); + if (!acl) + goto cleanup; + + if (strcmp(action, "policy") == 0) { + if (strcmp(value, "allow") == 0) + acl->defaultDeny = 0; + else if (strcmp(value, "deny") == 0) + acl->defaultDeny = 1; + else + fprintf(stderr, "unknown ACL policy '%s'\n", value); + } else if (strcmp(action, "allow") == 0 || + strcmp(action, "deny") == 0) { + int deny = strcmp(action, "deny") == 0 ? 1 : 0; + qemu_acl_append(acl, deny, value); + } else { + fprintf(stderr, "unknown ACL action '%s'\n", action); + } + } + ret = 0; + + cleanup: + fclose(f); + + return ret; +} + int qemu_acl_party_is_allowed(qemu_acl *acl, const char *party) { diff -r 4afd17da1465 acl.h --- a/acl.h Mon Mar 02 12:11:04 2009 +0000 +++ b/acl.h Mon Mar 02 12:12:48 2009 +0000 @@ -48,6 +48,9 @@ qemu_acl *qemu_acl_init(const char *acln qemu_acl *qemu_acl_find(const char *aclname); +int qemu_acl_load_file(const char *path, + const char *name); + int qemu_acl_party_is_allowed(qemu_acl *acl, const char *party); diff -r 4afd17da1465 vl.c --- a/vl.c Mon Mar 02 12:11:04 2009 +0000 +++ b/vl.c Mon Mar 02 12:12:48 2009 +0000 @@ -42,6 +42,7 @@ #include "migration.h" #include "kvm.h" #include "balloon.h" +#include "acl.h" #include #include @@ -3956,6 +3957,7 @@ static void help(int exitcode) "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n" #endif "-vnc display start a VNC server on display\n" + "-acl path path to access control list for network services\n" "\n" "Network options:\n" "-net nic[,vlan=n][,macaddr=addr][,model=type][,name=str]\n" @@ -4143,6 +4145,7 @@ enum { QEMU_OPTION_full_screen, QEMU_OPTION_g, QEMU_OPTION_vnc, + QEMU_OPTION_acl, /* Network options: */ QEMU_OPTION_net, @@ -4260,6 +4263,7 @@ static const QEMUOption qemu_options[] = { "g", 1, QEMU_OPTION_g }, #endif { "vnc", HAS_ARG, QEMU_OPTION_vnc }, + { "acl", HAS_ARG, QEMU_OPTION_acl }, /* Network options: */ { "net", HAS_ARG, QEMU_OPTION_net}, @@ -4641,6 +4645,7 @@ int main(int argc, char **argv, char **e const char *pid_file = NULL; int autostart; const char *incoming = NULL; + const char *acl_file = NULL; int fd = 0; struct passwd *pwd = NULL; const char *chroot_dir = NULL; @@ -5189,6 +5194,9 @@ int main(int argc, char **argv, char **e case QEMU_OPTION_vnc: vnc_display = optarg; break; + case QEMU_OPTION_acl: + acl_file = optarg; + break; case QEMU_OPTION_no_acpi: acpi_enabled = 0; break; @@ -5644,6 +5652,10 @@ int main(int argc, char **argv, char **e } } + /* Load access control data */ + if (acl_file) + qemu_acl_load_file(acl_file, qemu_name); + if (!display_state) dumb_display_init(); /* just use the first displaystate for the moment */ -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|