On 29.3.2013 10:55, Martin Kosek wrote:
This looks much better, thanks for catching more errors and the unit test. I
have few more:

1) API minor number in VERSION file needs a bump

Whoops, fixed.


2) I did some functional testing and found strange behavior with services.
Adding our custom krbticketflags disables some flags ipa-kdb adds by default,
like REQUIRES_PRE_AUTH.

Example:

# ipa host-add foo.example.com --force
----------------------------
Added host "foo.example.com"
----------------------------
   Host name: foo.example.com
   Principal name: host/foo.example....@idm.lab.bos.redhat.com
   Password: False
   Keytab: False
   Managed by: foo.example.com

# ipa-getkeytab -s `hostname` -p host/foo.example.com -k foo.keytab
Keytab successfully retrieved and stored in: foo.keytab

# kinit -kt foo.keytab host/foo.example.com

# kadmin.local -q "getprinc host/foo.example....@idm.lab.bos.redhat.com"
...
Attributes: REQUIRES_PRE_AUTH
Policy: [none]


krb5kdc.log correctly shows that preauth is needed:

Mar 29 05:21:00 vm-037.idm.lab.bos.redhat.com krb5kdc[3977](info): AS_REQ (4
etypes {18 17 16 23}) 10.16.78.37: NEEDED_PREAUTH:
host/foo.example....@idm.lab.bos.redhat.com for
krbtgt/idm.lab.bos.redhat....@idm.lab.bos.redhat.com, Additional
pre-authentication required
Mar 29 05:21:00 vm-037.idm.lab.bos.redhat.com krb5kdc[3977](info): AS_REQ (4
etypes {18 17 16 23}) 10.16.78.37: ISSUE: authtime 1364548860, etypes {rep=18
tkt=18 ses=18}, host/foo.example....@idm.lab.bos.redhat.com for
krbtgt/idm.lab.bos.redhat....@idm.lab.bos.redhat.com


However, when I add OK_AS_DELEGATE, REQUIRES_PRE_AUTH vanishes:
# ipa host-mod foo.example.com --ok-as-delegate=1
-------------------------------
Modified host "foo.example.com"
-------------------------------
   Host name: foo.example.com
   Principal name: host/foo.example....@idm.lab.bos.redhat.com
   Trusted for delegation: True
   Password: False
   Keytab: True
   Managed by: foo.example.com

# ipa service-mod HTTP/foo.example....@idm.lab.bos.redhat.com --ok-as-delegate=1
--------------------------------------------------------------
Modified service "HTTP/foo.example....@idm.lab.bos.redhat.com"
--------------------------------------------------------------
   Principal: HTTP/foo.example....@idm.lab.bos.redhat.com
   Trusted for delegation: True
   Managed by: foo.example.com

# kadmin.local -q "getprinc host/foo.example....@idm.lab.bos.redhat.com"
...
Attributes: OK_AS_DELEGATE
Policy: [none]


Is this intentional?

Shouldn't "ipa host-add $HOST" or "ipa service-add $SERVICE" always set
"krbticketflags" with this flag (0x00000080) on instead of adding it silently
in ipa-kdb? (adding Simo to CC to help us with that).

If no, shouldn't we at least add means to set this flag in host-mod or
service-mod so that admins can set it? I.e. option like --requires-pre-auth=1

I assumed the default value is 0. I changed it to 0x00000080.

Updated patch attached.

Honza

--
Jan Cholasta
From 9879dcf9487387111daf8b832d67e213a4c87ff0 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Mon, 18 Mar 2013 12:31:23 +0100
Subject: [PATCH] Add Kerberos ticket flags management to service and host
 plugins.

https://fedorahosted.org/freeipa/ticket/3329
---
 API.txt                                  | 12 +++--
 VERSION                                  |  2 +-
 install/share/default-aci.ldif           |  2 +-
 install/updates/60-trusts.update         |  4 +-
 ipalib/plugins/host.py                   | 25 ++++++++--
 ipalib/plugins/service.py                | 80 +++++++++++++++++++++++++++++--
 tests/test_xmlrpc/test_service_plugin.py | 82 +++++++++++++++++++++++++++++++-
 7 files changed, 192 insertions(+), 15 deletions(-)

