2009-05-27 08:55:49 -05:00
|
|
|
# Authors:
|
|
|
|
# Rob Crittenden <rcritten@redhat.com>
|
|
|
|
# Pavel Zuna <pzuna@redhat.com>
|
|
|
|
#
|
|
|
|
# Copyright (C) 2008 Red Hat
|
|
|
|
# see file 'COPYING' for use and warranty information
|
|
|
|
#
|
2010-12-09 06:59:11 -06:00
|
|
|
# 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, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
2009-05-27 08:55:49 -05:00
|
|
|
#
|
|
|
|
# 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
|
2010-12-09 06:59:11 -06:00
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2011-08-24 21:48:30 -05:00
|
|
|
|
|
|
|
import platform
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
from nss.error import NSPRError
|
2011-12-07 01:50:31 -06:00
|
|
|
import nss.nss as nss
|
|
|
|
import netaddr
|
2011-08-24 21:48:30 -05:00
|
|
|
|
|
|
|
from ipalib import api, errors, util
|
|
|
|
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.plugins.service import set_certificate_attrs
|
|
|
|
from ipalib.plugins.dns import dns_container_exists, _record_types
|
|
|
|
from ipalib.plugins.dns import add_forward_record
|
|
|
|
from ipalib import _, ngettext
|
|
|
|
from ipalib import x509
|
2012-01-26 06:41:39 -06:00
|
|
|
from ipalib.dn import *
|
2011-08-24 21:48:30 -05:00
|
|
|
from ipalib.request import context
|
2011-12-07 01:50:31 -06:00
|
|
|
from ipalib.util import validate_sshpubkey, output_sshpubkey
|
|
|
|
from ipapython.ipautil import ipa_generate_password, CheckedIPAddress, make_sshfp
|
2011-08-24 21:48:30 -05:00
|
|
|
|
|
|
|
__doc__ = _("""
|
2010-08-24 22:40:32 -05:00
|
|
|
Hosts/Machines
|
2010-06-02 13:08:50 -05:00
|
|
|
|
|
|
|
A host represents a machine. It can be used in a number of contexts:
|
|
|
|
- service entries are associated with a host
|
|
|
|
- a host stores the host/ service principal
|
2011-03-04 10:08:54 -06:00
|
|
|
- a host can be used in Host-based Access Control (HBAC) rules
|
2010-06-02 13:08:50 -05:00
|
|
|
- every enrolled client generates a host entry
|
|
|
|
|
|
|
|
ENROLLMENT:
|
|
|
|
|
2010-08-24 22:40:32 -05:00
|
|
|
There are three enrollment scenarios when enrolling a new client:
|
2010-06-02 13:08:50 -05:00
|
|
|
|
2010-08-24 22:40:32 -05:00
|
|
|
1. You are enrolling as a full administrator. The host entry may exist
|
2010-12-01 10:23:52 -06:00
|
|
|
or not. A full administrator is a member of the hostadmin role
|
2010-08-24 22:40:32 -05:00
|
|
|
or the admins group.
|
|
|
|
2. You are enrolling as a limited administrator. The host must already
|
2011-03-04 10:08:54 -06:00
|
|
|
exist. A limited administrator is a member a role with the
|
|
|
|
Host Enrollment privilege.
|
2010-06-02 13:08:50 -05:00
|
|
|
3. The host has been created with a one-time password.
|
|
|
|
|
2010-08-24 22:40:32 -05:00
|
|
|
A host can only be enrolled once. If a client has enrolled and needs to
|
|
|
|
be re-enrolled, the host entry must be removed and re-created. Note that
|
|
|
|
re-creating the host entry will result in all services for the host being
|
|
|
|
removed, and all SSL certificates associated with those services being
|
|
|
|
revoked.
|
2010-06-02 13:08:50 -05:00
|
|
|
|
|
|
|
A host can optionally store information such as where it is located,
|
|
|
|
the OS that it runs, etc.
|
|
|
|
|
|
|
|
EXAMPLES:
|
|
|
|
|
2010-08-24 22:40:32 -05:00
|
|
|
Add a new host:
|
|
|
|
ipa host-add --location="3rd floor lab" --locality=Dallas test.example.com
|
2010-06-02 13:08:50 -05:00
|
|
|
|
2010-08-24 22:40:32 -05:00
|
|
|
Delete a host:
|
2010-06-02 13:08:50 -05:00
|
|
|
ipa host-del test.example.com
|
|
|
|
|
2010-08-24 22:40:32 -05:00
|
|
|
Add a new host with a one-time password:
|
2010-06-02 13:08:50 -05:00
|
|
|
ipa host-add --os='Fedora 12' --password=Secret123 test.example.com
|
|
|
|
|
2010-10-05 21:00:40 -05:00
|
|
|
Add a new host with a random one-time password:
|
|
|
|
ipa host-add --os='Fedora 12' --random test.example.com
|
|
|
|
|
2010-08-24 22:40:32 -05:00
|
|
|
Modify information about a host:
|
2010-06-02 13:08:50 -05:00
|
|
|
ipa host-mod --os='Fedora 12' test.example.com
|
2010-07-12 16:45:06 -05:00
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
Remove SSH public keys of a host and update DNS to reflect this change:
|
|
|
|
ipa host-mod --sshpubkey= --updatedns test.example.com
|
|
|
|
|
2011-02-16 03:35:49 -06:00
|
|
|
Disable the host Kerberos key, SSL certificate and all of its services:
|
2010-07-12 16:45:06 -05:00
|
|
|
ipa host-disable test.example.com
|
2010-11-10 15:47:29 -06:00
|
|
|
|
|
|
|
Add a host that can manage this host's keytab and certificate:
|
|
|
|
ipa host-add-managedby --hosts=test2 test
|
2011-08-24 21:48:30 -05:00
|
|
|
""")
|
2009-05-27 08:55:49 -05:00
|
|
|
|
|
|
|
def validate_host(ugettext, fqdn):
|
|
|
|
"""
|
|
|
|
Require at least one dot in the hostname (to support localhost.localdomain)
|
|
|
|
"""
|
2010-02-23 08:24:44 -06:00
|
|
|
if fqdn.find('.') == -1:
|
2010-03-29 10:31:10 -05:00
|
|
|
return _('Fully-qualified hostname required')
|
2009-05-27 08:55:49 -05:00
|
|
|
return None
|
|
|
|
|
2011-01-31 08:30:43 -06:00
|
|
|
def is_forward_record(zone, str_address):
|
|
|
|
addr = netaddr.IPAddress(str_address)
|
|
|
|
if addr.version == 4:
|
|
|
|
result = api.Command['dnsrecord_find'](zone, arecord=str_address)
|
|
|
|
elif addr.version == 6:
|
2011-05-27 13:29:33 -05:00
|
|
|
result = api.Command['dnsrecord_find'](zone, aaaarecord=str_address)
|
2011-01-31 08:30:43 -06:00
|
|
|
else:
|
|
|
|
raise ValueError('Invalid address family')
|
|
|
|
|
|
|
|
return result['count'] > 0
|
|
|
|
|
2011-06-14 09:31:36 -05:00
|
|
|
def get_reverse_zone(ipaddr, prefixlen=None):
|
2011-05-27 13:29:33 -05:00
|
|
|
ip = netaddr.IPAddress(ipaddr)
|
|
|
|
revdns = unicode(ip.reverse_dns)
|
|
|
|
|
2011-06-14 09:31:36 -05:00
|
|
|
if prefixlen is None:
|
|
|
|
revzone = u''
|
2011-05-27 13:29:33 -05:00
|
|
|
|
2011-06-14 09:31:36 -05:00
|
|
|
result = api.Command['dnszone_find']()['result']
|
|
|
|
for zone in result:
|
|
|
|
zonename = zone['idnsname'][0]
|
|
|
|
if revdns.endswith(zonename) and len(zonename) > len(revzone):
|
|
|
|
revzone = zonename
|
|
|
|
else:
|
|
|
|
if ip.version == 4:
|
|
|
|
pos = 4 - prefixlen / 8
|
|
|
|
elif ip.version == 6:
|
|
|
|
pos = 32 - prefixlen / 4
|
|
|
|
items = ip.reverse_dns.split('.')
|
|
|
|
revzone = u'.'.join(items[pos:])
|
|
|
|
|
|
|
|
try:
|
|
|
|
api.Command['dnszone_show'](revzone)
|
|
|
|
except errors.NotFound:
|
|
|
|
revzone = u''
|
2011-05-27 13:29:33 -05:00
|
|
|
|
|
|
|
if len(revzone) == 0:
|
|
|
|
raise errors.NotFound(
|
|
|
|
reason=_('DNS reverse zone for IP address %(addr)s not found') % dict(addr=ipaddr)
|
|
|
|
)
|
|
|
|
|
|
|
|
revname = revdns[:-len(revzone)-1]
|
|
|
|
|
|
|
|
return revzone, revname
|
|
|
|
|
2011-01-31 08:30:43 -06:00
|
|
|
def remove_fwd_ptr(ipaddr, host, domain, recordtype):
|
|
|
|
api.log.debug('deleting ipaddr %s' % ipaddr)
|
|
|
|
try:
|
2011-05-27 13:29:33 -05:00
|
|
|
revzone, revname = get_reverse_zone(ipaddr)
|
2011-01-31 08:30:43 -06:00
|
|
|
delkw = { 'ptrrecord' : "%s.%s." % (host, domain) }
|
|
|
|
api.Command['dnsrecord_del'](revzone, revname, **delkw)
|
|
|
|
except errors.NotFound:
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
delkw = { recordtype : ipaddr }
|
|
|
|
api.Command['dnsrecord_del'](domain, host, **delkw)
|
|
|
|
except errors.NotFound:
|
|
|
|
pass
|
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
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
|
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
host_output_params = (
|
2011-11-10 06:46:16 -06:00
|
|
|
Flag('has_keytab',
|
|
|
|
label=_('Keytab'),
|
|
|
|
),
|
2010-11-10 15:47:29 -06:00
|
|
|
Str('managedby_host',
|
|
|
|
label='Managed by',
|
|
|
|
),
|
2011-06-13 09:23:09 -05:00
|
|
|
Str('managing_host',
|
|
|
|
label='Managing',
|
|
|
|
),
|
2010-11-05 14:16:53 -05:00
|
|
|
Str('subject',
|
|
|
|
label=_('Subject'),
|
|
|
|
),
|
|
|
|
Str('serial_number',
|
|
|
|
label=_('Serial Number'),
|
|
|
|
),
|
|
|
|
Str('issuer',
|
|
|
|
label=_('Issuer'),
|
|
|
|
),
|
|
|
|
Str('valid_not_before',
|
|
|
|
label=_('Not Before'),
|
|
|
|
),
|
|
|
|
Str('valid_not_after',
|
|
|
|
label=_('Not After'),
|
|
|
|
),
|
|
|
|
Str('md5_fingerprint',
|
|
|
|
label=_('Fingerprint (MD5)'),
|
|
|
|
),
|
|
|
|
Str('sha1_fingerprint',
|
|
|
|
label=_('Fingerprint (SHA1)'),
|
|
|
|
),
|
|
|
|
Str('revocation_reason?',
|
|
|
|
label=_('Revocation reason'),
|
2011-01-10 13:21:45 -06:00
|
|
|
),
|
2010-11-05 14:16:53 -05:00
|
|
|
)
|
|
|
|
|
2010-11-23 16:47:29 -06:00
|
|
|
def validate_ipaddr(ugettext, ipaddr):
|
|
|
|
"""
|
|
|
|
Verify that we have either an IPv4 or IPv6 address.
|
|
|
|
"""
|
2011-06-14 09:31:36 -05:00
|
|
|
try:
|
|
|
|
ip = CheckedIPAddress(ipaddr, match_local=False)
|
2012-01-20 08:15:11 -06:00
|
|
|
except Exception, e:
|
|
|
|
return unicode(e)
|
2010-11-23 16:47:29 -06:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
class host(LDAPObject):
|
2009-05-27 08:55:49 -05:00
|
|
|
"""
|
|
|
|
Host object.
|
|
|
|
"""
|
2009-09-15 07:01:58 -05:00
|
|
|
container_dn = api.env.container_host
|
2011-07-12 11:01:25 -05:00
|
|
|
object_name = _('host')
|
|
|
|
object_name_plural = _('hosts')
|
2009-12-16 15:04:53 -06:00
|
|
|
object_class = ['ipaobject', 'nshost', 'ipahost', 'pkiuser', 'ipaservice']
|
2009-09-15 07:01:58 -05:00
|
|
|
# object_class_config = 'ipahostobjectclasses'
|
2010-07-12 16:45:06 -05:00
|
|
|
search_attributes = [
|
|
|
|
'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
|
2010-11-10 15:47:29 -06:00
|
|
|
'nshardwareplatform', 'nsosversion', 'managedby'
|
2010-07-12 16:45:06 -05:00
|
|
|
]
|
2009-09-15 07:01:58 -05:00
|
|
|
default_attributes = [
|
2010-02-12 15:34:21 -06:00
|
|
|
'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
|
|
|
|
'nshardwareplatform', 'nsosversion', 'usercertificate', 'memberof',
|
2012-01-20 14:10:44 -06:00
|
|
|
'managedby', 'memberindirect', 'memberofindirect', 'macaddress',
|
2011-12-07 01:50:31 -06:00
|
|
|
'sshpubkeyfp',
|
2009-09-15 07:01:58 -05:00
|
|
|
]
|
|
|
|
uuid_attribute = 'ipauniqueid'
|
|
|
|
attribute_members = {
|
|
|
|
'enrolledby': ['user'],
|
2011-05-31 16:52:35 -05:00
|
|
|
'memberof': ['hostgroup', 'netgroup', 'role', 'hbacrule', 'sudorule'],
|
2010-11-10 15:47:29 -06:00
|
|
|
'managedby': ['host'],
|
2011-06-13 09:23:09 -05:00
|
|
|
'managing': ['host'],
|
2011-05-31 16:52:35 -05:00
|
|
|
'memberofindirect': ['hostgroup', 'netgroup', 'role', 'hbacrule',
|
|
|
|
'sudorule'],
|
2009-09-15 07:01:58 -05:00
|
|
|
}
|
2010-12-02 10:05:54 -06:00
|
|
|
bindable = True
|
2011-01-04 14:15:54 -06:00
|
|
|
relationships = {
|
2011-01-06 16:14:13 -06:00
|
|
|
'memberof': ('Member Of', 'in_', 'not_in_'),
|
2011-01-04 14:15:54 -06:00
|
|
|
'enrolledby': ('Enrolled by', 'enroll_by_', 'not_enroll_by_'),
|
|
|
|
'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
|
2011-06-13 09:23:09 -05:00
|
|
|
'managing': ('Managing', 'man_', 'not_man_'),
|
2011-01-04 14:15:54 -06:00
|
|
|
}
|
2011-08-22 15:24:07 -05:00
|
|
|
password_attributes = [('userpassword', 'has_password'),
|
|
|
|
('krbprincipalkey', 'has_keytab')]
|
2009-09-15 07:01:58 -05:00
|
|
|
|
2010-02-08 06:03:28 -06:00
|
|
|
label = _('Hosts')
|
2011-07-13 21:10:47 -05:00
|
|
|
label_singular = _('Host')
|
2010-02-08 06:03:28 -06:00
|
|
|
|
2009-05-27 08:55:49 -05:00
|
|
|
takes_params = (
|
|
|
|
Str('fqdn', validate_host,
|
2011-09-12 12:51:31 -05:00
|
|
|
pattern='^[a-zA-Z0-9][a-zA-Z0-9-\.]{0,254}$',
|
|
|
|
pattern_errmsg='may only include letters, numbers, and -',
|
|
|
|
maxlength=255,
|
2009-05-27 08:55:49 -05:00
|
|
|
cli_name='hostname',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Host name'),
|
2009-05-27 08:55:49 -05:00
|
|
|
primary_key=True,
|
|
|
|
normalizer=lambda value: value.lower(),
|
|
|
|
),
|
|
|
|
Str('description?',
|
2009-09-15 07:01:58 -05:00
|
|
|
cli_name='desc',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Description'),
|
|
|
|
doc=_('A description of this host'),
|
2009-05-27 08:55:49 -05:00
|
|
|
),
|
2010-02-12 15:34:21 -06:00
|
|
|
Str('l?',
|
2009-05-27 08:55:49 -05:00
|
|
|
cli_name='locality',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Locality'),
|
|
|
|
doc=_('Host locality (e.g. "Baltimore, MD")'),
|
2009-05-27 08:55:49 -05:00
|
|
|
),
|
|
|
|
Str('nshostlocation?',
|
|
|
|
cli_name='location',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Location'),
|
|
|
|
doc=_('Host location (e.g. "Lab 2")'),
|
2009-05-27 08:55:49 -05:00
|
|
|
),
|
|
|
|
Str('nshardwareplatform?',
|
|
|
|
cli_name='platform',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Platform'),
|
|
|
|
doc=_('Host hardware platform (e.g. "Lenovo T61")'),
|
2009-05-27 08:55:49 -05:00
|
|
|
),
|
|
|
|
Str('nsosversion?',
|
|
|
|
cli_name='os',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Operating system'),
|
|
|
|
doc=_('Host operating system and version (e.g. "Fedora 9")'),
|
2009-05-27 08:55:49 -05:00
|
|
|
),
|
|
|
|
Str('userpassword?',
|
|
|
|
cli_name='password',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('User password'),
|
|
|
|
doc=_('Password used in bulk enrollment'),
|
2009-05-27 08:55:49 -05:00
|
|
|
),
|
2010-10-05 21:00:40 -05:00
|
|
|
Flag('random?',
|
|
|
|
doc=_('Generate a random password to be used in bulk enrollment'),
|
2011-11-14 10:03:44 -06:00
|
|
|
flags=('no_search', 'virtual_attribute'),
|
2010-10-05 21:00:40 -05:00
|
|
|
default=False,
|
|
|
|
),
|
|
|
|
Str('randompassword?',
|
|
|
|
label=_('Random password'),
|
2011-11-14 10:03:44 -06:00
|
|
|
flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'),
|
2010-10-05 21:00:40 -05:00
|
|
|
),
|
2009-12-16 15:04:53 -06:00
|
|
|
Bytes('usercertificate?', validate_certificate,
|
|
|
|
cli_name='certificate',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Certificate'),
|
|
|
|
doc=_('Base-64 encoded server certificate'),
|
2009-12-16 15:04:53 -06:00
|
|
|
),
|
2010-02-12 15:34:21 -06:00
|
|
|
Str('krbprincipalname?',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Principal name'),
|
2010-02-12 15:34:21 -06:00
|
|
|
flags=['no_create', 'no_update', 'no_search'],
|
|
|
|
),
|
2012-01-20 14:10:44 -06:00
|
|
|
Str('macaddress*',
|
|
|
|
normalizer=lambda value: value.upper(),
|
|
|
|
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.',
|
|
|
|
csv=True,
|
|
|
|
label=_('MAC address'),
|
|
|
|
doc=_('Hardware MAC address(es) on this host'),
|
|
|
|
),
|
2011-12-07 01:50:31 -06:00
|
|
|
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'],
|
|
|
|
),
|
2009-05-27 08:55:49 -05:00
|
|
|
)
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
def get_dn(self, *keys, **options):
|
2011-01-18 12:28:37 -06:00
|
|
|
hostname = keys[-1]
|
|
|
|
if hostname.endswith('.'):
|
|
|
|
hostname = hostname[:-1]
|
|
|
|
dn = super(host, self).get_dn(hostname, **options)
|
2009-09-15 07:01:58 -05:00
|
|
|
try:
|
|
|
|
self.backend.get_entry(dn, [''])
|
|
|
|
except errors.NotFound:
|
|
|
|
try:
|
|
|
|
(dn, entry_attrs) = self.backend.find_entry_by_attr(
|
2011-01-18 12:28:37 -06:00
|
|
|
'serverhostname', hostname, self.object_class, [''],
|
2009-09-15 07:01:58 -05:00
|
|
|
self.container_dn
|
|
|
|
)
|
|
|
|
except errors.NotFound:
|
|
|
|
pass
|
|
|
|
return dn
|
|
|
|
|
2011-06-13 09:23:09 -05:00
|
|
|
def get_managed_hosts(self, dn):
|
|
|
|
host_filter = 'managedBy=%s' % dn
|
|
|
|
host_attrs = ['fqdn']
|
|
|
|
ldap = self.api.Backend.ldap2
|
|
|
|
managed_hosts = []
|
|
|
|
|
|
|
|
try:
|
|
|
|
(hosts, truncated) = ldap.find_entries(base_dn=self.container_dn,
|
|
|
|
filter=host_filter, attrs_list=host_attrs)
|
|
|
|
|
|
|
|
for host in hosts:
|
|
|
|
managed_hosts.append(host[0])
|
|
|
|
except errors.NotFound:
|
|
|
|
return []
|
|
|
|
|
|
|
|
return managed_hosts
|
|
|
|
|
2011-09-14 15:33:33 -05:00
|
|
|
def suppress_netgroup_memberof(self, entry_attrs):
|
|
|
|
"""
|
|
|
|
We don't want to show managed netgroups so remove them from the
|
|
|
|
memberofindirect list.
|
|
|
|
"""
|
|
|
|
ng_container = DN(api.env.container_netgroup, api.env.basedn)
|
|
|
|
if 'memberofindirect' in entry_attrs:
|
|
|
|
for member in entry_attrs['memberofindirect']:
|
|
|
|
memberdn = DN(member)
|
|
|
|
if memberdn.endswith(ng_container):
|
|
|
|
try:
|
|
|
|
netgroup = api.Command['netgroup_show'](memberdn['cn'], all=True)['result']
|
|
|
|
if self.has_objectclass(netgroup['objectclass'], 'mepmanagedentry'):
|
|
|
|
entry_attrs['memberofindirect'].remove(member)
|
|
|
|
except errors.NotFound:
|
|
|
|
pass
|
|
|
|
|
2009-06-16 07:38:27 -05:00
|
|
|
api.register(host)
|
2009-05-27 08:55:49 -05:00
|
|
|
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
class host_add(LDAPCreate):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Add a new host.')
|
2009-12-09 10:09:53 -06:00
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
has_output_params = LDAPCreate.has_output_params + host_output_params
|
2009-12-09 10:09:53 -06:00
|
|
|
msg_summary = _('Added host "%(value)s"')
|
2010-11-10 15:47:29 -06:00
|
|
|
member_attributes = ['managedby']
|
2010-07-22 13:16:22 -05:00
|
|
|
takes_options = (
|
|
|
|
Flag('force',
|
2011-02-18 00:02:51 -06:00
|
|
|
label=_('Force'),
|
2010-07-22 13:16:22 -05:00
|
|
|
doc=_('force host name even if not in DNS'),
|
|
|
|
),
|
2010-12-14 12:02:18 -06:00
|
|
|
Flag('no_reverse',
|
|
|
|
doc=_('skip reverse DNS detection'),
|
|
|
|
),
|
2011-01-05 14:37:53 -06:00
|
|
|
Str('ip_address?', validate_ipaddr,
|
2010-11-23 16:47:29 -06:00
|
|
|
doc=_('Add the host to DNS with this IP address'),
|
2011-06-20 11:04:02 -05:00
|
|
|
label=_('IP Address'),
|
2010-11-23 16:47:29 -06:00
|
|
|
),
|
2010-07-22 13:16:22 -05:00
|
|
|
)
|
2009-12-09 10:09:53 -06:00
|
|
|
|
2009-12-10 09:39:24 -06:00
|
|
|
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
2011-01-12 11:18:01 -06:00
|
|
|
if 'ip_address' in options and dns_container_exists(ldap):
|
2010-11-23 16:47:29 -06:00
|
|
|
parts = keys[-1].split('.')
|
|
|
|
domain = unicode('.'.join(parts[1:]))
|
2011-01-12 14:02:05 -06:00
|
|
|
result = api.Command['dnszone_find']()['result']
|
2010-11-23 16:47:29 -06:00
|
|
|
match = False
|
|
|
|
for zone in result:
|
|
|
|
if domain == zone['idnsname'][0]:
|
|
|
|
match = True
|
|
|
|
break
|
|
|
|
if not match:
|
2011-02-23 15:47:49 -06:00
|
|
|
raise errors.NotFound(
|
|
|
|
reason=_('DNS zone %(zone)s not found') % dict(zone=domain)
|
|
|
|
)
|
2011-06-14 09:31:36 -05:00
|
|
|
ip = CheckedIPAddress(options['ip_address'], match_local=False)
|
2011-01-12 14:02:05 -06:00
|
|
|
if not options.get('no_reverse', False):
|
2010-12-14 12:02:18 -06:00
|
|
|
try:
|
2011-06-14 09:31:36 -05:00
|
|
|
prefixlen = None
|
|
|
|
if not ip.defaultnet:
|
|
|
|
prefixlen = ip.prefixlen
|
2011-05-27 13:29:33 -05:00
|
|
|
# we prefer lookup of the IP through the reverse zone
|
2011-06-14 09:31:36 -05:00
|
|
|
revzone, revname = get_reverse_zone(ip, prefixlen)
|
2011-01-12 14:02:05 -06:00
|
|
|
reverse = api.Command['dnsrecord_find'](revzone, idnsname=revname)
|
2010-12-14 12:02:18 -06:00
|
|
|
if reverse['count'] > 0:
|
|
|
|
raise errors.DuplicateEntry(message=u'This IP address is already assigned.')
|
|
|
|
except errors.NotFound:
|
|
|
|
pass
|
|
|
|
else:
|
2011-06-14 09:31:36 -05:00
|
|
|
if is_forward_record(domain, unicode(ip)):
|
2010-11-23 16:47:29 -06:00
|
|
|
raise errors.DuplicateEntry(message=u'This IP address is already assigned.')
|
2011-01-12 11:18:01 -06:00
|
|
|
if not options.get('force', False) and not 'ip_address' in options:
|
2010-07-22 13:16:22 -05:00
|
|
|
util.validate_host_dns(self.log, keys[-1])
|
2010-01-12 13:09:17 -06:00
|
|
|
if 'locality' in entry_attrs:
|
|
|
|
entry_attrs['l'] = entry_attrs['locality']
|
|
|
|
del entry_attrs['locality']
|
2009-09-15 07:01:58 -05:00
|
|
|
entry_attrs['cn'] = keys[-1]
|
|
|
|
entry_attrs['serverhostname'] = keys[-1].split('.', 1)[0]
|
2011-01-18 14:43:07 -06:00
|
|
|
if 'userpassword' not in entry_attrs and not options.get('random', False):
|
2009-05-27 08:55:49 -05:00
|
|
|
entry_attrs['krbprincipalname'] = 'host/%s@%s' % (
|
2009-09-15 07:01:58 -05:00
|
|
|
keys[-1], self.api.env.realm
|
2009-05-27 08:55:49 -05:00
|
|
|
)
|
2011-02-02 12:47:21 -06:00
|
|
|
if 'krbprincipalaux' not in entry_attrs['objectclass']:
|
2011-01-24 09:46:44 -06:00
|
|
|
entry_attrs['objectclass'].append('krbprincipalaux')
|
2011-02-02 12:47:21 -06:00
|
|
|
if 'krbprincipal' not in entry_attrs['objectclass']:
|
2009-05-27 08:55:49 -05:00
|
|
|
entry_attrs['objectclass'].append('krbprincipal')
|
2011-01-18 14:43:07 -06:00
|
|
|
else:
|
|
|
|
if 'krbprincipalaux' in entry_attrs['objectclass']:
|
|
|
|
entry_attrs['objectclass'].remove('krbprincipalaux')
|
2011-01-24 09:46:44 -06:00
|
|
|
if 'krbprincipal' in entry_attrs['objectclass']:
|
|
|
|
entry_attrs['objectclass'].remove('krbprincipal')
|
2011-11-14 10:03:44 -06:00
|
|
|
if options.get('random'):
|
|
|
|
entry_attrs['userpassword'] = ipa_generate_password()
|
|
|
|
# save the password so it can be displayed in post_callback
|
|
|
|
setattr(context, 'randompassword', entry_attrs['userpassword'])
|
2011-04-26 15:45:19 -05:00
|
|
|
cert = options.get('usercertificate')
|
|
|
|
if cert:
|
2011-06-08 09:54:41 -05:00
|
|
|
cert = x509.normalize_certificate(cert)
|
|
|
|
x509.verify_cert_subject(ldap, keys[-1], cert)
|
2011-04-26 15:45:19 -05:00
|
|
|
entry_attrs['usercertificate'] = cert
|
2009-12-16 15:04:53 -06:00
|
|
|
entry_attrs['managedby'] = dn
|
2012-01-20 14:10:44 -06:00
|
|
|
entry_attrs['objectclass'].append('ieee802device')
|
2011-12-07 01:50:31 -06:00
|
|
|
entry_attrs['objectclass'].append('ipasshhost')
|
2009-09-15 07:01:58 -05:00
|
|
|
return dn
|
2009-05-27 08:55:49 -05:00
|
|
|
|
2010-10-05 21:00:40 -05:00
|
|
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
2010-11-23 16:47:29 -06:00
|
|
|
exc = None
|
2011-12-07 01:50:31 -06:00
|
|
|
if dns_container_exists(ldap):
|
|
|
|
try:
|
2010-11-23 16:47:29 -06:00
|
|
|
parts = keys[-1].split('.')
|
|
|
|
domain = unicode('.'.join(parts[1:]))
|
2011-01-31 08:30:43 -06:00
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
if 'ip_address' in options:
|
|
|
|
ip = CheckedIPAddress(options['ip_address'], match_local=False)
|
|
|
|
add_forward_record(domain, parts[0], unicode(ip))
|
2011-01-28 07:25:47 -06:00
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
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
|
2011-01-28 07:25:47 -06:00
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
del options['ip_address']
|
|
|
|
|
|
|
|
update_sshfp_record(domain, unicode(parts[0]), entry_attrs)
|
|
|
|
except Exception, e:
|
|
|
|
exc = e
|
2010-10-05 21:00:40 -05:00
|
|
|
if options.get('random', False):
|
|
|
|
try:
|
|
|
|
entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
|
|
|
|
except AttributeError:
|
|
|
|
# On the off-chance some other extension deletes this from the
|
|
|
|
# context, don't crash.
|
|
|
|
pass
|
2010-11-23 16:47:29 -06:00
|
|
|
if exc:
|
2011-02-23 15:47:49 -06:00
|
|
|
raise errors.NonFatalError(
|
|
|
|
reason=_('The host was added but the DNS update failed with: %(exc)s') % dict(exc=exc)
|
|
|
|
)
|
2010-11-05 14:16:53 -05:00
|
|
|
set_certificate_attrs(entry_attrs)
|
2011-06-13 09:23:09 -05:00
|
|
|
|
|
|
|
if options.get('all', False):
|
2011-12-07 01:50:31 -06:00
|
|
|
entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
|
2011-08-22 15:24:07 -05:00
|
|
|
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
|
2011-06-13 09:23:09 -05:00
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
output_sshpubkey(ldap, dn, entry_attrs)
|
|
|
|
|
2010-10-05 21:00:40 -05:00
|
|
|
return dn
|
|
|
|
|
2009-06-16 09:51:44 -05:00
|
|
|
api.register(host_add)
|
2009-05-27 08:55:49 -05:00
|
|
|
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
class host_del(LDAPDelete):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Delete a host.')
|
2009-12-09 10:09:53 -06:00
|
|
|
|
|
|
|
msg_summary = _('Deleted host "%(value)s"')
|
2010-11-10 15:47:29 -06:00
|
|
|
member_attributes = ['managedby']
|
2009-12-09 10:09:53 -06:00
|
|
|
|
2011-01-03 15:03:06 -06:00
|
|
|
takes_options = (
|
2010-11-23 16:47:29 -06:00
|
|
|
Flag('updatedns?',
|
|
|
|
doc=_('Remove entries from DNS'),
|
|
|
|
default=False,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
def pre_callback(self, ldap, dn, *keys, **options):
|
2010-03-29 10:31:10 -05:00
|
|
|
# If we aren't given a fqdn, find it
|
|
|
|
if validate_host(None, keys[-1]) is not None:
|
|
|
|
hostentry = api.Command['host_show'](keys[-1])['result']
|
|
|
|
fqdn = hostentry['fqdn'][0]
|
|
|
|
else:
|
|
|
|
fqdn = keys[-1]
|
2009-05-27 08:55:49 -05:00
|
|
|
# Remove all service records for this host
|
2009-09-15 07:01:58 -05:00
|
|
|
truncated = True
|
|
|
|
while truncated:
|
|
|
|
try:
|
2010-03-29 10:31:10 -05:00
|
|
|
ret = api.Command['service_find'](fqdn)
|
2009-12-09 10:09:53 -06:00
|
|
|
truncated = ret['truncated']
|
|
|
|
services = ret['result']
|
2009-09-15 07:01:58 -05:00
|
|
|
except errors.NotFound:
|
|
|
|
break
|
|
|
|
else:
|
2009-12-09 10:09:53 -06:00
|
|
|
for entry_attrs in services:
|
2009-09-15 07:01:58 -05:00
|
|
|
principal = entry_attrs['krbprincipalname'][0]
|
|
|
|
(service, hostname, realm) = split_principal(principal)
|
2010-03-29 10:31:10 -05:00
|
|
|
if hostname.lower() == fqdn:
|
2009-09-15 07:01:58 -05:00
|
|
|
api.Command['service_del'](principal)
|
2010-11-23 16:47:29 -06:00
|
|
|
updatedns = options.get('updatedns', False)
|
|
|
|
if updatedns:
|
|
|
|
try:
|
|
|
|
updatedns = dns_container_exists(ldap)
|
|
|
|
except errors.NotFound:
|
|
|
|
updatedns = False
|
|
|
|
|
|
|
|
if updatedns:
|
|
|
|
# Remove DNS entries
|
|
|
|
parts = fqdn.split('.')
|
|
|
|
domain = unicode('.'.join(parts[1:]))
|
2011-01-12 14:02:05 -06:00
|
|
|
result = api.Command['dnszone_find']()['result']
|
2010-11-23 16:47:29 -06:00
|
|
|
match = False
|
|
|
|
for zone in result:
|
|
|
|
if domain == zone['idnsname'][0]:
|
|
|
|
match = True
|
|
|
|
break
|
|
|
|
if not match:
|
2011-02-23 15:47:49 -06:00
|
|
|
raise errors.NotFound(
|
|
|
|
reason=_('DNS zone %(zone)s not found') % dict(zone=domain)
|
|
|
|
)
|
2010-11-23 16:47:29 -06:00
|
|
|
# Get all forward resources for this host
|
2011-01-12 14:02:05 -06:00
|
|
|
records = api.Command['dnsrecord_find'](domain, idnsname=parts[0])['result']
|
2010-11-23 16:47:29 -06:00
|
|
|
for record in records:
|
|
|
|
if 'arecord' in record:
|
2011-01-31 08:30:43 -06:00
|
|
|
remove_fwd_ptr(record['arecord'][0], parts[0],
|
|
|
|
domain, 'arecord')
|
|
|
|
if 'aaaarecord' in record:
|
|
|
|
remove_fwd_ptr(record['aaaarecord'][0], parts[0],
|
|
|
|
domain, 'aaaarecord')
|
2010-11-23 16:47:29 -06:00
|
|
|
else:
|
|
|
|
# Try to delete all other record types too
|
2011-01-12 14:02:05 -06:00
|
|
|
_attribute_types = [str('%srecord' % t.lower()) for t in _record_types]
|
2010-11-23 16:47:29 -06:00
|
|
|
for attr in _attribute_types:
|
2011-01-31 08:30:43 -06:00
|
|
|
if attr not in ['arecord', 'aaaarecord'] and attr in record:
|
2010-11-23 16:47:29 -06:00
|
|
|
for i in xrange(len(record[attr])):
|
|
|
|
if (record[attr][i].endswith(parts[0]) or
|
|
|
|
record[attr][i].endswith(fqdn+'.')):
|
2011-01-12 14:02:05 -06:00
|
|
|
delkw = { unicode(attr) : record[attr][i] }
|
|
|
|
api.Command['dnsrecord_del'](domain,
|
|
|
|
record['idnsname'][0],
|
|
|
|
**delkw)
|
2010-11-23 16:47:29 -06:00
|
|
|
break
|
|
|
|
|
2011-01-06 06:27:24 -06:00
|
|
|
try:
|
|
|
|
(dn, entry_attrs) = ldap.get_entry(dn, ['usercertificate'])
|
|
|
|
except errors.NotFound:
|
|
|
|
self.obj.handle_not_found(*keys)
|
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
if 'usercertificate' in entry_attrs:
|
2011-06-08 09:54:41 -05:00
|
|
|
cert = x509.normalize_certificate(entry_attrs.get('usercertificate')[0])
|
2010-11-05 14:16:53 -05:00
|
|
|
try:
|
|
|
|
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
|
|
|
try:
|
|
|
|
result = api.Command['cert_show'](unicode(serial))['result'
|
|
|
|
]
|
|
|
|
if 'revocation_reason' not in result:
|
|
|
|
try:
|
|
|
|
api.Command['cert_revoke'](unicode(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
|
|
|
|
# removing the host.
|
|
|
|
self.log.info("Problem decoding certificate %s" % nsprerr.args[1])
|
|
|
|
else:
|
|
|
|
raise nsprerr
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
return dn
|
2009-05-27 08:55:49 -05:00
|
|
|
|
2009-06-16 09:51:44 -05:00
|
|
|
api.register(host_del)
|
2009-05-27 08:55:49 -05:00
|
|
|
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
class host_mod(LDAPUpdate):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Modify information about a host.')
|
2009-12-09 10:09:53 -06:00
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
has_output_params = LDAPUpdate.has_output_params + host_output_params
|
2009-12-09 10:09:53 -06:00
|
|
|
msg_summary = _('Modified host "%(value)s"')
|
2010-11-10 15:47:29 -06:00
|
|
|
member_attributes = ['managedby']
|
2009-12-09 10:09:53 -06:00
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
takes_options = LDAPUpdate.takes_options + (
|
2009-09-14 16:04:08 -05:00
|
|
|
Str('krbprincipalname?',
|
|
|
|
cli_name='principalname',
|
2010-02-19 10:08:16 -06:00
|
|
|
label=_('Principal name'),
|
|
|
|
doc=_('Kerberos principal name for this host'),
|
2009-09-15 07:01:58 -05:00
|
|
|
attribute=True,
|
2009-09-14 16:04:08 -05:00
|
|
|
),
|
2011-12-07 01:50:31 -06:00
|
|
|
Flag('updatedns?',
|
|
|
|
doc=_('Update DNS entries'),
|
|
|
|
default=False,
|
|
|
|
),
|
2009-09-14 16:04:08 -05:00
|
|
|
)
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
2011-09-08 12:47:37 -05:00
|
|
|
# Allow an existing OTP to be reset but don't allow a OTP to be
|
|
|
|
# added to an enrolled host.
|
|
|
|
if 'userpassword' in options:
|
|
|
|
entry = {}
|
|
|
|
self.obj.get_password_attributes(ldap, dn, entry)
|
|
|
|
if not entry['has_password'] and entry['has_keytab']:
|
|
|
|
raise errors.ValidationError(name='password', error=_('Password cannot be set on enrolled host.'))
|
|
|
|
|
2009-09-14 16:04:08 -05:00
|
|
|
# Once a principal name is set it cannot be changed
|
2011-02-15 14:04:40 -06:00
|
|
|
if 'cn' in entry_attrs:
|
|
|
|
raise errors.ACIError(info='cn is immutable')
|
2010-01-12 13:09:17 -06:00
|
|
|
if 'locality' in entry_attrs:
|
|
|
|
entry_attrs['l'] = entry_attrs['locality']
|
|
|
|
del entry_attrs['locality']
|
2009-09-14 16:04:08 -05:00
|
|
|
if 'krbprincipalname' in entry_attrs:
|
2009-09-15 07:01:58 -05:00
|
|
|
(dn, entry_attrs_old) = ldap.get_entry(
|
|
|
|
dn, ['objectclass', 'krbprincipalname']
|
|
|
|
)
|
|
|
|
if 'krbprincipalname' in entry_attrs_old:
|
|
|
|
msg = 'Principal name already set, it is unchangeable.'
|
|
|
|
raise errors.ACIError(info=msg)
|
|
|
|
obj_classes = entry_attrs_old['objectclass']
|
|
|
|
if 'krbprincipalaux' not in obj_classes:
|
|
|
|
obj_classes.append('krbprincipalaux')
|
|
|
|
entry_attrs['objectclass'] = obj_classes
|
2011-06-08 09:54:41 -05:00
|
|
|
cert = x509.normalize_certificate(entry_attrs.get('usercertificate'))
|
2009-12-16 15:04:53 -06:00
|
|
|
if cert:
|
2011-06-08 09:54:41 -05:00
|
|
|
x509.verify_cert_subject(ldap, keys[-1], cert)
|
2009-12-16 15:04:53 -06:00
|
|
|
(dn, entry_attrs_old) = ldap.get_entry(dn, ['usercertificate'])
|
|
|
|
if 'usercertificate' in entry_attrs_old:
|
2011-06-08 09:54:41 -05:00
|
|
|
oldcert = x509.normalize_certificate(entry_attrs_old.get('usercertificate')[0])
|
2010-11-05 14:16:53 -05:00
|
|
|
try:
|
|
|
|
serial = unicode(x509.get_serial_number(oldcert, x509.DER))
|
|
|
|
try:
|
|
|
|
result = api.Command['cert_show'](unicode(serial))['result']
|
|
|
|
if 'revocation_reason' not in result:
|
|
|
|
try:
|
|
|
|
api.Command['cert_revoke'](unicode(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
|
2011-12-07 01:50:31 -06:00
|
|
|
|
2011-11-14 10:03:44 -06:00
|
|
|
if options.get('random'):
|
|
|
|
entry_attrs['userpassword'] = ipa_generate_password()
|
|
|
|
setattr(context, 'randompassword', entry_attrs['userpassword'])
|
2012-01-20 14:10:44 -06:00
|
|
|
if 'macaddress' 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']
|
|
|
|
if 'ieee802device' not in obj_classes:
|
|
|
|
obj_classes.append('ieee802device')
|
|
|
|
entry_attrs['objectclass'] = obj_classes
|
2010-10-05 21:00:40 -05:00
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
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')
|
|
|
|
|
2010-10-05 21:00:40 -05:00
|
|
|
return dn
|
2009-12-16 15:04:53 -06:00
|
|
|
|
2010-10-05 21:00:40 -05:00
|
|
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
|
|
|
if options.get('random', False):
|
|
|
|
entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
|
2010-11-05 14:16:53 -05:00
|
|
|
set_certificate_attrs(entry_attrs)
|
2011-08-25 08:24:47 -05:00
|
|
|
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
|
2011-06-13 09:23:09 -05:00
|
|
|
|
|
|
|
if options.get('all', False):
|
|
|
|
entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
|
|
|
|
|
2011-09-14 15:33:33 -05:00
|
|
|
self.obj.suppress_netgroup_memberof(entry_attrs)
|
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
output_sshpubkey(ldap, dn, entry_attrs)
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
return dn
|
2009-05-27 08:55:49 -05:00
|
|
|
|
2009-06-16 07:38:27 -05:00
|
|
|
api.register(host_mod)
|
2009-05-27 08:55:49 -05:00
|
|
|
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
class host_find(LDAPSearch):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Search for hosts.')
|
2009-05-27 08:55:49 -05:00
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
has_output_params = LDAPSearch.has_output_params + host_output_params
|
2009-12-09 10:09:53 -06:00
|
|
|
msg_summary = ngettext(
|
2011-02-23 15:47:49 -06:00
|
|
|
'%(count)d host matched', '%(count)d hosts matched', 0
|
2009-12-09 10:09:53 -06:00
|
|
|
)
|
2011-01-04 14:15:54 -06:00
|
|
|
member_attributes = ['memberof', 'enrolledby', 'managedby']
|
2009-12-09 10:09:53 -06:00
|
|
|
|
2012-01-26 06:41:39 -06:00
|
|
|
def get_options(self):
|
|
|
|
for option in super(host_find, self).get_options():
|
|
|
|
yield option
|
|
|
|
# "managing" membership has to be added and processed separately
|
|
|
|
for option in self.get_member_options('managing'):
|
|
|
|
yield option
|
|
|
|
|
2010-11-23 08:02:54 -06:00
|
|
|
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
|
2010-01-12 13:09:17 -06:00
|
|
|
if 'locality' in attrs_list:
|
|
|
|
attrs_list.remove('locality')
|
|
|
|
attrs_list.append('l')
|
2012-01-26 06:41:39 -06:00
|
|
|
if 'man_host' in options or 'not_man_host' in options:
|
|
|
|
hosts = []
|
|
|
|
if options.get('man_host') is not None:
|
|
|
|
for pkey in options.get('man_host', []):
|
|
|
|
dn = self.obj.get_dn(pkey)
|
|
|
|
try:
|
|
|
|
(dn, entry_attrs) = ldap.get_entry(dn, ['managedby'])
|
|
|
|
except errors.NotFound:
|
|
|
|
self.obj.handle_not_found(pkey)
|
|
|
|
hosts.append(set(entry_attrs.get('managedby', '')))
|
|
|
|
hosts = list(reduce(lambda s1, s2: s1 & s2, hosts))
|
|
|
|
|
|
|
|
if not hosts:
|
|
|
|
# There is no host managing _all_ hosts in --man-hosts
|
|
|
|
filter = ldap.combine_filters(
|
|
|
|
(filter, '(objectclass=disabled)'), ldap.MATCH_ALL
|
|
|
|
)
|
|
|
|
|
|
|
|
not_hosts = []
|
|
|
|
if options.get('not_man_host') is not None:
|
|
|
|
for pkey in options.get('not_man_host', []):
|
|
|
|
dn = self.obj.get_dn(pkey)
|
|
|
|
try:
|
|
|
|
(dn, entry_attrs) = ldap.get_entry(dn, ['managedby'])
|
|
|
|
except errors.NotFound:
|
|
|
|
self.obj.handle_not_found(pkey)
|
|
|
|
not_hosts += entry_attrs.get('managedby', [])
|
|
|
|
not_hosts = list(set(not_hosts))
|
|
|
|
|
|
|
|
for target_hosts, filter_op in ((hosts, ldap.MATCH_ANY),
|
|
|
|
(not_hosts, ldap.MATCH_NONE)):
|
|
|
|
hosts_avas = [DN(host)[0][0] for host in target_hosts]
|
|
|
|
hosts_filters = [ldap.make_filter_from_attr(ava.attr, ava.value) for ava in hosts_avas]
|
|
|
|
hosts_filter = ldap.combine_filters(hosts_filters, filter_op)
|
|
|
|
|
|
|
|
filter = ldap.combine_filters(
|
|
|
|
(filter, hosts_filter), ldap.MATCH_ALL
|
|
|
|
)
|
|
|
|
|
2010-11-23 08:02:54 -06:00
|
|
|
return (filter.replace('locality', 'l'), base_dn, scope)
|
2010-01-12 13:09:17 -06:00
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
def post_callback(self, ldap, entries, truncated, *args, **options):
|
2011-10-26 04:12:38 -05:00
|
|
|
if options.get('pkey_only', False):
|
|
|
|
return
|
2010-11-05 14:16:53 -05:00
|
|
|
for entry in entries:
|
2011-08-22 15:24:07 -05:00
|
|
|
(dn, entry_attrs) = entry
|
2010-11-05 14:16:53 -05:00
|
|
|
set_certificate_attrs(entry_attrs)
|
2011-08-22 15:24:07 -05:00
|
|
|
self.obj.get_password_attributes(ldap, dn, entry_attrs)
|
2011-09-14 15:33:33 -05:00
|
|
|
self.obj.suppress_netgroup_memberof(entry_attrs)
|
2011-08-22 15:24:07 -05:00
|
|
|
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
|
2010-11-05 14:16:53 -05:00
|
|
|
|
2011-06-13 09:23:09 -05:00
|
|
|
if options.get('all', False):
|
|
|
|
entry_attrs['managing'] = self.obj.get_managed_hosts(entry[0])
|
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
output_sshpubkey(ldap, dn, entry_attrs)
|
|
|
|
|
2009-06-16 07:38:27 -05:00
|
|
|
api.register(host_find)
|
2009-05-27 08:55:49 -05:00
|
|
|
|
|
|
|
|
2009-09-15 07:01:58 -05:00
|
|
|
class host_show(LDAPRetrieve):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Display information about a host.')
|
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
has_output_params = LDAPRetrieve.has_output_params + host_output_params
|
2010-12-10 09:53:20 -06:00
|
|
|
takes_options = LDAPRetrieve.takes_options + (
|
|
|
|
Str('out?',
|
|
|
|
doc=_('file to store certificate in'),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2010-11-10 15:47:29 -06:00
|
|
|
member_attributes = ['managedby']
|
2010-07-12 16:45:06 -05:00
|
|
|
|
|
|
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
2011-08-22 15:24:07 -05:00
|
|
|
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.
|
2010-07-12 16:45:06 -05:00
|
|
|
entry_attrs['has_keytab'] = False
|
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
set_certificate_attrs(entry_attrs)
|
2010-10-15 23:40:38 -05:00
|
|
|
|
2011-06-13 09:23:09 -05:00
|
|
|
if options.get('all', False):
|
|
|
|
entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
|
|
|
|
|
2011-09-14 15:33:33 -05:00
|
|
|
self.obj.suppress_netgroup_memberof(entry_attrs)
|
|
|
|
|
2011-12-07 01:50:31 -06:00
|
|
|
output_sshpubkey(ldap, dn, entry_attrs)
|
|
|
|
|
2010-07-12 16:45:06 -05:00
|
|
|
return dn
|
2009-05-27 08:55:49 -05:00
|
|
|
|
2010-12-10 09:53:20 -06:00
|
|
|
def forward(self, *keys, **options):
|
|
|
|
if 'out' in options:
|
2011-06-08 09:54:41 -05:00
|
|
|
util.check_writable_file(options['out'])
|
2010-12-10 09:53:20 -06:00
|
|
|
result = super(host_show, self).forward(*keys, **options)
|
|
|
|
if 'usercertificate' in result['result']:
|
2011-06-08 09:54:41 -05:00
|
|
|
x509.write_certificate(result['result']['usercertificate'][0], options['out'])
|
2010-12-10 09:53:20 -06:00
|
|
|
result['summary'] = _('Certificate stored in file \'%(file)s\'') % dict(file=options['out'])
|
|
|
|
return result
|
|
|
|
else:
|
|
|
|
raise errors.NoCertificateError(entry=keys[-1])
|
|
|
|
else:
|
|
|
|
return super(host_show, self).forward(*keys, **options)
|
|
|
|
|
2009-06-16 07:38:27 -05:00
|
|
|
api.register(host_show)
|
2010-07-12 16:45:06 -05:00
|
|
|
|
|
|
|
|
|
|
|
class host_disable(LDAPQuery):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Disable the Kerberos key, SSL certificate and all services of a host.')
|
|
|
|
|
2010-07-12 16:45:06 -05:00
|
|
|
has_output = output.standard_value
|
2011-02-16 03:35:49 -06:00
|
|
|
msg_summary = _('Disabled host "%(value)s"')
|
2010-07-12 16:45:06 -05:00
|
|
|
|
|
|
|
def execute(self, *keys, **options):
|
|
|
|
ldap = self.obj.backend
|
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
# If we aren't given a fqdn, find it
|
|
|
|
if validate_host(None, keys[-1]) is not None:
|
|
|
|
hostentry = api.Command['host_show'](keys[-1])['result']
|
|
|
|
fqdn = hostentry['fqdn'][0]
|
|
|
|
else:
|
|
|
|
fqdn = keys[-1]
|
|
|
|
|
|
|
|
# See if we actually do anthing here, and if not raise an exception
|
|
|
|
done_work = False
|
|
|
|
|
2010-07-12 16:45:06 -05:00
|
|
|
dn = self.obj.get_dn(*keys, **options)
|
2011-01-06 06:27:24 -06:00
|
|
|
try:
|
2011-08-22 15:24:07 -05:00
|
|
|
(dn, entry_attrs) = ldap.get_entry(dn, ['usercertificate'])
|
2011-01-06 06:27:24 -06:00
|
|
|
except errors.NotFound:
|
|
|
|
self.obj.handle_not_found(*keys)
|
2010-07-12 16:45:06 -05:00
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
truncated = True
|
|
|
|
while truncated:
|
|
|
|
try:
|
|
|
|
ret = api.Command['service_find'](fqdn)
|
|
|
|
truncated = ret['truncated']
|
|
|
|
services = ret['result']
|
|
|
|
except errors.NotFound:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
for entry_attrs in services:
|
|
|
|
principal = entry_attrs['krbprincipalname'][0]
|
|
|
|
(service, hostname, realm) = split_principal(principal)
|
|
|
|
if hostname.lower() == fqdn:
|
|
|
|
try:
|
|
|
|
api.Command['service_disable'](principal)
|
|
|
|
done_work = True
|
|
|
|
except errors.AlreadyInactive:
|
|
|
|
pass
|
|
|
|
if 'usercertificate' in entry_attrs:
|
2011-06-08 09:54:41 -05:00
|
|
|
cert = x509.normalize_certificate(entry_attrs.get('usercertificate')[0])
|
2010-11-05 14:16:53 -05:00
|
|
|
try:
|
|
|
|
serial = unicode(x509.get_serial_number(cert, x509.DER))
|
|
|
|
try:
|
|
|
|
result = api.Command['cert_show'](unicode(serial))['result'
|
|
|
|
]
|
|
|
|
if 'revocation_reason' not in result:
|
|
|
|
try:
|
|
|
|
api.Command['cert_revoke'](unicode(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
|
|
|
|
# disabling the host.
|
|
|
|
self.log.info("Problem decoding certificate %s" % nsprerr.args[1])
|
|
|
|
else:
|
|
|
|
raise nsprerr
|
|
|
|
|
|
|
|
# Remove the usercertificate altogether
|
|
|
|
ldap.update_entry(dn, {'usercertificate': None})
|
|
|
|
done_work = True
|
|
|
|
|
2011-08-22 15:24:07 -05:00
|
|
|
self.obj.get_password_attributes(ldap, dn, entry_attrs)
|
|
|
|
if entry_attrs['has_keytab']:
|
2010-11-05 14:16:53 -05:00
|
|
|
ldap.remove_principal_key(dn)
|
|
|
|
done_work = True
|
2010-07-12 16:45:06 -05:00
|
|
|
|
2010-11-05 14:16:53 -05:00
|
|
|
if not done_work:
|
|
|
|
raise errors.AlreadyInactive()
|
2010-07-12 16:45:06 -05:00
|
|
|
|
|
|
|
return dict(
|
|
|
|
result=True,
|
|
|
|
value=keys[0],
|
|
|
|
)
|
|
|
|
|
2011-09-14 15:33:33 -05:00
|
|
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
|
|
|
self.obj.suppress_netgroup_memberof(entry_attrs)
|
|
|
|
return dn
|
|
|
|
|
2010-07-12 16:45:06 -05:00
|
|
|
api.register(host_disable)
|
2010-11-10 15:47:29 -06:00
|
|
|
|
|
|
|
class host_add_managedby(LDAPAddMember):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Add hosts that can manage this host.')
|
|
|
|
|
2010-11-10 15:47:29 -06:00
|
|
|
member_attributes = ['managedby']
|
|
|
|
has_output_params = LDAPAddMember.has_output_params + host_output_params
|
2011-01-10 13:21:45 -06:00
|
|
|
allow_same = True
|
2010-11-10 15:47:29 -06:00
|
|
|
|
2011-09-14 15:33:33 -05:00
|
|
|
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
|
|
|
|
self.obj.suppress_netgroup_memberof(entry_attrs)
|
|
|
|
return (completed, dn)
|
|
|
|
|
2010-11-10 15:47:29 -06:00
|
|
|
api.register(host_add_managedby)
|
|
|
|
|
|
|
|
|
|
|
|
class host_remove_managedby(LDAPRemoveMember):
|
2011-08-24 21:48:30 -05:00
|
|
|
__doc__ = _('Remove hosts that can manage this host.')
|
|
|
|
|
2010-11-10 15:47:29 -06:00
|
|
|
member_attributes = ['managedby']
|
|
|
|
has_output_params = LDAPRemoveMember.has_output_params + host_output_params
|
|
|
|
|
2011-09-14 15:33:33 -05:00
|
|
|
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
|
|
|
|
self.obj.suppress_netgroup_memberof(entry_attrs)
|
|
|
|
return (completed, dn)
|
|
|
|
|
2010-11-10 15:47:29 -06:00
|
|
|
api.register(host_remove_managedby)
|