Browse code

Implement keytabv1 format. This is completely untested though.

Xavier G authored on25/04/2020 17:57:30
Showing1 changed files

  • yamltab index 444f1c9..6ba1a2d 100755
... ...
@@ -30,6 +30,8 @@ DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
30 30
 KEYTAB_FIRST_BYTE = 0x05
31 31
 # Default prefix for struct's format strings, defining big-endian byte order:
32 32
 BIG_ENDIAN='>'
33
+LITTLE_ENDIAN='<'
34
+NATIVE_ENDIANNESS='='
33 35
 DEFAULT_PREFIX=BIG_ENDIAN
34 36
 DEFAULT_ENCODING='ascii'
35 37
 VERBOSITY=1
... ...
@@ -102,7 +104,7 @@ def int_to_enc_type(lookup_value):
102 104
 	return lookup(lookup_value, ENC_TYPES, 'ENCTYPE_UNKNOWN')
103 105
 
104 106
 def principal_to_spn(principal):
105
-	if principal['name_type_raw'] != 1:
107
+	if principal.get('name_type_raw') != NAME_TYPES['KRB5_NT_PRINCIPAL']:
106 108
 		return None
107 109
 	components = principal['components']
108 110
 	count = len(components)
... ...
@@ -145,29 +147,34 @@ def parse_data(buf, prefix=DEFAULT_PREFIX):
145 147
 def parse_str(buf, prefix=DEFAULT_PREFIX, encoding=DEFAULT_ENCODING):
146 148
 	return parse_data(buf, prefix).decode(encoding)
147 149
 
148
-def parse_principal(buf, prefix=DEFAULT_PREFIX):
150
+def parse_principal(buf, prefix=DEFAULT_PREFIX, version=2):
149 151
 	principal = {}
150 152
 	# [1] states "count of components (32 bits)" but [2] says int16:
151 153
 	component_count = unpack(buf, prefix, 'H')
154
+	# [1] states "[includes realm in version 1]"
155
+	if version == 1:
156
+		component_count -= 1
152 157
 	principal['realm'] = parse_str(buf, prefix)
153 158
 	components = []
154 159
 	for i in range(component_count):
155 160
 		components.append(parse_str(buf, prefix))
156 161
 	principal['components'] = components
157
-	# [3] states int32:
158
-	principal['name_type_raw'] = unpack(buf, prefix, 'i')
162
+	# [1] states "[omitted in version 1]"
163
+	if version != 1:
164
+		# [3] states int32:
165
+		principal['name_type_raw'] = unpack(buf, prefix, 'i')
159 166
 	return principal
160 167
 
161
-def parse_entry(buf, prefix=DEFAULT_PREFIX):
168
+def parse_entry(buf, prefix=DEFAULT_PREFIX, version=2):
162 169
 	entry = {}
163
-	entry['principal'] = parse_principal(buf, prefix)
170
+	entry['principal'] = parse_principal(buf, prefix, version)
164 171
 	entry['timestamp'], entry['kvno'], entry['enctype_raw'], entry['key_length'] = unpack(buf, prefix, 'IBHH')
165 172
 	entry['key'] = buf.read(entry['key_length'])
166 173
 	return entry
167 174
 
168
-def parse_record(buf, prefix=DEFAULT_PREFIX):
175
+def parse_record(buf, prefix=DEFAULT_PREFIX, version=2):
169 176
 	record = {'type': 'record'}
170
-	record['entry'] = parse_entry(buf, prefix)
177
+	record['entry'] = parse_entry(buf, prefix, version)
171 178
 	record['tail'] = buf.read()
172 179
 	return record
173 180
 
... ...
@@ -175,8 +182,12 @@ def parse_keytab(buf, args):
175 182
 	second_byte = buf.read(2)[1]
176 183
 	verbose(2, 'keytab v{}', second_byte)
177 184
 	if second_byte == 1:
178
-		verbose(1, 'Keytab v1 not supported yet!')
179
-		sys.exit(1)
185
+		# Version 1 uses native byte order:
186
+		prefix = NATIVE_ENDIANNESS
187
+		if args.v1_endianness == 'big':
188
+			prefix = BIG_ENDIAN
189
+		elif args.v1_endianness == 'little':
190
+			prefix = LITTLE_ENDIAN
180 191
 	elif second_byte == 2:
181 192
 		# Version 2 always uses big-endian byte order:
182 193
 		prefix = BIG_ENDIAN
... ...
@@ -200,7 +211,7 @@ def parse_keytab(buf, args):
200 211
 		verbose(3, 'Record #{} of length {}', len(keytab['records']) + 1, record_length)
201 212
 		record = buf.read(abs(record_length))
202 213
 		if record_length > 0:
203
-			record = parse_record(BufferedReader(BytesIO(record)), prefix)
214
+			record = parse_record(BufferedReader(BytesIO(record)), prefix, second_byte)
204 215
 		else:
205 216
 			record = {'type': 'hole', 'data': record}
206 217
 		record['length'] = record_length
... ...
@@ -300,6 +311,7 @@ def parse_args():
300 311
 	parser.add_argument('--verbose', '-v', dest='verbose', action='count', help='increase verbosity level', default=VERBOSITY)
301 312
 	parser.add_argument('--data-layout', '-l',  dest='data_layout', choices=DATA_LAYOUTS.keys(), default='simple', help='data layout (keytab to YAML/JSON only)')
302 313
 	parser.add_argument('--output-format', '-f', dest='output_format', choices=['json', 'yaml'], default='yaml', help='output format (keytab to YAML/JSON only)')
314
+	parser.add_argument('--v1-endianness', '-e', dest='v1_endianness', choices=['native', 'little', 'big'], default='native', help='Enforce endianness (keytab v1 to YAML/JSON only)')
303 315
 	parser.add_argument('input', nargs='?', type=argparse.FileType('rb'), default=sys.stdin.buffer, help='input file; defaults to standard input')
304 316
 	args = parser.parse_args()
305 317
 	return args