diff --git a/API.txt b/API.txt
index 734f99e..a370e88 100644
--- a/API.txt
+++ b/API.txt
@@ -1716,13 +1716,14 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('value', <type 'bool'>, None)
 output: Output('warning', (<type 'list'>, <type 'tuple'>, <type 'NoneType'>), None)
 command: host_add
-args: 1,18,3
+args: 1,19,3
 arg: Str('fqdn', attribute=True, cli_name='hostname', multivalue=False, primary_key=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
 option: Flag('force', autofill=True, default=False)
 option: Str('ip_address?')
+option: Bool('ipakrbokasdelegate', attribute=False, cli_name='ok_as_delegate', multivalue=False, required=False)
 option: Str('ipasshpubkey', attribute=True, cli_name='sshpubkey', csv=True, multivalue=True, required=False)
 option: Str('l', attribute=True, cli_name='locality', 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}$', required=False)
@@ -1803,12 +1804,13 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: host_mod
-args: 1,19,3
+args: 1,20,3
 arg: Str('fqdn', attribute=True, cli_name='hostname', multivalue=False, primary_key=True, query=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('delattr*', cli_name='delattr', exclude='webui')
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
+option: Bool('ipakrbokasdelegate', attribute=False, autofill=False, cli_name='ok_as_delegate', multivalue=False, required=False)
 option: Str('ipasshpubkey', attribute=True, autofill=False, cli_name='sshpubkey', csv=True, multivalue=True, required=False)
 option: Str('krbprincipalname?', attribute=True, cli_name='principalname')
 option: Str('l', attribute=True, autofill=False, cli_name='locality', multivalue=False, required=False)
@@ -2840,12 +2842,13 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('value', <type 'unicode'>, None)
 command: service_add
-args: 1,8,3
+args: 1,9,3
 arg: Str('krbprincipalname', attribute=True, cli_name='principal', multivalue=False, primary_key=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Flag('force', autofill=True, default=False)
 option: StrEnum('ipakrbauthzdata', attribute=True, cli_name='pac_type', csv=True, multivalue=True, required=False, values=(u'MS-PAC', u'PAD', u'NONE'))
+option: Bool('ipakrbokasdelegate', attribute=False, cli_name='ok_as_delegate', multivalue=False, required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('setattr*', cli_name='setattr', exclude='webui')
 option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=False, required=False)
@@ -2896,12 +2899,13 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: service_mod
-args: 1,9,3
+args: 1,10,3
 arg: Str('krbprincipalname', attribute=True, cli_name='principal', multivalue=False, primary_key=True, query=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('delattr*', cli_name='delattr', exclude='webui')
 option: StrEnum('ipakrbauthzdata', attribute=True, autofill=False, cli_name='pac_type', csv=True, multivalue=True, required=False, values=(u'MS-PAC', u'PAD', u'NONE'))
+option: Bool('ipakrbokasdelegate', attribute=False, autofill=False, cli_name='ok_as_delegate', multivalue=False, required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
 option: Str('setattr*', cli_name='setattr', exclude='webui')
diff --git a/VERSION b/VERSION
index 4108bc8..5c4c932 100644
--- a/VERSION
+++ b/VERSION
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=54
+IPA_API_VERSION_MINOR=55
diff --git a/install/share/default-aci.ldif b/install/share/default-aci.ldif
index 3e6c100..f173f79 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -5,7 +5,7 @@ changetype: modify
 add: aci
 aci: (target != "ldap:///idnsname=*,cn=dns,$SUFFIX";)(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || userPKCS12 || ipaNTHash || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming")(version 3.0; acl "Enable Anonymous access"; allow (read, search, compare) userdn = "ldap:///anyone";;)
 aci: (targetattr = "memberOf || memberHost || memberUser")(version 3.0; acl "No anonymous access to member information"; deny (read,search,compare) userdn != "ldap:///all";;)
-aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbUPEnabled || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || krbTicketFlags || ipaUniqueId || memberOf || serverHostName || enrolledBy || ipaNTHash")(version 3.0; acl "Admin can manage any entry"; allow (all) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)
+aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbUPEnabled || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || ipaUniqueId || memberOf || serverHostName || enrolledBy || ipaNTHash")(version 3.0; acl "Admin can manage any entry"; allow (all) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)
 aci: (targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword")(version 3.0; acl "selfservice:Self can write own password"; allow (write) userdn="ldap:///self";;)
 aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || ipaNTHash")(version 3.0; acl "Admins can write passwords"; allow (add,delete,write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)
 aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || ipaNTHash")(version 3.0; acl "Password change service can read/write passwords"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";;)
diff --git a/install/updates/60-trusts.update b/install/updates/60-trusts.update
index 454ecb7..f63651f 100644
--- a/install/updates/60-trusts.update
+++ b/install/updates/60-trusts.update
@@ -63,7 +63,9 @@ dn: $SUFFIX
 add:aci: '(targetattr = "ipaNTHash")(version 3.0; acl "Samba system principals can read and write NT passwords"; allow (read,write) groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)'
 remove:aci: '(targetattr = "ipaNTHash")(version 3.0; acl "Samba system principals can read NT passwords"; allow (read) groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)'
 replace:aci:'(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || userPKCS12")(version 3.0; acl "Enable Anonymous access"; allow (read, search, compare) userdn = "ldap:///anyone";;)::(target != "ldap:///idnsname=*,cn=dns,$SUFFIX";)(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || userPKCS12 || ipaNTHash")(version 3.0; acl "Enable Anonymous access"; allow (read, search, compare) userdn = "ldap:///anyone";;)'
-replace:aci:'(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbUPEnabled || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || krbTicketFlags || ipaUniqueId || memberOf || serverHostName || enrolledBy")(version 3.0; acl "Admin can manage any entry"; allow (all) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)::(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbUPEnabled || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || krbTicketFlags || ipaUniqueId || memberOf || serverHostName || enrolledBy || ipaNTHash")(version 3.0; acl "Admin can manage any entry"; allow (all) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)'
+remove:aci:'(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbUPEnabled || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || krbTicketFlags || ipaUniqueId || memberOf || serverHostName || enrolledBy")(version 3.0; acl "Admin can manage any entry"; allow (all) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)'
+remove:aci:'(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbUPEnabled || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || krbTicketFlags || ipaUniqueId || memberOf || serverHostName || enrolledBy || ipaNTHash")(version 3.0; acl "Admin can manage any entry"; allow (all) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)'
+add:aci:'(targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbUPEnabled || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || ipaUniqueId || memberOf || serverHostName || enrolledBy || ipaNTHash")(version 3.0; acl "Admin can manage any entry"; allow (all) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)'
 replace:aci:'(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Admins can write passwords"; allow (add,delete,write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)::(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || ipaNTHash")(version 3.0; acl "Admins can write passwords"; allow (add,delete,write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)'
 replace:aci:'(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Password change service can read/write passwords"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";;)::(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || ipaNTHash")(version 3.0; acl "Password change service can read/write passwords"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";;)'
 
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index d92bc56..131c36e 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -29,9 +29,9 @@ import string
 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.service import (split_principal, validate_certificate,
+    set_certificate_attrs, ticket_flags_params, update_krbticketflags,
+    set_kerberos_attrs)
 from ipalib.plugins.dns import (dns_container_exists, _record_types,
         add_records_for_host_validation, add_records_for_host,
         _hostname_validator, get_reverse_zone)
@@ -323,7 +323,7 @@ class host(LDAPObject):
             csv=True,
             flags=['no_search'],
         ),
-    )
+    ) + ticket_flags_params
 
     def get_dn(self, *keys, **options):
         hostname = keys[-1]
@@ -439,6 +439,9 @@ class host_add(LDAPCreate):
         entry_attrs['managedby'] = dn
         entry_attrs['objectclass'].append('ieee802device')
         entry_attrs['objectclass'].append('ipasshhost')
+        update_krbticketflags(ldap, entry_attrs, attrs_list, options, False)
+        if 'krbticketflags' in entry_attrs:
+            entry_attrs['objectclass'].append('krbticketpolicyaux')
         return dn
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
@@ -473,6 +476,7 @@ class host_add(LDAPCreate):
                 reason=_('The host was added but the DNS update failed with: %(exc)s') % dict(exc=exc)
             )
         set_certificate_attrs(entry_attrs)
+        set_kerberos_attrs(entry_attrs, options)
 
         if options.get('all', False):
             entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
@@ -677,6 +681,7 @@ class host_mod(LDAPUpdate):
         if options.get('random'):
             entry_attrs['userpassword'] = ipa_generate_password(characters=host_pwd_chars)
             setattr(context, 'randompassword', entry_attrs['userpassword'])
+
         if 'macaddress' in entry_attrs:
             if 'objectclass' in entry_attrs:
                 obj_classes = entry_attrs['objectclass']
@@ -708,6 +713,15 @@ class host_mod(LDAPUpdate):
             if 'ipasshhost' not in obj_classes:
                 obj_classes.append('ipasshhost')
 
+        update_krbticketflags(ldap, entry_attrs, attrs_list, options, True)
+
+        if 'krbticketflags' in entry_attrs:
+            if 'objectclass' not in entry_attrs:
+                entry_attrs_old = ldap.get_entry(dn, ['objectclass'])
+                entry_attrs['objectclass'] = entry_attrs_old['objectclass']
+            if 'krbticketpolicyaux' not in entry_attrs['objectclass']:
+                entry_attrs['objectclass'].append('krbticketpolicyaux')
+
         return dn
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
@@ -715,6 +729,7 @@ class host_mod(LDAPUpdate):
         if options.get('random', False):
             entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
         set_certificate_attrs(entry_attrs)
+        set_kerberos_attrs(entry_attrs, options)
         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
@@ -801,6 +816,7 @@ class host_find(LDAPSearch):
         for entry in entries:
             (dn, entry_attrs) = entry
             set_certificate_attrs(entry_attrs)
+            set_kerberos_attrs(entry_attrs, options)
             self.obj.get_password_attributes(ldap, dn, entry_attrs)
             self.obj.suppress_netgroup_memberof(entry_attrs)
             if entry_attrs['has_password']:
@@ -839,6 +855,7 @@ class host_show(LDAPRetrieve):
             entry_attrs['has_keytab'] = False
 
         set_certificate_attrs(entry_attrs)
+        set_kerberos_attrs(entry_attrs, options)
 
         if options.get('all', False):
             entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 6b66344..2d2b171 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -23,7 +23,7 @@ import base64
 import os
 
 from ipalib import api, errors, util
-from ipalib import Str, Flag, Bytes, StrEnum
+from ipalib import Str, Flag, Bytes, StrEnum, Bool
 from ipalib.plugins.baseldap import *
 from ipalib import x509
 from ipalib import _, ngettext
@@ -127,6 +127,19 @@ output_params = (
     )
 )
 
+ticket_flags_params = (
+    Bool('ipakrbokasdelegate?',
+        cli_name='ok_as_delegate',
+        label=_('Trusted for delegation'),
+        doc=_('Client credentials may be delegated to the service'),
+        flags=['virtual_attribute', 'no_search'],
+    ),
+)
+
+_ticket_flags_map = {
+    'ipakrbokasdelegate': 0x00100000,
+}
+
 def split_principal(principal):
     service = hostname = realm = None
 
@@ -217,6 +230,54 @@ def check_required_principal(ldap, hostname, service):
         if service in service_types:
             raise errors.ValidationError(name='principal', error=_('This principal is required by the IPA master'))
 
+def update_krbticketflags(ldap, entry_attrs, attrs_list, options, existing):
+    add = remove = 0
+
+    for (name, value) in _ticket_flags_map.iteritems():
+        if name not in options:
+            continue
+        if options[name]:
+            add |= value
+        else:
+            remove |= value
+
+    if not add and not remove:
+        return
+
+    if 'krbticketflags' not in entry_attrs and existing:
+        old_entry_attrs = ldap.get_entry(entry_attrs.dn, ['krbticketflags'])
+    else:
+        old_entry_attrs = entry_attrs
+
+    try:
+        ticket_flags = old_entry_attrs.single_value('krbticketflags')
+        ticket_flags = int(ticket_flags)
+    except (KeyError, ValueError):
+        # include REQUIRES_PRE_AUTH by default
+        ticket_flags = 0x00000080
+
+    ticket_flags |= add
+    ticket_flags &= ~remove
+
+    entry_attrs['krbticketflags'] = [ticket_flags]
+    attrs_list.append('krbticketflags')
+
+def set_kerberos_attrs(entry_attrs, options):
+    if options.get('raw', False):
+        return
+
+    try:
+        ticket_flags = entry_attrs.single_value('krbticketflags', 0)
+        ticket_flags = int(ticket_flags)
+    except ValueError:
+        return
+
+    all_opt = options.get('all', False)
+
+    for (name, value) in _ticket_flags_map.iteritems():
+        if name in options or all_opt:
+            entry_attrs[name] = bool(ticket_flags & value)
+
 class service(LDAPObject):
     """
     Service object.
@@ -268,7 +329,7 @@ class service(LDAPObject):
             values=(u'MS-PAC', u'PAD', u'NONE'),
             csv=True,
         ),
-    )
+    ) + ticket_flags_params
 
     def validate_ipakrbauthzdata(self, entry):
         new_value = entry.get('ipakrbauthzdata', [])
@@ -300,6 +361,7 @@ class service_add(LDAPCreate):
             doc=_('force principal name even if not in DNS'),
         ),
     )
+
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         assert isinstance(dn, DN)
         (service, hostname, realm) = split_principal(keys[-1])
@@ -338,6 +400,12 @@ class service_add(LDAPCreate):
         # in a list of default objectclasses, add it manually
         entry_attrs['objectclass'].append('ipakrbprincipal')
 
+        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)
         return dn
 
 api.register(service_add)
@@ -397,7 +465,7 @@ class service_mod(LDAPUpdate):
 
     member_attributes = ['managedby']
 
-    def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         assert isinstance(dn, DN)
 
         self.obj.validate_ipakrbauthzdata(entry_attrs)
@@ -422,11 +490,15 @@ class service_mod(LDAPUpdate):
                 entry_attrs['usercertificate'] = dercert
             else:
                 entry_attrs['usercertificate'] = None
+
+        update_krbticketflags(ldap, entry_attrs, attrs_list, options, True)
+
         return dn
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
         assert isinstance(dn, DN)
         set_certificate_attrs(entry_attrs)
+        set_kerberos_attrs(entry_attrs, options)
         return dn
 
 api.register(service_mod)
@@ -464,6 +536,7 @@ class service_find(LDAPSearch):
             (dn, entry_attrs) = entry
             self.obj.get_password_attributes(ldap, dn, entry_attrs)
             set_certificate_attrs(entry_attrs)
+            set_kerberos_attrs(entry_attrs, options)
         return truncated
 
 api.register(service_find)
@@ -485,6 +558,7 @@ class service_show(LDAPRetrieve):
         self.obj.get_password_attributes(ldap, dn, entry_attrs)
 
         set_certificate_attrs(entry_attrs)
+        set_kerberos_attrs(entry_attrs, options)
 
         return dn
 
diff --git a/tests/test_xmlrpc/test_service_plugin.py b/tests/test_xmlrpc/test_service_plugin.py
index 29c94e3..cddc55e 100644
--- a/tests/test_xmlrpc/test_service_plugin.py
+++ b/tests/test_xmlrpc/test_service_plugin.py
@@ -228,7 +228,8 @@ class test_service(Declarative):
                     objectclass=objectclasses.service,
                     ipauniqueid=[fuzzy_uuid],
                     managedby_host=[fqdn1],
-                    has_keytab=False
+                    has_keytab=False,
+                    ipakrbokasdelegate=False,
                 ),
             ),
         ),
@@ -269,6 +270,7 @@ class test_service(Declarative):
                         ipauniqueid=[fuzzy_uuid],
                         has_keytab=False,
                         managedby_host=[fqdn1],
+                        ipakrbokasdelegate=False,
                     ),
                 ],
             ),
@@ -462,6 +464,84 @@ class test_service(Declarative):
 
 
         dict(
+            desc='Enable %r OK_AS_DELEGATE Kerberos ticket flag' % service1,
+            command=('service_mod', [service1], dict(ipakrbokasdelegate=True)),
+            expected=dict(
+                value=service1,
+                summary=u'Modified service "%s"' % service1,
+                result=dict(
+                    usercertificate=[base64.b64decode(servercert)],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                    ipakrbauthzdata=[u'MS-PAC'],
+                    valid_not_before=fuzzy_date,
+                    valid_not_after=fuzzy_date,
+                    subject=DN(('CN',api.env.host),x509.subject_base()),
+                    serial_number=fuzzy_digits,
+                    serial_number_hex=fuzzy_hex,
+                    md5_fingerprint=fuzzy_hash,
+                    sha1_fingerprint=fuzzy_hash,
+                    issuer=fuzzy_issuer,
+                    krbticketflags=[u'1048576'],
+                    ipakrbokasdelegate=True,
+                ),
+            ),
+        ),
+
+
+        dict(
+            desc='Update %r Kerberos ticket flags with setattr' % service1,
+            command=('service_mod', [service1],
+                     dict(setattr=[u'krbTicketFlags=1048577'])),
+            expected=dict(
+                value=service1,
+                summary=u'Modified service "%s"' % service1,
+                result=dict(
+                    usercertificate=[base64.b64decode(servercert)],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                    ipakrbauthzdata=[u'MS-PAC'],
+                    valid_not_before=fuzzy_date,
+                    valid_not_after=fuzzy_date,
+                    subject=DN(('CN',api.env.host),x509.subject_base()),
+                    serial_number=fuzzy_digits,
+                    serial_number_hex=fuzzy_hex,
+                    md5_fingerprint=fuzzy_hash,
+                    sha1_fingerprint=fuzzy_hash,
+                    issuer=fuzzy_issuer,
+                    krbticketflags=[u'1048577'],
+                ),
+            ),
+        ),
+
+
+        dict(
+            desc='Disable %r OK_AS_DELEGATE Kerberos ticket flag' % service1,
+            command=('service_mod', [service1], dict(ipakrbokasdelegate=False)),
+            expected=dict(
+                value=service1,
+                summary=u'Modified service "%s"' % service1,
+                result=dict(
+                    usercertificate=[base64.b64decode(servercert)],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                    ipakrbauthzdata=[u'MS-PAC'],
+                    valid_not_before=fuzzy_date,
+                    valid_not_after=fuzzy_date,
+                    subject=DN(('CN',api.env.host),x509.subject_base()),
+                    serial_number=fuzzy_digits,
+                    serial_number_hex=fuzzy_hex,
+                    md5_fingerprint=fuzzy_hash,
+                    sha1_fingerprint=fuzzy_hash,
+                    issuer=fuzzy_issuer,
+                    krbticketflags=[u'1'],
+                    ipakrbokasdelegate=False,
+                ),
+            ),
+        ),
+
+
+        dict(
             desc='Delete %r' % service1,
             command=('service_del', [service1], {}),
             expected=dict(
-- 
1.8.1.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to