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 public Pac(byte[] pacDataBytes, PacSignatureVerifier signatureVerifier) {
78 Objects.requireNonNull(pacDataBytes, "pacDataBytes cannot be null");
79 if (pacDataBytes.length == 0) throw new IllegalArgumentException("pacDataBytes cannot be empty");
80
81 PacDataBuffer buf = new PacDataBuffer(pacDataBytes);
82 this.signatureVerifier = Objects.requireNonNull(signatureVerifier, "signatureVerifier cannot be null");
83
84
85 if (logger.isTraceEnabled()) logger.trace("Parsing PACTYPE structure...");
86
87 long buffers = buf.getUnsignedInt();
88
89 long version = buf.getUnsignedInt();
90 if (version != 0L) throw new IllegalArgumentException("PAC must have version 0, but has " + version);
91
92 if (logger.isTraceEnabled())
93 logger.trace("PAC has version " + version + " and contains " + buffers + " buffers");
94
95
96 if (logger.isTraceEnabled()) logger.trace("Parsing " + buffers + " PAC_INFO_BUFFER structures...");
97 List<PacInfoBuffer> pacInfoBuffers = new ArrayList<>();
98 for (long l = 0L; l < buffers; l++) {
99
100 long type = buf.getUnsignedInt();
101
102 long bufferSize = buf.getUnsignedInt();
103
104 BigInteger offset = buf.getUnsignedLong();
105 if (!offset.mod(EIGHT).equals(BigInteger.ZERO))
106 throw new IllegalArgumentException("PAC_INFO_BUFFER offset must be multiple of 8, but is " + offset);
107 int pos = buf.position();
108 buf.position(offset.intValue());
109 byte[] data = new byte[(int) bufferSize];
110 buf.get(data);
111 buf.position(pos);
112 if (logger.isTraceEnabled())
113 logger.trace("PAC_INFO_BUFFER describes type " + String.format("0x%08X", type)
114 + " with size " + bufferSize + " and offset " + offset + " containing data "
115 + Base64.getEncoder().encodeToString(data));
116
117 pacInfoBuffers.add(new PacInfoBuffer(type, bufferSize, offset, data));
118 }
119
120 zeroedPacData = Arrays.copyOf(pacDataBytes, pacDataBytes.length);
121
122 for (PacInfoBuffer pacInfoBuffer : pacInfoBuffers) {
123 long type = pacInfoBuffer.getType();
124 byte[] data = pacInfoBuffer.getData();
125 if (type == KERB_VALIDATION_INFO) {
126 if (kerbValidationInfo != null) {
127 if (logger.isTraceEnabled()) logger.trace("Ignoring additional KERB_VALIDATION_INFO structure");
128 } else {
129 if (logger.isTraceEnabled()) logger.trace("Parsing KERB_VALIDATION_INFO structure...");
130 kerbValidationInfo = new KerbValidationInfo(data);
131 }
132 } else if (type == UPN_DNS_INFO) {
133 if (upnDnsInfo != null) {
134 if (logger.isTraceEnabled()) logger.trace("Ignoring additional UPN_DNS_INFO structure");
135 } else {
136 if (logger.isTraceEnabled()) logger.trace("Parsing UPN_DNS_INFO structure...");
137 upnDnsInfo = new UpnDnsInfo(data);
138 }
139 } else if (type == PAC_CLIENT_INFO) {
140 if (pacClientInfo != null) {
141 if (logger.isTraceEnabled()) logger.trace("Ignoring additional PAC_CLIENT_INFO structure");
142 } else {
143 if (logger.isTraceEnabled()) logger.trace("Parsing PAC_CLIENT_INFO structure...");
144 pacClientInfo = new PacClientInfo(data);
145 }
146 } else if (type == SERVER_SIGNATURE) {
147 if (serverSignature != null) {
148 if (logger.isTraceEnabled())
149 logger.trace("Ignoring additional PAC_SIGNATURE_DATA (Server Signature) structure");
150 } else {
151 if (logger.isTraceEnabled())
152 logger.trace("Parsing PAC_SIGNATURE_DATA (Server Signature) structure...");
153 serverSignature = new PacSignatureData(data);
154 int from = pacInfoBuffer.getOffset().intValue() + 4;
155 int to = from + serverSignature.getType().getSize();
156 Arrays.fill(zeroedPacData, from, to, (byte) 0);
157 }
158 } else if (type == KDC_SIGNATURE) {
159 if (kdcSignature != null) {
160 if (logger.isTraceEnabled())
161 logger.trace("Ignoring additional PAC_SIGNATURE_DATA (KDC Signature) structure");
162 } else {
163 if (logger.isTraceEnabled())
164 logger.trace("Parsing PAC_SIGNATURE_DATA (KDC Signature) structure...");
165 kdcSignature = new PacSignatureData(data);
166 int from = pacInfoBuffer.getOffset().intValue() + 4;
167 int to = from + kdcSignature.getType().getSize();
168 Arrays.fill(zeroedPacData, from, to, (byte) 0);
169 }
170 } else {
171 if (logger.isTraceEnabled())
172 logger.trace("Ignoring unsupported structure type " + String.format("0x%08X", type) + " with data "
173 + Base64.getEncoder().encodeToString(data));
174 }
175 }
176
177 if (kerbValidationInfo == null)
178 throw new IllegalArgumentException("PAC does not contain required KERB_VALIDATION_INFO structure");
179
180 if (pacClientInfo == null)
181 throw new IllegalArgumentException("PAC does not contain required PAC_CLIENT_INFO structure");
182
183 if (serverSignature == null)
184 throw new IllegalArgumentException(
185 "PAC does not contain required PAC_SIGNATURE_DATA (Server Signature) structure");
186
187 if (kdcSignature == null)
188 throw new IllegalArgumentException(
189 "PAC does not contain required PAC_SIGNATURE_DATA (KDC Signature) structure");
190 }
191
192 public KerbValidationInfo getKerbValidationInfo() {
193 return kerbValidationInfo;
194 }
195
196 public UpnDnsInfo getUpnDnsInfo() {
197 return upnDnsInfo;
198 }
199
200 public PacClientInfo getPacClientInfo() {
201 return pacClientInfo;
202 }
203
204 public PacSignatureData getServerSignature() {
205 return serverSignature;
206 }
207
208 public PacSignatureData getKdcSignature() {
209 return kdcSignature;
210 }
211
212
213
214
215
216
217
218
219
220
221 public void verifySignature(Key[] keys) throws SignatureException {
222 signatureVerifier.verify(serverSignature, zeroedPacData, keys);
223 }
224 }