001: package jcifs.smb;
002:
003: import java.security.MessageDigest;
004: import java.security.NoSuchAlgorithmException;
005: import java.io.IOException;
006: import jcifs.Config;
007: import jcifs.util.LogStream;
008: import jcifs.util.Hexdump;
009:
010: /**
011: * To filter 0 len updates and for debugging
012: */
013:
014: public class SigningDigest {
015:
016: private static final int LM_COMPATIBILITY = Config.getInt(
017: "jcifs.smb.lmCompatibility", 0);
018:
019: static LogStream log = LogStream.getInstance();
020:
021: private MessageDigest digest;
022: private byte[] macSigningKey;
023: private int updates;
024: private int signSequence;
025:
026: public SigningDigest(SmbTransport transport,
027: NtlmPasswordAuthentication auth) throws SmbException {
028: try {
029: digest = MessageDigest.getInstance("MD5");
030: } catch (NoSuchAlgorithmException ex) {
031: if (log.level > 0)
032: ex.printStackTrace(log);
033: throw new SmbException("MD5", ex);
034: }
035:
036: try {
037: switch (LM_COMPATIBILITY) {
038: case 0:
039: case 1:
040: case 2:
041: macSigningKey = new byte[40];
042: auth.getUserSessionKey(transport.server.encryptionKey,
043: macSigningKey, 0);
044: System
045: .arraycopy(
046: auth
047: .getUnicodeHash(transport.server.encryptionKey),
048: 0, macSigningKey, 16, 24);
049: break;
050: case 3:
051: case 4:
052: case 5:
053: macSigningKey = new byte[16];
054: auth.getUserSessionKey(transport.server.encryptionKey,
055: macSigningKey, 0);
056: break;
057: default:
058: macSigningKey = new byte[40];
059: auth.getUserSessionKey(transport.server.encryptionKey,
060: macSigningKey, 0);
061: System
062: .arraycopy(
063: auth
064: .getUnicodeHash(transport.server.encryptionKey),
065: 0, macSigningKey, 16, 24);
066: break;
067: }
068: } catch (Exception ex) {
069: throw new SmbException("", ex);
070: }
071: if (log.level > 3) {
072: log.println("LM_COMPATIBILITY=" + LM_COMPATIBILITY);
073: Hexdump
074: .hexdump(log, macSigningKey, 0,
075: macSigningKey.length);
076: }
077: }
078:
079: public void update(byte[] input, int offset, int len) {
080: if (log.level > 3) {
081: log
082: .println("update: " + updates + " " + offset + ":"
083: + len);
084: Hexdump.hexdump(log, input, offset, Math.min(len, 256));
085: log.flush();
086: }
087: if (len == 0) {
088: return; /* CRITICAL */
089: }
090: digest.update(input, offset, len);
091: updates++;
092: }
093:
094: public byte[] digest() {
095: byte[] b;
096:
097: b = digest.digest();
098:
099: if (log.level > 3) {
100: log.println("digest: ");
101: Hexdump.hexdump(log, b, 0, b.length);
102: log.flush();
103: }
104: updates = 0;
105:
106: return b;
107: }
108:
109: /**
110: * Performs MAC signing of the SMB. This is done as follows.
111: * The signature field of the SMB is overwritted with the sequence number;
112: * The MD5 digest of the MAC signing key + the entire SMB is taken;
113: * The first 8 bytes of this are placed in the signature field.
114: *
115: * @param data The data.
116: * @param offset The starting offset at which the SMB header begins.
117: * @param length The length of the SMB data starting at offset.
118: */
119: void sign(byte[] data, int offset, int length,
120: ServerMessageBlock request, ServerMessageBlock response) {
121: request.signSeq = signSequence;
122: if (response != null) {
123: response.signSeq = signSequence + 1;
124: response.verifyFailed = false;
125: }
126:
127: try {
128: update(macSigningKey, 0, macSigningKey.length);
129: int index = offset + ServerMessageBlock.SIGNATURE_OFFSET;
130: for (int i = 0; i < 8; i++)
131: data[index + i] = 0;
132: ServerMessageBlock.writeInt4(signSequence, data, index);
133: update(data, offset, length);
134: System.arraycopy(digest(), 0, data, index, 8);
135: } catch (Exception ex) {
136: if (log.level > 0)
137: ex.printStackTrace(log);
138: } finally {
139: signSequence += 2;
140: }
141: }
142:
143: /**
144: * Performs MAC signature verification. This calculates the signature
145: * of the SMB and compares it to the signature field on the SMB itself.
146: *
147: * @param data The data.
148: * @param offset The starting offset at which the SMB header begins.
149: * @param length The length of the SMB data starting at offset.
150: */
151: boolean verify(byte[] data, int offset, ServerMessageBlock response) {
152: update(macSigningKey, 0, macSigningKey.length);
153: int index = offset;
154: update(data, index, ServerMessageBlock.SIGNATURE_OFFSET);
155: index += ServerMessageBlock.SIGNATURE_OFFSET;
156: byte[] sequence = new byte[8];
157: ServerMessageBlock.writeInt4(response.signSeq, sequence, 0);
158: update(sequence, 0, sequence.length);
159: index += 8;
160: if (response.command == ServerMessageBlock.SMB_COM_READ_ANDX) {
161: /* SmbComReadAndXResponse reads directly from the stream into separate byte[] b.
162: */
163: SmbComReadAndXResponse raxr = (SmbComReadAndXResponse) response;
164: int length = response.length - raxr.dataLength;
165: update(data, index, length
166: - ServerMessageBlock.SIGNATURE_OFFSET - 8);
167: update(raxr.b, raxr.off, raxr.dataLength);
168: } else {
169: update(data, index, response.length
170: - ServerMessageBlock.SIGNATURE_OFFSET - 8);
171: }
172: byte[] signature = digest();
173: for (int i = 0; i < 8; i++) {
174: if (signature[i] != data[offset
175: + ServerMessageBlock.SIGNATURE_OFFSET + i]) {
176: if (log.level > 2) {
177: log.println("signature verification failure");
178: Hexdump.hexdump(log, signature, 0, 8);
179: Hexdump.hexdump(log, data, offset
180: + ServerMessageBlock.SIGNATURE_OFFSET, 8);
181: }
182: return response.verifyFailed = true;
183: }
184: }
185:
186: return response.verifyFailed = false;
187: }
188: }
|