aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Yu <tlyu@mit.edu>2015-07-01 16:28:45 -0400
committerTom Yu <tlyu@mit.edu>2015-07-09 08:37:59 -0400
commitea62dd834f343b2dddea81c8295f2f8876c83090 (patch)
treea82a5f4a2e0e197b14313aa073a653b1dcd53de5 /src
parentbe20a5f5cee8d6c4072d1b81712520dbf9f6eefd (diff)
downloadkrb5-ea62dd834f343b2dddea81c8295f2f8876c83090.zip
krb5-ea62dd834f343b2dddea81c8295f2f8876c83090.tar.gz
krb5-ea62dd834f343b2dddea81c8295f2f8876c83090.tar.bz2
Add test suite for KDB principal flags
Test kadmin.local reading of principal flag specifiers, kdc.conf setting of default_principal_flags, and kadm5.acl restrictions. Only really tests one flag at a time. Also start requiring Python 2.5 for the test suite. It's been around for long enough, and some syntax features such as conditional expressions are useful. ticket: 8215 (new) subject: Unify KDB principal flag specifiers target_version: 1.14
Diffstat (limited to 'src')
-rw-r--r--src/configure.in3
-rw-r--r--src/tests/Makefile.in1
-rwxr-xr-xsrc/tests/t_princflags.py126
-rw-r--r--src/util/k5test.py2
-rw-r--r--src/util/princflags.py264
5 files changed, 394 insertions, 2 deletions
diff --git a/src/configure.in b/src/configure.in
index d3938fc..b2b1d70 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1110,7 +1110,8 @@ AC_CHECK_PROG(PYTHON,python,python)
HAVE_PYTHON=no
if test x"$PYTHON" != x; then
# k5test.py requires python 2.4 (for the subprocess module).
- vercheck="import sys;sys.exit((sys.hexversion < 0x2040000) and 1 or 0)"
+ # Some code needs python 2.5 (for syntax like conditional expressions).
+ vercheck="import sys;sys.exit((sys.hexversion < 0x2050000) and 1 or 0)"
if python -c "$vercheck"; then
HAVE_PYTHON=yes
fi
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 602d74a..cd6fc4a 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -150,6 +150,7 @@ check-pytests:: responder s2p t_init_creds t_localauth unlockiter
$(RUNPYTEST) $(srcdir)/t_unlockiter.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_errmsg.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_authdata.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_princflags.py $(PYTESTFLAGS)
clean::
$(RM) gcred hist hrealm kdbtest plugorder rdreq responder s2p
diff --git a/src/tests/t_princflags.py b/src/tests/t_princflags.py
new file mode 100755
index 0000000..03817bf
--- /dev/null
+++ b/src/tests/t_princflags.py
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+from k5test import *
+from princflags import *
+import re
+
+realm = K5Realm(create_host=False, get_creds=False)
+
+# Regex pattern to match an empty attribute line from kadmin getprinc
+emptyattr = re.compile('^Attributes:$', re.MULTILINE)
+
+
+# Regex pattern to match a kadmin getprinc output for a flag tuple
+def attr_pat(ftuple):
+ return re.compile('^Attributes: ' + ftuple.flagname() + '$',
+ re.MULTILINE)
+
+
+# Test one flag tuple for kadmin ank.
+def one_kadmin_flag(ftuple):
+ pat = attr_pat(ftuple)
+ realm.run([kadminl, 'ank', ftuple.setspec(),
+ '-pw', 'password', 'test'])
+ out = realm.run([kadminl, 'getprinc', 'test'])
+ if not pat.search(out):
+ fail('Failed to set flag ' + ftuple.flagname())
+
+ realm.run([kadminl, 'modprinc', ftuple.clearspec(), 'test'])
+ out = realm.run([kadminl, 'getprinc', 'test'])
+ if not emptyattr.search(out):
+ fail('Failed to clear flag ' + ftuple.flagname())
+ realm.run([kadminl, 'delprinc', 'test'])
+
+
+# Generate a custom kdc.conf with default_principal_flags set
+# according to ftuple.
+def genkdcconf(ftuple):
+ d = { 'realms': { '$realm': {
+ 'default_principal_flags': ftuple.setspec()
+ }}}
+ return realm.special_env('tmp', True, kdc_conf=d)
+
+
+# Test one ftuple for kdc.conf default_principal_flags.
+def one_kdcconf(ftuple):
+ e = genkdcconf(ftuple)
+ pat = attr_pat(ftuple)
+ realm.run([kadminl, 'ank', '-pw', 'password', 'test'], env=e)
+ out = realm.run([kadminl, 'getprinc', 'test'])
+ if not pat.search(out):
+ fail('Failed to set flag ' + ftuple.flagname() + ' via kdc.conf')
+
+ realm.run([kadminl, 'delprinc', 'test'])
+
+
+# Principal name for kadm5.acl line
+def ftuple2pname(ftuple, doset):
+ pname = 'set_' if doset else 'clear_'
+ return pname + ftuple.flagname()
+
+
+# Translate a strconv ftuple to a spec string for kadmin.
+def ftuple2kadm_spec(ftuple, doset):
+ ktuple = kadmin_itable[ftuple.flag]
+ if ktuple.invert != ftuple.invert:
+ # Could do:
+ # doset = not doset
+ # but this shouldn't happen.
+ raise ValueError
+ return ktuple.spec(doset)
+
+
+# Generate a line for kadm5.acl.
+def acl_line(ftuple, doset):
+ pname = ftuple2pname(ftuple, doset)
+ spec = ftuple.spec(doset)
+ return "%s * %s %s\n" % (realm.admin_princ, pname, spec)
+
+
+# Test one kadm5.acl line for a ftuple.
+def one_aclcheck(ftuple, doset):
+ pname = ftuple2pname(ftuple, doset)
+ pat = attr_pat(ftuple)
+ outname = ftuple.flagname()
+ # Create the principal and check that the flag is correctly set or
+ # cleared.
+ realm.run_kadmin(['ank', '-pw', 'password', pname])
+ out = realm.run([kadminl, 'getprinc', pname])
+ if doset:
+ if not pat.search(out):
+ fail('Failed to set flag ' + outname + ' via kadm5.acl')
+ else:
+ if not emptyattr.search(out):
+ fail('Failed to clear flag ' + outname + ' via kadm5.acl')
+ # If acl forces flag to be set, try to clear it, and vice versa.
+ spec = ftuple2kadm_spec(ftuple, not doset)
+ realm.run_kadmin(['modprinc', spec, pname])
+ out = realm.run([kadminl, 'getprinc', pname])
+ if doset:
+ if not pat.search(out):
+ fail('Failed to keep flag ' + outname + ' set')
+ else:
+ if not emptyattr.search(out):
+ fail('Failed to keep flag ' + outname + ' clear')
+
+
+for ftuple in kadmin_ftuples:
+ one_kadmin_flag(ftuple)
+
+for ftuple in strconv_ftuples:
+ one_kdcconf(ftuple)
+
+f = open(os.path.join(realm.testdir, 'acl'), 'w')
+for ftuple in strconv_ftuples:
+ f.write(acl_line(ftuple, True))
+ f.write(acl_line(ftuple, False))
+f.close()
+
+realm.start_kadmind()
+realm.prep_kadmin()
+
+for ftuple in strconv_ftuples:
+ one_aclcheck(ftuple, True)
+ one_aclcheck(ftuple, False)
+
+
+success('KDB principal flags')
diff --git a/src/util/k5test.py b/src/util/k5test.py
index 5aedff8..df6a68a 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -22,7 +22,7 @@
"""A module for krb5 test scripts
-To run test scripts during "make check" (if Python 2.4 or later is
+To run test scripts during "make check" (if Python 2.5 or later is
available), add rules like the following to Makefile.in:
check-pytests::
diff --git a/src/util/princflags.py b/src/util/princflags.py
new file mode 100644
index 0000000..16485c5
--- /dev/null
+++ b/src/util/princflags.py
@@ -0,0 +1,264 @@
+import re
+import string
+
+# Module for translating KDB principal flags between string and
+# integer forms.
+#
+# When run as a standalone script, print out C tables to insert into
+# lib/kadm5/str_conv.c.
+
+# KDB principal flag definitions copied from kdb.h
+
+KRB5_KDB_DISALLOW_POSTDATED = 0x00000001
+KRB5_KDB_DISALLOW_FORWARDABLE = 0x00000002
+KRB5_KDB_DISALLOW_TGT_BASED = 0x00000004
+KRB5_KDB_DISALLOW_RENEWABLE = 0x00000008
+KRB5_KDB_DISALLOW_PROXIABLE = 0x00000010
+KRB5_KDB_DISALLOW_DUP_SKEY = 0x00000020
+KRB5_KDB_DISALLOW_ALL_TIX = 0x00000040
+KRB5_KDB_REQUIRES_PRE_AUTH = 0x00000080
+KRB5_KDB_REQUIRES_HW_AUTH = 0x00000100
+KRB5_KDB_REQUIRES_PWCHANGE = 0x00000200
+KRB5_KDB_DISALLOW_SVR = 0x00001000
+KRB5_KDB_PWCHANGE_SERVICE = 0x00002000
+KRB5_KDB_SUPPORT_DESMD5 = 0x00004000
+KRB5_KDB_NEW_PRINC = 0x00008000
+KRB5_KDB_OK_AS_DELEGATE = 0x00100000
+KRB5_KDB_OK_TO_AUTH_AS_DELEGATE = 0x00200000
+KRB5_KDB_NO_AUTH_DATA_REQUIRED = 0x00400000
+
+# Input tables -- list of tuples of the form (name, flag, invert)
+
+# Input forms from kadmin.c
+_kadmin_pflags = [
+ ("allow_postdated", KRB5_KDB_DISALLOW_POSTDATED, True),
+ ("allow_forwardable", KRB5_KDB_DISALLOW_FORWARDABLE, True),
+ ("allow_tgs_req", KRB5_KDB_DISALLOW_TGT_BASED, True),
+ ("allow_renewable", KRB5_KDB_DISALLOW_RENEWABLE, True),
+ ("allow_proxiable", KRB5_KDB_DISALLOW_PROXIABLE, True),
+ ("allow_dup_skey", KRB5_KDB_DISALLOW_DUP_SKEY, True),
+ ("allow_tix", KRB5_KDB_DISALLOW_ALL_TIX, True),
+ ("requires_preauth", KRB5_KDB_REQUIRES_PRE_AUTH, False),
+ ("requires_hwauth", KRB5_KDB_REQUIRES_HW_AUTH, False),
+ ("needchange", KRB5_KDB_REQUIRES_PWCHANGE, False),
+ ("allow_svr", KRB5_KDB_DISALLOW_SVR, True),
+ ("password_changing_service", KRB5_KDB_PWCHANGE_SERVICE, False),
+ ("support_desmd5", KRB5_KDB_SUPPORT_DESMD5, False),
+ ("ok_as_delegate", KRB5_KDB_OK_AS_DELEGATE, False),
+ ("ok_to_auth_as_delegate", KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, False),
+ ("no_auth_data_required", KRB5_KDB_NO_AUTH_DATA_REQUIRED, False),
+]
+
+# Input forms from lib/kadm5/str_conv.c
+_strconv_pflags = [
+ ("postdateable", KRB5_KDB_DISALLOW_POSTDATED, True),
+ ("forwardable", KRB5_KDB_DISALLOW_FORWARDABLE, True),
+ ("tgt-based", KRB5_KDB_DISALLOW_TGT_BASED, True),
+ ("renewable", KRB5_KDB_DISALLOW_RENEWABLE, True),
+ ("proxiable", KRB5_KDB_DISALLOW_PROXIABLE, True),
+ ("dup-skey", KRB5_KDB_DISALLOW_DUP_SKEY, True),
+ ("allow-tickets", KRB5_KDB_DISALLOW_ALL_TIX, True),
+ ("preauth", KRB5_KDB_REQUIRES_PRE_AUTH, False),
+ ("hwauth", KRB5_KDB_REQUIRES_HW_AUTH, False),
+ ("ok-as-delegate", KRB5_KDB_OK_AS_DELEGATE, False),
+ ("pwchange", KRB5_KDB_REQUIRES_PWCHANGE, False),
+ ("service", KRB5_KDB_DISALLOW_SVR, True),
+ ("pwservice", KRB5_KDB_PWCHANGE_SERVICE, False),
+ ("md5", KRB5_KDB_SUPPORT_DESMD5, False),
+ ("ok-to-auth-as-delegate", KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, False),
+ ("no-auth-data-required", KRB5_KDB_NO_AUTH_DATA_REQUIRED, False),
+]
+
+# kdb.h symbol prefix
+_prefix = 'KRB5_KDB_'
+_prefixlen = len(_prefix)
+
+# Names of flags, as printed by kadmin (derived from kdb.h symbols).
+# To be filled in by _setup_tables().
+_flagnames = {}
+
+# Translation table to map hyphens to underscores
+_squash = string.maketrans('-', '_')
+
+# Combined input-to-flag lookup table, to be filled in by
+# _setup_tables()
+pflags = {}
+
+# Tables of ftuples, to be filled in by _setup_tables()
+kadmin_ftuples = []
+strconv_ftuples = []
+sym_ftuples = []
+all_ftuples = []
+
+# Inverted table to look up ftuples by flag value, to be filled in by
+# _setup_tables()
+kadmin_itable = {}
+strconv_itable = {}
+sym_itable = {}
+
+
+# Bundle some methods that are useful for writing tests.
+class Ftuple(object):
+ def __init__(self, name, flag, invert):
+ self.name = name
+ self.flag = flag
+ self.invert = invert
+
+ def __repr__(self):
+ return "Ftuple" + str((self.name, self.flag, self.invert))
+
+ def flagname(self):
+ return _flagnames[self.flag]
+
+ def setspec(self):
+ return ('-' if self.invert else '+') + self.name
+
+ def clearspec(self):
+ return ('+' if self.invert else '-') + self.name
+
+ def spec(self, doset):
+ return self.setspec() if doset else self.clearspec()
+
+
+def _setup_tables():
+ # Filter globals for 'KRB5_KDB_' prefix to create lookup tables.
+ # Make the reasonable assumption that the Python runtime doesn't
+ # define any names with that prefix by default.
+ global _flagnames
+ for k, v in globals().items():
+ if k.startswith(_prefix):
+ _flagnames[v] = k[_prefixlen:]
+
+ # Construct an input table based on kdb.h constant names by
+ # truncating the "KRB5_KDB_" prefix and downcasing.
+ sym_pflags = []
+ for v, k in sorted(_flagnames.items()):
+ sym_pflags.append((k.lower(), v, False))
+
+ global kadmin_ftuples, strconv_ftuples, sym_ftuples, all_ftuples
+ for x in _kadmin_pflags:
+ kadmin_ftuples.append(Ftuple(*x))
+ for x in _strconv_pflags:
+ strconv_ftuples.append(Ftuple(*x))
+ for x in sym_pflags:
+ sym_ftuples.append(Ftuple(*x))
+ all_ftuples = kadmin_ftuples + strconv_ftuples + sym_ftuples
+
+ # Populate combined input-to-flag lookup table. This will
+ # eliminate some duplicates.
+ global pflags
+ for x in all_ftuples:
+ name = x.name.translate(_squash)
+ pflags[name] = x
+
+ global kadmin_itable, strconv_itable, sym_itable
+ for x in kadmin_ftuples:
+ kadmin_itable[x.flag] = x
+ for x in strconv_ftuples:
+ strconv_itable[x.flag] = x
+ for x in sym_ftuples:
+ sym_itable[x.flag] = x
+
+
+# Convert the bit number of a flag to a string. Remove the
+# 'KRB5_KDB_' prefix. Give an 8-digit hexadecimal number if the flag
+# is unknown.
+def flagnum2str(n):
+ s = _flagnames.get(1 << n)
+ if s is None:
+ return "0x%08x" % ((1 << n) & 0xffffffff)
+ return s
+
+
+# Return a list of flag names from a flag word.
+def flags2namelist(flags):
+ a = []
+ for n in xrange(32):
+ if flags & (1 << n):
+ a.append(flagnum2str(n))
+ return a
+
+
+# Given a single specifier in the form {+|-}flagname, return a tuple
+# of the form (flagstoset, flagstoclear).
+def flagspec2mask(s):
+ req_neg = False
+ if s[0] == '-':
+ req_neg = True
+ s = s[1:]
+ elif s[0] == '+':
+ s = s[1:]
+
+ s = s.lower().translate(_squash)
+ x = pflags.get(s)
+ if x is not None:
+ flag, invert = x.flag, x.invert
+ else:
+ # Maybe it's a hex number.
+ if not s.startswith('0x'):
+ raise ValueError
+ flag, invert = int(s, 16), False
+
+ if req_neg:
+ invert = not invert
+ return (0, ~flag) if invert else (flag, ~0)
+
+
+# Given a string containing a space/comma separated list of specifiers
+# of the form {+|-}flagname, return a tuple of the form (flagstoset,
+# flagstoclear). This shares the same limitation as
+# kadm5int_acl_parse_restrictions() of losing the distinction between
+# orderings when the same flag bit appears in both the positive and
+# the negative sense.
+def speclist2mask(s):
+ toset, toclear = (0, ~0)
+ for x in re.split('[\t, ]+', s):
+ fset, fclear = flagspec2mask(x)
+ toset |= fset
+ toclear &= fclear
+
+ return toset, toclear
+
+
+# Print C table of input flag specifiers for lib/kadm5/str_conv.c.
+def _print_ftbl():
+ print 'static const struct flag_table_row ftbl[] = {'
+ a = sorted(pflags.items(), key=lambda (k, v): (v.flag, -v.invert, k))
+ for k, v in a:
+ s1 = ' {"%s",' % k
+ s2 = '%-31s KRB5_KDB_%s,' % (s1, v.flagname())
+ print '%-63s %d},' % (s2, 1 if v.invert else 0)
+
+ print '};'
+ print '#define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))'
+
+
+# Print C table of output flag names for lib/kadm5/str_conv.c.
+def _print_outflags():
+ print 'static const char *outflags[] = {'
+ for i in xrange(32):
+ flag = 1 << i
+ if flag > max(_flagnames.keys()):
+ break
+ try:
+ s = ' "%s",' % _flagnames[flag]
+ except KeyError:
+ s = ' NULL,'
+ print '%-32s/* 0x%08x */' % (s, flag)
+
+ print '};'
+ print '#define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))'
+
+
+# Print out C tables to insert into lib/kadm5/str_conv.c.
+def _main():
+ _print_ftbl()
+ print
+ _print_outflags()
+
+
+_setup_tables()
+
+
+if __name__ == '__main__':
+ _main()