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 }