ipaserver.plugins.service: add service-add-smb to set up an SMB service

SMB service has a number of predefined properties that must be set at a
creation time. Thus, we provide a special command that handles all the
needed changes. In addition, since SMB principal name is predefined, it
is generated automatically based on the machine hostname.

Since we generate the service's object primary key, its argument/option
should be removed from the list of the command's arguments and options.
We also remove those options that make no sense in the context of SMB
service.

Most controversial would probably be a lack of the authentication
indicator that could be associated with the service.  However, this is
intended: SMB service on the domain member is used by both humans and
other SMB services in the domain. Thus, it is not possible to require a
specific authentication indicator to be present: automated acquisition
of the credentials by a domain controller or other domain member machine
accounts is based on a single factor creds and cannot be changed.

Access to SMB service should be regulated on the SMB protocol level,
with access controls in share ACLs.

Fixes: https://pagure.io/freeipa/issue/3999
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
Alexander Bokovoy
2019-03-05 15:03:57 +02:00
parent d631e008cc
commit afb8305ada
4 changed files with 161 additions and 4 deletions

View File

@@ -273,6 +273,8 @@ aci: (targetattr = "krbcanonicalname || krbprincipalname")(targetfilter = "(obje
dn: cn=services,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "krbprincipalauthind || usercertificate")(targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Modify Services";allow (write) groupdn = "ldap:///cn=System: Modify Services,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=services,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || entryusn || gecos || gidnumber || homedirectory || ipantsecurityidentifier || loginshell || modifytimestamp || objectclass || uid || uidnumber")(targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Read POSIX details of SMB services";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=services,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "createtimestamp || entryusn || ipakrbauthzdata || ipakrbprincipalalias || ipauniqueid || krbcanonicalname || krblastpwdchange || krbobjectreferences || krbpasswordexpiration || krbprincipalaliases || krbprincipalauthind || krbprincipalexpiration || krbprincipalname || managedby || memberof || modifytimestamp || objectclass || usercertificate")(targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Read Services";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=services,cn=accounts,dc=ipa,dc=example
aci: (targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Remove Services";allow (delete) groupdn = "ldap:///cn=System: Remove Services,cn=permissions,cn=pbac,dc=ipa,dc=example";)

17
API.txt
View File

@@ -4537,6 +4537,22 @@ option: Str('version?')
output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: service_add_smb/1
args: 2,9,3
arg: Str('fqdn', cli_name='hostname')
arg: Str('ipantflatname?', cli_name='netbiosname')
option: Str('addattr*', cli_name='addattr')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Bool('ipakrbokasdelegate?', cli_name='ok_as_delegate')
option: Bool('ipakrboktoauthasdelegate?', cli_name='ok_to_auth_as_delegate')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('setattr*', cli_name='setattr')
option: Certificate('usercertificate*', cli_name='certificate')
option: Str('version?')
output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: service_allow_create_keytab/1
args: 1,8,3
arg: Principal('krbcanonicalname', cli_name='canonical_principal')
@@ -6922,6 +6938,7 @@ default: service_add/1
default: service_add_cert/1
default: service_add_host/1
default: service_add_principal/1
default: service_add_smb/1
default: service_allow_create_keytab/1
default: service_allow_retrieve_keytab/1
default: service_del/1

View File

@@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
define(IPA_API_VERSION_MINOR, 232)
# Last change: Add ipamaxhostnamelength to config
define(IPA_API_VERSION_MINOR, 233)
# Last change: Added service_add_smb command
########################################################

View File

@@ -44,14 +44,15 @@ from .baseldap import (
LDAPAddAttribute,
LDAPRemoveAttribute,
LDAPAddAttributeViaOption,
LDAPRemoveAttributeViaOption)
LDAPRemoveAttributeViaOption,
DNA_MAGIC)
from ipalib import x509
from ipalib import _, ngettext
from ipalib import util
from ipalib import output
from ipapython import kerberos
from ipapython.dn import DN
from ipapython.dnsutil import DNSName
if six.PY3:
unicode = str
@@ -447,6 +448,16 @@ class service(LDAPObject):
],
'default_privileges': {'Service Administrators'},
},
'System: Read POSIX details of SMB services': {
'replaces_global_anonymous_aci': True,
'ipapermbindruletype': 'all',
'ipapermright': {'read', 'search', 'compare'},
'ipapermdefaultattr': {
'objectclass', 'cn', 'uid', 'gecos', 'gidnumber',
'homedirectory', 'loginshell', 'uidnumber',
'ipantsecurityidentifier',
},
}
}
label = _('Services')
@@ -664,6 +675,133 @@ class service_add(LDAPCreate):
return dn
@register()
class service_add_smb(LDAPCreate):
__doc__ = _('Add a new SMB service.')
msg_summary = _('Added service "%(value)s"')
member_attributes = ['managedby']
has_output_params = LDAPCreate.has_output_params + output_params
smb_takes_args = (
Str('fqdn', util.hostname_validator,
cli_name='hostname',
label=_('Host name'),
primary_key=True,
normalizer=util.normalize_hostname,
flags={'virtual_attribute', 'no_display', 'no_update',
'no_search'},
),
Str('ipantflatname?',
cli_name='netbiosname',
label=_('SMB service NetBIOS name'),
flags={'virtual_attribute', 'no_display', 'no_update',
'no_search'},
),
)
takes_options = LDAPCreate.takes_options
def get_args(self):
"""
Rewrite arguments to service-add-smb command to make sure we accept
hostname instead of a principal as we'll be constructing the principal
ourselves
"""
for arg in self.smb_takes_args:
yield arg
for arg in super(service_add_smb, self).get_args():
if arg not in self.smb_takes_args and not arg.primary_key:
yield arg
def get_options(self):
"""
Rewrite options to service-add-smb command to filter out cannonical
principal which is autoconstructed. Also filter out options which
make no sense for SMB service.
"""
excluded = ('ipakrbauthzdata', 'krbprincipalauthind',
'ipakrbrequirespreauth')
for arg in self.takes_options:
yield arg
for arg in super(service_add_smb, self).get_options():
check = all([arg not in self.takes_options,
not arg.primary_key,
arg.name not in excluded])
if check:
yield arg
def pre_callback(self, ldap, dn, entry_attrs, attrs_list,
*keys, **options):
assert isinstance(dn, DN)
hostname = keys[0]
if len(keys) == 2:
netbiosname = keys[1]
else:
# By default take leftmost label from the host name
netbiosname = DNSName.from_text(hostname)[0].decode().upper()
# SMB service requires existence of the host object
# because DCE RPC calls authenticated with GSSAPI are using
# host/.. principal by default for validation
try:
hostresult = self.api.Command['host_show'](hostname)['result']
except errors.NotFound:
raise errors.NotFound(reason=_(
"The host '%s' does not exist to add a service to.") %
hostname)
# We cannot afford the host not being resolvable even for
# clustered environments with CTDB because the target name
# has to exist even in that case
util.verify_host_resolvable(hostname)
smbaccount = '{name}$'.format(name=netbiosname)
smbprincipal = 'cifs/{hostname}'.format(hostname=hostname)
entry_attrs['krbprincipalname'] = [
str(kerberos.Principal(smbprincipal, realm=self.api.env.realm)),
str(kerberos.Principal(smbaccount, realm=self.api.env.realm))]
entry_attrs['krbcanonicalname'] = entry_attrs['krbprincipalname'][0]
# Rewrite DN using proper rdn and new canonical name because when
# LDAPCreate.execute() was called, it set DN to krbcanonicalname=$value
dn = DN(('krbprincipalname', entry_attrs['krbcanonicalname']),
DN(self.obj.container_dn, api.env.basedn))
# Enforce ipaKrbPrincipalAlias to aid case-insensitive searches as
# krbPrincipalName/krbCanonicalName are case-sensitive in Kerberos
# schema
entry_attrs['ipakrbprincipalalias'] = entry_attrs['krbcanonicalname']
for o in ('ipakrbprincipal', 'ipaidobject', 'krbprincipalaux',
'posixaccount'):
if o not in entry_attrs['objectclass']:
entry_attrs['objectclass'].append(o)
entry_attrs['uid'] = ['/'.join(
kerberos.Principal(smbprincipal).components)]
entry_attrs['uid'].append(smbaccount)
entry_attrs['cn'] = netbiosname
entry_attrs['homeDirectory'] = '/dev/null'
entry_attrs['uidNumber'] = DNA_MAGIC
entry_attrs['gidNumber'] = DNA_MAGIC
self.obj.validate_ipakrbauthzdata(entry_attrs)
if 'managedby' not in entry_attrs:
entry_attrs['managedby'] = hostresult['dn']
update_krbticketflags(ldap, entry_attrs, attrs_list, options, False)
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
set_kerberos_attrs(entry_attrs, options)
rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
self.obj.populate_krbcanonicalname(entry_attrs, options)
return dn
@register()
class service_del(LDAPDelete):