1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.michaelo.tomcat.pac;
17
18 import java.nio.charset.StandardCharsets;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Objects;
23
24 import org.apache.juli.logging.Log;
25 import org.apache.juli.logging.LogFactory;
26
27 import net.sf.michaelo.tomcat.realm.Sid;
28
29
30
31
32
33
34
35 public class KerbValidationInfo {
36
37 public final static long EXTRA_SIDS_USER_FLAG = 0x00000020L;
38 public final static long RESOURCE_GROUP_IDS_USER_FLAG = 0x00000200L;
39
40 protected final Log logger = LogFactory.getLog(getClass());
41
42 private final String effectiveName;
43 private final String fullName;
44 private final String logonScript;
45 private final String profilePath;
46 private final String homeDirectory;
47 private final String homeDirectoryDrive;
48
49 private final long userId;
50 private final long primaryGroupId;
51 private final List<GroupMembership> groupIds;
52
53 private final long userFlags;
54
55 private final String logonServer;
56 private final String logonDomainName;
57 private final Sid logonDomainId;
58
59 private final long userAccountControl;
60
61 private List<KerbSidAndAttributes> extraSids;
62
63 private Sid resourceGroupDomainSid;
64 private List<GroupMembership> resourceGroupIds;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 public KerbValidationInfo(byte[] infoBytes) {
99 Objects.requireNonNull(infoBytes, "infoBytes cannot be null");
100 if (infoBytes.length == 0)
101 throw new IllegalArgumentException("infoBytes cannot be empty");
102
103 PacDataBuffer buf = new PacDataBuffer(infoBytes);
104
105
106 buf.skip(8);
107
108 buf.skip(8);
109
110 long uniquePointer = buf.getUnsignedInt();
111 logPointer("RPC unique", uniquePointer);
112
113
114 buf.skip(8);
115
116 buf.skip(8);
117
118 buf.skip(8);
119
120 buf.skip(8);
121
122 buf.skip(8);
123
124 buf.skip(8);
125
126 RpcUnicodeString effectiveName = getRpcUnicodeString(buf);
127 logPointer("effectiveName", effectiveName.getPointer());
128
129 RpcUnicodeString fullName = getRpcUnicodeString(buf);
130 logPointer("fullName", fullName.getPointer());
131
132 RpcUnicodeString logonScript = getRpcUnicodeString(buf);
133 logPointer("logonScript", logonScript.getPointer());
134
135 RpcUnicodeString profilePath = getRpcUnicodeString(buf);
136 logPointer("profilePath", profilePath.getPointer());
137
138 RpcUnicodeString homeDirectory = getRpcUnicodeString(buf);
139 logPointer("homeDirectory", homeDirectory.getPointer());
140
141 RpcUnicodeString homeDirectoryDrive = getRpcUnicodeString(buf);
142 logPointer("homeDirectoryDrive", homeDirectoryDrive.getPointer());
143
144 buf.skip(2);
145
146 buf.skip(2);
147
148 this.userId = buf.getUnsignedInt();
149
150 this.primaryGroupId = buf.getUnsignedInt();
151
152 long groupCount = buf.getUnsignedInt();
153
154 long groupIdsPointer = buf.getUnsignedInt();
155 logPointer("groupIds", groupIdsPointer);
156
157
158
159
160
161
162
163
164 this.userFlags = buf.getUnsignedInt();
165
166 buf.skip(16);
167
168 RpcUnicodeString logonServer = getRpcUnicodeString(buf);
169 logPointer("logonServer", logonServer.getPointer());
170
171 RpcUnicodeString logonDomainName = getRpcUnicodeString(buf);
172 logPointer("logonDomainName", logonDomainName.getPointer());
173
174 long logonDomainIdPointer = buf.getUnsignedInt();
175 logPointer("logonDomainId", logonDomainIdPointer);
176
177 buf.skip(8);
178
179
180
181
182
183 this.userAccountControl = buf.getUnsignedInt();
184
185 buf.skip(4);
186
187 buf.skip(8);
188
189 buf.skip(8);
190
191 buf.skip(4);
192
193 buf.skip(4);
194
195 long sidCount = buf.getUnsignedInt();
196
197 long extraSidsPointer = buf.getUnsignedInt();
198 logPointer("extraSids", extraSidsPointer);
199
200 long resourceGroupDomainSidPointer = buf.getUnsignedInt();
201 logPointer("resourceGroupDomainSid", resourceGroupDomainSidPointer);
202
203 long resourceGroupCount = buf.getUnsignedInt();
204
205 long resourceGroupIdsPointer = buf.getUnsignedInt();
206 logPointer("resourceGroupIds", resourceGroupIdsPointer);
207
208 this.effectiveName = getNdrString(buf, effectiveName);
209 this.fullName = getNdrString(buf, fullName);
210 this.logonScript = getNdrString(buf, logonScript);
211 this.profilePath = getNdrString(buf, profilePath);
212 this.homeDirectory = getNdrString(buf, homeDirectory);
213 this.homeDirectoryDrive = getNdrString(buf, homeDirectoryDrive);
214
215 long actualGroupCount = buf.getUnsignedInt();
216 if (groupCount != actualGroupCount)
217 throw new IllegalArgumentException("GroupCount is " + groupCount
218 + ", but actual GroupCount is " + actualGroupCount);
219
220 this.groupIds = new ArrayList<GroupMembership>();
221 for (long l = 0L; l < groupCount; l++) {
222 long relativeId = buf.getUnsignedInt();
223 long attributes = buf.getUnsignedInt();
224 this.groupIds.add(new GroupMembership(relativeId, attributes));
225 }
226
227 this.logonServer = getNdrString(buf, logonServer);
228 this.logonDomainName = getNdrString(buf, logonDomainName);
229 this.logonDomainId = getRpcSid(buf);
230
231 if (sidCount != 0L && (userFlags & EXTRA_SIDS_USER_FLAG) == 0L)
232 throw new IllegalArgumentException("SidCount is " + sidCount
233 + ", but flag D is not set in UserFlags (" + toHexString(userFlags) + ")");
234
235 if (extraSidsPointer != 0L && (userFlags & EXTRA_SIDS_USER_FLAG) == 0L)
236 throw new IllegalArgumentException("ExtraSids is not null ("
237 + toHexString(extraSidsPointer)
238 + "), but flag D is not set in UserFlags (" + toHexString(userFlags) + ")");
239
240
241 if (extraSidsPointer != 0L) {
242 this.extraSids = new ArrayList<>();
243 long actualSidCount = buf.getUnsignedInt();
244 if (sidCount != actualSidCount)
245 throw new IllegalArgumentException(
246 "SidCount is " + sidCount + ", but actual SidCount is " + actualSidCount);
247 long[] sidAttrs = new long[(int) sidCount];
248 for (long l = 0L; l < sidCount; l++) {
249 long extraSidPointer = buf.getUnsignedInt();
250 long attributes = buf.getUnsignedInt();
251 sidAttrs[(int) l] = attributes;
252 logPointer("extraSid[" + l + "]", extraSidPointer);
253 }
254 for (long l = 0L; l < sidCount; l++) {
255 Sid extraSid = getRpcSid(buf);
256 this.extraSids.add(new KerbSidAndAttributes(extraSid, sidAttrs[(int) l]));
257 }
258 }
259
260 if (resourceGroupDomainSidPointer != 0L && (userFlags & RESOURCE_GROUP_IDS_USER_FLAG) == 0L)
261 throw new IllegalArgumentException("ResourceGroupDomainSid is not null ("
262 + toHexString(resourceGroupDomainSidPointer)
263 + "), but flag H is not set in UserFlags (" + toHexString(userFlags) + ")");
264
265 if (resourceGroupCount != 0L && (userFlags & RESOURCE_GROUP_IDS_USER_FLAG) == 0L)
266 throw new IllegalArgumentException("ResourceGroupCount is " + sidCount
267 + ", but flag H is not set in UserFlags (" + toHexString(userFlags) + ")");
268
269 if (resourceGroupIdsPointer != 0L && (userFlags & RESOURCE_GROUP_IDS_USER_FLAG) == 0L)
270 throw new IllegalArgumentException("ResourceGroupIds is not null ("
271 + toHexString(resourceGroupIdsPointer)
272 + "), but flag H is not set in UserFlags (" + toHexString(userFlags) + ")");
273
274
275 if (resourceGroupDomainSidPointer != 0L) {
276 this.resourceGroupDomainSid = getRpcSid(buf);
277
278 long actualResourceGroupCount = buf.getUnsignedInt();
279 if (resourceGroupCount != actualResourceGroupCount)
280 throw new IllegalArgumentException("ResourceGroupCount is " + resourceGroupCount
281 + ", but actual ResourceGroupCount is " + actualResourceGroupCount);
282
283
284 if (resourceGroupIdsPointer != 0L) {
285 this.resourceGroupIds = new ArrayList<>();
286 for (long l = 0L; l < resourceGroupCount; l++) {
287 long relativeId = buf.getUnsignedInt();
288 long attributes = buf.getUnsignedInt();
289 this.resourceGroupIds.add(new GroupMembership(relativeId, attributes));
290 }
291 }
292 }
293 }
294
295 public String getEffectiveName() {
296 return effectiveName;
297 }
298
299 public String getFullName() {
300 return fullName;
301 }
302
303 public String getLogonScript() {
304 return logonScript;
305 }
306
307 public String getProfilePath() {
308 return profilePath;
309 }
310
311 public String getHomeDirectory() {
312 return homeDirectory;
313 }
314
315 public String getHomeDirectoryDrive() {
316 return homeDirectoryDrive;
317 }
318
319 public long getUserId() {
320 return userId;
321 }
322
323 public long getPrimaryGroupId() {
324 return primaryGroupId;
325 }
326
327 public List<GroupMembership> getGroupIds() {
328 return Collections.unmodifiableList(groupIds);
329 }
330
331 public long getUserFlags() {
332 return userFlags;
333 }
334
335 public String getLogonServer() {
336 return logonServer;
337 }
338
339 public String getLogonDomainName() {
340 return logonDomainName;
341 }
342
343 public Sid getLogonDomainId() {
344 return logonDomainId;
345 }
346
347 public long getUserAccountControl() {
348 return userAccountControl;
349 }
350
351 public List<KerbSidAndAttributes> getExtraSids() {
352 return extraSids != null ? Collections.unmodifiableList(extraSids) : extraSids;
353 }
354
355 public Sid getResourceGroupDomainSid() {
356 return resourceGroupDomainSid;
357 }
358
359 public List<GroupMembership> getResourceGroupIds() {
360 return resourceGroupIds != null ? Collections.unmodifiableList(resourceGroupIds)
361 : resourceGroupIds;
362 }
363
364 private RpcUnicodeString getRpcUnicodeString(PacDataBuffer buf) {
365 int length = buf.getUnsignedShort();
366 int maximumLength = buf.getUnsignedShort();
367 if (maximumLength % 2 == 1)
368 maximumLength -= 1;
369 long pointer = buf.getUnsignedInt();
370
371 return new RpcUnicodeString(length, maximumLength, pointer);
372 }
373
374
375
376
377
378 private String getNdrString(PacDataBuffer buf, RpcUnicodeString string) {
379 long maximumCount = buf.getUnsignedInt();
380 long offset = buf.getUnsignedInt();
381 long actualCount = buf.getUnsignedInt();
382
383 if (offset > maximumCount || actualCount > maximumCount - offset)
384 throw new IllegalArgumentException(
385 "Incorrectly NDR-encoded UNICODE_STRING: maximumCount: " + maximumCount
386 + ", offset: " + offset + ", actualCount: " + actualCount);
387
388 if (maximumCount != string.getMaximumLength() / 2L
389 || actualCount != string.getLength() / 2L)
390 throw new IllegalArgumentException(
391 "NDR-encoded UNICODE_STRING does not match RPC_UNICODE_STRING: maximumCount: "
392 + maximumCount + ", actualCount: " + actualCount + ", maximumLength: "
393 + string.getMaximumLength() + ", length: " + string.getLength());
394
395 buf.skip(2 * (int) offset);
396
397 byte[] dst = new byte[2 * (int) actualCount];
398 buf.get(dst);
399
400 return new String(dst, StandardCharsets.UTF_16LE);
401 }
402
403 private Sid getRpcSid(PacDataBuffer buf) {
404 long actualSubAuthorityCount = buf.getUnsignedInt();
405 byte[] sidBytes = new byte[8 + (int) actualSubAuthorityCount * 4];
406 buf.get(sidBytes);
407 return new Sid(sidBytes);
408 }
409
410 private void logPointer(String name, long pointer) {
411 if (logger.isTraceEnabled())
412 logger.trace(name + " pointer: " + toHexString(pointer));
413 }
414
415 private String toHexString(long l) {
416 return String.format("0x%08X", l);
417 }
418
419 }