Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
8218021: Have jarsigner preserve posix permission attributes
Reviewed-by: weijun, lancea, alanb
  • Loading branch information
coffeys committed Jul 2, 2020
1 parent dc63bf2 commit 3d9bad16d1c081a9c860d90ddbb8c40d8457ed90
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 17 deletions.
@@ -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
/**
* Compression method for uncompressed entries.
*/
@@ -131,6 +131,7 @@ public ZipEntry(ZipEntry e) {
flag = e.flag;
extra = e.extra;
comment = e.comment;
posixPerms = e.posixPerms;
}

/**
@@ -657,6 +657,11 @@ private ZipEntry getZipEntry(String name, int pos) {
e.size = CENLEN(cen, pos);
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;
}

if (elen != 0) {
int start = pos + CENHDR + nlen;
e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true, false);
@@ -1092,6 +1097,16 @@ public Stream<JarEntry> stream(ZipFile zip) {
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;
}
@Override
public void setPosixPerms(ZipEntry ze, int perms) {
ze.posixPerms = perms;
}

}
);
isWindows = VM.getSavedProperty("os.name").contains("Windows");
@@ -506,6 +506,15 @@ private void writeEXT(ZipEntry e) throws IOException {
}
}

/**
* Adds information about compatibility of file attribute information
* to a version value.
*/
private int versionMadeBy(ZipEntry e, int version) {
return (e.posixPerms < 0) ? version :
VERSION_MADE_BY_BASE_UNIX | (version & 0xff);
}

/*
* Write central directory (CEN) header for specified entry.
* REMIND: add support for file attributes
@@ -537,10 +546,10 @@ private void writeCEN(XEntry xentry) throws IOException {
}
writeInt(CENSIG); // CEN header signature
if (hasZip64) {
writeShort(45); // ver 4.5 for zip64
writeShort(versionMadeBy(e,45)); // ver 4.5 for zip64
writeShort(45);
} else {
writeShort(version); // version made by
writeShort(versionMadeBy(e, version)); // version made by
writeShort(version); // version needed to extract
}
writeShort(flag); // general purpose bit flag
@@ -597,7 +606,8 @@ private void writeCEN(XEntry xentry) throws IOException {
}
writeShort(0); // starting disk number
writeShort(0); // internal file attributes (unused)
writeInt(0); // external file attributes (unused)
// external file attributes, used for storing posix permissions
writeInt(e.posixPerms > 0 ? e.posixPerms << 16 : 0);
writeInt(offset); // relative offset of local header
writeBytes(nameBytes, 0, nameBytes.length);

@@ -215,6 +215,17 @@ static final long GETSIG(byte[] b) {
return LG(b, 0);
}

/*
* File attribute compatibility types of CEN field "version made by"
*/
static final int FILE_ATTRIBUTES_UNIX = 3; // Unix

/*
* Base values for CEN field "version made by"
*/
static final int VERSION_MADE_BY_BASE_UNIX = FILE_ATTRIBUTES_UNIX << 8; // Unix


// local file (LOC) header fields
static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature
static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract
@@ -250,6 +261,7 @@ static final long GETSIG(byte[] b) {
// central directory header (CEN) fields
static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); }
static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); }
static final int CENVEM_FA(byte[] b, int pos) { return CH(b, pos + 5); } // file attribute compatibility
static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); }
static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); }
static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);}
@@ -263,6 +275,7 @@ static final long GETSIG(byte[] b) {
static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);}
static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);}
static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);}
static final int CENATX_PERMS(byte[] b, int pos) { return SH(b, pos + 40);} // posix permission data
static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);}

// The END header is followed by a variable length comment of size < 64k.
@@ -30,6 +30,7 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public interface JavaUtilZipFileAccess {
@@ -40,5 +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);
}

@@ -149,6 +149,7 @@
java.management,
java.naming,
java.rmi,
jdk.jartool,
jdk.jlink,
jdk.net,
jdk.incubator.foreign;
@@ -248,7 +248,7 @@ private static X509CRL getCRL(URIName name) throws CertStoreException {
debug.println("Trying to fetch CRL from DP " + uri);
}

