1   package uk.co.westhawk.snmp.stack;
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  import java.io.ByteArrayInputStream;
65  import java.io.IOException;
66  import java.io.InputStream;
67  
68  import uk.co.westhawk.snmp.util.SnmpUtilities;
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  class AsnDecoderv3 extends AsnDecoderBase implements usmStatsConstants {
79  	private static final String version_id = "@(#)$Id: AsnDecoderv3.java,v 3.9 2009/03/05 12:48:59 birgita Exp $ Copyright Westhawk Ltd";
80  
81  	
82  
83  
84  	int getMessageId(AsnSequence asnTopSeq) throws DecodingException {
85  		int msgId = -1;
86  		AsnSequence asnHeaderData = getAsnHeaderData(asnTopSeq);
87  		AsnObject obj = asnHeaderData.getObj(0);
88  		if (obj instanceof AsnInteger) {
89  			AsnInteger value = (AsnInteger) obj;
90  			msgId = value.getValue();
91  		} else {
92  			String msg = "msgId should be of type AsnInteger"
93  					+ " instead of " + obj.getRespTypeString();
94  			throw new DecodingException(msg);
95  		}
96  		return msgId;
97  	}
98  
99  	
100 
101 
102 
103 
104 
105 
106 
107 	AsnSequence DecodeSNMPv3(InputStream in)
108 			throws IOException, DecodingException {
109 		AsnSequence asnTopSeq = getAsnSequence(in);
110 		int snmpVersion = getSNMPVersion(asnTopSeq);
111 		if (snmpVersion != SnmpConstants.SNMP_VERSION_3) {
112 			String str = SnmpUtilities.getSnmpVersionString(snmpVersion);
113 			String msg = "Wrong SNMP version: expected SNMPv3, received "
114 					+ str;
115 			throw new DecodingException(msg);
116 		} else {
117 			int securityModel = -1;
118 			AsnSequence asnHeaderData = getAsnHeaderData(asnTopSeq);
119 			AsnObject obj = asnHeaderData.getObj(3);
120 			if (obj instanceof AsnInteger) {
121 				AsnInteger value = (AsnInteger) obj;
122 				securityModel = value.getValue();
123 				if (securityModel != SnmpContextv3Face.USM_Security_Model) {
124 					String msg = "Wrong v3 Security Model: expected USM("
125 							+ SnmpContextv3Face.USM_Security_Model
126 							+ "), received "
127 							+ securityModel;
128 					throw new DecodingException(msg);
129 				}
130 			} else {
131 				String msg = "securityModel should be of type AsnInteger"
132 						+ " instead of " + obj.getRespTypeString();
133 				throw new DecodingException(msg);
134 			}
135 		}
136 		return asnTopSeq;
137 	}
138 
139 	
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 	AsnPduSequence processSNMPv3(SnmpContextv3Basis context, AsnSequence asnTopSeq, byte[] message,
152 			boolean amIAuthoritative) throws IOException, DecodingException {
153 		AsnPduSequence pduSeq = null;
154 
155 		
156 		boolean isCorrect = asnTopSeq.isCorrect;
157 
158 		AsnSequence asnHeaderData = getAsnHeaderData(asnTopSeq);
159 		
160 		
161 		byte[] msgFlags = ((AsnOctets) asnHeaderData.getObj(2)).getBytes();
162 		boolean isUseAuthentication = isUseAuthentication(msgFlags[0]);
163 		boolean isUsePrivacy = isUsePrivacy(msgFlags[0]);
164 
165 		AsnOctets asnSecurityParameters = (AsnOctets) asnTopSeq.getObj(2);
166 		AsnSequence usmObject = decodeUSM(asnSecurityParameters);
167 
168 		byte[] engineIdBytes = ((AsnOctets) usmObject.getObj(0)).getBytes();
169 		String engineId = SnmpUtilities.toHexString(engineIdBytes);
170 		int boots = ((AsnInteger) usmObject.getObj(1)).getValue();
171 		int time = ((AsnInteger) usmObject.getObj(2)).getValue();
172 		String userName = ((AsnOctets) usmObject.getObj(3)).getValue();
173 		AsnOctets realFingerPrintObject = (AsnOctets) usmObject.getObj(4);
174 		byte[] realFingerPrint = realFingerPrintObject.getBytes();
175 		byte[] salt = ((AsnOctets) usmObject.getObj(5)).getBytes();
176 
177 		TimeWindow timeWindow = TimeWindow.getCurrent();
178 		if (amIAuthoritative == false) {
179 			
180 
181 
182 
183 			if (engineId.length() > 0
184 					&& timeWindow.isEngineIdOK(context.getReceivedFromHostAddress(), context.getPort(),
185 							engineId) == false) {
186 				String msg = "Received engine Id ('" + engineId + "') is not correct.";
187 				msg += " amIAuthoritative == false";
188 				throw new DecodingException(msg);
189 			} else {
190 				
191 				
192 				
193 				
194 				
195 				
196 				String sendToHostAddress = context.getSendToHostAddress();
197 				String receivedFromHostAddress = context.getReceivedFromHostAddress();
198 				if (sendToHostAddress.equals(receivedFromHostAddress) == false) {
199 					String storedEngineId;
200 					storedEngineId = timeWindow.getSnmpEngineId(sendToHostAddress, context.getPort());
201 					if (storedEngineId == null) {
202 						timeWindow.setSnmpEngineId(sendToHostAddress, context.getPort(), "00");
203 					}
204 				}
205 			}
206 		} else {
207 			
208 			
209 			
210 			if (engineId.length() > 0
211 					&& timeWindow.isEngineIdOK(context.getUsmAgent().MYFAKEHOSTNAME, context.getPort(),
212 							engineId) == false) {
213 				String msg = "Received engine Id ('" + engineId + "') is not correct.";
214 				msg += " amIAuthoritative == true";
215 				throw new DecodingException(msg);
216 			}
217 		}
218 
219 		if (userName.equals(context.getUserName()) == false) {
220 			String msg = "Received userName ('" + userName + "') is not correct";
221 			throw new DecodingException(msg);
222 		}
223 
224 		
225 		
226 		
227 		DecodingException encryptionDecodingException = null;
228 		IOException encryptionIOException = null;
229 		int authenticationProtocol = context.getAuthenticationProtocol();
230 		try {
231 			AsnObject asnScopedObject = asnTopSeq.getObj(3);
232 			AsnSequence asnPlainScopedPdu = null;
233 			if (isUsePrivacy == true) {
234 				int privacyProtocol = context.getPrivacyProtocol();
235 				
236 				byte[] privacyKey = context.generatePrivacyKey(engineId, authenticationProtocol, privacyProtocol);
237 
238 				AsnOctets asnEncryptedScopedPdu = (AsnOctets) asnScopedObject;
239 				byte[] encryptedText = asnEncryptedScopedPdu.getBytes();
240 
241 				byte[] plainText = null;
242 				if (SnmpContextv3Basis.AES_PRIVACY_PROTOCOLS.contains(privacyProtocol)){
243 					plainText = SnmpUtilities.AESdecrypt(encryptedText, privacyKey, boots, time, salt, privacyProtocol);
244 				} else {
245 					plainText = SnmpUtilities.DESdecrypt(encryptedText, salt, privacyKey);
246 				}
247 
248 				if (AsnObject.debug > 10) {
249 					System.out.println("Encrypted PDU: ");
250 					System.out.println("Decoding with : " + SnmpContextv3Basis.PROTOCOL_NAMES[privacyProtocol]);
251 				}
252 
253 				ByteArrayInputStream plainIn = new ByteArrayInputStream(plainText);
254 				asnPlainScopedPdu = getAsnSequence(plainIn);
255 			} else {
256 				asnPlainScopedPdu = (AsnSequence) asnScopedObject;
257 			}
258 			pduSeq = (AsnPduSequence) asnPlainScopedPdu.findPdu();
259 		} catch (DecodingException exc) {
260 			encryptionDecodingException = exc;
261 		} catch (IOException exc) {
262 			encryptionIOException = exc;
263 		}
264 		if (pduSeq != null && engineId.length() == 0) {
265 			pduSeq.setSnmpv3Discovery(true);
266 		}
267 
268 		boolean userIsUsingAuthentication = context.isUseAuthentication();
269 		if (isCorrect == true && (isUseAuthentication != userIsUsingAuthentication)) {
270 			String msg = "User " + userName + " does ";
271 			if (userIsUsingAuthentication == false) {
272 				msg += "not ";
273 			}
274 			msg += "support authentication, but received message ";
275 
276 			if (isUseAuthentication) {
277 				msg += "with authentication.";
278 			} else {
279 				msg += "without authentication";
280 				msg += getUsmStats(pduSeq);
281 			}
282 			throw new DecodingException(msg);
283 		}
284 
285 		boolean isAuthentic = false;
286 		if (isCorrect == true && isUseAuthentication == true) {
287 			int fpPos = realFingerPrintObject.getContentsPos();
288 			if (AsnObject.debug > 10) {
289 				int fpLength = realFingerPrintObject.getContentsLength();
290 				String str = "Pos finger print = " + fpPos + ", len = " + fpLength;
291 				SnmpUtilities.dumpBytes(str, realFingerPrint);
292 			}
293 
294 			byte[] computedFingerprint = null;
295 			
296 			byte[] dummyFingerPrint = SnmpUtilities.initFingerprint(authenticationProtocol);
297 			System.arraycopy(dummyFingerPrint, 0, message, fpPos, realFingerPrint.length);
298 
299 			
300 			computedFingerprint = context.computeFingerprint(engineId, authenticationProtocol, computedFingerprint,
301 					message);
302 
303 			if (SnmpUtilities.areBytesEqual(realFingerPrint, computedFingerprint) == false) {
304 				String msg = "Authentication comparison failed";
305 				throw new DecodingException(msg);
306 			} else {
307 				if (pduSeq != null && boots == 0 && time == 0) {
308 					pduSeq.setSnmpv3Discovery(true);
309 				}
310 				if (timeWindow.isOutsideTimeWindow(engineId, boots, time)) {
311 					String msg = "Message is outside time window";
312 					throw new DecodingException(msg);
313 				}
314 				isAuthentic = true;
315 			}
316 		}
317 		timeWindow.updateTimeWindow(engineId, boots, time, isAuthentic);
318 
319 		boolean userIsUsingPrivacy = context.isUsePrivacy();
320 		if (isCorrect == true && (isUsePrivacy != userIsUsingPrivacy)) {
321 			String msg = "User " + userName + " does ";
322 			if (userIsUsingPrivacy == false) {
323 				msg += "not ";
324 			}
325 			msg += "support privacy, but received message ";
326 			if (isUsePrivacy) {
327 				msg += "with privacy.";
328 			} else {
329 				msg += "without privacy";
330 				msg += getUsmStats(pduSeq);
331 			}
332 			throw new DecodingException(msg);
333 		}
334 
335 		if (encryptionDecodingException != null) {
336 			throw encryptionDecodingException;
337 		}
338 		if (encryptionIOException != null) {
339 			throw encryptionIOException;
340 		}
341 
342 		if (pduSeq != null && isCorrect == false) {
343 			pduSeq.isCorrect = false;
344 		}
345 		return pduSeq;
346 	}
347 
348 	private boolean isUseAuthentication(byte msgFlags) {
349 		boolean isUseAuthentication = ((byte) (0x01) & msgFlags) > 0;
350 		return isUseAuthentication;
351 	}
352 
353 	private boolean isUsePrivacy(byte msgFlags) {
354 		boolean isUsePrivacy = ((byte) (0x02) & msgFlags) > 0;
355 		return isUsePrivacy;
356 	}
357 
358 	private AsnSequence decodeUSM(AsnOctets asnSecurityParameters)
359 			throws IOException {
360 		byte[] usmBytes = asnSecurityParameters.getBytes();
361 		if (AsnObject.debug > 10) {
362 			SnmpUtilities.dumpBytes("Decoding USM:", usmBytes);
363 		}
364 
365 		ByteArrayInputStream usmIn = new ByteArrayInputStream(usmBytes);
366 		AsnSequence usmOctets = new AsnSequence(usmIn, usmBytes.length,
367 				asnSecurityParameters.getContentsPos());
368 		AsnSequence usmObject = (AsnSequence) usmOctets.getObj(0);
369 		return usmObject;
370 	}
371 
372 	
373 
374 
375 
376 	private String getUsmStats(AsnPduSequence pduSeq) {
377 		String msg = "";
378 		AsnSequence varBind = (AsnSequence) pduSeq.getObj(3);
379 		int size = varBind.getObjCount();
380 		if (size > 0) {
381 			AsnSequence varSeq = (AsnSequence) varBind.getObj(0);
382 			varbind vb = new varbind(varSeq);
383 			AsnObjectId oid = vb.getOid();
384 			boolean found = false;
385 			int i = 0;
386 			while (i < usmStatsOids.length && found == false) {
387 				AsnObjectId usmOid = new AsnObjectId(usmStatsOids[i]);
388 				found = (oid.startsWith(usmOid) == true);
389 				i++;
390 			}
391 			if (found == true) {
392 				i--;
393 				msg += ": " + usmStatsStrings[i] + " " + vb.getValue();
394 			} else {
395 				msg += ": " + vb;
396 			}
397 		}
398 		return msg;
399 	}
400 
401 }