6948909: Jarsigner removes MANIFEST.MF info for badly packages jar's

Reviewed-by: mullan, xuelei
This commit is contained in:
Weijun Wang 2010-05-06 11:26:16 +08:00
parent e3e5b8ad72
commit a94d06f6b7
2 changed files with 172 additions and 40 deletions
jdk
src/share/classes/sun/security/tools
test/sun/security/tools/jarsigner

@ -1,5 +1,5 @@
/*
* Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1997-2010 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1123,6 +1123,8 @@ public class JarSigner {
BASE64Encoder encoder = new JarBASE64Encoder();
Vector<ZipEntry> mfFiles = new Vector<ZipEntry>();
boolean wasSigned = false;
for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
enum_.hasMoreElements();) {
ZipEntry ze = enum_.nextElement();
@ -1132,6 +1134,11 @@ public class JarSigner {
// out first
mfFiles.addElement(ze);
if (SignatureFileVerifier.isBlockOrSF(
ze.getName().toUpperCase(Locale.ENGLISH))) {
wasSigned = true;
}
if (signatureRelated(ze.getName())) {
// ignore signature-related and manifest files
continue;
@ -1159,37 +1166,41 @@ public class JarSigner {
if (mfModified) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
manifest.write(baos);
byte[] newBytes = baos.toByteArray();
if (mfRawBytes != null
&& oldAttr.equals(manifest.getMainAttributes())) {
if (wasSigned) {
byte[] newBytes = baos.toByteArray();
if (mfRawBytes != null
&& oldAttr.equals(manifest.getMainAttributes())) {
/*
* Note:
*
* The Attributes object is based on HashMap and can handle
* continuation columns. Therefore, even if the contents are
* not changed (in a Map view), the bytes that it write()
* may be different from the original bytes that it read()
* from. Since the signature on the main attributes is based
* on raw bytes, we must retain the exact bytes.
*/
/*
* Note:
*
* The Attributes object is based on HashMap and can handle
* continuation columns. Therefore, even if the contents are
* not changed (in a Map view), the bytes that it write()
* may be different from the original bytes that it read()
* from. Since the signature on the main attributes is based
* on raw bytes, we must retain the exact bytes.
*/
int newPos = findHeaderEnd(newBytes);
int oldPos = findHeaderEnd(mfRawBytes);
int newPos = findHeaderEnd(newBytes);
int oldPos = findHeaderEnd(mfRawBytes);
if (newPos == oldPos) {
System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
} else {
// cat oldHead newTail > newBytes
byte[] lastBytes = new byte[oldPos +
newBytes.length - newPos];
System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
System.arraycopy(newBytes, newPos, lastBytes, oldPos,
newBytes.length - newPos);
newBytes = lastBytes;
if (newPos == oldPos) {
System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
} else {
// cat oldHead newTail > newBytes
byte[] lastBytes = new byte[oldPos +
newBytes.length - newPos];
System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
System.arraycopy(newBytes, newPos, lastBytes, oldPos,
newBytes.length - newPos);
newBytes = lastBytes;
}
}
mfRawBytes = newBytes;
} else {
mfRawBytes = baos.toByteArray();
}
mfRawBytes = newBytes;
}
// Write out the manifest
@ -1411,23 +1422,31 @@ public class JarSigner {
}
/**
* Find the position of an empty line inside bs
* Find the length of header inside bs. The header is a multiple (>=0)
* lines of attributes plus an empty line. The empty line is included
* in the header.
*/
private int findHeaderEnd(byte[] bs) {
// An empty line can be at the beginning...
if (bs.length > 1 && bs[0] == '\r' && bs[1] == '\n') {
return 0;
}
// ... or after another line
for (int i=0; i<bs.length-3; i++) {
if (bs[i] == '\r' && bs[i+1] == '\n' &&
bs[i+2] == '\r' && bs[i+3] == '\n') {
return i;
// Initial state true to deal with empty header
boolean newline = true; // just met a newline
int len = bs.length;
for (int i=0; i<len; i++) {
switch (bs[i]) {
case '\r':
if (i < len && bs[i+1] == '\n') i++;
// fallthrough
case '\n':
if (newline) return i+1; //+1 to get length
newline = true;
break;
default:
newline = false;
}
}
// If header end is not found, return 0,
// which means no behavior change.
return 0;
// If header end is not found, it means the MANIFEST.MF has only
// the main attributes section and it does not end with 2 newlines.
// Returns the whole length so that it can be completely replaced.
return len;
}
/**

@ -0,0 +1,113 @@
#
# Copyright 2010 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
# @test
# @bug 6948909
# @summary Jarsigner removes MANIFEST.MF info for badly packages jar's
#
if [ "${TESTSRC}" = "" ] ; then
TESTSRC="."
fi
if [ "${TESTCLASSES}" = "" ] ; then
TESTCLASSES="."
fi
if [ "${TESTJAVA}" = "" ] ; then
echo "TESTJAVA not set. Test cannot execute."
echo "FAILED!!!"
exit 1
fi
# set platform-dependent variables
OS=`uname -s`
case "$OS" in
SunOS | Linux )
NULL=/dev/null
PS=":"
FS="/"
CP="${FS}bin${FS}cp -f"
TMP=/tmp
;;
CYGWIN* )
NULL=/dev/null
PS=";"
FS="/"
CP="cp -f"
TMP=/tmp
;;
Windows_* )
NULL=NUL
PS=";"
FS="\\"
CP="cp -f"
TMP="c:/temp"
;;
* )
echo "Unrecognized operating system!"
exit 1;
;;
esac
echo 1 > 1
mkdir META-INF
# Create a fake .RSA file so that jarsigner believes it's signed
touch META-INF/x.RSA
# A MANIFEST.MF using \n as newlines and no double newlines at the end
cat > META-INF/MANIFEST.MF <<EOF
Manifest-Version: 1.0
Created-By: 1.7.0-internal (Sun Microsystems Inc.)
Today: Monday
EOF
# With the fake .RSA file, to trigger the if (wasSigned) block
rm diffend.jar
zip diffend.jar META-INF/MANIFEST.MF META-INF/x.RSA 1
${TESTJAVA}${FS}bin${FS}jarsigner \
-keystore ${TESTSRC}${FS}JarSigning.keystore \
-storepass bbbbbb \
-digestalg SHA1 \
-signedjar diffend.new.jar \
diffend.jar c
unzip -p diffend.new.jar META-INF/MANIFEST.MF | grep Today || exit 1
# Without the fake .RSA file, to trigger the else block
rm diffend.jar
zip diffend.jar META-INF/MANIFEST.MF 1
${TESTJAVA}${FS}bin${FS}jarsigner \
-keystore ${TESTSRC}${FS}JarSigning.keystore \
-storepass bbbbbb \
-digestalg SHA1 \
-signedjar diffend.new.jar \
diffend.jar c
unzip -p diffend.new.jar META-INF/MANIFEST.MF | grep Today || exit 2