Event.report("event.crl.check", uri.toString());
Event.report(Event.ReporterCategory.CRLCHECK, "event.crl.check", uri.toString());
CertStore ucs = null;
try {
ucs = URICertStore.getInstance(new URICertStoreParameters(uri));
@@ -234,7 +234,7 @@ public static byte[] getOCSPBytes(List<CertId> certIds, URI responderURI,
debug.println("connecting to OCSP service at: " + url);
}

Event.report("event.ocsp.check", url.toString());
Event.report(Event.ReporterCategory.CRLCHECK, "event.ocsp.check", url.toString());
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(CONNECT_TIMEOUT);
con.setReadTimeout(CONNECT_TIMEOUT);
@@ -35,21 +35,27 @@
public final class Event {
private Event() {}

public enum ReporterCategory {
CRLCHECK(),
POSIXPERMS();

private Reporter reporter;
}

public interface Reporter {
public void handle(String type, Object... args);
}

private static Reporter reporter;
public static void setReportListener(Reporter re) {
reporter = re;
public static void setReportListener(ReporterCategory cat, Reporter re) {
cat.reporter = re;
}

public static void clearReportListener() {
reporter = null;
public static void clearReportListener(ReporterCategory cat) {
cat.reporter = null;
}

public static void report(String type, Object... args) {
Reporter currentReporter = reporter;
public static void report(ReporterCategory cat, String type, Object... args) {
Reporter currentReporter = cat.reporter;

if (currentReporter != null) {
currentReporter.handle(type, args);
@@ -27,8 +27,11 @@

import com.sun.jarsigner.ContentSigner;
import com.sun.jarsigner.ContentSignerParameters;
import jdk.internal.access.JavaUtilZipFileAccess;
import jdk.internal.access.SharedSecrets;
import sun.security.tools.PathList;
import sun.security.tools.jarsigner.TimestampedSigner;
import sun.security.util.Event;
import sun.security.util.ManifestDigester;
import sun.security.util.SignatureFileVerifier;
import sun.security.x509.AlgorithmId;
@@ -82,6 +85,8 @@
*/
public final class JarSigner {

static final JavaUtilZipFileAccess JUZFA = SharedSecrets.getJavaUtilZipFileAccess();

/**
* A mutable builder class that can create an immutable {@code JarSigner}
* from various signing-related parameters.
@@ -500,6 +505,7 @@ public JarSigner build() {
private final boolean externalSF; // leave the .SF out of the PKCS7 block
private final String altSignerPath;
private final String altSigner;
private boolean posixPermsDetected;

private JarSigner(JarSigner.Builder builder) {

@@ -943,6 +949,12 @@ private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
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");
}
JUZFA.setPosixPerms(ze2, perms);
if (ze.getMethod() == ZipEntry.STORED) {
ze2.setSize(ze.getSize());
ze2.setCrc(ze.getCrc());
@@ -50,6 +50,8 @@
import java.security.cert.TrustAnchor;
import java.util.Map.Entry;

import jdk.internal.access.JavaUtilZipFileAccess;
import jdk.internal.access.SharedSecrets;
import jdk.security.jarsigner.JarSigner;
import jdk.security.jarsigner.JarSignerException;
import sun.security.pkcs.PKCS7;
@@ -108,13 +110,17 @@ public class Main {
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));

private static boolean permsDetected;

static final String VERSION = "1.0";

static final int IN_KEYSTORE = 0x01; // signer is in keystore
static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
// signer is not in alias list
static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list

static final JavaUtilZipFileAccess JUZFA = SharedSecrets.getJavaUtilZipFileAccess();

// Attention:
// This is the entry that get launched by the security tool jarsigner.
public static void main(String args[]) throws Exception {
@@ -294,7 +300,7 @@ public void run(String args[]) {
Arrays.fill(storepass, ' ');
storepass = null;
}
Event.clearReportListener();
Event.clearReportListener(Event.ReporterCategory.CRLCHECK);
}

if (strict) {
@@ -776,6 +782,9 @@ void verifyJar(String jarName)
JarEntry je = e.nextElement();
String name = je.getName();

if (!permsDetected && JUZFA.getPosixPerms(je) != -1) {
permsDetected = true;
}
hasSignature = hasSignature
|| SignatureFileVerifier.isBlockOrSF(name);

@@ -1217,7 +1226,8 @@ private void displayMessagesAndResult(boolean isSigning) {
if (hasExpiringCert ||
(hasExpiringTsaCert && expireDate != null) ||
(noTimestamp && expireDate != null) ||
(hasExpiredTsaCert && signerNotExpired)) {
(hasExpiredTsaCert && signerNotExpired) ||
permsDetected) {

if (hasExpiredTsaCert && signerNotExpired) {
if (expireDate != null) {
@@ -1254,6 +1264,9 @@ private void displayMessagesAndResult(boolean isSigning) {
: rb.getString("no.timestamp.verifying"), expireDate));
}
}
if (permsDetected) {
warnings.add(rb.getString("posix.attributes.detected"));
}
}

System.out.println(result);
@@ -1771,6 +1784,8 @@ void signJar(String jarName, String alias)
String failedMessage = null;

try {
Event.setReportListener(Event.ReporterCategory.POSIXPERMS,
(t, o) -> permsDetected = true);
builder.build().sign(zipFile, fos);
} catch (JarSignerException e) {
failedCause = e.getCause();
@@ -1805,6 +1820,7 @@ void signJar(String jarName, String alias)
fos.close();
}

Event.clearReportListener(Event.ReporterCategory.POSIXPERMS);
}

if (failedCause != null) {
@@ -2064,7 +2080,8 @@ void loadKeyStore(String keyStoreName, boolean prompt) {
if (revocationCheck) {
Security.setProperty("ocsp.enable", "true");
System.setProperty("com.sun.security.enableCRLDP", "true");
Event.setReportListener((t, o) -> System.out.println(String.format(rb.getString(t), o)));
Event.setReportListener(Event.ReporterCategory.CRLCHECK,
(t, o) -> System.out.println(String.format(rb.getString(t), o)));
}
pkixParameters.setRevocationEnabled(revocationCheck);
} catch (InvalidAlgorithmParameterException ex) {
@@ -170,6 +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."},

{"jarsigner.", "jarsigner: "},
{"signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.",

0 comments on commit 3d9bad1

Please sign in to comment.