mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Support multiple host and service certificates
Update the framework to support multiple host and service certificates. host-mod and service-mod revoke existing certificates that are not included in the modified entry. Using addattr=certificate=... will result in no certificates being revoked. The existing behaviour of host-disable, host-del, service-disable and service-del (revoke existing certificate) is preserved but now applies to all certificates in the host or service entry. Also update host-show and service-show to write all the principal's certificates to the file given by the ``--out=FILE`` option. Part of: http://www.freeipa.org/page/V4/User_Certificates https://fedorahosted.org/freeipa/ticket/4238 Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
parent
b98077ea68
commit
7f7c247bb5
10
API.txt
10
API.txt
@ -1812,7 +1812,7 @@ option: Str('nsosversion', attribute=True, cli_name='os', multivalue=False, requ
|
||||
option: Flag('random', attribute=False, autofill=True, cli_name='random', default=False, multivalue=False, required=False)
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('setattr*', cli_name='setattr', exclude='webui')
|
||||
option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=False, required=False)
|
||||
option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=True, required=False)
|
||||
option: Str('userclass', attribute=True, cli_name='class', multivalue=True, required=False)
|
||||
option: Str('userpassword', attribute=True, cli_name='password', multivalue=False, required=False)
|
||||
option: Str('version?', exclude='webui')
|
||||
@ -1935,7 +1935,7 @@ option: Flag('pkey_only?', autofill=True, default=False)
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Int('sizelimit?', autofill=False, minvalue=0)
|
||||
option: Int('timelimit?', autofill=False, minvalue=0)
|
||||
option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=False, query=True, required=False)
|
||||
option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=True, query=True, required=False)
|
||||
option: Str('userclass', attribute=True, autofill=False, cli_name='class', multivalue=True, query=True, required=False)
|
||||
option: Str('userpassword', attribute=True, autofill=False, cli_name='password', multivalue=False, query=True, required=False)
|
||||
option: Str('version?', exclude='webui')
|
||||
@ -1966,7 +1966,7 @@ option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui
|
||||
option: Flag('rights', autofill=True, default=False)
|
||||
option: Str('setattr*', cli_name='setattr', exclude='webui')
|
||||
option: Flag('updatedns?', autofill=True, default=False)
|
||||
option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=False, required=False)
|
||||
option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=True, required=False)
|
||||
option: Str('userclass', attribute=True, autofill=False, cli_name='class', multivalue=True, required=False)
|
||||
option: Str('userpassword', attribute=True, autofill=False, cli_name='password', multivalue=False, required=False)
|
||||
option: Str('version?', exclude='webui')
|
||||
@ -3584,7 +3584,7 @@ option: Bool('ipakrbrequirespreauth', attribute=False, cli_name='requires_pre_au
|
||||
option: Flag('no_members', autofill=True, default=False, exclude='webui')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('setattr*', cli_name='setattr', exclude='webui')
|
||||
option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=False, required=False)
|
||||
option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=True, required=False)
|
||||
option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
@ -3702,7 +3702,7 @@ option: Flag('no_members', autofill=True, default=False, exclude='webui')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Flag('rights', autofill=True, default=False)
|
||||
option: Str('setattr*', cli_name='setattr', exclude='webui')
|
||||
option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=False, required=False)
|
||||
option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=True, required=False)
|
||||
option: Str('version?', exclude='webui')
|
||||
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||
|
4
VERSION
4
VERSION
@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
|
||||
# #
|
||||
########################################################
|
||||
IPA_API_VERSION_MAJOR=2
|
||||
IPA_API_VERSION_MINOR=121
|
||||
# Last change: pvoborni - added server-find and server-show
|
||||
IPA_API_VERSION_MINOR=122
|
||||
# Last change: ftweedal - allow multiple host/service certificates
|
||||
|
@ -493,7 +493,7 @@ class host(LDAPObject):
|
||||
label=_('Random password'),
|
||||
flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'),
|
||||
),
|
||||
Bytes('usercertificate?', validate_certificate,
|
||||
Bytes('usercertificate*', validate_certificate,
|
||||
cli_name='certificate',
|
||||
label=_('Certificate'),
|
||||
doc=_('Base-64 encoded server certificate'),
|
||||
@ -640,11 +640,11 @@ class host_add(LDAPCreate):
|
||||
entry_attrs['userpassword'] = ipa_generate_password(characters=host_pwd_chars)
|
||||
# save the password so it can be displayed in post_callback
|
||||
setattr(context, 'randompassword', entry_attrs['userpassword'])
|
||||
cert = options.get('usercertificate')
|
||||
if cert:
|
||||
cert = x509.normalize_certificate(cert)
|
||||
certs = options.get('usercertificate', [])
|
||||
certs_der = map(x509.normalize_certificate, certs)
|
||||
for cert in certs_der:
|
||||
x509.verify_cert_subject(ldap, keys[-1], cert)
|
||||
entry_attrs['usercertificate'] = cert
|
||||
entry_attrs['usercertificate'] = certs_der
|
||||
entry_attrs['managedby'] = dn
|
||||
entry_attrs['objectclass'].append('ieee802device')
|
||||
entry_attrs['objectclass'].append('ipasshhost')
|
||||
@ -786,8 +786,7 @@ class host_del(LDAPDelete):
|
||||
entry_attrs = ldap.get_entry(dn, ['usercertificate'])
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
cert = entry_attrs.single_value.get('usercertificate')
|
||||
if cert:
|
||||
for cert in entry_attrs.get('usercertificate', []):
|
||||
cert = x509.normalize_certificate(cert)
|
||||
try:
|
||||
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
||||
@ -864,39 +863,43 @@ class host_mod(LDAPUpdate):
|
||||
if 'krbprincipalaux' not in obj_classes:
|
||||
obj_classes.append('krbprincipalaux')
|
||||
entry_attrs['objectclass'] = obj_classes
|
||||
cert = x509.normalize_certificate(entry_attrs.get('usercertificate'))
|
||||
if cert:
|
||||
if self.api.Command.ca_is_enabled()['result']:
|
||||
x509.verify_cert_subject(ldap, keys[-1], cert)
|
||||
entry_attrs_old = ldap.get_entry(dn, ['usercertificate'])
|
||||
oldcert = entry_attrs_old.single_value.get('usercertificate')
|
||||
if oldcert:
|
||||
oldcert = x509.normalize_certificate(oldcert)
|
||||
try:
|
||||
serial = x509.get_serial_number(oldcert, x509.DER)
|
||||
serial = unicode(serial)
|
||||
try:
|
||||
result = api.Command['cert_show'](serial)['result']
|
||||
if 'revocation_reason' not in result:
|
||||
try:
|
||||
api.Command['cert_revoke'](
|
||||
serial, revocation_reason=4)
|
||||
except errors.NotImplementedError:
|
||||
# some CA's might not implement revoke
|
||||
pass
|
||||
except errors.NotImplementedError:
|
||||
# some CA's might not implement revoke
|
||||
pass
|
||||
except NSPRError, nsprerr:
|
||||
if nsprerr.errno == -8183:
|
||||
# If we can't decode the cert them proceed with
|
||||
# modifying the host.
|
||||
self.log.info("Problem decoding certificate %s" %
|
||||
nsprerr.args[1])
|
||||
else:
|
||||
raise nsprerr
|
||||
|
||||
entry_attrs['usercertificate'] = cert
|
||||
# verify certificates
|
||||
certs = entry_attrs.get('usercertificate') or []
|
||||
certs_der = map(x509.normalize_certificate, certs)
|
||||
for cert in certs_der:
|
||||
x509.verify_cert_subject(ldap, keys[-1], cert)
|
||||
|
||||
# revoke removed certificates
|
||||
if self.api.Command.ca_is_enabled()['result']:
|
||||
entry_attrs_old = ldap.get_entry(dn, ['usercertificate'])
|
||||
old_certs = entry_attrs_old.get('usercertificate', [])
|
||||
old_certs_der = map(x509.normalize_certificate, old_certs)
|
||||
removed_certs_der = set(old_certs_der) - set(certs_der)
|
||||
for cert in removed_certs_der:
|
||||
try:
|
||||
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
||||
try:
|
||||
result = api.Command['cert_show'](serial)['result']
|
||||
if 'revocation_reason' not in result:
|
||||
try:
|
||||
api.Command['cert_revoke'](
|
||||
serial, revocation_reason=4)
|
||||
except errors.NotImplementedError:
|
||||
# some CA's might not implement revoke
|
||||
pass
|
||||
except errors.NotImplementedError:
|
||||
# some CA's might not implement revoke
|
||||
pass
|
||||
except NSPRError, nsprerr:
|
||||
if nsprerr.errno == -8183:
|
||||
# If we can't decode the cert them proceed with
|
||||
# modifying the host.
|
||||
self.log.info("Problem decoding certificate %s" %
|
||||
nsprerr.args[1])
|
||||
else:
|
||||
raise nsprerr
|
||||
entry_attrs['usercertificate'] = certs_der
|
||||
|
||||
if options.get('random'):
|
||||
entry_attrs['userpassword'] = ipa_generate_password(characters=host_pwd_chars)
|
||||
@ -1093,8 +1096,14 @@ class host_show(LDAPRetrieve):
|
||||
util.check_writable_file(options['out'])
|
||||
result = super(host_show, self).forward(*keys, **options)
|
||||
if 'usercertificate' in result['result']:
|
||||
x509.write_certificate(result['result']['usercertificate'][0], options['out'])
|
||||
result['summary'] = _('Certificate stored in file \'%(file)s\'') % dict(file=options['out'])
|
||||
x509.write_certificate_list(
|
||||
result['result']['usercertificate'],
|
||||
options['out']
|
||||
)
|
||||
result['summary'] = (
|
||||
_('Certificate(s) stored in file \'%(file)s\'')
|
||||
% dict(file=options['out'])
|
||||
)
|
||||
return result
|
||||
else:
|
||||
raise errors.NoCertificateError(entry=keys[-1])
|
||||
@ -1148,10 +1157,9 @@ class host_disable(LDAPQuery):
|
||||
entry_attrs = ldap.get_entry(dn, ['usercertificate'])
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
cert = entry_attrs.single_value.get('usercertificate')
|
||||
if cert:
|
||||
if self.api.Command.ca_is_enabled()['result']:
|
||||
cert = x509.normalize_certificate(cert)
|
||||
if self.api.Command.ca_is_enabled()['result']:
|
||||
certs = entry_attrs.get('usercertificate', [])
|
||||
for cert in map(x509.normalize_certificate, certs):
|
||||
try:
|
||||
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
||||
try:
|
||||
@ -1175,10 +1183,11 @@ class host_disable(LDAPQuery):
|
||||
else:
|
||||
raise nsprerr
|
||||
|
||||
# Remove the usercertificate altogether
|
||||
entry_attrs['usercertificate'] = None
|
||||
ldap.update_entry(entry_attrs)
|
||||
done_work = True
|
||||
if certs:
|
||||
# Remove the usercertificate altogether
|
||||
entry_attrs['usercertificate'] = None
|
||||
ldap.update_entry(entry_attrs)
|
||||
done_work = True
|
||||
|
||||
self.obj.get_password_attributes(ldap, dn, entry_attrs)
|
||||
if entry_attrs['has_keytab']:
|
||||
|
@ -437,7 +437,7 @@ class service(LDAPObject):
|
||||
primary_key=True,
|
||||
normalizer=lambda value: normalize_principal(value),
|
||||
),
|
||||
Bytes('usercertificate?', validate_certificate,
|
||||
Bytes('usercertificate*', validate_certificate,
|
||||
cli_name='certificate',
|
||||
label=_('Certificate'),
|
||||
doc=_('Base-64 encoded server certificate'),
|
||||
@ -503,11 +503,11 @@ class service_add(LDAPCreate):
|
||||
|
||||
self.obj.validate_ipakrbauthzdata(entry_attrs)
|
||||
|
||||
cert = options.get('usercertificate')
|
||||
if cert:
|
||||
dercert = x509.normalize_certificate(cert)
|
||||
certs = options.get('usercertificate', [])
|
||||
certs_der = map(x509.normalize_certificate, certs)
|
||||
for dercert in certs_der:
|
||||
x509.verify_cert_subject(ldap, hostname, dercert)
|
||||
entry_attrs['usercertificate'] = dercert
|
||||
entry_attrs['usercertificate'] = certs_der
|
||||
|
||||
if not options.get('force', False):
|
||||
# We know the host exists if we've gotten this far but we
|
||||
@ -555,9 +555,7 @@ class service_del(LDAPDelete):
|
||||
entry_attrs = ldap.get_entry(dn, ['usercertificate'])
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
cert = entry_attrs.get('usercertificate')
|
||||
if cert:
|
||||
cert = cert[0]
|
||||
for cert in entry_attrs.get('usercertificate', []):
|
||||
try:
|
||||
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
||||
try:
|
||||
@ -597,25 +595,44 @@ class service_mod(LDAPUpdate):
|
||||
|
||||
self.obj.validate_ipakrbauthzdata(entry_attrs)
|
||||
|
||||
if 'usercertificate' in options:
|
||||
(service, hostname, realm) = split_principal(keys[-1])
|
||||
cert = options.get('usercertificate')
|
||||
if cert:
|
||||
dercert = x509.normalize_certificate(cert)
|
||||
x509.verify_cert_subject(ldap, hostname, dercert)
|
||||
(service, hostname, realm) = split_principal(keys[-1])
|
||||
|
||||
# verify certificates
|
||||
certs = options.get('usercertificate') or []
|
||||
certs_der = map(x509.normalize_certificate, certs)
|
||||
for dercert in certs_der:
|
||||
x509.verify_cert_subject(ldap, hostname, dercert)
|
||||
|
||||
# revoke removed certificates
|
||||
if self.api.Command.ca_is_enabled()['result']:
|
||||
entry_attrs_old = ldap.get_entry(dn, ['usercertificate'])
|
||||
old_certs = entry_attrs_old.get('usercertificate', [])
|
||||
old_certs_der = map(x509.normalize_certificate, old_certs)
|
||||
removed_certs_der = set(old_certs_der) - set(certs_der)
|
||||
for cert in removed_certs_der:
|
||||
try:
|
||||
entry_attrs_old = ldap.get_entry(dn, ['usercertificate'])
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
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' % (
|
||||
x509.get_serial_number(entry_attrs_old['usercertificate'][0], x509.DER)
|
||||
)
|
||||
raise errors.GenericError(format=fmt)
|
||||
entry_attrs['usercertificate'] = dercert
|
||||
else:
|
||||
entry_attrs['usercertificate'] = None
|
||||
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
||||
try:
|
||||
result = api.Command['cert_show'](serial)['result']
|
||||
if 'revocation_reason' not in result:
|
||||
try:
|
||||
api.Command['cert_revoke'](
|
||||
serial, revocation_reason=4)
|
||||
except errors.NotImplementedError:
|
||||
# some CA's might not implement revoke
|
||||
pass
|
||||
except errors.NotImplementedError:
|
||||
# some CA's might not implement revoke
|
||||
pass
|
||||
except NSPRError, nsprerr:
|
||||
if nsprerr.errno == -8183:
|
||||
# If we can't decode the cert them proceed with
|
||||
# modifying the host.
|
||||
self.log.info("Problem decoding certificate %s" %
|
||||
nsprerr.args[1])
|
||||
else:
|
||||
raise nsprerr
|
||||
entry_attrs['usercertificate'] = certs_der
|
||||
|
||||
update_krbticketflags(ldap, entry_attrs, attrs_list, options, True)
|
||||
|
||||
@ -695,8 +712,14 @@ class service_show(LDAPRetrieve):
|
||||
util.check_writable_file(options['out'])
|
||||
result = super(service_show, self).forward(*keys, **options)
|
||||
if 'usercertificate' in result['result']:
|
||||
x509.write_certificate(result['result']['usercertificate'][0], options['out'])
|
||||
result['summary'] = _('Certificate stored in file \'%(file)s\'') % dict(file=options['out'])
|
||||
x509.write_certificate_list(
|
||||
result['result']['usercertificate'],
|
||||
options['out']
|
||||
)
|
||||
result['summary'] = (
|
||||
_('Certificate(s) stored in file \'%(file)s\'')
|
||||
% dict(file=options['out'])
|
||||
)
|
||||
return result
|
||||
else:
|
||||
raise errors.NoCertificateError(entry=keys[-1])
|
||||
@ -815,9 +838,9 @@ class service_disable(LDAPQuery):
|
||||
# See if we do any work at all here and if not raise an exception
|
||||
done_work = False
|
||||
|
||||
if 'usercertificate' in entry_attrs:
|
||||
if self.api.Command.ca_is_enabled()['result']:
|
||||
cert = x509.normalize_certificate(entry_attrs.get('usercertificate')[0])
|
||||
if self.api.Command.ca_is_enabled()['result']:
|
||||
certs = entry_attrs.get('usercertificate', [])
|
||||
for cert in map(x509.normalize_certificate, certs):
|
||||
try:
|
||||
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
||||
try:
|
||||
@ -839,10 +862,11 @@ class service_disable(LDAPQuery):
|
||||
else:
|
||||
raise nsprerr
|
||||
|
||||
# Remove the usercertificate altogether
|
||||
entry_attrs['usercertificate'] = None
|
||||
ldap.update_entry(entry_attrs)
|
||||
done_work = True
|
||||
if len(certs) > 0:
|
||||
# Remove the usercertificate altogether
|
||||
entry_attrs['usercertificate'] = None
|
||||
ldap.update_entry(entry_attrs)
|
||||
done_work = True
|
||||
|
||||
self.obj.get_password_attributes(ldap, dn, entry_attrs)
|
||||
if entry_attrs['has_keytab']:
|
||||
|
Loading…
Reference in New Issue
Block a user