mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Implement support for non-LDAP-based actions that use the LDAP ACI subsystem.
There are some operations, like those for the certificate system, that don't need to write to the directory server. So instead we have an entry that we test against to determine whether the operation is allowed or not. This is done by attempting a write on the entry. If it would succeed then permission is granted. If not then denied. The write we attempt is actually invalid so the write itself will fail but the attempt will fail first if access is not permitted, so we can distinguish between the two without polluting the entry.
This commit is contained in:
parent
5149803873
commit
e31d5fb1cf
@ -66,6 +66,12 @@ add:objectClass: groupofnames
|
||||
add:cn: dnsserver
|
||||
add:description: DNS Servers
|
||||
|
||||
dn: cn=certadmin,cn=rolegroups,cn=accounts,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: groupofnames
|
||||
add:cn: certadmin
|
||||
add:description: Certificate Administrators
|
||||
|
||||
# Add the taskgroups referenced by the ACIs for user administration
|
||||
|
||||
dn: cn=taskgroups,cn=accounts,$SUFFIX
|
||||
@ -456,3 +462,136 @@ add:cn: manage_host_keytab
|
||||
add:description: Updates DNS
|
||||
add:member:'cn=dnsadmin,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
add:member:'cn=dnsserver,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
|
||||
# Create virtual operations entry. This is used to control access to
|
||||
# operations that don't rely on LDAP directly.
|
||||
dn: cn=virtual operations,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: nsContainer
|
||||
add:cn: virtual operations
|
||||
|
||||
# Retrieve Certificate virtual op
|
||||
dn: cn=retrieve certificate,cn=virtual operations,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: nsContainer
|
||||
add:cn: retrieve certificate
|
||||
|
||||
# Taskgroup for retrieving certs
|
||||
dn: cn=retrieve_certs,cn=taskgroups,cn=accounts,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: groupofnames
|
||||
add:cn: retrieve_certs
|
||||
add:description: Retrieve SSL Certificates
|
||||
add:member:'cn=certadmin,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
|
||||
dn: $SUFFIX
|
||||
add: aci: '(targetattr = "objectClass")(target =
|
||||
"ldap:///cn=retrieve certificate,cn=virtual operations,
|
||||
$SUFFIX" )(version 3.0 ; acl "Retrieve Certificates from the
|
||||
CA" ; allow (write) groupdn = "ldap:///cn=retrieve_certs,cn=taskgroups,
|
||||
cn=accounts,dc=greyoak,dc=com";)'
|
||||
|
||||
# Request Certificate virtual op
|
||||
dn: cn=request certificate,cn=virtual operations,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: nsContainer
|
||||
add:cn: request certificate
|
||||
|
||||
# Taskgroup for requesting certs
|
||||
dn: cn=request_certs,cn=taskgroups,cn=accounts,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: groupofnames
|
||||
add:cn: reqeust_certs
|
||||
add:description: Request a SSL Certificate
|
||||
add:member:'cn=certadmin,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
|
||||
dn: $SUFFIX
|
||||
add: aci: '(targetattr = "objectClass")(target =
|
||||
"ldap:///cn=request certificate,cn=virtual operations,
|
||||
$SUFFIX" )(version 3.0 ; acl "Request Certificates from the
|
||||
CA" ; allow (write) groupdn = "ldap:///cn=request_certs,cn=taskgroups,
|
||||
cn=accounts,dc=greyoak,dc=com";)'
|
||||
|
||||
# Certificate Status virtual op
|
||||
dn: cn=certificate status,cn=virtual operations,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: nsContainer
|
||||
add:cn: certificate status
|
||||
|
||||
# Taskgroup for requesting certs
|
||||
dn: cn=certificate_status,cn=taskgroups,cn=accounts,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: groupofnames
|
||||
add:cn: reqeust_certs
|
||||
add:description: Status of cert request
|
||||
add:member:'cn=certadmin,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
|
||||
dn: $SUFFIX
|
||||
add: aci: '(targetattr = "objectClass")(target =
|
||||
"ldap:///cn=certificate status,cn=virtual operations,
|
||||
$SUFFIX" )(version 3.0 ; acl "Get Certificates status from the
|
||||
CA" ; allow (write) groupdn = "ldap:///cn=certificate_status,
|
||||
cn=taskgroups,cn=accounts,dc=greyoak,dc=com";)'
|
||||
|
||||
# Revoke Certificate virtual op
|
||||
dn: cn=revoke certificate,cn=virtual operations,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: nsContainer
|
||||
add:cn: revoke certificate
|
||||
|
||||
# Taskgroup for requesting certs
|
||||
dn: cn=revoke_certificate,cn=taskgroups,cn=accounts,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: groupofnames
|
||||
add:cn: reqeust_certs
|
||||
add:description: Revoke Certificate
|
||||
add:member:'cn=certadmin,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
|
||||
dn: $SUFFIX
|
||||
add: aci: '(targetattr = "objectClass")(target =
|
||||
"ldap:///cn=revoke certificate,cn=virtual operations,
|
||||
$SUFFIX" )(version 3.0 ; acl "Revoke Certificate"
|
||||
; allow (write) groupdn = "ldap:///cn=revoke_certificate,
|
||||
cn=taskgroups,cn=accounts,dc=greyoak,dc=com";)'
|
||||
|
||||
# Revoke Certificate virtual op
|
||||
dn: cn=revoke certificate,cn=virtual operations,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: nsContainer
|
||||
add:cn: revoke certificate
|
||||
|
||||
# Taskgroup for requesting certs
|
||||
dn: cn=revoke_certificate,cn=taskgroups,cn=accounts,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: groupofnames
|
||||
add:cn: reqeust_certs
|
||||
add:description: Revoke Certificate
|
||||
add:member:'cn=certadmin,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
|
||||
dn: $SUFFIX
|
||||
add: aci: '(targetattr = "objectClass")(target =
|
||||
"ldap:///cn=revoke certificate,cn=virtual operations,
|
||||
$SUFFIX" )(version 3.0 ; acl "Revoke Certificate"
|
||||
; allow (write) groupdn = "ldap:///cn=revoke_certificate,
|
||||
cn=taskgroups,cn=accounts,dc=greyoak,dc=com";)'
|
||||
|
||||
# Certificate Remove Hold virtual op
|
||||
dn: cn=certificate remove hold,cn=virtual operations,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: nsContainer
|
||||
add:cn: certificate remove hold
|
||||
|
||||
# Taskgroup for requesting certs
|
||||
dn: cn=certificate_remove_hold,cn=taskgroups,cn=accounts,$SUFFIX
|
||||
add:objectClass: top
|
||||
add:objectClass: groupofnames
|
||||
add:cn: reqeust_certs
|
||||
add:description: Certificate Remove Hold
|
||||
add:member:'cn=certadmin,cn=rolegroups,cn=accounts,$SUFFIX'
|
||||
|
||||
dn: $SUFFIX
|
||||
add: aci: '(targetattr = "objectClass")(target =
|
||||
"ldap:///cn=certificate remove hold,cn=virtual operations,
|
||||
$SUFFIX" )(version 3.0 ; acl "Certificate Remove Hold"
|
||||
; allow (write) groupdn = "ldap:///cn=certificate_remove_hold,
|
||||
cn=taskgroups,cn=accounts,dc=greyoak,dc=com";)'
|
||||
|
@ -96,6 +96,7 @@ DEFAULT_CONFIG = (
|
||||
('container_netgroup', 'cn=ng,cn=alt'),
|
||||
('container_hbac', 'cn=hbac'),
|
||||
('container_dns', 'cn=dns'),
|
||||
('container_virtual', 'cn=virtual operations'),
|
||||
|
||||
# Ports, hosts, and URIs:
|
||||
('lite_xmlrpc_port', 8888),
|
||||
|
@ -563,7 +563,7 @@ class ACIError(AuthorizationError):
|
||||
"""
|
||||
|
||||
errno = 2100
|
||||
format = _('Insufficient access: %(info)r')
|
||||
format = _('Insufficient access: %(info)s')
|
||||
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@ if api.env.enable_ra is not True:
|
||||
raise SkipPluginModule(reason='env.enable_ra is not True')
|
||||
from ipalib import Command, Str, Int, Bytes, Flag
|
||||
from ipalib import errors
|
||||
from ipalib.plugins.virtual import *
|
||||
import base64
|
||||
|
||||
def validate_csr(ugettext, csr):
|
||||
@ -40,12 +41,13 @@ def validate_csr(ugettext, csr):
|
||||
raise errors.Base64DecodeError(reason=str(e))
|
||||
|
||||
|
||||
class cert_request(Command):
|
||||
class cert_request(VirtualCommand):
|
||||
"""
|
||||
Submit a certificate singing request.
|
||||
"""
|
||||
|
||||
takes_args = (Str('csr', validate_csr),)
|
||||
operation="request certificate"
|
||||
|
||||
takes_options = (
|
||||
Str('principal',
|
||||
@ -63,6 +65,7 @@ class cert_request(Command):
|
||||
)
|
||||
|
||||
def execute(self, csr, **kw):
|
||||
super(cert_request, self).execute()
|
||||
skw = {"all": True}
|
||||
principal = kw.get('principal')
|
||||
add = kw.get('add')
|
||||
@ -104,15 +107,17 @@ class cert_request(Command):
|
||||
api.register(cert_request)
|
||||
|
||||
|
||||
class cert_status(Command):
|
||||
class cert_status(VirtualCommand):
|
||||
"""
|
||||
Check status of a certificate signing request.
|
||||
"""
|
||||
|
||||
takes_args = ('request_id')
|
||||
operation = "certificate status"
|
||||
|
||||
|
||||
def execute(self, request_id, **kw):
|
||||
super(cert_status, self).execute()
|
||||
return self.Backend.ra.check_request_status(request_id)
|
||||
|
||||
def output_for_cli(self, textui, result, *args, **kw):
|
||||
@ -124,14 +129,16 @@ class cert_status(Command):
|
||||
api.register(cert_status)
|
||||
|
||||
|
||||
class cert_get(Command):
|
||||
class cert_get(VirtualCommand):
|
||||
"""
|
||||
Retrieve an existing certificate.
|
||||
"""
|
||||
|
||||
takes_args = ('serial_number')
|
||||
operation="retrieve certificate"
|
||||
|
||||
def execute(self, serial_number):
|
||||
super(cert_get, self).execute()
|
||||
return self.Backend.ra.get_certificate(serial_number)
|
||||
|
||||
def output_for_cli(self, textui, result, *args, **kw):
|
||||
@ -143,12 +150,13 @@ class cert_get(Command):
|
||||
api.register(cert_get)
|
||||
|
||||
|
||||
class cert_revoke(Command):
|
||||
class cert_revoke(VirtualCommand):
|
||||
"""
|
||||
Revoke a certificate.
|
||||
"""
|
||||
|
||||
takes_args = ('serial_number')
|
||||
operation = "revoke certificate"
|
||||
|
||||
# FIXME: The default is 0. Is this really an Int param?
|
||||
takes_options = (
|
||||
@ -162,6 +170,7 @@ class cert_revoke(Command):
|
||||
|
||||
|
||||
def execute(self, serial_number, **kw):
|
||||
super(cert_revoke, self).execute()
|
||||
return self.Backend.ra.revoke_certificate(serial_number, **kw)
|
||||
|
||||
def output_for_cli(self, textui, result, *args, **kw):
|
||||
@ -173,14 +182,16 @@ class cert_revoke(Command):
|
||||
api.register(cert_revoke)
|
||||
|
||||
|
||||
class cert_remove_hold(Command):
|
||||
class cert_remove_hold(VirtualCommand):
|
||||
"""
|
||||
Take a revoked certificate off hold.
|
||||
"""
|
||||
|
||||
takes_args = ('serial_number')
|
||||
operation = "certificate remove hold"
|
||||
|
||||
def execute(self, serial_number, **kw):
|
||||
super(cert_remove_hold, self).execute()
|
||||
return self.Backend.ra.take_certificate_off_hold(serial_number)
|
||||
|
||||
def output_for_cli(self, textui, result, *args, **kw):
|
||||
|
73
ipalib/plugins/virtual.py
Normal file
73
ipalib/plugins/virtual.py
Normal file
@ -0,0 +1,73 @@
|
||||
# Authors:
|
||||
# Rob Crittenden <rcritten@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2009 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 only
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
"""
|
||||
Base classes for non-LDAP backend plugins.
|
||||
"""
|
||||
from ipalib import api
|
||||
from ipalib import Command
|
||||
from ipalib import errors
|
||||
|
||||
class VirtualCommand(Command):
|
||||
"""
|
||||
A command that doesn't use the LDAP backend but wants to use the
|
||||
LDAP access control system to make authorization decisions.
|
||||
|
||||
The class variable operation is the commonName attribute of the
|
||||
entry to be tested against.
|
||||
|
||||
In advance, you need to create an entry of the form:
|
||||
cn=<operation>, api.env.container_virtual, api.env.basedn
|
||||
|
||||
Ex.
|
||||
cn=request certificate, cn=virtual operations, dc=example, dc=com
|
||||
"""
|
||||
operation = None
|
||||
|
||||
def execute(self, *args, **kw):
|
||||
"""
|
||||
Perform the LDAP query to determine authorization.
|
||||
|
||||
This should be executed via super() before any actual work is done.
|
||||
"""
|
||||
if self.operation is None:
|
||||
raise errors.ACIError(info='operation not defined')
|
||||
|
||||
ldap = self.api.Backend.ldap
|
||||
self.log.info("IPA: virtual verify %s" % self.operation)
|
||||
|
||||
operationdn = "cn=%s,%s,%s" % (self.operation, self.api.env.container_virtual, self.api.env.basedn)
|
||||
|
||||
# By adding this unknown objectclass we do several things.
|
||||
# DS checks ACIs before the objectclass so we can test for ACI
|
||||
# errors to know if we have rights. If we do have rights then the
|
||||
# update will fail anyway with a Database error because of an
|
||||
# unknown objectclass, so we can catch that gracefully as well.
|
||||
try:
|
||||
updatekw = {'objectclass': ['somerandomunknownclass']}
|
||||
ldap.update(operationdn, **updatekw)
|
||||
except errors.ACIError, e:
|
||||
self.log.debug("%s" % str(e))
|
||||
raise errors.ACIError(info='not allowed to perform this command')
|
||||
except errors.DatabaseError:
|
||||
return
|
||||
except Exception, e:
|
||||
# Something unexpected happened. Log it and deny access to be safe.
|
||||
self.log.info("Virtual verify failed: %s" % str(e))
|
||||
raise errors.ACIError(info='not allowed to perform this command')
|
Loading…
Reference in New Issue
Block a user