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.math.BigInteger;
19 import java.security.Key;
20 import java.security.SignatureException;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Base64;
24 import java.util.List;
25 import java.util.Objects;
26
27 import org.apache.juli.logging.Log;
28 import org.apache.juli.logging.LogFactory;
29
30
31
32
33
34
35
36
37
38
39
40
41 public class Pac {
42
43 private static final BigInteger EIGHT = BigInteger.valueOf(8L);
44
45 private static final long KERB_VALIDATION_INFO = 0x00000001L;
46 private static final long PAC_CLIENT_INFO = 0x0000000AL;
47 private static final long UPN_DNS_INFO = 0x0000000CL;
48 private static final long SERVER_SIGNATURE = 0x00000006L;
49 private static final long KDC_SIGNATURE = 0x00000007L;
50
51 protected final Log logger = LogFactory.getLog(getClass());
52
53 private KerbValidationInfo kerbValidationInfo;
54 private UpnDnsInfo upnDnsInfo;
55 private PacClientInfo pacClientInfo;
56 private PacSignatureData serverSignature;
57 private PacSignatureData kdcSignature;
58
59 private final PacSignatureVerifier signatureVerifier;
60 private final byte[] zeroedPacData;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public Pac(byte[] pacDataBytes, PacSignatureVerifier signatureVerifier) {
87 Objects.requireNonNull(pacDataBytes, "pacDataBytes cannot be null");
88 if (pacDataBytes.length == 0)
89 throw new IllegalArgumentException("pacDataBytes cannot be empty");
90
91 PacDataBuffer buf = new PacDataBuffer(pacDataBytes);
92 this.signatureVerifier = Objects.requireNonNull(signatureVerifier,
93 "signatureVerifier cannot be null");
94
95
96 if (logger.isTraceEnabled())
97 logger.trace("Parsing PACTYPE structure...");
98
99 long buffers = buf.getUnsignedInt();
100
101 long version = buf.getUnsignedInt();
102 if (version != 0L)
103 throw new IllegalArgumentException("PAC must have version 0, but has " + version);
104
105 if (logger.isTraceEnabled())
106 logger.trace("PAC has version " + version + " and contains " + buffers + " buffers");
107
108
109 if (logger.isTraceEnabled())
110 logger.trace("Parsing " + buffers + " PAC_INFO_BUFFER structures...");
111 List<PacInfoBuffer> pacInfoBuffers = new ArrayList<>();
112 for (long l = 0L; l < buffers; l++) {
113
114 long type = buf.getUnsignedInt();
115
116 long bufferSize = buf.getUnsignedInt();
117
118 BigInteger offset = buf.getUnsignedLong();
119 if (!offset.mod(EIGHT).equals(BigInteger.ZERO))
120 throw new IllegalArgumentException(
121 "PAC_INFO_BUFFER offset must be multiple of 8, but is " + offset);
122 int pos = buf.position();
123 buf.position(offset.intValue());
124 byte[] data = new byte[(int) bufferSize];
125 buf.get(data);
126 buf.position(pos);
127 if (logger.isTraceEnabled())
128 logger.trace("PAC_INFO_BUFFER describes type " + String.format("0x%08X", type)
129 + " with size " + bufferSize + " and offset " + offset + " containing data "
130 + Base64.getEncoder().encodeToString(data));
131
132 pacInfoBuffers.add(new PacInfoBuffer(type, bufferSize, offset, data));
133 }
134
135 zeroedPacData = Arrays.copyOf(pacDataBytes, pacDataBytes.length);
136
137 for (PacInfoBuffer pacInfoBuffer : pacInfoBuffers) {
138 long type = pacInfoBuffer.getType();
139 byte[] data = pacInfoBuffer.getData();
140 if (type == KERB_VALIDATION_INFO) {
141 if (kerbValidationInfo != null) {
142 if (logger.isTraceEnabled())
143 logger.trace("Ignoring additional KERB_VALIDATION_INFO structure");
144 } else {
145 if (logger.isTraceEnabled())
146 logger.trace("Parsing KERB_VALIDATION_INFO structure...");
147 kerbValidationInfo = new KerbValidationInfo(data);
148 }
149 } else if (type == UPN_DNS_INFO) {
150 if (upnDnsInfo != null) {
151 if (logger.isTraceEnabled())
152 logger.trace("Ignoring additional UPN_DNS_INFO structure");
153 } else {
154 if (logger.isTraceEnabled())
155 logger.trace("Parsing UPN_DNS_INFO structure...");
156 upnDnsInfo = new UpnDnsInfo(data);
157 }
158 } else if (type == PAC_CLIENT_INFO) {
159 if (upnDnsInfo != null) {
160 if (logger.isTraceEnabled())
161 logger.trace("Ignoring additional PAC_CLIENT_INFO structure");
162 } else {
163 if (logger.isTraceEnabled())
164 logger.trace("Parsing PAC_CLIENT_INFO structure...");
165 pacClientInfo = new PacClientInfo(data);
166 }
167 } else if (type == SERVER_SIGNATURE) {
168 if (serverSignature != null) {
169 if (logger.isTraceEnabled())
170 logger.trace(
171 "Ignoring additional PAC_SIGNATURE_DATA (Server Signature) structure");
172 } else {
173 if (logger.isTraceEnabled())
174 logger.trace("Parsing PAC_SIGNATURE_DATA (Server Signature) structure...");
175 serverSignature = new PacSignatureData(data);
176 int from = pacInfoBuffer.getOffset().intValue() + 4;
177 int to = from + serverSignature.getType().getSize();
178 Arrays.fill(zeroedPacData, from, to, (byte) 0);
179 }
180 } else if (type == KDC_SIGNATURE) {
181 if (kdcSignature != null) {
182 if (logger.isTraceEnabled())
183 logger.trace(
184 "Ignoring additional PAC_SIGNATURE_DATA (KDC Signature) structure");
185 } else {
186 if (logger.isTraceEnabled())
187 logger.trace("Parsing PAC_SIGNATURE_DATA (KDC Signature) structure...");
188 kdcSignature = new PacSignatureData(data);
189 int from = pacInfoBuffer.getOffset().intValue() + 4;
190 int to = from + kdcSignature.getType().getSize();
191 Arrays.fill(zeroedPacData, from, to, (byte) 0);
192 }
193 } else {
194 if (logger.isTraceEnabled())
195 logger.trace(
196 "Ignoring unsupported structure type " + String.format("0x%08X", type)
197 + " with data " + Base64.getEncoder().encodeToString(data));
198 }
199 }
200
201 if (kerbValidationInfo == null)
202 throw new IllegalArgumentException(
203 "PAC does not contain required KERB_VALIDATION_INFO structure");
204
205 if (pacClientInfo == null)
206 throw new IllegalArgumentException(
207 "PAC does not contain required PAC_CLIENT_INFO structure");
208
209 if (serverSignature == null)
210 throw new IllegalArgumentException(
211 "PAC does not contain required PAC_SIGNATURE_DATA (Server Signature) structure");
212
213 if (kdcSignature == null)
214 throw new IllegalArgumentException(
215 "PAC does not contain required PAC_SIGNATURE_DATA (KDC Signature) structure");
216 }
217
218 public KerbValidationInfo getKerbValidationInfo() {
219 return kerbValidationInfo;
220 }
221
222 public UpnDnsInfo getUpnDnsInfo() {
223 return upnDnsInfo;
224 }
225
226 public PacClientInfo getPacClientInfo() {
227 return pacClientInfo;
228 }
229
230 public PacSignatureData getServerSignature() {
231 return serverSignature;
232 }
233
234 public PacSignatureData getKdcSignature() {
235 return kdcSignature;
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249 public void verifySignature(Key[] keys) throws SignatureException {
250 signatureVerifier.verify(serverSignature, zeroedPacData, keys);
251 }
252
253 }