From c18ee9b641ddc1e6b52d0413caa1fb98ac13785d Mon Sep 17 00:00:00 2001 From: Tibor Dudlák Date: Jul 01 2019 11:21:21 +0000 Subject: Add SMB attributes for users SMB attributes are used by Samba domain controller when reporting details about IPA users via LSA DCE RPC calls. Based on the initial work from the external plugin: https://github.com/abbra/freeipa-user-trust-attributes Related: https://pagure.io/freeipa/issue/3999 Signed-off-by: Alexander Bokovoy Signed-off-by: Tibor Dudlák Reviewed-By: Alexander Bokovoy Reviewed-By: Tibor Dudlak --- diff --git a/API.txt b/API.txt index 8fb94b1..fdcbf17 100644 --- a/API.txt +++ b/API.txt @@ -4988,7 +4988,7 @@ output: Output('result', type=[]) output: Output('summary', type=[, ]) output: ListOfPrimaryKeys('value') command: stageuser_find/1 -args: 1,54,4 +args: 1,58,4 arg: Str('criteria?') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('carlicense*', autofill=False) @@ -5008,6 +5008,10 @@ option: Str('in_netgroup*', cli_name='in_netgroups') option: Str('in_role*', cli_name='in_roles') option: Str('in_sudorule*', cli_name='in_sudorules') option: Str('initials?', autofill=False) +option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') +option: StrEnum('ipanthomedirectoryrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) +option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') +option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp']) @@ -5049,7 +5053,7 @@ output: ListOfEntries('result') output: Output('summary', type=[, ]) output: Output('truncated', type=[]) command: stageuser_mod/1 -args: 1,47,3 +args: 1,51,3 arg: Str('uid', cli_name='login') option: Str('addattr*', cli_name='addattr') option: Flag('all', autofill=True, cli_name='all', default=False) @@ -5066,6 +5070,10 @@ option: Int('gidnumber?', autofill=False) option: Str('givenname?', autofill=False, cli_name='first') option: Str('homedirectory?', autofill=False, cli_name='homedir') option: Str('initials?', autofill=False) +option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') +option: StrEnum('ipanthomedirectoryrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) +option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') +option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey') option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') @@ -6072,7 +6080,7 @@ output: Output('result', type=[]) output: Output('summary', type=[, ]) output: PrimaryKey('value') command: user_find/1 -args: 1,57,4 +args: 1,61,4 arg: Str('criteria?') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('carlicense*', autofill=False) @@ -6092,6 +6100,10 @@ option: Str('in_netgroup*', cli_name='in_netgroups') option: Str('in_role*', cli_name='in_roles') option: Str('in_sudorule*', cli_name='in_sudorules') option: Str('initials?', autofill=False) +option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') +option: StrEnum('ipanthomedirectoryrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) +option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') +option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp']) @@ -6136,7 +6148,7 @@ output: ListOfEntries('result') output: Output('summary', type=[, ]) output: Output('truncated', type=[]) command: user_mod/1 -args: 1,48,3 +args: 1,52,3 arg: Str('uid', cli_name='login') option: Str('addattr*', cli_name='addattr') option: Flag('all', autofill=True, cli_name='all', default=False) @@ -6153,6 +6165,10 @@ option: Int('gidnumber?', autofill=False) option: Str('givenname?', autofill=False, cli_name='first') option: Str('homedirectory?', autofill=False, cli_name='homedir') option: Str('initials?', autofill=False) +option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') +option: StrEnum('ipanthomedirectoryrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) +option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') +option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey') option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') diff --git a/doc/designs/adtrust/samba-domain-controller.md b/doc/designs/adtrust/samba-domain-controller.md index 5ca095b..a003189 100644 --- a/doc/designs/adtrust/samba-domain-controller.md +++ b/doc/designs/adtrust/samba-domain-controller.md @@ -129,7 +129,85 @@ The SMB service object needs to have: - NT attributes, including `ipaNTSecurityIdentifier` `ipaNTSecurityIdentifier` is filled in by the SID generation plugin at the -object creation time. +object creation time for SMB service. + +`ipaNTSecurityIdentifier` attribute is a part of `ipaNTUserAttrs` object class +for users and SMB services. IPA groups also can contain the attribute via +`ipaNTGroupAttrs` object class. + +With the help of the `sidgen` plugin, ipaNTSecurityIdentifier attribute is only +added when: + - the object has POSIX attributes `uidNumber` and `gidNumber` + - the values of those attributes are within 32-bit unsigned integer + - the object has any of the following object classes: `ipaIDObject`, + `posixAccount`, or `posixGroup` + - the object has no `ipaNTSecurityIdentifier` attribute already. + +`sidgen` plugin will add `ipaNTUserAttrs` object class for non-group objects and +`ipaNTGroupAttr` for the group object type. A plugin is triggered at an object +creation or via an LDAP task. One can trigger task run by running +`ipa-adtrust-install --add-sids` on the trust controller. + +LDAP object class `ipaNTUserAttrs` defines few other attributes. These +attributes, called below 'SMB attributes', are required by the domain controller +to define content of an NT token for an authenticated identity (user or a +machine account). + +SMB attributes are: + - `ipaNTLogonScript` + : Path to a script executed on a Windows system at logon + - `ipaNTProfilePath` + : Path to a user profile, in UNC format `\\server\share\` + - `ipaNTHomeDirectory` + : Path to a user's home directory, in UNC format `\\server\share` + - `ipaNTHomeDirectoryDrive` + : a letter `[A-Z]` for the drive to mount the home directory to on a Windows system + +All SMB attributes require the presence of `ipaNTUserAttrs` object class in the +user object LDAP entry. This object class cannot be added without +`ipaNTSecurityIdentifier`. Adding SID requires to consume IDs from a range +suitable for SIDs and this logic is recorded in the `sidgen` plugin. Thus, until +SID is generated, no attributes can be set on the user entry. + +As result of it, SMB attributes are not available at `ipa user-add` or +`ipa stageuser-add` level. Instead, it is possible to modify a user object with +`ipa user-mod` or `ipa stageuser-mod` commands: + +``` +$ ipa user-mod --help +Usage: ipa [global-options] user-mod LOGIN [options] + +Modify a user. +Options: +... + --smb-logon-script=STR SMB logon script path + --smb-profile-path=STR SMB profile path + --smb-home-dir=STR SMB Home Directory + --smb-home-drive=['A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', + 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:', 'U:', 'V:', + 'W:', 'X:', 'Y:', 'Z:'] + SMB Home Directory Drive +... + +$ ipa stageuser-mod --help +Usage: ipa [global-options] stageuser-mod LOGIN [options] + +Modify a stage user. +Options: +... + --smb-logon-script=STR SMB logon script path + --smb-profile-path=STR SMB profile path + --smb-home-dir=STR SMB Home Directory + --smb-home-drive=['A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', + 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:', 'U:', 'V:', + 'W:', 'X:', 'Y:', 'Z:'] + SMB Home Directory Drive +... +``` + +Due to limitations on how SMB attributes can be added, Web UI shows the section +"User attributes for SMB services" without any values for those users who have +no SID assigned. ### Changes to LDAP storage @@ -139,7 +217,7 @@ Since SMB service belongs to `cn=services,cn=accounts,$basedn` subtree, new ACI has to be added. ``` -'System: Read POSIX details of the services': { +'System: Read POSIX details of the SMB services': { 'replaces_global_anonymous_aci': True, 'ipapermbindruletype': 'all', 'ipapermright': {'read', 'search', 'compare'}, @@ -151,6 +229,11 @@ has to be added. } ``` +SMB attributes for users are now accessible for self-modification and also +readable by the members of `cn=adtrust agents,cn=sysaccounts,cn=etc,$basedn` +group which contains, among others, service principals of the domain +controllers. + ### Changes to LDAP plugins As mentioned above, both domain controller and domain member need to know common diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js index cd042b4..0211672 100644 --- a/install/ui/src/freeipa/user.js +++ b/install/ui/src/freeipa/user.js @@ -361,6 +361,42 @@ return { fields: [ { $type: 'multivalued', name: 'carlicense' } ] + }, + { + name: 'smb_attributes', + label: '@i18n:objects.smb_attributes.title', + show_cond: ['oc_ipantuserattrs'], + fields: [{ + name: 'ipantlogonscript', + tooltip: { + title: '@i18n:objects.smb_attributes.ipantlogonscript_tooltip' + } + }, + { + name: 'ipantprofilepath', + tooltip: { + title: '@i18n:objects.smb_attributes.ipantprofilepath_tooltip' + } + }, + { + name: 'ipanthomedirectory', + tooltip: { + title: '@i18n:objects.smb_attributes.ipanthomedirectory_tooltip' + } + }, + { + name: 'ipanthomedirectorydrive', + $type: 'select', + options: IPA.create_options([ + 'A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', + 'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', + 'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:' + ]), + tooltip: { + title: '@i18n:objects.smb_attributes.ipanthomedirectorydrive_tooltip' + } + } + ] } ], actions: [ @@ -444,6 +480,7 @@ return { IPA.user.self_service_other_user_evaluator, IPA.user.preserved_user_evaluator, IPA.user.no_password_evaluator, + IPA.object_class_evaluator, IPA.cert.certificate_evaluator ], summary_conditions: [ @@ -576,6 +613,7 @@ IPA.user.details_facet = function(spec, no_init) { }); var user_command = that.details_facet_create_refresh_command(); + batch.add_command(user_command); var pwpolicy_command = rpc.command({ diff --git a/install/updates/75-user-trust-attributes.update b/install/updates/75-user-trust-attributes.update new file mode 100644 index 0000000..43bb40c --- /dev/null +++ b/install/updates/75-user-trust-attributes.update @@ -0,0 +1,5 @@ +# Add an explicit self-service ACI to allow writing to manage trust attributes +# for the owner of the object +dn: cn=users,cn=accounts,$SUFFIX +add:aci:(targetattr = "ipantlogonscript || ipantprofilepath || ipanthomedirectory || ipanthomedirectorydrive")(version 3.0;acl "system:Allow trust agents to read user SMB attributes";allow (read) groupdn = "ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";) +add:aci:(targetattr = "ipantlogonscript || ipantprofilepath || ipanthomedirectory || ipanthomedirectorydrive")(version 3.0;acl "selfservice:Users can manage their SMB attributes";allow (write) userdn = "ldap:///self";) diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py index 3a591f2..1103e55 100644 --- a/ipaserver/plugins/baseuser.py +++ b/ipaserver/plugins/baseuser.py @@ -50,6 +50,7 @@ from ipalib.util import ( ensure_krbcanonicalname_set ) + if six.PY3: unicode = str @@ -123,6 +124,29 @@ def fix_addressbook_permission_bindrule(name, template, is_new, template['ipapermbindruletype'] = 'anonymous' +def update_samba_attrs(ldap, dn, entry_attrs, **options): + smb_attrs = {'ipantlogonscript', 'ipantprofilepath', + 'ipanthomedirectory', 'ipanthomedirectorydrive'} + if 'objectclass' not in entry_attrs: + try: + oc = ldap.get_entry(dn, ['objectclass'])['objectclass'] + except errors.NotFound: + # In case the entry really does not exist, + # compare against an empty list + oc = [] + else: + oc = entry_attrs['objectclass'] + if 'ipantuserattrs' not in (item.lower() for item in oc): + for attr in smb_attrs: + if options.get(attr, None): + raise errors.ValidationError( + name=attr, + error=_( + 'Object class ipaNTUserAttrs is missing, ' + 'user entry cannot have SMB attributes.' + ) + ) + class baseuser(LDAPObject): """ @@ -136,7 +160,8 @@ class baseuser(LDAPObject): object_class_config = 'ipauserobjectclasses' possible_objectclasses = [ 'meporiginentry', 'ipauserauthtypeclass', 'ipauser', - 'ipatokenradiusproxyuser', 'ipacertmapobject' + 'ipatokenradiusproxyuser', 'ipacertmapobject', + 'ipantuserattrs' ] disallow_object_classes = ['krbticketpolicyaux'] permission_filter_objectclasses = ['posixaccount'] @@ -149,7 +174,8 @@ class baseuser(LDAPObject): 'ipatokenradiusconfiglink', 'ipatokenradiususername', 'krbprincipalexpiration', 'usercertificate;binary', 'krbprincipalname', 'krbcanonicalname', - 'ipacertmapdata' + 'ipacertmapdata', 'ipantlogonscript', 'ipantprofilepath', + 'ipanthomedirectory', 'ipanthomedirectorydrive' ] search_display_attributes = [ 'uid', 'givenname', 'sn', 'homedirectory', 'krbcanonicalname', @@ -378,6 +404,30 @@ class baseuser(LDAPObject): doc=_('Certificate mapping data'), flags=['no_create', 'no_update', 'no_search'], ), + Str('ipantlogonscript?', + cli_name='smb_logon_script', + label=_('SMB logon script path'), + flags=['no_create'], + ), + Str('ipantprofilepath?', + cli_name='smb_profile_path', + label=_('SMB profile path'), + flags=['no_create'], + ), + Str('ipanthomedirectory?', + cli_name='smb_home_dir', + label=_('SMB Home Directory'), + flags=['no_create'], + ), + StrEnum('ipanthomedirectoryrive?', + cli_name='smb_home_drive', + label=_('SMB Home Directory Drive'), + flags=['no_create'], + values=( + 'A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', + 'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', + 'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:'), + ), ) def normalize_and_validate_email(self, email, config=None): @@ -496,6 +546,7 @@ class baseuser_add(LDAPCreate): convert_sshpubkey_post(entry_attrs) radius_dn2pk(self.api, entry_attrs) + class baseuser_del(LDAPDelete): """ Prototype command plugin to be implemented by real plugin @@ -632,6 +683,7 @@ class baseuser_mod(LDAPUpdate): self.check_objectclass(ldap, dn, entry_attrs) self.obj.convert_usercertificate_pre(entry_attrs) self.preserve_krbprincipalname_pre(ldap, entry_attrs, *keys, **options) + update_samba_attrs(ldap, dn, entry_attrs, **options) def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py index 4b06027..0f0ad3a 100644 --- a/ipaserver/plugins/internal.py +++ b/ipaserver/plugins/internal.py @@ -1483,6 +1483,23 @@ class i18n_messages(Command): "trusttype": _("Trust type"), "ipantadditionalsuffixes": _("Alternative UPN suffixes"), }, + 'smb_attributes': { + "title": _( + "User attributes for SMB services" + ), + "ipantlogonscript_tooltip": _( + "Path to a script executed on a Windows system at logon" + ), + "ipantprofilepath_tooltip": _( + "Path to a user profile, in UNC format \\\\server\\share\\" + ), + "ipanthomedirectory_tooltip": _( + "Path to a user home directory, in UNC format" + ), + "ipanthomedirectorydrive_tooltip": _( + "Drive to mount a home directory" + ), + }, "trustconfig": { "options": _("Options"), },