• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/scripting/python/samba/
1#!/usr/bin/env python
2#
3# create schema.ldif (as a string) from WSPP documentation
4#
5# based on minschema.py and minschema_wspp
6#
7
8import re
9import base64
10
11bitFields = {}
12
13# ADTS: 2.2.9
14# bit positions as labeled in the docs
15bitFields["searchflags"] = {
16    'fATTINDEX': 31,         # IX
17    'fPDNTATTINDEX': 30,     # PI
18    'fANR': 29, #AR
19    'fPRESERVEONDELETE': 28,         # PR
20    'fCOPY': 27,     # CP
21    'fTUPLEINDEX': 26,       # TP
22    'fSUBTREEATTINDEX': 25,  # ST
23    'fCONFIDENTIAL': 24,     # CF
24    'fNEVERVALUEAUDIT': 23,  # NV
25    'fRODCAttribute': 22,    # RO
26
27
28    # missing in ADTS but required by LDIF
29    'fRODCFilteredAttribute': 22,    # RO ?
30    'fCONFIDENTAIL': 24, # typo
31    'fRODCFILTEREDATTRIBUTE': 22 # case
32    }
33
34# ADTS: 2.2.10
35bitFields["systemflags"] = {
36    'FLAG_ATTR_NOT_REPLICATED': 31, 'FLAG_CR_NTDS_NC': 31, 	# NR
37    'FLAG_ATTR_REQ_PARTIAL_SET_MEMBER': 30, 'FLAG_CR_NTDS_DOMAIN': 30, 	# PS
38    'FLAG_ATTR_IS_CONSTRUCTED': 29, 'FLAG_CR_NTDS_NOT_GC_REPLICATED': 29, 	# CS
39    'FLAG_ATTR_IS_OPERATIONAL': 28, 	# OP
40    'FLAG_SCHEMA_BASE_OBJECT': 27, 	# BS
41    'FLAG_ATTR_IS_RDN': 26, 	# RD
42    'FLAG_DISALLOW_MOVE_ON_DELETE': 6, 	# DE
43    'FLAG_DOMAIN_DISALLOW_MOVE': 5, 	# DM
44    'FLAG_DOMAIN_DISALLOW_RENAME': 4, 	# DR
45    'FLAG_CONFIG_ALLOW_LIMITED_MOVE': 3, 	# AL
46    'FLAG_CONFIG_ALLOW_MOVE': 2, 	# AM
47    'FLAG_CONFIG_ALLOW_RENAME': 1, 	# AR
48    'FLAG_DISALLOW_DELETE': 0 	# DD
49    }
50
51# ADTS: 2.2.11
52bitFields["schemaflagsex"] = {
53    'FLAG_ATTR_IS_CRITICAL': 31
54    }
55
56# ADTS: 3.1.1.2.2.2
57oMObjectClassBER = {
58    '1.3.12.2.1011.28.0.702' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x3E'),
59    '1.2.840.113556.1.1.1.12': base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0C'),
60    '2.6.6.1.2.5.11.29'      : base64.b64encode('\x56\x06\x01\x02\x05\x0B\x1D'),
61    '1.2.840.113556.1.1.1.11': base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0B'),
62    '1.3.12.2.1011.28.0.714' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x4A'),
63    '1.3.12.2.1011.28.0.732' : base64.b64encode('\x2B\x0C\x02\x87\x73\x1C\x00\x85\x5C'),
64    '1.2.840.113556.1.1.1.6' : base64.b64encode('\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x06')
65}
66
67# separated by commas in docs, and must be broken up
68multivalued_attrs = set(["auxiliaryclass","maycontain","mustcontain","posssuperiors",
69                         "systemauxiliaryclass","systemmaycontain","systemmustcontain",
70                         "systemposssuperiors"])
71
72def __read_folded_line(f, buffer):
73    """ reads a line from an LDIF file, unfolding it"""
74    line = buffer
75
76    while True:
77        l = f.readline()
78
79        if l[:1] == " ":
80            # continued line
81
82            # cannot fold an empty line
83            assert(line != "" and line != "\n")
84
85            # preserves '\n '
86            line = line + l
87        else:
88            # non-continued line
89            if line == "":
90                line = l
91
92                if l == "":
93                    # eof, definitely won't be folded
94                    break
95            else:
96                # marks end of a folded line
97                # line contains the now unfolded line
98                # buffer contains the start of the next possibly folded line
99                buffer = l
100                break
101
102    return (line, buffer)
103
104
105def __read_raw_entries(f):
106    """reads an LDIF entry, only unfolding lines"""
107
108    # will not match options after the attribute type
109    attr_type_re = re.compile("^([A-Za-z]+[A-Za-z0-9-]*):")
110
111    buffer = ""
112
113    while True:
114        entry = []
115
116        while True:
117            (l, buffer) = __read_folded_line(f, buffer)
118
119            if l[:1] == "#":
120                continue
121
122            if l == "\n" or l == "":
123                break
124
125            m = attr_type_re.match(l)
126
127            if m:
128                if l[-1:] == "\n":
129                    l = l[:-1]
130
131                entry.append(l)
132            else:
133                print >>sys.stderr, "Invalid line: %s" % l,
134                sys.exit(1)
135
136        if len(entry):
137            yield entry
138
139        if l == "":
140            break
141
142
143def fix_dn(dn):
144    """fix a string DN to use ${SCHEMADN}"""
145
146    # folding?
147    if dn.find("<RootDomainDN>") != -1:
148        dn = dn.replace("\n ", "")
149        dn = dn.replace(" ", "")
150        return dn.replace("CN=Schema,CN=Configuration,<RootDomainDN>", "${SCHEMADN}")
151    else:
152        return dn
153
154def __convert_bitfield(key, value):
155    """Evaluate the OR expression in 'value'"""
156    assert(isinstance(value, str))
157
158    value = value.replace("\n ", "")
159    value = value.replace(" ", "")
160
161    try:
162        # some attributes already have numeric values
163        o = int(value)
164    except ValueError:
165        o = 0
166        flags = value.split("|")
167        for f in flags:
168            bitpos = bitFields[key][f]
169            o = o | (1 << (31 - bitpos))
170
171    return str(o)
172
173def __write_ldif_one(entry):
174    """Write out entry as LDIF"""
175    out = []
176
177    for l in entry:
178        if isinstance(l[1], str):
179            vl = [l[1]]
180        else:
181            vl = l[1]
182
183        if l[0].lower() == 'omobjectclass':
184            out.append("%s:: %s" % (l[0], l[1]))
185            continue
186
187        for v in vl:
188            out.append("%s: %s" % (l[0], v))
189
190
191    return "\n".join(out)
192
193def __transform_entry(entry, objectClass):
194    """Perform transformations required to convert the LDIF-like schema
195       file entries to LDIF, including Samba-specific stuff."""
196
197    entry = [l.split(":", 1) for l in entry]
198
199    cn = ""
200
201    for l in entry:
202        key = l[0].lower()
203        l[1] = l[1].lstrip()
204        l[1] = l[1].rstrip()
205
206        if not cn and key == "cn":
207            cn = l[1]
208
209        if key in multivalued_attrs:
210            # unlike LDIF, these are comma-separated
211            l[1] = l[1].replace("\n ", "")
212            l[1] = l[1].replace(" ", "")
213
214            l[1] = l[1].split(",")
215
216        if key in bitFields:
217            l[1] = __convert_bitfield(key, l[1])
218
219        if key == "omobjectclass":
220            l[1] = oMObjectClassBER[l[1].strip()]
221
222        if isinstance(l[1], str):
223            l[1] = fix_dn(l[1])
224
225
226    assert(cn)
227    entry.insert(0, ["dn", "CN=%s,${SCHEMADN}" % cn])
228    entry.insert(1, ["objectClass", ["top", objectClass]])
229    entry.insert(2, ["cn", cn])
230
231    for l in entry:
232        key = l[0].lower()
233
234        if key == "cn":
235            entry.remove(l)
236
237    return entry
238
239def __parse_schema_file(filename, objectClass):
240    """Load and transform a schema file."""
241
242    out = []
243
244    f = open(filename, "rU")
245    for entry in __read_raw_entries(f):
246        out.append(__write_ldif_one(__transform_entry(entry, objectClass)))
247
248    return "\n\n".join(out)
249
250
251def read_ms_schema(attr_file, classes_file, dump_attributes = True, dump_classes = True, debug = False):
252    """Read WSPP documentation-derived schema files."""
253
254    attr_ldif = ""
255    classes_ldif = ""
256
257    if dump_attributes:
258        attr_ldif =  __parse_schema_file(attr_file, "attributeSchema")
259    if dump_classes:
260        classes_ldif = __parse_schema_file(classes_file, "classSchema")
261
262    return attr_ldif + "\n\n" + classes_ldif + "\n\n"
263
264if __name__ == '__main__':
265    import sys
266
267    try:
268        attr_file = sys.argv[1]
269        classes_file = sys.argv[2]
270    except IndexError:
271        print >>sys.stderr, "Usage: %s attr-file.txt classes-file.txt" % (sys.argv[0])
272        sys.exit(1)
273
274    print read_ms_schema(attr_file, classes_file)
275
276
277