mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-06 20:00:05 +00:00
Auto-generate helpers for checking access control rules
Extend the 'gendispatch.pl' script to be able to generate three new types of file. - 'aclheader' - defines signatures of helper APIs for doing authorization checks. There is one helper API for each API requiring an auth check. Any @acl annotations result in a method being generated with a suffix of 'EnsureACL'. If the ACL check requires examination of flags, an extra 'flags' param will be present. Some examples extern int virConnectBaselineCPUEnsureACL(void); extern int virConnectDomainEventDeregisterEnsureACL(virDomainDefPtr domain); extern int virDomainAttachDeviceFlagsEnsureACL(virDomainDefPtr domain, unsigned int flags); Any @aclfilter annotations resuilt in a method being generated with a suffix of 'CheckACL'. extern int virConnectListAllDomainsCheckACL(virDomainDefPtr domain); These are used for filtering individual objects from APIs which return a list of objects - 'aclbody' - defines the actual implementation of the methods described above. This calls into the access manager APIs. A complex example: /* Returns: -1 on error (denied==error), 0 on allowed */ int virDomainAttachDeviceFlagsEnsureACL(virConnectPtr conn, virDomainDefPtr domain, unsigned int flags) { virAccessManagerPtr mgr; int rv; if (!(mgr = virAccessManagerGetDefault())) return -1; if ((rv = virAccessManagerCheckDomain(mgr, conn->driver->name, domain, VIR_ACCESS_PERM_DOMAIN_WRITE)) <= 0) { virObjectUnref(mgr); if (rv == 0) virReportError(VIR_ERR_ACCESS_DENIED, NULL); return -1; } if (((flags & (VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE)) == 0) && (rv = virAccessManagerCheckDomain(mgr, conn->driver->name, domain, VIR_ACCESS_PERM_DOMAIN_SAVE)) <= 0) { virObjectUnref(mgr); if (rv == 0) virReportError(VIR_ERR_ACCESS_DENIED, NULL); return -1; } if (((flags & (VIR_DOMAIN_AFFECT_CONFIG)) == (VIR_DOMAIN_AFFECT_CONFIG)) && (rv = virAccessManagerCheckDomain(mgr, conn->driver->name, domain, VIR_ACCESS_PERM_DOMAIN_SAVE)) <= 0) { virObjectUnref(mgr); if (rv == 0) virReportError(VIR_ERR_ACCESS_DENIED, NULL); return -1; } virObjectUnref(mgr); return 0; } - 'aclsyms' - generates a linker script to export the APIs to drivers. Some examples virConnectBaselineCPUEnsureACL; virConnectCompareCPUEnsureACL; Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
e341435e50
commit
686026225e
9
.gitignore
vendored
9
.gitignore
vendored
@ -104,10 +104,19 @@
|
||||
/sc_*
|
||||
/src/.*.stamp
|
||||
/src/access/org.libvirt.api.policy
|
||||
/src/access/viraccessapicheck.c
|
||||
/src/access/viraccessapicheck.h
|
||||
/src/access/viraccessapichecklxc.c
|
||||
/src/access/viraccessapichecklxc.h
|
||||
/src/access/viraccessapicheckqemu.c
|
||||
/src/access/viraccessapicheckqemu.h
|
||||
/src/esx/*.generated.*
|
||||
/src/hyperv/*.generated.*
|
||||
/src/libvirt*.def
|
||||
/src/libvirt.syms
|
||||
/src/libvirt_access.syms
|
||||
/src/libvirt_access_lxc.syms
|
||||
/src/libvirt_access_qemu.syms
|
||||
/src/libvirt_*.stp
|
||||
/src/libvirt_*helper
|
||||
/src/libvirt_*probes.h
|
||||
|
@ -789,6 +789,15 @@ SECURITY_DRIVER_SELINUX_SOURCES = \
|
||||
SECURITY_DRIVER_APPARMOR_SOURCES = \
|
||||
security/security_apparmor.h security/security_apparmor.c
|
||||
|
||||
ACCESS_DRIVER_GENERATED = \
|
||||
access/viraccessapicheck.h access/viraccessapicheck.c \
|
||||
access/viraccessapicheckqemu.h access/viraccessapicheckqemu.c \
|
||||
access/viraccessapichecklxc.h access/viraccessapichecklxc.c
|
||||
|
||||
ACCESS_DRIVER_SYMFILES = \
|
||||
libvirt_access.syms \
|
||||
libvirt_access_qemu.syms \
|
||||
libvirt_access_lxc.syms
|
||||
|
||||
ACCESS_DRIVER_SOURCES = \
|
||||
access/viraccessperm.h access/viraccessperm.c \
|
||||
@ -1390,7 +1399,7 @@ libvirt_security_manager_la_SOURCES += $(SECURITY_DRIVER_APPARMOR_SOURCES)
|
||||
libvirt_security_manager_la_CFLAGS += $(APPARMOR_CFLAGS)
|
||||
endif
|
||||
|
||||
libvirt_driver_access_la_SOURCES = $(ACCESS_DRIVER_SOURCES)
|
||||
libvirt_driver_access_la_SOURCES = $(ACCESS_DRIVER_SOURCES) $(ACCESS_DRIVER_GENERATED)
|
||||
noinst_LTLIBRARIES += libvirt_driver_access.la
|
||||
libvirt_la_BUILT_LIBADD += libvirt_driver_access.la
|
||||
libvirt_driver_access_la_CFLAGS = \
|
||||
@ -1417,6 +1426,50 @@ EXTRA_DIST += $(ACCESS_DRIVER_POLKIT_SOURCES)
|
||||
endif
|
||||
|
||||
|
||||
USED_SYM_FILES += $(ACCESS_DRIVER_SYMFILES)
|
||||
BUILT_SOURCES += $(ACCESS_DRIVER_GENERATED) $(ACCESS_DRIVER_SYMFILES)
|
||||
CLEANFILES += $(ACCESS_DRIVER_GENERATED) $(ACCESS_DRIVER_SYMFILES)
|
||||
|
||||
libvirt_access.syms: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(REMOTE_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclsym \
|
||||
remote REMOTE $(REMOTE_PROTOCOL) > $@
|
||||
libvirt_access_qemu.syms: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(QEMU_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclsym \
|
||||
qemu QEMU $(QEMU_PROTOCOL) > $@
|
||||
libvirt_access_lxc.syms: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(LXC_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclsym \
|
||||
lxc LXC $(LXC_PROTOCOL) > $@
|
||||
|
||||
access/viraccessapicheck.h: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(REMOTE_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclheader \
|
||||
remote REMOTE $(REMOTE_PROTOCOL) > $@
|
||||
access/viraccessapicheck.c: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(REMOTE_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclbody \
|
||||
remote REMOTE $(REMOTE_PROTOCOL) access/viraccessapicheck.h > $@
|
||||
|
||||
access/viraccessapicheckqemu.h: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(QEMU_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclheader \
|
||||
qemu QEMU $(QEMU_PROTOCOL) > $@
|
||||
access/viraccessapicheckqemu.c: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(QEMU_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclbody \
|
||||
qemu QEMU $(QEMU_PROTOCOL) access/viraccessapicheckqemu.h > $@
|
||||
|
||||
access/viraccessapichecklxc.h: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(LXC_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclheader \
|
||||
lxc LXC $(LXC_PROTOCOL) > $@
|
||||
access/viraccessapichecklxc.c: $(srcdir)/rpc/gendispatch.pl \
|
||||
$(LXC_PROTOCOL) Makefile.am
|
||||
$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl --mode=aclbody \
|
||||
lxc LXC $(LXC_PROTOCOL) access/viraccessapichecklxc.h > $@
|
||||
|
||||
# Add all conditional sources just in case...
|
||||
EXTRA_DIST += \
|
||||
$(TEST_DRIVER_SOURCES) \
|
||||
|
@ -40,8 +40,9 @@ my $res = GetOptions("mode=s" => \$mode);
|
||||
|
||||
die "cannot parse command line options" unless $res;
|
||||
|
||||
die "unknown mode '$mode', expecting 'client', 'server' or 'debug'"
|
||||
unless $mode =~ /^(client|server|debug)$/;
|
||||
die "unknown mode '$mode', expecting 'client', 'server', " .
|
||||
"'aclheader', 'aclbody', 'aclsym' or 'debug'"
|
||||
unless $mode =~ /^(client|server|aclheader|aclbody|aclsym|debug)$/;
|
||||
|
||||
my $structprefix = shift or die "missing struct prefix argument";
|
||||
my $procprefix = shift or die "missing procedure prefix argument";
|
||||
@ -124,7 +125,13 @@ while (<PROTOCOL>) {
|
||||
}
|
||||
} elsif ($collect_opts) {
|
||||
if (m,^\s*\*\s*\@(\w+)\s*:\s*((?:\w|:|\!|\|)+)\s*$,) {
|
||||
$opts{$1} = $2;
|
||||
if ($1 eq "acl" ||
|
||||
$1 eq "aclfilter") {
|
||||
$opts{$1} = [] unless exists $opts{$1};
|
||||
push @{$opts{$1}}, $2;
|
||||
} else {
|
||||
$opts{$1} = $2;
|
||||
}
|
||||
} elsif (m,^\s*\*/\s*$,) {
|
||||
$collect_opts = 0;
|
||||
} elsif (m,^\s*\*\s*$,) {
|
||||
@ -251,6 +258,8 @@ while (<PROTOCOL>) {
|
||||
$calls{$name}->{streamflag} = "none";
|
||||
}
|
||||
|
||||
$calls{$name}->{acl} = $opts{acl};
|
||||
$calls{$name}->{aclfilter} = $opts{aclfilter};
|
||||
|
||||
# for now, we distinguish only two levels of priority:
|
||||
# low (0) and high (1)
|
||||
@ -337,11 +346,18 @@ sub hyper_to_long
|
||||
#----------------------------------------------------------------------
|
||||
# Output
|
||||
|
||||
print <<__EOF__;
|
||||
if ($mode eq "aclsym") {
|
||||
print <<__EOF__;
|
||||
# Automatically generated by gendispatch.pl.
|
||||
# Do not edit this file. Any changes you make will be lost.
|
||||
__EOF__
|
||||
} else {
|
||||
print <<__EOF__;
|
||||
/* Automatically generated by gendispatch.pl.
|
||||
* Do not edit this file. Any changes you make will be lost.
|
||||
*/
|
||||
__EOF__
|
||||
}
|
||||
|
||||
# Debugging.
|
||||
if ($mode eq "debug") {
|
||||
@ -1626,4 +1642,191 @@ elsif ($mode eq "client") {
|
||||
print " return rv;\n";
|
||||
print "}\n";
|
||||
}
|
||||
} elsif ($mode eq "aclheader" ||
|
||||
$mode eq "aclbody" ||
|
||||
$mode eq "aclsym") {
|
||||
my %generate = map { $_ => 1 } @autogen;
|
||||
my @keys = keys %calls;
|
||||
|
||||
if ($mode eq "aclsym") {
|
||||
@keys = sort { my $c = $a . "ensureacl";
|
||||
my $d = $b . "ensureacl";
|
||||
$c cmp $d } @keys;
|
||||
} else {
|
||||
@keys = sort { $a cmp $b } @keys;
|
||||
}
|
||||
|
||||
if ($mode eq "aclheader") {
|
||||
my @headers = (
|
||||
"internal.h",
|
||||
"domain_conf.h",
|
||||
"network_conf.h",
|
||||
"secret_conf.h",
|
||||
"storage_conf.h",
|
||||
"nwfilter_conf.h",
|
||||
"node_device_conf.h",
|
||||
"interface_conf.h"
|
||||
);
|
||||
foreach my $hdr (@headers) {
|
||||
print "#include \"$hdr\"\n";
|
||||
}
|
||||
} elsif ($mode eq "aclbody") {
|
||||
my $header = shift;
|
||||
print "#include <config.h>\n";
|
||||
print "#include \"$header\"\n";
|
||||
print "#include \"access/viraccessmanager.h\"\n";
|
||||
print "#include \"datatypes.h\"\n";
|
||||
print "#include \"virerror.h\"\n";
|
||||
print "\n";
|
||||
print "#define VIR_FROM_THIS VIR_FROM_ACCESS\n";
|
||||
}
|
||||
print "\n";
|
||||
|
||||
foreach (@keys) {
|
||||
my $call = $calls{$_};
|
||||
|
||||
die "missing 'acl' option for $call->{ProcName}"
|
||||
unless exists $call->{acl} &&
|
||||
$#{$call->{acl}} != -1;
|
||||
|
||||
next if $call->{acl}->[0] eq "none";
|
||||
|
||||
if ($mode eq "aclsym") {
|
||||
my $apiname = "vir" . $call->{ProcName};
|
||||
if ($structprefix eq "qemu") {
|
||||
$apiname =~ s/virDomain/virDomainQemu/;
|
||||
} elsif ($structprefix eq "lxc") {
|
||||
$apiname =~ s/virDomain/virDomainLxc/;
|
||||
}
|
||||
if (defined $call->{aclfilter}) {
|
||||
print $apiname . "CheckACL;\n";
|
||||
}
|
||||
print $apiname . "EnsureACL;\n";
|
||||
} else {
|
||||
&generate_acl($call, $call->{acl}, "Ensure");
|
||||
if (defined $call->{aclfilter}) {
|
||||
&generate_acl($call, $call->{aclfilter}, "Check");
|
||||
}
|
||||
}
|
||||
|
||||
sub generate_acl {
|
||||
my $call = shift;
|
||||
my $acl = shift;
|
||||
my $action = shift;
|
||||
|
||||
my @acl;
|
||||
foreach (@{$acl}) {
|
||||
my @bits = split /:/;
|
||||
push @acl, { object => $bits[0], perm => $bits[1], flags => $bits[2] }
|
||||
}
|
||||
|
||||
my $checkflags = 0;
|
||||
for (my $i = 1 ; $i <= $#acl ; $i++) {
|
||||
if ($acl[$i]->{object} ne $acl[0]->{object}) {
|
||||
die "acl for '$call->{ProcName}' cannot check different objects";
|
||||
}
|
||||
if (defined $acl[$i]->{flags}) {
|
||||
$checkflags = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $apiname = "vir" . $call->{ProcName};
|
||||
if ($structprefix eq "qemu") {
|
||||
$apiname =~ s/virDomain/virDomainQemu/;
|
||||
} elsif ($structprefix eq "lxc") {
|
||||
$apiname =~ s/virDomain/virDomainLxc/;
|
||||
}
|
||||
|
||||
my $object = $acl[0]->{object};
|
||||
my $arg = $acl[0]->{object};
|
||||
$arg =~ s/^.*_(\w+)$/$1/;
|
||||
$object =~ s/^(\w)/uc $1/e;
|
||||
$object =~ s/_(\w)/uc $1/e;
|
||||
$object =~ s/Nwfilter/NWFilter/;
|
||||
my $objecttype = "vir" . $object . "DefPtr";
|
||||
$apiname .= $action . "ACL";
|
||||
|
||||
if ($arg eq "interface") {
|
||||
$arg = "iface";
|
||||
}
|
||||
|
||||
my @argdecls;
|
||||
push @argdecls, "virConnectPtr conn";
|
||||
if ($object ne "Connect") {
|
||||
if ($object eq "StorageVol") {
|
||||
push @argdecls, "virStoragePoolDefPtr pool";
|
||||
}
|
||||
push @argdecls, "$objecttype $arg";
|
||||
}
|
||||
if ($checkflags) {
|
||||
push @argdecls, "unsigned int flags";
|
||||
}
|
||||
|
||||
if ($mode eq "aclheader") {
|
||||
print "extern int $apiname(" . join(", ", @argdecls) . ");\n";
|
||||
} else {
|
||||
my @argvars;
|
||||
push @argvars, "mgr";
|
||||
push @argvars, "conn->driver->name";
|
||||
if ($object ne "Connect") {
|
||||
if ($object eq "StorageVol") {
|
||||
push @argvars, "pool";
|
||||
}
|
||||
push @argvars, $arg;
|
||||
}
|
||||
|
||||
if ($action eq "Check") {
|
||||
print "/* Returns: -1 on error, 0 on denied, 1 on allowed */\n";
|
||||
} else {
|
||||
print "/* Returns: -1 on error (denied==error), 0 on allowed */\n";
|
||||
}
|
||||
print "int $apiname(" . join(", ", @argdecls) . ")\n";
|
||||
print "{\n";
|
||||
print " virAccessManagerPtr mgr;\n";
|
||||
print " int rv;\n";
|
||||
print "\n";
|
||||
print " if (!(mgr = virAccessManagerGetDefault()))\n";
|
||||
print " return -1;\n";
|
||||
print "\n";
|
||||
|
||||
foreach my $acl (@acl) {
|
||||
my $perm = "vir_access_perm_" . $acl->{object} . "_" . $acl->{perm};
|
||||
$perm =~ tr/a-z/A-Z/;
|
||||
|
||||
my $method = "virAccessManagerCheck" . $object;
|
||||
my $space = ' ' x length($method);
|
||||
print " if (";
|
||||
if (defined $acl->{flags}) {
|
||||
my $flags = $acl->{flags};
|
||||
if ($flags =~ /^\!/) {
|
||||
$flags = substr $flags, 1;
|
||||
print "((flags & ($flags)) == 0) &&\n";
|
||||
} else {
|
||||
print "((flags & ($flags)) == ($flags)) &&\n";
|
||||
}
|
||||
print " ";
|
||||
}
|
||||
print "(rv = $method(" . join(", ", @argvars, $perm) . ")) <= 0) {\n";
|
||||
print " virObjectUnref(mgr);\n";
|
||||
if ($action eq "Ensure") {
|
||||
print " if (rv == 0)\n";
|
||||
print " virReportError(VIR_ERR_ACCESS_DENIED, NULL);\n";
|
||||
print " return -1;\n";
|
||||
} else {
|
||||
print " return rv;\n";
|
||||
}
|
||||
print " }";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
print " virObjectUnref(mgr);\n";
|
||||
if ($action eq "Check") {
|
||||
print " return 1;\n";
|
||||
} else {
|
||||
print " return 0;\n";
|
||||
}
|
||||
print "}\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user