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