diff options
author | Tom Yu <tlyu@mit.edu> | 2015-07-01 16:28:45 -0400 |
---|---|---|
committer | Tom Yu <tlyu@mit.edu> | 2015-07-09 08:37:59 -0400 |
commit | ea62dd834f343b2dddea81c8295f2f8876c83090 (patch) | |
tree | a82a5f4a2e0e197b14313aa073a653b1dcd53de5 /src | |
parent | be20a5f5cee8d6c4072d1b81712520dbf9f6eefd (diff) | |
download | krb5-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.in | 3 | ||||
-rw-r--r-- | src/tests/Makefile.in | 1 | ||||
-rwxr-xr-x | src/tests/t_princflags.py | 126 | ||||
-rw-r--r-- | src/util/k5test.py | 2 | ||||
-rw-r--r-- | src/util/princflags.py | 264 |
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() |