summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2009-11-03 09:35:19 -0500
committerJason Gerard DeRose <jderose@redhat.com>2009-11-03 09:04:05 -0700
commitbd619adb5c1cfcd9e72c18896aded82e2ab33faa (patch)
tree749d6ef90707772a5146d03c6bc78ef59a5f664c
parente4c119ed4b05fe600377360e697483bd59000b37 (diff)
downloadfreeipa-bd619adb5c1cfcd9e72c18896aded82e2ab33faa.tar.gz
freeipa-bd619adb5c1cfcd9e72c18896aded82e2ab33faa.tar.xz
freeipa-bd619adb5c1cfcd9e72c18896aded82e2ab33faa.zip
Use a new mechanism for delegating certificate issuance.
Using the client IP address was a rather poor mechanism for controlling who could request certificates for whom. Instead the client machine will bind using the host service principal and request the certificate. In order to do this: * the service will need to exist * the machine needs to be in the certadmin rolegroup * the host needs to be in the managedBy attribute of the service It might look something like: admin ipa host-add client.example.com --password=secret123 ipa service-add HTTP/client.example.com ipa service-add-host --hosts=client.example.com HTTP/client.example.com ipa rolegroup-add-member --hosts=client.example.com certadmin client ipa-client-install ipa-join -w secret123 kinit -kt /etc/krb5.keytab host/client.example.com ipa -d cert-request file://web.csr --principal=HTTP/client.example.com
-rw-r--r--install/share/60basev2.ldif3
-rw-r--r--install/share/default-aci.ldif7
-rw-r--r--ipalib/plugins/cert.py51
-rw-r--r--ipalib/plugins/rolegroup.py6
-rw-r--r--ipalib/plugins/service.py28
-rw-r--r--ipaserver/plugins/ldap2.py2
6 files changed, 52 insertions, 45 deletions
diff --git a/install/share/60basev2.ldif b/install/share/60basev2.ldif
index 7c3fa43bd..9296e0607 100644
--- a/install/share/60basev2.ldif
+++ b/install/share/60basev2.ldif
@@ -4,9 +4,10 @@ attributeTypes: (2.16.840.1.113730.3.8.3.2 NAME 'ipaClientVersion' DESC 'Text st
attributeTypes: (2.16.840.1.113730.3.8.3.3 NAME 'enrolledBy' DESC 'DN of administrator who performed manual enrollment of the host' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'IPA v2' )
attributeTypes: (2.16.840.1.113730.3.8.3.4 NAME 'enrollmentPwd' DESC 'Password used to bulk enroll machines' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} X-ORIGIN 'IPA v2' )
attributeTypes: (2.16.840.1.113730.3.8.3.43 NAME 'fqdn' DESC 'FQDN' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' )
+attributeTypes: (2.16.840.1.113730.3.8.3.53 NAME 'managedBy' DESC 'DNs of entries allowed to manage' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2')
objectClasses: (2.16.840.1.113730.3.8.4.1 NAME 'ipaHost' AUXILIARY MUST ( fqdn ) MAY ( userPassword $ ipaClientVersion $ enrolledBy $ memberOf) X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.44 NAME 'ipaObject' DESC 'IPA objectclass' AUXILIARY MUST ( ipaUniqueId ) X-ORIGIN 'IPA v2' )
-objectClasses: (2.16.840.1.113730.3.8.4.2 NAME 'ipaService' DESC 'IPA service objectclass' AUXILIARY MAY ( memberOf ) X-ORIGIN 'IPA v2' )
+objectClasses: (2.16.840.1.113730.3.8.4.2 NAME 'ipaService' DESC 'IPA service objectclass' AUXILIARY MAY ( memberOf $ managedBy ) X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.3 NAME 'nestedGroup' DESC 'Group that supports nesting' SUP groupOfNames STRUCTURAL MAY memberOf X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.4 NAME 'ipaUserGroup' DESC 'IPA user group object class' SUP nestedGroup STRUCTURAL X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.5 NAME 'ipaHostGroup' DESC 'IPA host group object class' SUP nestedGroup STRUCTURAL X-ORIGIN 'IPA v2' )
diff --git a/install/share/default-aci.ldif b/install/share/default-aci.ldif
index 25bd3b224..7f08518b8 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -36,3 +36,10 @@ dn: cn=services,cn=accounts,$SUFFIX
changetype: modify
add: aci
aci: (targetattr="krbPrincipalName || krbUPEnabled || krbPrincipalKey || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData")(version 3.0; acl "KDC System Account"; allow (read, search, compare, write) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+
+# Define which hosts can edit services
+dn: cn=services,cn=accounts,$SUFFIX
+changetype: modify
+add: aci
+aci: (targetattr=userCertificate)(version 3.0; aci "Hosts can modify service userCertificate"; allow(write) userattr = "parent[0,1].managedby#USERDN";)
+
diff --git a/ipalib/plugins/cert.py b/ipalib/plugins/cert.py
index 9750de6f4..0416730f8 100644
--- a/ipalib/plugins/cert.py
+++ b/ipalib/plugins/cert.py
@@ -102,6 +102,7 @@ class cert_request(VirtualCommand):
)
def execute(self, csr, **kw):
+ ldap = self.api.Backend.ldap2
skw = {"all": True}
principal = kw.get('principal')
add = kw.get('add')
@@ -121,35 +122,6 @@ class cert_request(VirtualCommand):
if subject_host.lower() != hostname.lower():
raise errors.ACIError(info="hostname in subject of request '%s' does not match principal hostname '%s'" % (subject_host, hostname))
- # Get the IP address of the machine that submitted the request. We
- # will compare this to the subjectname of the CSR.
- client_ip = getattr(context, 'client_ip')
- rhost = None
- if client_ip not in (None, ''):
- rev = client_ip.split('.')
- if len(rev) == 0:
- rev = client_ip.split(':')
- rev.reverse()
- addr = "%s.in-addr.arpa." % ".".join(rev)
- else:
- rev.reverse()
- addr = "%s.in-addr.arpa." % ".".join(rev)
- rs = dnsclient.query(addr, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
- if len(rs) == 0:
- raise errors.ACIError(info='DNS lookup on client failed for IP %s' % client_ip)
- for rsn in rs:
- if rsn.dns_type == dnsclient.DNS_T_PTR:
- rhost = rsn
- break
-
- if rhost is None:
- raise errors.ACIError(info='DNS lookup on client failed for IP %s' % client_ip)
-
- client_hostname = rhost.rdata.ptrdname[:-1]
- if subject_host.lower() != client_hostname.lower():
- self.log.debug("IPA: hostname in subject of request '%s' does not match requesting hostname '%s'" % (subject_host, client_hostname))
- self.check_access(operation="request certificate different host")
-
# See if the service exists and punt if it doesn't and we aren't
# going to add it
try:
@@ -157,24 +129,23 @@ class cert_request(VirtualCommand):
if 'usercertificate' in service:
# FIXME, what to do here? Do we revoke the old cert?
raise errors.GenericError(format='entry already has a certificate, serial number %s' % get_serial(service['usercertificate']))
- if not can_write(dn, "usercertificate"):
- raise errors.ACIError(info='You need to be a member of the serviceadmin role to update services')
-
except errors.NotFound, e:
if not add:
raise errors.NotFound(reason="The service principal for this request doesn't exist.")
+ try:
+ (dn, service) = api.Command['service_add'](principal, **{})
+ except errors.ACIError:
+ raise errors.ACIError(info='You need to be a member of the serviceadmin role to add services')
+
+ # We got this far so the service entry exists, can we write it?
+ if not ldap.can_write(dn, "usercertificate"):
+ raise errors.ACIError(info="Insufficient 'write' privilege to the 'userCertificate' attribute of entry '%s'." % dn)
# Request the certificate
result = self.Backend.ra.request_certificate(csr, **kw)
- # Success? Then add it to the service entry. We know that it
- # either exists or we should add it.
- if result.get('status') == '0':
- if service is None:
- try:
- service = api.Command['service_add'](principal, **{})
- except errors.ACIError:
- raise errors.ACIError(info='You need to be a member of the serviceadmin role to add services')
+ # Success? Then add it to the service entry.
+ if result.get('status') == 0:
skw = {"usercertificate": str(result.get('certificate'))}
api.Command['service_mod'](principal, **skw)
diff --git a/ipalib/plugins/rolegroup.py b/ipalib/plugins/rolegroup.py
index 85f34157e..fdc632450 100644
--- a/ipalib/plugins/rolegroup.py
+++ b/ipalib/plugins/rolegroup.py
@@ -40,10 +40,12 @@ class rolegroup(LDAPObject):
'cn': 'name',
'member user': 'member users',
'member group': 'member groups',
+ 'member host': 'member hosts',
+ 'member hostgroup': 'member hostgroups',
'memberof taskgroup': 'member of taskgroup',
}
attribute_members = {
- 'member': ['user', 'group'],
+ 'member': ['user', 'group', 'host', 'hostgroup'],
'memberof': ['taskgroup'],
}
@@ -99,7 +101,7 @@ class rolegroup_show(LDAPRetrieve):
"""
Display rolegroup.
"""
-
+
api.register(rolegroup_show)
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 481a9f6dc..449acbaec 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -103,12 +103,16 @@ class service(LDAPObject):
'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', 'ipaobject',
'ipaservice', 'pkiuser'
]
- default_attributes = ['krbprincipalname', 'usercertificate']
+ default_attributes = ['krbprincipalname', 'usercertificate', 'managedby']
uuid_attribute = 'ipauniqueid'
attribute_names = {
'krbprincipalname': 'kerberos principal',
'usercertificate': 'user certificate',
'ipauniqueid': 'unique identifier',
+ 'managedby': 'managed by',
+ }
+ attribute_members = {
+ 'managedby': ['host'],
}
takes_params = (
@@ -131,6 +135,7 @@ class service_add(LDAPCreate):
"""
Add new service.
"""
+ member_attributes = ['managedby']
takes_options = (
Flag('force',
doc='force principal name even if not in DNS',
@@ -176,6 +181,7 @@ class service_del(LDAPDelete):
"""
Delete an existing service.
"""
+ member_attributes = ['managedby']
def pre_callback(self, ldap, dn, *keys, **options):
if self.api.env.enable_ra:
(dn, entry_attrs) = ldap.get_entry(dn, ['usercertificate'])
@@ -192,6 +198,7 @@ class service_mod(LDAPUpdate):
"""
Modify service.
"""
+ member_attributes = ['managedby']
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
cert = entry_attrs.get('usercertificate')
if cert:
@@ -213,6 +220,7 @@ class service_find(LDAPSearch):
"""
Search for services.
"""
+ member_attributes = ['managedby']
def pre_callback(self, ldap, filter, attrs_list, base_dn, *args, **options):
# lisp style!
custom_filter = '(&(objectclass=ipaService)' \
@@ -233,6 +241,24 @@ class service_show(LDAPRetrieve):
"""
Display service.
"""
+ member_attributes = ['managedby']
api.register(service_show)
+class service_add_host(LDAPAddMember):
+ """
+ Add members to service.
+ """
+ member_attributes = ['managedby']
+
+api.register(service_add_host)
+
+
+class service_remove_host(LDAPRemoveMember):
+ """
+ Remove members from service.
+ """
+ member_attributes = ['managedby']
+
+api.register(service_remove_host)
+
diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py
index f8e06576d..38fdb6210 100644
--- a/ipaserver/plugins/ldap2.py
+++ b/ipaserver/plugins/ldap2.py
@@ -512,7 +512,7 @@ class ldap2(CrudBackend, Encoder):
attributes and the entryLevelRights for the entry itself.
"""
principal = getattr(context, 'principal')
- (binddn, attrs) = self.find_entry_by_attr("krbprincipalname", principal, "posixAccount")
+ (binddn, attrs) = self.find_entry_by_attr("krbprincipalname", principal, "krbPrincipalAux")
sctrl = [LDAPControl("1.3.6.1.4.1.42.2.27.9.5.2", True, "dn: " + binddn.encode('UTF-8'))]
self.conn.set_option(_ldap.OPT_SERVER_CONTROLS, sctrl)
(dn, attrs) = self.get_entry(dn, entry_attrs)