summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Cholasta <jcholast@redhat.com>2011-12-07 02:50:31 -0500
committerRob Crittenden <rcritten@redhat.com>2012-02-13 22:23:54 -0500
commit502eafb92d71762da0f3ba4a95a59dfe2264ba94 (patch)
treef9c35942f0c210b9e208d8bf02ffa666f4151869
parent853904459046dc2a548e20298780e3d578532345 (diff)
downloadfreeipa.git-502eafb92d71762da0f3ba4a95a59dfe2264ba94.tar.gz
freeipa.git-502eafb92d71762da0f3ba4a95a59dfe2264ba94.tar.xz
freeipa.git-502eafb92d71762da0f3ba4a95a59dfe2264ba94.zip
Add support for SSH public keys to user and host objects.
This patch adds a new multivalue param "sshpubkey" for specifying SSH public keys to both user and host objects. The accepted value is base64-encoded public key blob as specified in RFC4253, section 6.6. Additionaly, host commands automatically update DNS SSHFP records when requested by user. https://fedorahosted.org/freeipa/ticket/754
-rw-r--r--API.txt13
-rw-r--r--VERSION2
-rw-r--r--ipalib/plugins/host.py116
-rw-r--r--ipalib/plugins/user.py37
-rw-r--r--ipalib/util.py32
-rw-r--r--ipapython/ipautil.py21
-rw-r--r--ipaserver/install/krbinstance.py2
-rw-r--r--tests/test_xmlrpc/objectclasses.py4
8 files changed, 192 insertions, 35 deletions
diff --git a/API.txt b/API.txt
index 9c3ad9e9..66713317 100644
--- a/API.txt
+++ b/API.txt
@@ -1657,7 +1657,7 @@ output: Output('notmatched', (<type 'list'>, <type 'tuple'>, <type 'NoneType'>),
output: Output('error', (<type 'list'>, <type 'tuple'>, <type 'NoneType'>), None)
output: Output('value', <type 'bool'>, None)
command: host_add
-args: 1,15,3
+args: 1,16,3
arg: Str('fqdn', attribute=True, cli_name='hostname', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9][a-zA-Z0-9-\\.]{0,254}$', pattern_errmsg='may only include letters, numbers, and -', primary_key=True, required=True)
option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
option: Str('l', attribute=True, cli_name='locality', multivalue=False, required=False)
@@ -1668,6 +1668,7 @@ option: Str('userpassword', attribute=True, cli_name='password', multivalue=Fals
option: Flag('random', attribute=False, autofill=True, cli_name='random', default=False, multivalue=False, required=False)
option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=False, required=False)
option: Str('macaddress', attribute=True, cli_name='macaddress', csv=True, multivalue=True, pattern='^([a-fA-F0-9]{2}[:|\\-]?){5}[a-fA-F0-9]{2}$', pattern_errmsg='Must be of the form HH:HH:HH:HH:HH:HH, where each H is a hexadecimal character.', required=False)
+option: Bytes('ipasshpubkey', attribute=True, cli_name='sshpubkey', multivalue=True, required=False)
option: Flag('force', autofill=True, default=False)
option: Flag('no_reverse', autofill=True, default=False)
option: Str('ip_address?')
@@ -1739,7 +1740,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
output: Output('count', <type 'int'>, None)
output: Output('truncated', <type 'bool'>, None)
command: host_mod
-args: 1,17,3
+args: 1,19,3
arg: Str('fqdn', attribute=True, cli_name='hostname', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9][a-zA-Z0-9-\\.]{0,254}$', pattern_errmsg='may only include letters, numbers, and -', primary_key=True, query=True, required=True)
option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
option: Str('l', attribute=True, autofill=False, cli_name='locality', multivalue=False, required=False)
@@ -1750,11 +1751,13 @@ option: Str('userpassword', attribute=True, autofill=False, cli_name='password',
option: Flag('random', attribute=False, autofill=True, cli_name='random', default=False, multivalue=False, required=False)
option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=False, required=False)
option: Str('macaddress', attribute=True, autofill=False, cli_name='macaddress', csv=True, multivalue=True, pattern='^([a-fA-F0-9]{2}[:|\\-]?){5}[a-fA-F0-9]{2}$', pattern_errmsg='Must be of the form HH:HH:HH:HH:HH:HH, where each H is a hexadecimal character.', required=False)
+option: Bytes('ipasshpubkey', attribute=True, autofill=False, cli_name='sshpubkey', multivalue=True, required=False)
option: Str('setattr*', cli_name='setattr', exclude='webui')
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Str('delattr*', cli_name='delattr', exclude='webui')
option: Flag('rights', autofill=True, default=False)
option: Str('krbprincipalname?', attribute=True, cli_name='principalname')
+option: Flag('updatedns?', autofill=True, default=False)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('version?', exclude='webui')
@@ -3087,7 +3090,7 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('value', <type 'unicode'>, None)
command: user_add
-args: 1,32,3
+args: 1,33,3
arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', pattern_errmsg='may only include letters, numbers, _, -, . and $', primary_key=True, required=True)
option: Str('givenname', attribute=True, cli_name='first', multivalue=False, required=True)
option: Str('sn', attribute=True, cli_name='last', multivalue=False, required=True)
@@ -3115,6 +3118,7 @@ option: Str('ou', attribute=True, cli_name='orgunit', multivalue=False, required
option: Str('title', attribute=True, cli_name='title', multivalue=False, required=False)
option: Str('manager', attribute=True, cli_name='manager', multivalue=False, required=False)
option: Str('carlicense', attribute=True, cli_name='carlicense', multivalue=False, required=False)
+option: Bytes('ipasshpubkey', attribute=True, cli_name='sshpubkey', multivalue=True, required=False)
option: Str('setattr*', cli_name='setattr', exclude='webui')
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('noprivate', autofill=True, cli_name='noprivate', default=False)
@@ -3194,7 +3198,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
output: Output('count', <type 'int'>, None)
output: Output('truncated', <type 'bool'>, None)
command: user_mod
-args: 1,33,3
+args: 1,34,3
arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', pattern_errmsg='may only include letters, numbers, _, -, . and $', primary_key=True, query=True, required=True)
option: Str('givenname', attribute=True, autofill=False, cli_name='first', multivalue=False, required=False)
option: Str('sn', attribute=True, autofill=False, cli_name='last', multivalue=False, required=False)
@@ -3221,6 +3225,7 @@ option: Str('ou', attribute=True, autofill=False, cli_name='orgunit', multivalue
option: Str('title', attribute=True, autofill=False, cli_name='title', multivalue=False, required=False)
option: Str('manager', attribute=True, autofill=False, cli_name='manager', multivalue=False, required=False)
option: Str('carlicense', attribute=True, autofill=False, cli_name='carlicense', multivalue=False, required=False)
+option: Bytes('ipasshpubkey', attribute=True, autofill=False, cli_name='sshpubkey', multivalue=True, required=False)
option: Str('setattr*', cli_name='setattr', exclude='webui')
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Str('delattr*', cli_name='delattr', exclude='webui')
diff --git a/VERSION b/VERSION
index 785acef3..23f8e643 100644
--- a/VERSION
+++ b/VERSION
@@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=25
+IPA_API_VERSION_MINOR=26
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index 0cae656b..682b8142 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -22,6 +22,8 @@ import platform
import os
import sys
from nss.error import NSPRError
+import nss.nss as nss
+import netaddr
from ipalib import api, errors, util
from ipalib import Str, Flag, Bytes
@@ -34,11 +36,9 @@ from ipalib.plugins.dns import add_forward_record
from ipalib import _, ngettext
from ipalib import x509
from ipalib.dn import *
-from ipapython.ipautil import ipa_generate_password, CheckedIPAddress
from ipalib.request import context
-import base64
-import nss.nss as nss
-import netaddr
+from ipalib.util import validate_sshpubkey, output_sshpubkey
+from ipapython.ipautil import ipa_generate_password, CheckedIPAddress, make_sshfp
__doc__ = _("""
Hosts/Machines
@@ -87,6 +87,9 @@ EXAMPLES:
Modify information about a host:
ipa host-mod --os='Fedora 12' test.example.com
+ Remove SSH public keys of a host and update DNS to reflect this change:
+ ipa host-mod --sshpubkey= --updatedns test.example.com
+
Disable the host Kerberos key, SSL certificate and all of its services:
ipa host-disable test.example.com
@@ -162,6 +165,22 @@ def remove_fwd_ptr(ipaddr, host, domain, recordtype):
except errors.NotFound:
pass
+def update_sshfp_record(zone, record, entry_attrs):
+ if 'ipasshpubkey' not in entry_attrs:
+ return
+
+ pubkeys = entry_attrs['ipasshpubkey'] or ()
+ sshfps=[]
+ for pubkey in pubkeys:
+ sshfp = unicode(make_sshfp(pubkey))
+ if sshfp is not None:
+ sshfps.append(sshfp)
+
+ try:
+ api.Command['dnsrecord_mod'](zone, record, sshfprecord=sshfps)
+ except errors.EmptyModlist:
+ pass
+
host_output_params = (
Flag('has_keytab',
label=_('Keytab'),
@@ -226,6 +245,7 @@ class host(LDAPObject):
'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
'nshardwareplatform', 'nsosversion', 'usercertificate', 'memberof',
'managedby', 'memberindirect', 'memberofindirect', 'macaddress',
+ 'sshpubkeyfp',
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
@@ -315,6 +335,15 @@ class host(LDAPObject):
label=_('MAC address'),
doc=_('Hardware MAC address(es) on this host'),
),
+ Bytes('ipasshpubkey*', validate_sshpubkey,
+ cli_name='sshpubkey',
+ label=_('Base-64 encoded SSH public key'),
+ flags=['no_search'],
+ ),
+ Str('sshpubkeyfp*',
+ label=_('SSH public key fingerprint'),
+ flags=['virtual_attribute', 'no_create', 'no_update', 'no_search'],
+ ),
)
def get_dn(self, *keys, **options):
@@ -452,33 +481,37 @@ class host_add(LDAPCreate):
entry_attrs['usercertificate'] = cert
entry_attrs['managedby'] = dn
entry_attrs['objectclass'].append('ieee802device')
+ entry_attrs['objectclass'].append('ipasshhost')
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
exc = None
- try:
- if 'ip_address' in options and dns_container_exists(ldap):
+ if dns_container_exists(ldap):
+ try:
parts = keys[-1].split('.')
domain = unicode('.'.join(parts[1:]))
- ip = CheckedIPAddress(options['ip_address'], match_local=False)
- add_forward_record(domain, parts[0], unicode(ip))
+ if 'ip_address' in options:
+ ip = CheckedIPAddress(options['ip_address'], match_local=False)
+ add_forward_record(domain, parts[0], unicode(ip))
- if not options.get('no_reverse', False):
- try:
- prefixlen = None
- if not ip.defaultnet:
- prefixlen = ip.prefixlen
- revzone, revname = get_reverse_zone(ip, prefixlen)
- addkw = { 'ptrrecord' : keys[-1]+'.' }
- api.Command['dnsrecord_add'](revzone, revname, **addkw)
- except errors.EmptyModlist:
- # the entry already exists and matches
- pass
+ if not options.get('no_reverse', False):
+ try:
+ prefixlen = None
+ if not ip.defaultnet:
+ prefixlen = ip.prefixlen
+ revzone, revname = get_reverse_zone(ip, prefixlen)
+ addkw = { 'ptrrecord' : keys[-1]+'.' }
+ api.Command['dnsrecord_add'](revzone, revname, **addkw)
+ except errors.EmptyModlist:
+ # the entry already exists and matches
+ pass
- del options['ip_address']
- except Exception, e:
- exc = e
+ del options['ip_address']
+
+ update_sshfp_record(domain, unicode(parts[0]), entry_attrs)
+ except Exception, e:
+ exc = e
if options.get('random', False):
try:
entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
@@ -493,13 +526,15 @@ class host_add(LDAPCreate):
set_certificate_attrs(entry_attrs)
if options.get('all', False):
- entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
+ entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
self.obj.get_password_attributes(ldap, dn, entry_attrs)
if entry_attrs['has_password']:
# If an OTP is set there is no keytab, at least not one
# fetched anywhere.
entry_attrs['has_keytab'] = False
+ output_sshpubkey(ldap, dn, entry_attrs)
+
return dn
api.register(host_add)
@@ -632,6 +667,10 @@ class host_mod(LDAPUpdate):
doc=_('Kerberos principal name for this host'),
attribute=True,
),
+ Flag('updatedns?',
+ doc=_('Update DNS entries'),
+ default=False,
+ ),
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
@@ -688,6 +727,7 @@ class host_mod(LDAPUpdate):
raise nsprerr
entry_attrs['usercertificate'] = cert
+
if options.get('random'):
entry_attrs['userpassword'] = ipa_generate_password()
setattr(context, 'randompassword', entry_attrs['userpassword'])
@@ -703,6 +743,30 @@ class host_mod(LDAPUpdate):
obj_classes.append('ieee802device')
entry_attrs['objectclass'] = obj_classes
+ if options.get('updatedns', False) and dns_container_exists(ldap):
+ parts = keys[-1].split('.')
+ domain = unicode('.'.join(parts[1:]))
+ result = api.Command['dnszone_find']()['result']
+ match = False
+ for zone in result:
+ if domain == zone['idnsname'][0]:
+ match = True
+ break
+ if not match:
+ raise errors.NotFound(
+ reason=_('DNS zone %(zone)s not found') % dict(zone=domain)
+ )
+ update_sshfp_record(domain, unicode(parts[0]), entry_attrs)
+
+ if 'ipasshpubkey' in entry_attrs:
+ if 'objectclass' in entry_attrs:
+ obj_classes = entry_attrs['objectclass']
+ else:
+ (_dn, _entry_attrs) = ldap.get_entry(dn, ['objectclass'])
+ obj_classes = entry_attrs['objectclass'] = _entry_attrs['objectclass']
+ if 'ipasshhost' not in obj_classes:
+ obj_classes.append('ipasshhost')
+
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
@@ -720,6 +784,8 @@ class host_mod(LDAPUpdate):
self.obj.suppress_netgroup_memberof(entry_attrs)
+ output_sshpubkey(ldap, dn, entry_attrs)
+
return dn
api.register(host_mod)
@@ -802,6 +868,8 @@ class host_find(LDAPSearch):
if options.get('all', False):
entry_attrs['managing'] = self.obj.get_managed_hosts(entry[0])
+ output_sshpubkey(ldap, dn, entry_attrs)
+
api.register(host_find)
@@ -831,6 +899,8 @@ class host_show(LDAPRetrieve):
self.obj.suppress_netgroup_memberof(entry_attrs)
+ output_sshpubkey(ldap, dn, entry_attrs)
+
return dn
def forward(self, *keys, **options):
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index 8c4cc49a..ad9805be 100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -18,16 +18,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from time import gmtime, strftime
+import copy
+import string
+
from ipalib import api, errors
-from ipalib import Flag, Int, Password, Str, Bool
+from ipalib import Flag, Int, Password, Str, Bool, Bytes
from ipalib.plugins.baseldap import *
from ipalib.request import context
-from time import gmtime, strftime
-import copy
from ipalib import _, ngettext
from ipapython.ipautil import ipa_generate_password
-import string
import posixpath
+from ipalib.util import validate_sshpubkey, output_sshpubkey
__doc__ = _("""
Users
@@ -154,12 +156,12 @@ class user(LDAPObject):
'uid', 'givenname', 'sn', 'homedirectory', 'loginshell',
'uidnumber', 'gidnumber', 'mail', 'ou',
'telephonenumber', 'title', 'memberof', 'nsaccountlock',
- 'memberofindirect',
+ 'memberofindirect', 'sshpubkeyfp',
]
search_display_attributes = [
'uid', 'givenname', 'sn', 'homedirectory', 'loginshell',
'mail', 'telephonenumber', 'title', 'nsaccountlock',
- 'uidnumber', 'gidnumber',
+ 'uidnumber', 'gidnumber', 'sshpubkeyfp',
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
@@ -310,6 +312,15 @@ class user(LDAPObject):
label=_('Account disabled'),
flags=['no_create', 'no_update', 'no_search'],
),
+ Bytes('ipasshpubkey*', validate_sshpubkey,
+ cli_name='sshpubkey',
+ label=_('Base-64 encoded SSH public key'),
+ flags=['no_search'],
+ ),
+ Str('sshpubkeyfp*',
+ label=_('SSH public key fingerprint'),
+ flags=['virtual_attribute', 'no_create', 'no_update', 'no_search'],
+ ),
)
def _normalize_email(self, email, config=None):
@@ -489,6 +500,9 @@ class user_add(LDAPCreate):
pass
self.obj.get_password_attributes(ldap, dn, entry_attrs)
+
+ output_sshpubkey(ldap, dn, entry_attrs)
+
return dn
api.register(user_add)
@@ -522,6 +536,14 @@ class user_mod(LDAPUpdate):
entry_attrs['userpassword'] = ipa_generate_password(user_pwdchars)
# save the password so it can be displayed in post_callback
setattr(context, 'randompassword', entry_attrs['userpassword'])
+ if 'ipasshpubkey' in entry_attrs:
+ if 'objectclass' in entry_attrs:
+ obj_classes = entry_attrs['objectclass']
+ else:
+ (_dn, _entry_attrs) = ldap.get_entry(dn, ['objectclass'])
+ obj_classes = entry_attrs['objectclass'] = _entry_attrs['objectclass']
+ if 'ipasshuser' not in obj_classes:
+ obj_classes.append('ipasshuser')
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
@@ -534,6 +556,7 @@ class user_mod(LDAPUpdate):
convert_nsaccountlock(entry_attrs)
self.obj._convert_manager(entry_attrs, **options)
self.obj.get_password_attributes(ldap, dn, entry_attrs)
+ output_sshpubkey(ldap, dn, entry_attrs)
return dn
api.register(user_mod)
@@ -567,6 +590,7 @@ class user_find(LDAPSearch):
self.obj._convert_manager(attrs, **options)
self.obj.get_password_attributes(ldap, dn, attrs)
convert_nsaccountlock(attrs)
+ output_sshpubkey(ldap, dn, attrs)
msg_summary = ngettext(
'%(count)d user matched', '%(count)d users matched', 0
@@ -584,6 +608,7 @@ class user_show(LDAPRetrieve):
convert_nsaccountlock(entry_attrs)
self.obj._convert_manager(entry_attrs, **options)
self.obj.get_password_attributes(ldap, dn, entry_attrs)
+ output_sshpubkey(ldap, dn, entry_attrs)
return dn
api.register(user_show)
diff --git a/ipalib/util.py b/ipalib/util.py
index f3d7970d..365dd339 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -32,6 +32,7 @@ from weakref import WeakKeyDictionary
from ipalib import errors
from ipalib.text import _
from ipapython import dnsclient
+from ipapython.ipautil import decode_ssh_pubkey
def json_serialize(obj):
@@ -278,6 +279,37 @@ def validate_hostname(hostname, check_fqdn=True):
raise ValueError(_('only letters, numbers, and - are allowed. ' \
'- must not be the last name character'))
+def validate_sshpubkey(ugettext, pubkey):
+ try:
+ algo, data, fp = decode_ssh_pubkey(pubkey)
+ except ValueError:
+ return _('invalid SSH public key')
+
+def output_sshpubkey(ldap, dn, entry_attrs):
+ if 'ipasshpubkey' in entry_attrs:
+ pubkeys = entry_attrs.get('ipasshpubkey')
+ else:
+ entry = ldap.get_entry(dn, ['ipasshpubkey'])
+ pubkeys = entry[1].get('ipasshpubkey')
+ if pubkeys is None:
+ return
+
+ fingerprints = []
+ for pubkey in pubkeys:
+ try:
+ algo, data, fp = decode_ssh_pubkey(pubkey)
+ fp = u':'.join([fp[j:j+2] for j in range(0, len(fp), 2)])
+ fingerprints.append(u'%s (%s)' % (fp, algo))
+ except ValueError:
+ pass
+ if fingerprints:
+ entry_attrs['sshpubkeyfp'] = fingerprints
+
+def normalize_sshpubkeyfp(value):
+ value = value.split()[0]
+ value = unicode(c for c in value if c in '0123456789ABCDEFabcdef')
+ return value
+
class cachedproperty(object):
"""
A property-like attribute that caches the return value of a method call.
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index fc0010d6..d9b0455e 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -36,6 +36,7 @@ import shutil
import urllib2
import socket
import ldap
+import struct
from ipapython import ipavalidate
from types import *
@@ -58,6 +59,7 @@ except ImportError:
self.cmd = cmd
def __str__(self):
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+from ipapython.compat import sha1, md5
def get_domain_name():
try:
@@ -1395,3 +1397,22 @@ def backup_config_and_replace_variables(fstore, filepath, replacevars=dict(), ap
old_values = config_replace_variables(filepath, replacevars, appendvars)
return old_values
+
+def decode_ssh_pubkey(data, fptype=md5):
+ try:
+ (algolen,) = struct.unpack('>I', data[:4])
+ if algolen > 0 and algolen <= len(data) - 4:
+ return (data[4:algolen+4], data[algolen+4:], fptype(data).hexdigest().upper())
+ except struct.error:
+ pass
+ raise ValueError('not a SSH public key')
+
+def make_sshfp(key):
+ algo, data, fp = decode_ssh_pubkey(key, fptype=sha1)
+ if algo == 'ssh-rsa':
+ algo = 1
+ elif algo == 'ssh-dss':
+ algo = 2
+ else:
+ return
+ return '%d 1 %s' % (algo, fp)
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index cb44ebf0..365ada25 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -110,7 +110,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', 'ipaservice', 'pkiuser', 'krbprincipalaux', 'krbprincipal', 'krbticketpolicyaux'])
+ host_entry.setValues('objectclass', ['top', 'ipaobject', 'nshost', 'ipahost', 'ipaservice', 'pkiuser', 'krbprincipalaux', 'krbprincipal', 'krbticketpolicyaux', 'ipasshhost'])
host_entry.setValues('krbextradata', service_entry.getValues('krbextradata'))
host_entry.setValue('krblastpwdchange', service_entry.getValue('krblastpwdchange'))
if 'krbpasswordexpiration' in service_entry.toDict():
diff --git a/tests/test_xmlrpc/objectclasses.py b/tests/test_xmlrpc/objectclasses.py
index cdcc6420..346d52c0 100644
--- a/tests/test_xmlrpc/objectclasses.py
+++ b/tests/test_xmlrpc/objectclasses.py
@@ -31,6 +31,8 @@ user_base = [
u'krbprincipalaux',
u'krbticketpolicyaux',
u'ipaobject',
+ u'ipasshuser',
+ u'ipaSshGroupOfPubKeys',
]
user = user_base + [u'mepOriginEntry']
@@ -44,6 +46,8 @@ group = [
]
host = [
+ u'ipasshhost',
+ u'ipaSshGroupOfPubKeys',
u'ieee802device',
u'ipaobject',
u'nshost',