summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--install/share/default-aci.ldif6
-rw-r--r--ipalib/plugins/cert.py43
-rw-r--r--ipalib/plugins/host.py25
-rw-r--r--ipaserver/install/krbinstance.py3
-rw-r--r--ipaserver/plugins/join.py11
5 files changed, 71 insertions, 17 deletions
diff --git a/install/share/default-aci.ldif b/install/share/default-aci.ldif
index 784377e9f..9c058ae58 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -43,3 +43,9 @@ changetype: modify
add: aci
aci: (targetattr=userCertificate)(version 3.0; aci "Hosts can modify service userCertificate"; allow(write) userattr = "parent[0,1].managedby#USERDN";)
+# Allow hosts to update their own certificate in host/
+dn: cn=computers,cn=accounts,$SUFFIX
+changetype: modify
+add: aci
+aci: (targetattr="userCertificate")(version 3.0; aci "Hosts can modify service userCertificate"; allow(write) userdn = "ldap:///self";)
+
diff --git a/ipalib/plugins/cert.py b/ipalib/plugins/cert.py
index 5540e6ecf..21d0ebcdb 100644
--- a/ipalib/plugins/cert.py
+++ b/ipalib/plugins/cert.py
@@ -40,6 +40,7 @@ from pyasn1.error import PyAsn1Error
import logging
import traceback
from ipalib.request import ugettext as _
+from ipalib.request import context
def get_serial(certificate):
"""
@@ -154,8 +155,10 @@ class cert_request(VirtualCommand):
taskgroup (directly or indirectly via role membership).
"""
+ bind_principal = getattr(context, 'principal')
# Can this user request certs?
- self.check_access()
+ if not bind_principal.startswith('host/'):
+ self.check_access()
# FIXME: add support for subject alt name
@@ -170,7 +173,17 @@ class cert_request(VirtualCommand):
# See if the service exists and punt if it doesn't and we aren't
# going to add it
try:
- (dn, service) = api.Command['service_show'](principal, all=True, raw=True)
+ if not principal.startswith('host/'):
+ service = api.Command['service_show'](principal, all=True, raw=True)
+ dn = service['dn']
+ else:
+ realm = principal.find('@')
+ if realm == -1:
+ realm = len(principal)
+ hostname = principal[5:realm]
+
+ service = api.Command['host_show'](hostname, all=True, raw=True)['result']
+ dn = service['dn']
if 'usercertificate' in service:
# FIXME, what to do here? Do we revoke the old cert?
raise errors.CertificateOperationError(error=_('entry already has a certificate, serial number %s') % get_serial(base64.b64encode(service['usercertificate'][0])))
@@ -178,7 +191,8 @@ class cert_request(VirtualCommand):
if not add:
raise errors.NotFound(reason="The service principal for this request doesn't exist.")
try:
- (dn, service) = api.Command['service_add'](principal, **{})
+ service = api.Command['service_add'](principal, **{})
+ dn = service['dn']
except errors.ACIError:
raise errors.ACIError(info='You need to be a member of the serviceadmin role to add services')
@@ -191,7 +205,8 @@ class cert_request(VirtualCommand):
if subjectaltname is not None:
for name in subjectaltname:
try:
- (hostdn, hostentry) = api.Command['host_show'](name, all=True, raw=True)
+ hostentry = api.Command['host_show'](name, all=True, raw=True)['result']
+ hostdn = hostentry['dn']
except errors.NotFound:
# We don't want to issue any certificates referencing
# machines we don't know about. Nothing is stored in this
@@ -206,11 +221,21 @@ class cert_request(VirtualCommand):
result = self.Backend.ra.request_certificate(csr, **kw)
# 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)
-
- return result
+ if 'certificate' in result:
+ if not principal.startswith('host/'):
+ skw = {"usercertificate": str(result.get('certificate'))}
+ api.Command['service_mod'](principal, **skw)
+ else:
+ realm = principal.find('@')
+ if realm == -1:
+ realm = len(principal)
+ hostname = principal[5:realm]
+ skw = {"usercertificate": str(result.get('certificate'))}
+ api.Command['host_mod'](hostname, **skw)
+
+ return dict(
+ result=result
+ )
def output_for_cli(self, textui, result, *args, **kw):
if isinstance(result, dict) and len(result) > 0:
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index dd19362bd..3d59be7cf 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -26,10 +26,12 @@ import os
import sys
from ipalib import api, errors, util
-from ipalib import Str, Flag
+from ipalib import Str, Flag, Bytes
from ipalib.plugins.baseldap import *
from ipalib.plugins.service import split_principal
+from ipalib.plugins.service import validate_certificate
from ipalib import _, ngettext
+import base64
def validate_host(ugettext, fqdn):
@@ -48,11 +50,11 @@ class host(LDAPObject):
container_dn = api.env.container_host
object_name = 'host'
object_name_plural = 'hosts'
- object_class = ['ipaobject', 'nshost', 'ipahost', 'pkiuser']
+ object_class = ['ipaobject', 'nshost', 'ipahost', 'pkiuser', 'ipaservice']
# object_class_config = 'ipahostobjectclasses'
default_attributes = [
'fqdn', 'description', 'localityname', 'nshostlocation',
- 'nshardwareplatform', 'nsosversion'
+ 'nshardwareplatform', 'nsosversion', 'usercertificate',
]
uuid_attribute = 'ipauniqueid'
attribute_names = {
@@ -107,6 +109,10 @@ class host(LDAPObject):
label='User password',
doc='Password used in bulk enrollment',
),
+ Bytes('usercertificate?', validate_certificate,
+ cli_name='certificate',
+ doc='base-64 encoded server certificate',
+ ),
)
def get_dn(self, *keys, **options):
@@ -148,6 +154,7 @@ class host_add(LDAPCreate):
entry_attrs['objectclass'].append('krbprincipal')
elif 'krbprincipalaux' in entry_attrs['objectclass']:
entry_attrs['objectclass'].remove('krbprincipalaux')
+ entry_attrs['managedby'] = dn
return dn
api.register(host_add)
@@ -209,6 +216,18 @@ class host_mod(LDAPUpdate):
if 'krbprincipalaux' not in obj_classes:
obj_classes.append('krbprincipalaux')
entry_attrs['objectclass'] = obj_classes
+ cert = entry_attrs.get('usercertificate')
+ if cert:
+ (dn, entry_attrs_old) = ldap.get_entry(dn, ['usercertificate'])
+ if 'usercertificate' in entry_attrs_old:
+ # FIXME: what to do here? do we revoke the old cert?
+ fmt = 'entry already has a certificate, serial number: %s' % (
+ get_serial(entry_attrs_old['usercertificate'])
+ )
+ raise errors.GenericError(format=fmt)
+ # FIXME: should be in normalizer; see service_add
+ entry_attrs['usercertificate'] = base64.b64decode(cert)
+
return dn
api.register(host_mod)
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index c4a20b545..71aeeb207 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -112,7 +112,7 @@ class KrbInstance(service.Service):
# Create a host entry for this master
host_dn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, self.suffix)
host_entry = ipaldap.Entry(host_dn)
- host_entry.setValues('objectclass', ['top', 'ipaobject', 'nshost', 'ipahost', 'pkiuser', 'krbprincipalaux', 'krbprincipal', 'krbticketpolicyaux'])
+ host_entry.setValues('objectclass', ['top', 'ipaobject', 'nshost', 'ipahost', 'ipaservice', 'pkiuser', 'krbprincipalaux', 'krbprincipal', 'krbticketpolicyaux'])
host_entry.setValue('krbextradata', service_entry.getValue('krbextradata'))
host_entry.setValue('krblastpwdchange', service_entry.getValue('krblastpwdchange'))
host_entry.setValue('krbpasswordexpiration', service_entry.getValue('krbpasswordexpiration'))
@@ -123,6 +123,7 @@ class KrbInstance(service.Service):
host_entry.setValue('cn', self.fqdn)
host_entry.setValue('fqdn', self.fqdn)
host_entry.setValue('ipauniqueid', str(uuid.uuid1()))
+ host_entry.setValue('managedby', host_dn)
conn.addEntry(host_entry)
conn.unbind()
diff --git a/ipaserver/plugins/join.py b/ipaserver/plugins/join.py
index 34f4c58cb..fe9f88dd9 100644
--- a/ipaserver/plugins/join.py
+++ b/ipaserver/plugins/join.py
@@ -91,13 +91,14 @@ class join(Command):
try:
# First see if the host exists
kw = {'fqdn': hostname, 'all': True}
- (dn, attrs_list) = api.Command['host_show'](**kw)
+ attrs_list = api.Command['host_show'](**kw)['result']
+ dn = attrs_list['dn']
# If no principal name is set yet we need to try to add
# one.
if 'krbprincipalname' not in attrs_list:
service = "host/%s@%s" % (hostname, api.env.realm)
- (d, a) = api.Command['host_mod'](hostname, krbprincipalname=service)
+ api.Command['host_mod'](hostname, krbprincipalname=service)
# It exists, can we write the password attributes?
allowed = ldap.can_write(dn, 'krblastpwdchange')
@@ -105,9 +106,11 @@ class join(Command):
raise errors.ACIError(info="Insufficient 'write' privilege to the 'krbLastPwdChange' attribute of entry '%s'." % dn)
kw = {'fqdn': hostname, 'all': True}
- (dn, attrs_list) = api.Command['host_show'](**kw)
+ attrs_list = api.Command['host_show'](**kw)['result']
+ dn = attrs_list['dn']
except errors.NotFound:
- (dn, attrs_list) = api.Command['host_add'](hostname)
+ attrs_list = api.Command['host_add'](hostname)['result']
+ dn = attrs_list['dn']
return (dn, attrs_list)