mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-11 05:59:52 +00:00
8250968: Symlinks attributes not preserved when using jarsigner on zip files
Reviewed-by: lancea, weijun, hchao
This commit is contained in:
parent
8d6d43c33b
commit
7686e87155
@ -57,7 +57,7 @@ public class ZipEntry implements ZipConstants, Cloneable {
|
||||
int flag = 0; // general purpose flag
|
||||
byte[] extra; // optional extra field data for entry
|
||||
String comment; // optional comment string for entry
|
||||
int posixPerms = -1;// posix permissions
|
||||
int extraAttributes = -1; // e.g. POSIX permissions, sym links.
|
||||
/**
|
||||
* Compression method for uncompressed entries.
|
||||
*/
|
||||
@ -131,7 +131,7 @@ public class ZipEntry implements ZipConstants, Cloneable {
|
||||
flag = e.flag;
|
||||
extra = e.extra;
|
||||
comment = e.comment;
|
||||
posixPerms = e.posixPerms;
|
||||
extraAttributes = e.extraAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -658,8 +658,8 @@ public class ZipFile implements ZipConstants, Closeable {
|
||||
e.csize = CENSIZ(cen, pos);
|
||||
e.method = CENHOW(cen, pos);
|
||||
if (CENVEM_FA(cen, pos) == FILE_ATTRIBUTES_UNIX) {
|
||||
// 12 bits for setuid, setgid, sticky + perms
|
||||
e.posixPerms = CENATX_PERMS(cen, pos) & 0xFFF;
|
||||
// read all bits in this field, including sym link attributes
|
||||
e.extraAttributes = CENATX_PERMS(cen, pos) & 0xFFFF;
|
||||
}
|
||||
|
||||
if (elen != 0) {
|
||||
@ -1096,14 +1096,13 @@ public class ZipFile implements ZipConstants, Closeable {
|
||||
public Stream<String> entryNameStream(ZipFile zip) {
|
||||
return zip.entryNameStream();
|
||||
}
|
||||
// only set posix perms value via ZipEntry contructor for now
|
||||
@Override
|
||||
public int getPosixPerms(ZipEntry ze) {
|
||||
return ze.posixPerms;
|
||||
public int getExtraAttributes(ZipEntry ze) {
|
||||
return ze.extraAttributes;
|
||||
}
|
||||
@Override
|
||||
public void setPosixPerms(ZipEntry ze, int perms) {
|
||||
ze.posixPerms = perms;
|
||||
public void setExtraAttributes(ZipEntry ze, int extraAttrs) {
|
||||
ze.extraAttributes = extraAttrs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. 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
|
||||
@ -511,7 +511,7 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
|
||||
* to a version value.
|
||||
*/
|
||||
private int versionMadeBy(ZipEntry e, int version) {
|
||||
return (e.posixPerms < 0) ? version :
|
||||
return (e.extraAttributes < 0) ? version :
|
||||
VERSION_MADE_BY_BASE_UNIX | (version & 0xff);
|
||||
}
|
||||
|
||||
@ -606,8 +606,8 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
|
||||
}
|
||||
writeShort(0); // starting disk number
|
||||
writeShort(0); // internal file attributes (unused)
|
||||
// external file attributes, used for storing posix permissions
|
||||
writeInt(e.posixPerms > 0 ? e.posixPerms << 16 : 0);
|
||||
// extra file attributes, used for storing posix permissions etc.
|
||||
writeInt(e.extraAttributes > 0 ? e.extraAttributes << 16 : 0);
|
||||
writeInt(offset); // relative offset of local header
|
||||
writeBytes(nameBytes, 0, nameBytes.length);
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ public interface JavaUtilZipFileAccess {
|
||||
public Enumeration<JarEntry> entries(ZipFile zip);
|
||||
public Stream<JarEntry> stream(ZipFile zip);
|
||||
public Stream<String> entryNameStream(ZipFile zip);
|
||||
public void setPosixPerms(ZipEntry ze, int posixPerms);
|
||||
public int getPosixPerms(ZipEntry ze);
|
||||
public void setExtraAttributes(ZipEntry ze, int extraAttrs);
|
||||
public int getExtraAttributes(ZipEntry ze);
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ public final class Event {
|
||||
|
||||
public enum ReporterCategory {
|
||||
CRLCHECK(),
|
||||
POSIXPERMS();
|
||||
ZIPFILEATTRS();
|
||||
|
||||
private Reporter reporter;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. 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
|
||||
@ -505,7 +505,7 @@ public final class JarSigner {
|
||||
private final boolean externalSF; // leave the .SF out of the PKCS7 block
|
||||
private final String altSignerPath;
|
||||
private final String altSigner;
|
||||
private boolean posixPermsDetected;
|
||||
private boolean extraAttrsDetected;
|
||||
|
||||
private JarSigner(JarSigner.Builder builder) {
|
||||
|
||||
@ -949,12 +949,12 @@ public final class JarSigner {
|
||||
ze2.setTime(ze.getTime());
|
||||
ze2.setComment(ze.getComment());
|
||||
ze2.setExtra(ze.getExtra());
|
||||
int perms = JUZFA.getPosixPerms(ze);
|
||||
if (!posixPermsDetected && perms != -1) {
|
||||
posixPermsDetected = true;
|
||||
Event.report(Event.ReporterCategory.POSIXPERMS, "detected");
|
||||
int extraAttrs = JUZFA.getExtraAttributes(ze);
|
||||
if (!extraAttrsDetected && extraAttrs != -1) {
|
||||
extraAttrsDetected = true;
|
||||
Event.report(Event.ReporterCategory.ZIPFILEATTRS, "detected");
|
||||
}
|
||||
JUZFA.setPosixPerms(ze2, perms);
|
||||
JUZFA.setExtraAttributes(ze2, extraAttrs);
|
||||
if (ze.getMethod() == ZipEntry.STORED) {
|
||||
ze2.setSize(ze.getSize());
|
||||
ze2.setCrc(ze.getCrc());
|
||||
|
||||
@ -110,7 +110,7 @@ public class Main {
|
||||
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
|
||||
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
|
||||
|
||||
private static boolean permsDetected;
|
||||
private static boolean extraAttrsDetected;
|
||||
|
||||
static final String VERSION = "1.0";
|
||||
|
||||
@ -782,8 +782,8 @@ public class Main {
|
||||
JarEntry je = e.nextElement();
|
||||
String name = je.getName();
|
||||
|
||||
if (!permsDetected && JUZFA.getPosixPerms(je) != -1) {
|
||||
permsDetected = true;
|
||||
if (!extraAttrsDetected && JUZFA.getExtraAttributes(je) != -1) {
|
||||
extraAttrsDetected = true;
|
||||
}
|
||||
hasSignature = hasSignature
|
||||
|| SignatureFileVerifier.isBlockOrSF(name);
|
||||
@ -1247,8 +1247,8 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
if (permsDetected) {
|
||||
warnings.add(rb.getString("posix.attributes.detected"));
|
||||
if (extraAttrsDetected) {
|
||||
warnings.add(rb.getString("extra.attributes.detected"));
|
||||
}
|
||||
|
||||
if ((strict) && (!errors.isEmpty())) {
|
||||
@ -1777,8 +1777,8 @@ public class Main {
|
||||
String failedMessage = null;
|
||||
|
||||
try {
|
||||
Event.setReportListener(Event.ReporterCategory.POSIXPERMS,
|
||||
(t, o) -> permsDetected = true);
|
||||
Event.setReportListener(Event.ReporterCategory.ZIPFILEATTRS,
|
||||
(t, o) -> extraAttrsDetected = true);
|
||||
builder.build().sign(zipFile, fos);
|
||||
} catch (JarSignerException e) {
|
||||
failedCause = e.getCause();
|
||||
@ -1813,7 +1813,7 @@ public class Main {
|
||||
fos.close();
|
||||
}
|
||||
|
||||
Event.clearReportListener(Event.ReporterCategory.POSIXPERMS);
|
||||
Event.clearReportListener(Event.ReporterCategory.ZIPFILEATTRS);
|
||||
}
|
||||
|
||||
if (failedCause != null) {
|
||||
|
||||
@ -170,7 +170,7 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
{"key.bit.weak", "%d-bit key (weak)"},
|
||||
{"key.bit.disabled", "%d-bit key (disabled)"},
|
||||
{"unknown.size", "unknown size"},
|
||||
{"posix.attributes.detected", "POSIX file permission attributes detected. These attributes are ignored when signing and are not protected by the signature."},
|
||||
{"extra.attributes.detected", "POSIX file permission and/or symlink attributes detected. These attributes are ignored when signing and are not protected by the signature."},
|
||||
|
||||
{"jarsigner.", "jarsigner: "},
|
||||
{"signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.",
|
||||
|
||||
@ -72,8 +72,9 @@ public class PosixPermissionsTest {
|
||||
private static int count;
|
||||
private static Set<PosixFilePermission> permsSet;
|
||||
private static String expectedJarPerms;
|
||||
private static final String POSIXWARNING = "POSIX file permission attributes detected. " +
|
||||
"These attributes are ignored when signing and are not protected by the signature.";
|
||||
private static final String WARNING_MSG = "POSIX file permission and/or symlink " +
|
||||
"attributes detected. These attributes are ignored when signing and are not " +
|
||||
"protected by the signature.";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
|
||||
@ -104,7 +105,7 @@ public class PosixPermissionsTest {
|
||||
"-keypass", "password",
|
||||
"examplekey")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain(POSIXWARNING);
|
||||
.shouldContain(WARNING_MSG);
|
||||
|
||||
// zip file now signed. Recheck file permissions
|
||||
verifyFilePermissions(ZIPURI, true);
|
||||
@ -116,7 +117,7 @@ public class PosixPermissionsTest {
|
||||
"-keypass", "password",
|
||||
"examplekey")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldNotContain(POSIXWARNING);
|
||||
.shouldNotContain(WARNING_MSG);
|
||||
|
||||
// default attributes expected
|
||||
verifyFilePermissions(JARURI, false);
|
||||
@ -127,7 +128,7 @@ public class PosixPermissionsTest {
|
||||
"-verbose",
|
||||
"-verify", ZIPFILENAME)
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain(POSIXWARNING);
|
||||
.shouldContain(WARNING_MSG);
|
||||
|
||||
// no warning expected for regular jar file
|
||||
SecurityTools.jarsigner("-keystore", "examplekeystore",
|
||||
@ -136,7 +137,7 @@ public class PosixPermissionsTest {
|
||||
"-verbose",
|
||||
"-verify", JARFILENAME)
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldNotContain(POSIXWARNING);
|
||||
.shouldNotContain(WARNING_MSG);
|
||||
}
|
||||
|
||||
private static void createFiles() throws Exception {
|
||||
|
||||
161
test/jdk/sun/security/tools/jarsigner/SymLinkTest.java
Normal file
161
test/jdk/sun/security/tools/jarsigner/SymLinkTest.java
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8250968
|
||||
* @summary Symlinks attributes not preserved when using jarsigner on zip files
|
||||
* @modules jdk.jartool/sun.security.tools.jarsigner
|
||||
* java.base/sun.security.tools.keytool
|
||||
* @library /test/lib
|
||||
* @run main/othervm SymLinkTest
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.nio.file.*;
|
||||
import java.util.Formatter;
|
||||
|
||||
import jdk.test.lib.SecurityTools;
|
||||
|
||||
public class SymLinkTest {
|
||||
private final static String ZIPFILENAME = "8250968-test.zip";
|
||||
private static final String WARNING_MSG = "POSIX file permission and/or symlink " +
|
||||
"attributes detected. These attributes are ignored when signing and are not " +
|
||||
"protected by the signature.";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Files.deleteIfExists(Paths.get(ZIPFILENAME));
|
||||
try (FileOutputStream fos = new FileOutputStream(ZIPFILENAME)) {
|
||||
fos.write(ZIPBYTES);
|
||||
}
|
||||
|
||||
// check permissions before signing
|
||||
verifyExtraAttrs(ZIPFILENAME);
|
||||
|
||||
SecurityTools.keytool(
|
||||
"-genkey",
|
||||
"-keyalg", "RSA",
|
||||
"-dname", "CN=Coffey, OU=JPG, O=Oracle, L=Santa Clara, ST=California, C=US",
|
||||
"-alias", "examplekey",
|
||||
"-storepass", "password",
|
||||
"-keypass", "password",
|
||||
"-keystore", "examplekeystore",
|
||||
"-validity", "365")
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
SecurityTools.jarsigner(
|
||||
"-keystore", "examplekeystore",
|
||||
"-verbose", ZIPFILENAME,
|
||||
"-storepass", "password",
|
||||
"-keypass", "password",
|
||||
"examplekey")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain(WARNING_MSG);
|
||||
|
||||
// zip file now signed. Recheck attributes
|
||||
verifyExtraAttrs(ZIPFILENAME);
|
||||
|
||||
SecurityTools.jarsigner("-keystore", "examplekeystore",
|
||||
"-storepass", "password",
|
||||
"-keypass", "password",
|
||||
"-verbose",
|
||||
"-verify", ZIPFILENAME)
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain(WARNING_MSG);
|
||||
}
|
||||
|
||||
private static void verifyExtraAttrs(String zipFileName) throws IOException {
|
||||
// the 16 bit extra attributes value should equal 0xa1ff - look for that pattern.
|
||||
// Such values can be read from zip file via 'unzip -Z -l -v <zipfile>'
|
||||
try (FileInputStream fis = new FileInputStream(ZIPFILENAME)) {
|
||||
byte[] b = fis.readAllBytes();
|
||||
boolean patternFound;
|
||||
for (int i = 0; i < b.length -1; i++) {
|
||||
patternFound = ((b[i] & 0xFF) == 0xFF) && ((b[i + 1] & 0xFF) == 0xA1);
|
||||
if (patternFound) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("extra attribute value not detected");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method which takes an byte array and converts to byte array
|
||||
* declaration. For example:
|
||||
* <pre>
|
||||
* {@code
|
||||
* var fooJar = Files.readAllBytes(Path.of("foo.jar"));
|
||||
* var result = createByteArray(fooJar, "FOOBYTES");
|
||||
* }
|
||||
* </pre>
|
||||
* @param bytes A byte array used to create a byte array declaration
|
||||
* @param name Name to be used in the byte array declaration
|
||||
* @return The formatted byte array declaration
|
||||
*/
|
||||
public static String createByteArray(byte[] bytes, String name) {
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 5);
|
||||
Formatter fmt = new Formatter(sb);
|
||||
fmt.format(" public static byte[] %s = {", name);
|
||||
final int linelen = 8;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
if (i % linelen == 0) {
|
||||
fmt.format("%n ");
|
||||
}
|
||||
fmt.format(" (byte) 0x%x,", bytes[i] & 0xff);
|
||||
}
|
||||
fmt.format("%n };%n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Created using the createByteArray utility method.
|
||||
* The zipfile itself was created via this example:
|
||||
* $ ls -l z
|
||||
* lrwxrwxrwx 1 test test 4 Aug 27 18:33 z -> ../z
|
||||
* $ zip -ry test.zip z
|
||||
*/
|
||||
public final static byte[] ZIPBYTES = {
|
||||
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0xa, (byte) 0x0, (byte) 0x0, (byte) 0x0,
|
||||
(byte) 0x0, (byte) 0x0, (byte) 0x2e, (byte) 0x94, (byte) 0x1b, (byte) 0x51, (byte) 0xb4, (byte) 0xcc,
|
||||
(byte) 0xb6, (byte) 0xf1, (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x4, (byte) 0x0,
|
||||
(byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x1c, (byte) 0x0, (byte) 0x7a, (byte) 0x55,
|
||||
(byte) 0x54, (byte) 0x9, (byte) 0x0, (byte) 0x3, (byte) 0x77, (byte) 0xfc, (byte) 0x47, (byte) 0x5f,
|
||||
(byte) 0x78, (byte) 0xfc, (byte) 0x47, (byte) 0x5f, (byte) 0x75, (byte) 0x78, (byte) 0xb, (byte) 0x0,
|
||||
(byte) 0x1, (byte) 0x4, (byte) 0xec, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x4, (byte) 0xec,
|
||||
(byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x2e, (byte) 0x2e, (byte) 0x2f, (byte) 0x7a, (byte) 0x50,
|
||||
(byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x1e, (byte) 0x3, (byte) 0xa, (byte) 0x0, (byte) 0x0,
|
||||
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2e, (byte) 0x94, (byte) 0x1b, (byte) 0x51, (byte) 0xb4,
|
||||
(byte) 0xcc, (byte) 0xb6, (byte) 0xf1, (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x4,
|
||||
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x18, (byte) 0x0, (byte) 0x0,
|
||||
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xff,
|
||||
(byte) 0xa1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x7a, (byte) 0x55, (byte) 0x54,
|
||||
(byte) 0x5, (byte) 0x0, (byte) 0x3, (byte) 0x77, (byte) 0xfc, (byte) 0x47, (byte) 0x5f, (byte) 0x75,
|
||||
(byte) 0x78, (byte) 0xb, (byte) 0x0, (byte) 0x1, (byte) 0x4, (byte) 0xec, (byte) 0x3, (byte) 0x0,
|
||||
(byte) 0x0, (byte) 0x4, (byte) 0xec, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b,
|
||||
(byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0,
|
||||
(byte) 0x1, (byte) 0x0, (byte) 0x47, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3f, (byte) 0x0,
|
||||
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user