1import re
2
3# Module for translating KDB principal flags between string and
4# integer forms.
5#
6# When run as a standalone script, print out C tables to insert into
7# lib/kadm5/str_conv.c.
8
9# KDB principal flag definitions copied from kdb.h
10
11KRB5_KDB_DISALLOW_POSTDATED     = 0x00000001
12KRB5_KDB_DISALLOW_FORWARDABLE   = 0x00000002
13KRB5_KDB_DISALLOW_TGT_BASED     = 0x00000004
14KRB5_KDB_DISALLOW_RENEWABLE     = 0x00000008
15KRB5_KDB_DISALLOW_PROXIABLE     = 0x00000010
16KRB5_KDB_DISALLOW_DUP_SKEY      = 0x00000020
17KRB5_KDB_DISALLOW_ALL_TIX       = 0x00000040
18KRB5_KDB_REQUIRES_PRE_AUTH      = 0x00000080
19KRB5_KDB_REQUIRES_HW_AUTH       = 0x00000100
20KRB5_KDB_REQUIRES_PWCHANGE      = 0x00000200
21KRB5_KDB_DISALLOW_SVR           = 0x00001000
22KRB5_KDB_PWCHANGE_SERVICE       = 0x00002000
23KRB5_KDB_SUPPORT_DESMD5         = 0x00004000
24KRB5_KDB_NEW_PRINC              = 0x00008000
25KRB5_KDB_OK_AS_DELEGATE         = 0x00100000
26KRB5_KDB_OK_TO_AUTH_AS_DELEGATE = 0x00200000
27KRB5_KDB_NO_AUTH_DATA_REQUIRED  = 0x00400000
28KRB5_KDB_LOCKDOWN_KEYS          = 0x00800000
29
30# Input tables -- list of tuples of the form (name, flag, invert)
31
32# Input forms from kadmin.c
33_kadmin_pflags = [
34    ("allow_postdated",         KRB5_KDB_DISALLOW_POSTDATED,    True),
35    ("allow_forwardable",       KRB5_KDB_DISALLOW_FORWARDABLE,  True),
36    ("allow_tgs_req",           KRB5_KDB_DISALLOW_TGT_BASED,    True),
37    ("allow_renewable",         KRB5_KDB_DISALLOW_RENEWABLE,    True),
38    ("allow_proxiable",         KRB5_KDB_DISALLOW_PROXIABLE,    True),
39    ("allow_dup_skey",          KRB5_KDB_DISALLOW_DUP_SKEY,     True),
40    ("allow_tix",               KRB5_KDB_DISALLOW_ALL_TIX,      True),
41    ("requires_preauth",        KRB5_KDB_REQUIRES_PRE_AUTH,     False),
42    ("requires_hwauth",         KRB5_KDB_REQUIRES_HW_AUTH,      False),
43    ("needchange",              KRB5_KDB_REQUIRES_PWCHANGE,     False),
44    ("allow_svr",               KRB5_KDB_DISALLOW_SVR,          True),
45    ("password_changing_service", KRB5_KDB_PWCHANGE_SERVICE,    False),
46    ("support_desmd5",          KRB5_KDB_SUPPORT_DESMD5,        False),
47    ("ok_as_delegate",          KRB5_KDB_OK_AS_DELEGATE,        False),
48    ("ok_to_auth_as_delegate",  KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, False),
49    ("no_auth_data_required",   KRB5_KDB_NO_AUTH_DATA_REQUIRED, False),
50    ("lockdown_keys",           KRB5_KDB_LOCKDOWN_KEYS,         False),
51]
52
53# Input forms from lib/kadm5/str_conv.c
54_strconv_pflags = [
55    ("postdateable",            KRB5_KDB_DISALLOW_POSTDATED,    True),
56    ("forwardable",             KRB5_KDB_DISALLOW_FORWARDABLE,  True),
57    ("tgt-based",               KRB5_KDB_DISALLOW_TGT_BASED,    True),
58    ("renewable",               KRB5_KDB_DISALLOW_RENEWABLE,    True),
59    ("proxiable",               KRB5_KDB_DISALLOW_PROXIABLE,    True),
60    ("dup-skey",                KRB5_KDB_DISALLOW_DUP_SKEY,     True),
61    ("allow-tickets",           KRB5_KDB_DISALLOW_ALL_TIX,      True),
62    ("preauth",                 KRB5_KDB_REQUIRES_PRE_AUTH,     False),
63    ("hwauth",                  KRB5_KDB_REQUIRES_HW_AUTH,      False),
64    ("ok-as-delegate",          KRB5_KDB_OK_AS_DELEGATE,        False),
65    ("pwchange",                KRB5_KDB_REQUIRES_PWCHANGE,     False),
66    ("service",                 KRB5_KDB_DISALLOW_SVR,          True),
67    ("pwservice",               KRB5_KDB_PWCHANGE_SERVICE,      False),
68    ("md5",                     KRB5_KDB_SUPPORT_DESMD5,        False),
69    ("ok-to-auth-as-delegate",  KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, False),
70    ("no-auth-data-required",   KRB5_KDB_NO_AUTH_DATA_REQUIRED, False),
71    ("lockdown-keys",           KRB5_KDB_LOCKDOWN_KEYS,         False),
72]
73
74# kdb.h symbol prefix
75_prefix = 'KRB5_KDB_'
76_prefixlen = len(_prefix)
77
78# Names of flags, as printed by kadmin (derived from kdb.h symbols).
79# To be filled in by _setup_tables().
80_flagnames = {}
81
82# Translation table to map hyphens to underscores
83_squash = str.maketrans('-', '_')
84
85# Combined input-to-flag lookup table, to be filled in by
86# _setup_tables()
87pflags = {}
88
89# Tables of ftuples, to be filled in by _setup_tables()
90kadmin_ftuples = []
91strconv_ftuples = []
92sym_ftuples = []
93all_ftuples = []
94
95# Inverted table to look up ftuples by flag value, to be filled in by
96# _setup_tables()
97kadmin_itable = {}
98strconv_itable = {}
99sym_itable = {}
100
101
102# Bundle some methods that are useful for writing tests.
103class Ftuple(object):
104    def __init__(self, name, flag, invert):
105        self.name = name
106        self.flag = flag
107        self.invert = invert
108
109    def __repr__(self):
110        return "Ftuple" + str((self.name, self.flag, self.invert))
111
112    def flagname(self):
113        return _flagnames[self.flag]
114
115    def setspec(self):
116        return ('-' if self.invert else '+') + self.name
117
118    def clearspec(self):
119        return ('+' if self.invert else '-') + self.name
120
121    def spec(self, doset):
122        return self.setspec() if doset else self.clearspec()
123
124
125def _setup_tables():
126    # Filter globals for 'KRB5_KDB_' prefix to create lookup tables.
127    # Make the reasonable assumption that the Python runtime doesn't
128    # define any names with that prefix by default.
129    global _flagnames
130    for k, v in globals().items():
131        if k.startswith(_prefix):
132            _flagnames[v] = k[_prefixlen:]
133
134    # Construct an input table based on kdb.h constant names by
135    # truncating the "KRB5_KDB_" prefix and downcasing.
136    sym_pflags = []
137    for v, k in sorted(_flagnames.items()):
138        sym_pflags.append((k.lower(), v, False))
139
140    global kadmin_ftuples, strconv_ftuples, sym_ftuples, all_ftuples
141    for x in _kadmin_pflags:
142        kadmin_ftuples.append(Ftuple(*x))
143    for x in _strconv_pflags:
144        strconv_ftuples.append(Ftuple(*x))
145    for x in sym_pflags:
146        sym_ftuples.append(Ftuple(*x))
147    all_ftuples = kadmin_ftuples + strconv_ftuples + sym_ftuples
148
149    # Populate combined input-to-flag lookup table.  This will
150    # eliminate some duplicates.
151    global pflags
152    for x in all_ftuples:
153        name = x.name.translate(_squash)
154        pflags[name] = x
155
156    global kadmin_itable, strconv_itable, sym_itable
157    for x in kadmin_ftuples:
158        kadmin_itable[x.flag] = x
159    for x in strconv_ftuples:
160        strconv_itable[x.flag] = x
161    for x in sym_ftuples:
162        sym_itable[x.flag] = x
163
164
165# Convert the bit number of a flag to a string.  Remove the
166# 'KRB5_KDB_' prefix.  Give an 8-digit hexadecimal number if the flag
167# is unknown.
168def flagnum2str(n):
169    s = _flagnames.get(1 << n)
170    if s is None:
171        return "0x%08x" % ((1 << n) & 0xffffffff)
172    return s
173
174
175# Return a list of flag names from a flag word.
176def flags2namelist(flags):
177    a = []
178    for n in range(32):
179        if flags & (1 << n):
180            a.append(flagnum2str(n))
181    return a
182
183
184# Given a single specifier in the form {+|-}flagname, return a tuple
185# of the form (flagstoset, flagstoclear).
186def flagspec2mask(s):
187    req_neg = False
188    if s[0] == '-':
189        req_neg = True
190        s = s[1:]
191    elif s[0] == '+':
192        s = s[1:]
193
194    s = s.lower().translate(_squash)
195    x = pflags.get(s)
196    if x is not None:
197        flag, invert = x.flag, x.invert
198    else:
199        # Maybe it's a hex number.
200        if not s.startswith('0x'):
201            raise ValueError
202        flag, invert = int(s, 16), False
203
204    if req_neg:
205        invert = not invert
206    return (0, ~flag) if invert else (flag, ~0)
207
208
209# Given a string containing a space/comma separated list of specifiers
210# of the form {+|-}flagname, return a tuple of the form (flagstoset,
211# flagstoclear).  This shares the same limitation as
212# kadm5int_acl_parse_restrictions() of losing the distinction between
213# orderings when the same flag bit appears in both the positive and
214# the negative sense.
215def speclist2mask(s):
216    toset, toclear = (0, ~0)
217    for x in re.split('[\t, ]+', s):
218        fset, fclear = flagspec2mask(x)
219        toset |= fset
220        toclear &= fclear
221
222    return toset, toclear
223
224
225# Print C table of input flag specifiers for lib/kadm5/str_conv.c.
226def _print_ftbl():
227    print('static const struct flag_table_row ftbl[] = {')
228    a = sorted(pflags.items(), key=lambda k, v: (v.flag, -v.invert, k))
229    for k, v in a:
230        s1 = '    {"%s",' % k
231        s2 = '%-31s KRB5_KDB_%s,' % (s1, v.flagname())
232        print('%-63s %d},' % (s2, 1 if v.invert else 0))
233
234    print('};')
235    print('#define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))')
236
237
238# Print C table of output flag names for lib/kadm5/str_conv.c.
239def _print_outflags():
240    print('static const char *outflags[] = {')
241    for i in range(32):
242        flag = 1 << i
243        if flag > max(_flagnames.keys()):
244            break
245        try:
246            s = '    "%s",' % _flagnames[flag]
247        except KeyError:
248            s = '    NULL,'
249        print('%-32s/* 0x%08x */' % (s, flag))
250
251    print('};')
252    print('#define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))')
253
254
255# Print out C tables to insert into lib/kadm5/str_conv.c.
256def _main():
257    _print_ftbl()
258    print
259    _print_outflags()
260
261
262_setup_tables()
263
264
265if __name__ == '__main__':
266    _main()
267