diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java index cef1682b0b1..abc9fdb348c 100644 --- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java +++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java @@ -35,6 +35,13 @@ package java.util.concurrent; +import jdk.internal.misc.Unsafe; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamException; +import java.io.Serial; +import java.io.StreamCorruptedException; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; @@ -445,4 +452,38 @@ public class CopyOnWriteArraySet extends AbstractSet return Spliterators.spliterator (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT); } + + /** + * De-serialization without data not supported for this class. + */ + @Serial + private void readObjectNoData() throws ObjectStreamException { + throw new StreamCorruptedException("Deserialized CopyOnWriteArraySet requires data"); + } + + /** + * Reconstitutes the {@code CopyOnWriteArraySet} instance from a stream + * (that is, deserializes it). + * @throws StreamCorruptedException if the object read from the stream is invalid. + */ + @Serial + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + CopyOnWriteArrayList newAl; // Set during the duplicate check + + @SuppressWarnings("unchecked") + CopyOnWriteArrayList inAl = (CopyOnWriteArrayList) in.readFields().get("al", null); + + if (inAl == null + || inAl.getClass() != CopyOnWriteArrayList.class + || (newAl = new CopyOnWriteArrayList<>()).addAllAbsent(inAl) != inAl.size()) { + throw new StreamCorruptedException("Content is invalid"); + } + + final Unsafe U = Unsafe.getUnsafe(); + U.putReference( + this, + U.objectFieldOffset(CopyOnWriteArraySet.class, "al"), + newAl + ); + } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java index 28729a56dbd..3e1fc8db164 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, 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 @@ -29,6 +29,7 @@ import java.io.InputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URISyntaxException; import java.net.URLConnection; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -48,8 +49,11 @@ import java.security.cert.X509CRL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Optional; +import java.util.Set; import sun.security.x509.AccessDescription; import sun.security.x509.GeneralNameInterface; @@ -58,6 +62,8 @@ import sun.security.util.Cache; import sun.security.util.Debug; import sun.security.util.SecurityProperties; +import javax.security.auth.x500.X500Principal; + /** * A CertStore that retrieves Certificates or * CRLs from a URI, for example, as specified in an X.509 @@ -182,6 +188,166 @@ class URICertStore extends CertStoreSpi { return timeoutVal; } + /** + * Enumeration for the allowed schemes we support when following a + * URI from an authorityInfoAccess extension on a certificate. + */ + private enum AllowedScheme { + HTTP(HttpFtpRuleMatcher.HTTP), + HTTPS(HttpFtpRuleMatcher.HTTPS), + LDAP(LdapRuleMatcher.LDAP), + LDAPS(LdapRuleMatcher.LDAPS), + FTP(HttpFtpRuleMatcher.FTP); + + final URIRuleMatcher ruleMatcher; + + AllowedScheme(URIRuleMatcher matcher) { + ruleMatcher = matcher; + } + + /** + * Return an {@code AllowedScheme} based on a case-insensitive match + * @param name the scheme name to be matched + * @return the {@code AllowedScheme} that corresponds to the + * {@code name} provided, or null if there is no match. + */ + static AllowedScheme nameOf(String name) { + if (name == null) { + return null; + } + + try { + return AllowedScheme.valueOf(name.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException _) { + return null; + } + } + } + + private static Set CA_ISS_URI_FILTERS = null; + private static final boolean CA_ISS_ALLOW_ANY; + + static { + boolean allowAny = false; + try { + if (Builder.USE_AIA) { + CA_ISS_URI_FILTERS = new LinkedHashSet<>(); + String aiaPropVal = Optional.ofNullable( + SecurityProperties.getOverridableProperty( + "com.sun.security.allowedAIALocations")). + map(String::trim).orElse(""); + if (aiaPropVal.equalsIgnoreCase("any")) { + allowAny = true; + if (debug != null) { + debug.println("allowedAIALocations: Warning: " + + "Allow-All URI filtering enabled!"); + } + } else { + // Load all the valid rules from the Security property + if (!aiaPropVal.isEmpty()) { + String[] aiaUriStrs = aiaPropVal.trim().split("\\s+"); + addCaIssUriFilters(aiaUriStrs); + } + + if (CA_ISS_URI_FILTERS.isEmpty()) { + if (debug != null) { + debug.println("allowedAIALocations: Warning: " + + "No valid filters found. Deny-all URI " + + "filtering is active."); + } + } + } + } + } finally { + CA_ISS_ALLOW_ANY = allowAny; + } + } + + /** + * Populate the filter collection from the list of AIA CA issuer URIs + * found in the {@code com.sun.security.allowedAIALocations} security + * or system property. + * + * @param aiaUriStrs array containing String URI filters + */ + private static void addCaIssUriFilters(String[] aiaUriStrs) { + for (String aiaStr : aiaUriStrs) { + if (aiaStr != null && !aiaStr.isEmpty()) { + try { + AllowedScheme scheme; + URI aiaUri = new URI(aiaStr).normalize(); + // It must be absolute and non-opaque + if (!aiaUri.isAbsolute() || aiaUri.isOpaque()) { + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "non-absolute or opaque URI " + aiaUri); + } + } else if (aiaUri.getHost() == null) { + // We do not allow rules with URIs that omit a hostname + // or address. + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "URI rule with no hostname or address: " + + aiaUri); + } + } else if ((scheme = AllowedScheme.nameOf( + aiaUri.getScheme())) != null) { + // When it is an LDAP type, we can check the path + // portion (the DN) for proper structure and reject + // the rule early if it isn't correct. + if (scheme == AllowedScheme.LDAP || + scheme == AllowedScheme.LDAPS) { + try { + new X500Principal(aiaUri.getPath(). + replaceFirst("^/+", "")); + } catch (IllegalArgumentException iae) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Skipping LDAP rule: " + iae); + } + continue; + } + } + + // When a URI has a non-null query or fragment + // warn the user upon adding the rule that those + // components will be ignored + if (aiaUri.getQuery() != null) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Rule will ignore non-null query"); + } + } + if (aiaUri.getFragment() != null) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Rule will ignore non-null fragment"); + } + } + + CA_ISS_URI_FILTERS.add(aiaUri); + if (debug != null) { + debug.println("allowedAIALocations: Added " + + aiaUri + " to URI filters"); + } + } else { + if (debug != null) { + debug.println("allowedAIALocations: Disallowed " + + "filter URI scheme: " + + aiaUri.getScheme()); + } + } + } catch (URISyntaxException urise) { + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "filter URI entry " + aiaStr + + ": parse failure at index " + urise.getIndex()); + } + } + } + } + } + /** * Creates a URICertStore. * @@ -244,6 +410,39 @@ class URICertStore extends CertStoreSpi { return null; } URI uri = ((URIName) gn).getURI(); + + // Before performing any instantiation make sure that + // the URI passes any filtering rules. This processing should + // only occur if the com.sun.security.enableAIAcaIssuers is true + // and the "any" rule has not been specified. + if (Builder.USE_AIA && !CA_ISS_ALLOW_ANY) { + URI normAIAUri = uri.normalize(); + AllowedScheme scheme = AllowedScheme.nameOf(normAIAUri.getScheme()); + + if (scheme == null) { + if (debug != null) { + debug.println("allowedAIALocations: No matching ruleset " + + "for scheme " + normAIAUri.getScheme()); + } + return null; + } + + // Go through each of the filter rules and see if any will + // make a positive match against the caIssuer URI. If nothing + // matches then we won't instantiate a URICertStore. + if (CA_ISS_URI_FILTERS.stream().noneMatch(rule -> + scheme.ruleMatcher.matchRule(rule, normAIAUri))) { + if (debug != null) { + debug.println("allowedAIALocations: Warning - " + + "The caIssuer URI " + normAIAUri + + " in the AuthorityInfoAccess extension is denied " + + "access. Use the com.sun.security.allowedAIALocations" + + " security/system property to allow access."); + } + return null; + } + } + try { return URICertStore.getInstance(new URICertStoreParameters(uri)); } catch (Exception ex) { @@ -270,7 +469,7 @@ class URICertStore extends CertStoreSpi { @Override @SuppressWarnings("unchecked") public synchronized Collection engineGetCertificates - (CertSelector selector) throws CertStoreException { + (CertSelector selector) throws CertStoreException { if (ldap) { // caching mechanism, see the class description for more info. @@ -462,4 +661,159 @@ class URICertStore extends CertStoreSpi { super(spi, p, type, params); } } + + /** + * URIRuleMatcher - abstract base class for the rule sets used for + * various URI schemes. + */ + static abstract class URIRuleMatcher { + protected final int wellKnownPort; + + protected URIRuleMatcher(int port) { + wellKnownPort = port; + } + + /** + * Attempt to match the scheme, host and port between a filter + * rule URI and a URI coming from an AIA extension. + * + * @param filterRule the filter rule to match against + * @param caIssuer the AIA URI being compared + * @return true if the scheme, host and port numbers match, false if + * any of the components do not match. If a port number is omitted in + * either the filter rule or AIA URI, the well-known port for that + * scheme is used in the comparison. + */ + boolean schemeHostPortCheck(URI filterRule, URI caIssuer) { + if (!filterRule.getScheme().equalsIgnoreCase( + caIssuer.getScheme())) { + return false; + } else if (!filterRule.getHost().equalsIgnoreCase( + caIssuer.getHost())) { + return false; + } else { + try { + // Check for port matching, taking into consideration + // default ports + int fPort = (filterRule.getPort() == -1) ? wellKnownPort : + filterRule.getPort(); + int caiPort = (caIssuer.getPort() == -1) ? wellKnownPort : + caIssuer.getPort(); + if (fPort != caiPort) { + return false; + } + } catch (IllegalArgumentException iae) { + return false; + } + } + return true; + } + + /** + * Attempt to match an AIA URI against a specific filter rule. The + * specific rules to apply are implementation dependent. + * + * @param filterRule the filter rule to match against + * @param caIssuer the AIA URI being compared + * @return true if all matching rules pass, false if any fail. + */ + abstract boolean matchRule(URI filterRule, URI caIssuer); + } + + static class HttpFtpRuleMatcher extends URIRuleMatcher { + static final HttpFtpRuleMatcher HTTP = new HttpFtpRuleMatcher(80); + static final HttpFtpRuleMatcher HTTPS = new HttpFtpRuleMatcher(443); + static final HttpFtpRuleMatcher FTP = new HttpFtpRuleMatcher(21); + + private HttpFtpRuleMatcher(int port) { + super(port); + } + + @Override + boolean matchRule(URI filterRule, URI caIssuer) { + // Check for scheme/host/port matching + if (!schemeHostPortCheck(filterRule, caIssuer)) { + return false; + } + + // Check the path component to make sure the filter is at + // least a root of the AIA caIssuer URI's path. It must be + // a case-sensitive match for all platforms. + if (!isRootOf(filterRule, caIssuer)) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed: " + + "AIA URI is not within the rule's path hierarchy."); + } + return false; + } + return true; + } + + /** + * Performs a hierarchical containment check, ensuring that the + * base URI's path is a root component of the candidate path. The + * path comparison is case-sensitive. If the base path ends in a + * slash (/) then all candidate paths that begin with the base + * path are allowed. If it does not end in a slash, then it is + * assumed that the leaf node in the base path is a file component + * and both paths must match exactly. + * + * @param base the URI that contains the root path + * @param candidate the URI that contains the path being evaluated + * @return true if {@code candidate} is a child path of {@code base}, + * false otherwise. + */ + private static boolean isRootOf(URI base, URI candidate) { + // Note: The URIs have already been normalized at this point and + // HTTP URIs cannot have null paths. If it's an empty path + // then consider the path to be "/". + String basePath = Optional.of(base.getPath()). + filter(p -> !p.isEmpty()).orElse("/"); + String candPath = Optional.of(candidate.getPath()). + filter(p -> !p.isEmpty()).orElse("/"); + return (basePath.endsWith("/")) ? candPath.startsWith(basePath) : + candPath.equals(basePath); + } + } + + static class LdapRuleMatcher extends URIRuleMatcher { + static final LdapRuleMatcher LDAP = new LdapRuleMatcher(389); + static final LdapRuleMatcher LDAPS = new LdapRuleMatcher(636); + + private LdapRuleMatcher(int port) { + super(port); + } + + @Override + boolean matchRule(URI filterRule, URI caIssuer) { + // Check for scheme/host/port matching + if (!schemeHostPortCheck(filterRule, caIssuer)) { + return false; + } + + // Obtain the base DN component and compare + try { + X500Principal filterBaseDn = new X500Principal( + filterRule.getPath().replaceFirst("^/+", "")); + X500Principal caIssBaseDn = new X500Principal( + caIssuer.getPath().replaceFirst("^/+", "")); + if (!filterBaseDn.equals(caIssBaseDn)) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed: " + + "Base DN mismatch (" + filterBaseDn + " vs " + + caIssBaseDn + ")"); + } + return false; + } + } catch (IllegalArgumentException iae) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed on DN: " + + iae); + } + return false; + } + + return true; + } + } } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index b5cbce413b2..9a81ba86268 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1655,3 +1655,48 @@ jdk.tls.alpnCharset=ISO_8859_1 # withEncryption method. # jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128 + +# +# X.509 AuthorityInfoAccess caIssuer URI Filtering +# +# This property defines a whitespace-separated list of filters that +# are applied to URIs found in the authorityInfoAccess extension in +# X.509 certificates. Any caIssuers URIs in X.509 certificates are only +# followed when the com.sun.security.enableAIAcaIssuers System property is +# enabled and the filter allows the URI. By default this property imposes a +# deny-all ruleset. This property may be overridden by a System property +# of the same name. +# +# The filters must take the form of absolute, hierarchical URIs as defined by +# the java.net.URI class. Additionally, only the following protocols are +# allowed as filters: http, https, ldap and ftp. +# See RFC 5280, section 4.2.2.1 for details about the types of URIs allowed for +# the extension and their specific requirements. +# The filter matching rules are applied to each CA issuer URI as follows: +# 1. The scheme must match (case-insensitive). +# 2. A hostname or address must be specified in the filter URI. It must match +# the host or address in the caIssuers URI (case-insensitive). No name +# resolution is performed on hostnames to match IP addresses. +# 3. The port number must match. For filter and caIssuer URIs, when a port +# number is omitted, the well-known port for that scheme will be used in the +# comparison. +# 4. For hierarchical filesystem schemes (e.g. http[s], ftp): +# a. The normalized path portion of the filter URI is matched in a +# case-sensitive manner. If the final component of the path does not end +# in a slash (/), it is considered to be a file path component and must +# be an exact match of the caIssuer's URI file path component. If the +# final filter component ends in a slash, then it must either match or be +# a prefix of the caIssuer's URI path component (e.g. a filter path of +# /ab/cd/ will match a caIssuer path of /ab/cd/, /ab/cd/ef and +# /ab/cd/ef/ghi). +# b. Query strings will be ignored in filter rules and caIssuer URIs. +# c. Fragments will be ignored in filter rules and caIssuer URIs. +# 5. For ldap URIs: +# a. The base DN must be an exact match (case-insensitive). +# b. Any query string in the rule, if specified, is ignored. +# 6. A single value "any" (case-insensitive) will create an allow-all rule. +# +# As an example, here is a valid filter policy consisting of two rules: +# com.sun.security.allowedAIALocations=http://some.company.com/cacert \ +# ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary +com.sun.security.allowedAIALocations= diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java index a4ec0767298..cc0e253f23b 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -34,7 +34,10 @@ import java.awt.peer.DesktopPeer; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.annotation.Native; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; /** @@ -44,6 +47,12 @@ import java.net.URI; */ public final class CDesktopPeer implements DesktopPeer { + @Native private static final int OPEN = 0; + @Native private static final int BROWSE = 1; + @Native private static final int EDIT = 2; + @Native private static final int PRINT = 3; + @Native private static final int MAIL = 4; + @Override public boolean isSupported(Action action) { return true; @@ -51,27 +60,27 @@ public final class CDesktopPeer implements DesktopPeer { @Override public void open(File file) throws IOException { - this.lsOpenFile(file, false); + this.lsOpenFile(file, OPEN); } @Override public void edit(File file) throws IOException { - this.lsOpenFile(file, false); + this.lsOpenFile(file, EDIT); } @Override public void print(File file) throws IOException { - this.lsOpenFile(file, true); + this.lsOpenFile(file, PRINT); } @Override public void mail(URI uri) throws IOException { - this.lsOpen(uri); + this.lsOpen(uri, MAIL); } @Override public void browse(URI uri) throws IOException { - this.lsOpen(uri); + this.lsOpen(uri, BROWSE); } @Override @@ -162,24 +171,44 @@ public final class CDesktopPeer implements DesktopPeer { } } - private void lsOpen(URI uri) throws IOException { - int status = _lsOpenURI(uri.toString()); + private void lsOpen(URI uri, int action) throws IOException { + int status = _lsOpenURI(uri.toString(), action); if (status != 0 /* noErr */) { - throw new IOException("Failed to mail or browse " + uri + ". Error code: " + status); + String actionString = (action == MAIL) ? "mail" : "browse"; + throw new IOException("Failed to " + actionString + " " + uri + + ". Error code: " + status); } } - private void lsOpenFile(File file, boolean print) throws IOException { - int status = _lsOpenFile(file.getCanonicalPath(), print); + private void lsOpenFile(File file, int action) throws IOException { + int status = -1; + Path tmpFile = null; + String tmpTxtPath = null; + try { + if (action == EDIT) { + tmpFile = Files.createTempFile("TmpFile", ".txt"); + tmpTxtPath = tmpFile.toAbsolutePath().toString(); + } + status = _lsOpenFile(file.getCanonicalPath(), action, tmpTxtPath); + } catch (Exception e) { + throw new IOException("Failed to create tmp file: ", e); + } finally { + if (tmpFile != null) { + Files.deleteIfExists(tmpFile); + } + } if (status != 0 /* noErr */) { - throw new IOException("Failed to open, edit or print " + file + ". Error code: " + status); + String actionString = (action == OPEN) ? "open" + : (action == EDIT) ? "edit" : "print"; + throw new IOException("Failed to " + actionString + " " + file + + ". Error code: " + status); } } - private static native int _lsOpenURI(String uri); + private static native int _lsOpenURI(String uri, int action); - private static native int _lsOpenFile(String path, boolean print); + private static native int _lsOpenFile(String path, int action, String tmpTxtPath); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m index 7555c7990c4..e1841c9398c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -27,27 +27,60 @@ #import "JNIUtilities.h" #import #import +#import "sun_lwawt_macosx_CDesktopPeer.h" /* * Class: sun_lwawt_macosx_CDesktopPeer * Method: _lsOpenURI - * Signature: (Ljava/lang/String;)I; + * Signature: (Ljava/lang/String;I)I */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenURI -(JNIEnv *env, jclass clz, jstring uri) +(JNIEnv *env, jclass clz, jstring uri, jint action) { - OSStatus status = noErr; + __block OSStatus status = noErr; JNI_COCOA_ENTER(env); - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. - // So we use LaunchServices directly. + NSURL *urlToOpen = [NSURL URLWithString:JavaStringToNSString(env, uri)]; + NSURL *appURI = nil; - NSURL *url = [NSURL URLWithString:JavaStringToNSString(env, uri)]; + if (action == sun_lwawt_macosx_CDesktopPeer_BROWSE) { + // To get the defaultBrowser + NSURL *httpsURL = [NSURL URLWithString:@"https://"]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + appURI = [workspace URLForApplicationToOpenURL:httpsURL]; + } else if (action == sun_lwawt_macosx_CDesktopPeer_MAIL) { + // To get the default mailer + NSURL *mailtoURL = [NSURL URLWithString:@"mailto://"]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + appURI = [workspace URLForApplicationToOpenURL:mailtoURL]; + } - LSLaunchFlags flags = kLSLaunchDefaults; + if (appURI == nil) { + return -1; + } - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); + // Prepare NSOpenConfig object + NSArray *urls = @[urlToOpen]; + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; + configuration.activates = YES; // To bring app to foreground + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts + + // dispatch semaphores used to wait for the completion handler to update and return status + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout + + // Asynchronous call to openURL + [[NSWorkspace sharedWorkspace] openURLs:urls + withApplicationAtURL:appURI + configuration:configuration + completionHandler:^(NSRunningApplication *app, NSError *error) { + if (error) { + status = (OSStatus) error.code; + } + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, timeout); JNI_COCOA_EXIT(env); return status; @@ -56,32 +89,73 @@ JNI_COCOA_EXIT(env); /* * Class: sun_lwawt_macosx_CDesktopPeer * Method: _lsOpenFile - * Signature: (Ljava/lang/String;Z)I; + * Signature: (Ljava/lang/String;I;Ljava/lang/String;)I; */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenFile -(JNIEnv *env, jclass clz, jstring jpath, jboolean print) +(JNIEnv *env, jclass clz, jstring jpath, jint action, jstring jtmpTxtPath) { - OSStatus status = noErr; + __block OSStatus status = noErr; JNI_COCOA_ENTER(env); - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. - // So we use LaunchServices directly. - NSString *path = NormalizedPathNSStringFromJavaString(env, jpath); - - NSURL *url = [NSURL fileURLWithPath:(NSString *)path]; + NSURL *urlToOpen = [NSURL fileURLWithPath:(NSString *)path]; // This byzantine workaround is necessary, or else directories won't open in Finder - url = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[url path], kCFURLPOSIXPathStyle, false); + urlToOpen = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[urlToOpen path], + kCFURLPOSIXPathStyle, false); - LSLaunchFlags flags = kLSLaunchDefaults; - if (print) flags |= kLSLaunchAndPrint; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSURL *appURI = [workspace URLForApplicationToOpenURL:urlToOpen]; + NSURL *defaultTerminalApp = [workspace URLForApplicationToOpenURL:[NSURL URLWithString:@"file:///bin/sh"]]; - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); - [url release]; + // Prepare NSOpenConfig object + NSArray *urls = @[urlToOpen]; + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; + configuration.activates = YES; // To bring app to foreground + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts + + // pre-checks for open/print/edit before calling openURLs API + if (action == sun_lwawt_macosx_CDesktopPeer_OPEN + || action == sun_lwawt_macosx_CDesktopPeer_PRINT) { + if (appURI == nil + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]] + || [[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { + return -1; + } + // Additionally set forPrinting=TRUE for print + if (action == sun_lwawt_macosx_CDesktopPeer_PRINT) { + configuration.forPrinting = YES; + } + } else if (action == sun_lwawt_macosx_CDesktopPeer_EDIT) { + if (appURI == nil + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]]) { + return -1; + } + // for EDIT: if (defaultApp = TerminalApp) then set appURI = DefaultTextEditor + if ([[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { + NSString *path = NormalizedPathNSStringFromJavaString(env, jtmpTxtPath); + NSURL *tempFilePath = [NSURL fileURLWithPath:(NSString *)path]; + appURI = [workspace URLForApplicationToOpenURL:tempFilePath]; + } + } + + // dispatch semaphores used to wait for the completion handler to update and return status + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout + + // Asynchronous call - openURLs:withApplicationAtURL + [[NSWorkspace sharedWorkspace] openURLs:urls + withApplicationAtURL:appURI + configuration:configuration + completionHandler:^(NSRunningApplication *app, NSError *error) { + if (error) { + status = (OSStatus) error.code; + } + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, timeout); JNI_COCOA_EXIT(env); return status; } - diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index a6d2a448186..8c7ac94b04e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -452,78 +452,54 @@ public class BasicOptionPaneUI extends OptionPaneUI { } else if ((nl = s.indexOf('\n')) >= 0) { nll = 1; } - if (s.contains("")) { - /* line break in html text is done by
tag - * and not by /n so it's incorrect to address newline - * same as non-html text. - * Text between tags are extracted - * and rendered as JLabel text - */ - int index1 = s.indexOf(""); - int index2 = s.indexOf(""); - String str = ""; - if (index2 >= 0) { - str = s.substring(index2 + "".length()); - s = s.substring(index1, index2 + + "".length()); + if (nl >= 0) { + // break up newlines + if (nl == 0) { + JPanel breakPanel = new JPanel() { + public Dimension getPreferredSize() { + Font f = getFont(); + + if (f != null) { + return new Dimension(1, f.getSize() + 2); + } + return new Dimension(0, 0); + } + }; + breakPanel.setName("OptionPane.break"); + addMessageComponents(container, cons, breakPanel, maxll, + true); + } else { + addMessageComponents(container, cons, s.substring(0, nl), + maxll, false); } - JLabel label; - label = new JLabel(s, JLabel.LEADING); + // Prevent recursion of more than + // 200 successive newlines in a message + // and indicate message is truncated via ellipsis + if (recursionCount++ > 200) { + recursionCount = 0; + addMessageComponents(container, cons, new String("..."), + maxll, false); + return; + } + addMessageComponents(container, cons, s.substring(nl + nll), maxll, + false); + + } else if (len > maxll) { + Container c = Box.createVerticalBox(); + c.setName("OptionPane.verticalBox"); + burstStringInto(c, s, maxll); + addMessageComponents(container, cons, c, maxll, true); + + } else { + JLabel label = new JLabel(); if (Boolean.TRUE.equals( - this.optionPane.getClientProperty("html.disable"))) { + optionPane.getClientProperty("html.disable"))) { label.putClientProperty("html.disable", true); } + label.setText(s); label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); - if (!str.isEmpty()) { - addMessageComponents(container, cons, str, maxll, false); - } - } else { - if (nl >= 0) { - // break up newlines - if (nl == 0) { - JPanel breakPanel = new JPanel() { - public Dimension getPreferredSize() { - Font f = getFont(); - - if (f != null) { - return new Dimension(1, f.getSize() + 2); - } - return new Dimension(0, 0); - } - }; - breakPanel.setName("OptionPane.break"); - addMessageComponents(container, cons, breakPanel, maxll, - true); - } else { - addMessageComponents(container, cons, s.substring(0, nl), - maxll, false); - } - // Prevent recursion of more than - // 200 successive newlines in a message - // and indicate message is truncated via ellipsis - if (recursionCount++ > 200) { - recursionCount = 0; - addMessageComponents(container, cons, new String("..."), - maxll, false); - return; - } - addMessageComponents(container, cons, s.substring(nl + nll), maxll, - false); - - } else if (len > maxll) { - Container c = Box.createVerticalBox(); - c.setName("OptionPane.verticalBox"); - burstStringInto(c, s, maxll); - addMessageComponents(container, cons, c, maxll, true); - - } else { - JLabel label; - label = new JLabel(s, JLabel.LEADING); - label.setName("OptionPane.label"); - configureMessageLabel(label); - addMessageComponents(container, cons, label, maxll, true); - } } } } diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 1bebf379997..9815d657eee 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, 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 @@ -1898,14 +1898,16 @@ public final class SunGraphics2D protected void validateCompClip() { int origClipState = clipState; - if (usrClip == null) { + final Shape clip = usrClip; + + if (clip == null) { clipState = CLIP_DEVICE; clipRegion = devClip; - } else if (usrClip instanceof Rectangle2D) { + } else if (clip instanceof Rectangle2D rect2d) { clipState = CLIP_RECTANGULAR; - clipRegion = devClip.getIntersection((Rectangle2D) usrClip); + clipRegion = devClip.getIntersection(rect2d); } else { - PathIterator cpi = usrClip.getPathIterator(null); + PathIterator cpi = clip.getPathIterator(null); int[] box = new int[4]; ShapeSpanIterator sr = LoopPipe.getFillSSI(this); try { diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java b/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java index 62d1f119f33..69c4954cac4 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, 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 @@ -27,6 +27,8 @@ package sun.java2d.pipe; import java.awt.Rectangle; import java.awt.Shape; + +import sun.java2d.InvalidPipeException; import sun.java2d.SunGraphics2D; /** @@ -67,7 +69,9 @@ public class SpanClipRenderer implements CompositePipe public Object startSequence(SunGraphics2D sg, Shape s, Rectangle devR, int[] abox) { RegionIterator ri = sg.clipRegion.getIterator(); - + if (ri.region.isRectangular()) { + throw new InvalidPipeException("Invalid clip data"); + } return new SCRcontext(ri, outpipe.startSequence(sg, s, devR, abox)); } diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c index fa9a186d6d7..bc2df9b0b1a 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -76,6 +76,7 @@ #include "mlib_ImageCheck.h" #include "mlib_SysMath.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ static void mlib_ImageConvMxNMulAdd_F32(mlib_f32 *dst, @@ -272,6 +273,9 @@ mlib_status mlib_convMxNext_f32(mlib_image *dst, mlib_s32 nch = mlib_ImageGetChannels(dst); mlib_s32 i, j, j1, k; + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); @@ -629,6 +633,9 @@ mlib_status mlib_convMxNext_d64(mlib_image *dst, mlib_s32 nch = mlib_ImageGetChannels(dst); mlib_s32 i, j, j1, k; + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c index ee15935dcfe..5869b0a54af 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -82,6 +82,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ static void mlib_ImageConvMxNMulAdd_S32(mlib_d64 *dst, @@ -229,6 +230,9 @@ mlib_status mlib_convMxNext_s32(mlib_image *dst, /* internal buffer */ + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c index 57486b1cae5..00469d25719 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -260,8 +261,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -509,8 +516,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -919,8 +934,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c index 3b6985b7876..2e035d12453 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, 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 @@ -32,6 +32,7 @@ #include "mlib_image.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -466,6 +467,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c index 380ed044878..bb264d9dcd2 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ #define CACHE_SIZE (64*1024) @@ -335,6 +336,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 2), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 2)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c index c8b58e6f138..136d5a2b814 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -245,8 +246,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -494,8 +501,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -904,8 +919,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c index f65fda45c58..c144404b0f4 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -467,6 +468,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c index b2757979a84..81a06f2fc28 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -270,8 +271,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -519,8 +526,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -927,8 +942,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c index a3234cf8959..49412c7d7ef 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -32,6 +32,7 @@ #include "mlib_image.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -466,6 +467,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c index 2e77c20aa57..cfd5e3e671e 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -50,6 +50,7 @@ #include "mlib_image.h" #include "mlib_ImageLookUp.h" +#include "safe_math.h" /***************************************************************/ #define MAX_WIDTH 512 @@ -302,6 +303,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_2(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 val0, val1; + if (!SAFE_TO_MULT(xsize, 2)) { + return MLIB_FAILURE; + } size = xsize * 2; if (size > MAX_WIDTH) { @@ -440,6 +444,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_3(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 l0, h0, v0, l1, h1, v1, l2, h2, v2; + if (!SAFE_TO_MULT(3, xsize)) { + return MLIB_FAILURE; + } size = 3 * xsize; if (size > MAX_WIDTH) { @@ -583,6 +590,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_4(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 l, h; + if (!SAFE_TO_MULT(xsize, 4)) { + return MLIB_FAILURE; + } size = xsize * 4; if (size > MAX_WIDTH) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c index a6f4cfdd36e..72adc212af6 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -101,6 +101,11 @@ mlib_status mlib_AffineEdges(mlib_affine_param *param, return MLIB_FAILURE; } + int intSize = sizeof(mlib_s32); + if (!SAFE_TO_MULT(dstHeight, intSize) || + !SAFE_TO_ADD(dstHeight * intSize, 7)) { + return MLIB_FAILURE; + } bsize0 = (dstHeight * sizeof(mlib_s32) + 7) & ~7; if (lineAddr == NULL) { @@ -109,6 +114,10 @@ mlib_status mlib_AffineEdges(mlib_affine_param *param, param->buff_malloc = NULL; + if (!SAFE_TO_MULT(4, bsize0) || !SAFE_TO_ADD(4 * bsize0, bsize1)) { + return MLIB_FAILURE; + } + if ((4 * bsize0 + bsize1) > buff_size) { buff = param->buff_malloc = mlib_malloc(4 * bsize0 + bsize1); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java index 788c1477265..e5b628dd74b 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, 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 @@ -38,6 +38,9 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.swing.event.EventListenerList; import sun.awt.shell.ShellFolder; @@ -50,9 +53,11 @@ import sun.awt.shell.ShellFolder; */ final class WDesktopPeer implements DesktopPeer { /* Constants for the operation verbs */ - private static String ACTION_OPEN_VERB = "open"; - private static String ACTION_EDIT_VERB = "edit"; - private static String ACTION_PRINT_VERB = "print"; + private static final String ACTION_OPEN_VERB = "open"; + private static final String ACTION_EDIT_VERB = "edit"; + private static final String ACTION_PRINT_VERB = "print"; + private static final String ACTION_BROWSE_VERB = "browse"; + private static final String ACTION_MAIL_VERB = "mail"; private static native void init(); @@ -95,12 +100,12 @@ final class WDesktopPeer implements DesktopPeer { @Override public void mail(URI uri) throws IOException { - this.ShellExecute(uri, ACTION_OPEN_VERB); + this.ShellExecute(uri, ACTION_MAIL_VERB); } @Override public void browse(URI uri) throws IOException { - this.ShellExecute(uri, ACTION_OPEN_VERB); + this.launchUriInBrowser(uri); } private void ShellExecute(File file, String verb) throws IOException { @@ -121,6 +126,42 @@ final class WDesktopPeer implements DesktopPeer { } } + private void launchUriInBrowser(URI uri) throws IOException { + String defaultBrowser = getDefaultBrowser(); + if (defaultBrowser == null) { + throw new IOException("Failed to get default browser"); + } + + List cmdLineTokens = getCmdLineTokens(uri, defaultBrowser); + try { + ProcessBuilder pb = new ProcessBuilder(cmdLineTokens); + pb.start(); + } catch (Exception e) { + throw new IOException("Error launching Browser: ", e); + } + } + + private static List getCmdLineTokens(URI uri, String defaultBrowser) { + if (defaultBrowser.contains("%1")) { + defaultBrowser = defaultBrowser.replace("%1", uri.toString()); + } else { + defaultBrowser = defaultBrowser + " " + uri.toString(); + } + + List cmdLineTokens = new ArrayList<>(); + int firstIndex = defaultBrowser.indexOf("\""); + int secondIndex = defaultBrowser.indexOf("\"", firstIndex + 1); + + if (firstIndex == 0 && secondIndex != firstIndex) { + cmdLineTokens.add(defaultBrowser.substring(firstIndex, secondIndex + 1)); + defaultBrowser = defaultBrowser.substring(secondIndex + 1).trim(); + } + cmdLineTokens.addAll(Arrays.asList(defaultBrowser.split(" "))); + return cmdLineTokens; + } + + private static native String getDefaultBrowser(); + private static native String ShellExecute(String fileOrUri, String verb); private static final EventListenerList listenerList = new EventListenerList(); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp index ba79523249c..ebb43b2f078 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, 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 @@ -30,6 +30,12 @@ #include #include #include "awt_Toolkit.h" +#include +#include // for AssocQueryStringW +#include +#include +#include +#include // for SaferiIsExecutableFileType #define BUFFER_LIMIT MAX_PATH+1 @@ -78,14 +84,23 @@ JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute LPCWSTR fileOrUri_c = JNU_GetStringPlatformChars(env, fileOrUri_j, NULL); CHECK_NULL_RETURN(fileOrUri_c, NULL); LPCWSTR verb_c = JNU_GetStringPlatformChars(env, verb_j, NULL); + if (verb_c == NULL) { JNU_ReleaseStringPlatformChars(env, fileOrUri_j, fileOrUri_c); return NULL; } + if (wcscmp(verb_c, L"open") == 0) { + BOOL isExecutable = SaferiIsExecutableFileType(fileOrUri_c, FALSE); + if (isExecutable) { + return env->NewStringUTF("Unsupported URI content"); + } + } + // set action verb for mail() to open before calling ShellExecute + LPCWSTR actionVerb = wcscmp(verb_c, L"mail") == 0 ? L"open" : verb_c; // 6457572: ShellExecute possibly changes FPU control word - saving it here unsigned oldcontrol87 = _control87(0, 0); - HINSTANCE retval = ::ShellExecute(NULL, verb_c, fileOrUri_c, NULL, NULL, + HINSTANCE retval = ::ShellExecute(NULL, actionVerb, fileOrUri_c, NULL, NULL, SW_SHOWNORMAL); DWORD error = ::GetLastError(); _control87(oldcontrol87, 0xffffffff); @@ -113,10 +128,38 @@ JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute return errmsg; } } - return NULL; } +/* + * Class: sun_awt_windows_WDesktopPeer + * Method: getDefaultBrowser + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_getDefaultBrowser +(JNIEnv *env, jclass cls) +{ + LPCWSTR fileExtension = L"https"; + WCHAR defaultBrowser_c [MAX_PATH]; + DWORD cchBuffer = MAX_PATH; + + // Use AssocQueryString to get the default browser + HRESULT hr = AssocQueryStringW( + ASSOCF_NONE, // No special flags + ASSOCSTR_COMMAND, // Request the command string + fileExtension, // File extension + NULL, // pszExtra (optional) + defaultBrowser_c, // Output buffer - result + &cchBuffer // Size of the output buffer + ); + + if (FAILED(hr)) { + return NULL; + } + + return JNU_NewStringPlatform(env, defaultBrowser_c); +} + /* * Class: sun_awt_windows_WDesktopPeer * Method: moveToTrash diff --git a/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java b/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java index ab6664b9d8f..a4475c8bd1e 100644 --- a/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java +++ b/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -31,6 +31,7 @@ import java.net.Socket; import java.rmi.server.RMIClientSocketFactory; import java.util.StringTokenizer; import javax.net.SocketFactory; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -119,6 +120,16 @@ public class SslRMIClientSocketFactory // final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port); + + if (Boolean.parseBoolean( + System.getProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "true"))) { + SSLParameters params = sslSocket.getSSLParameters(); + if (params == null) { + params = new SSLParameters(); + } + params.setEndpointIdentificationAlgorithm("HTTPS"); + sslSocket.setSSLParameters(params); + } // Set the SSLSocket Enabled Cipher Suites // final String enabledCipherSuites = diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c index d969fabffd0..052c7011860 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -184,9 +184,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -194,7 +197,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -208,7 +211,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt (CK_BYTE_PTR)(outBufP + jOutOfs), &ckEncryptedLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -251,9 +254,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -261,7 +267,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -275,7 +281,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate (CK_BYTE_PTR)(outBufP + jOutOfs), &ckEncryptedPartLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -462,9 +468,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -472,7 +481,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -485,7 +494,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt (CK_BYTE_PTR)(outBufP + jOutOfs), &ckOutLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -528,9 +537,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -538,7 +550,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -551,7 +563,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate (CK_BYTE_PTR)(outBufP + jOutOfs), &ckDecryptedPartLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java index 7d11bd42c94..ef89088674f 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java @@ -36,6 +36,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; import sun.net.httpserver.UnmodifiableHeaders; +import sun.net.httpserver.Utils; /** * HTTP request and response headers are represented by this class which @@ -216,8 +217,13 @@ public class Headers implements Map> { @Override public List put(String key, List value) { + // checkHeader is called in this class to fail fast + // It also must be called in sendResponseHeaders because + // Headers instances internal state can be modified + // external to these methods. + Utils.checkHeader(key, false); for (String v : value) - checkValue(v); + Utils.checkHeader(v, true); return map.put(normalize(key), value); } @@ -229,7 +235,8 @@ public class Headers implements Map> { * @param value the value to add to the header */ public void add(String key, String value) { - checkValue(value); + Utils.checkHeader(key, false); + Utils.checkHeader(value, true); String k = normalize(key); List l = map.get(k); if (l == null) { @@ -239,30 +246,6 @@ public class Headers implements Map> { l.add(value); } - private static void checkValue(String value) { - int len = value.length(); - for (int i=0; i= len - 2) { - throw new IllegalArgumentException("Illegal CR found in header"); - } - char c1 = value.charAt(i+1); - char c2 = value.charAt(i+2); - if (c1 != '\n') { - throw new IllegalArgumentException("Illegal char found after CR in header"); - } - if (c2 != ' ' && c2 != '\t') { - throw new IllegalArgumentException("No whitespace found after CRLF in header"); - } - i+=2; - } else if (c == '\n') { - throw new IllegalArgumentException("Illegal LF found in header"); - } - } - } - /** * Sets the given {@code value} as the sole header value for the given * {@code key}. If the mapping does not already exist, then it is created. @@ -304,7 +287,7 @@ public class Headers implements Map> { public void replaceAll(BiFunction, ? extends List> function) { var f = function.andThen(values -> { Objects.requireNonNull(values); - values.forEach(Headers::checkValue); + values.forEach(value -> Utils.checkHeader(value, true)); return values; }); Map.super.replaceAll(f); diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java index 17dff13d4e5..3b35c56100f 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java @@ -207,6 +207,8 @@ class ExchangeImpl { return uos_orig; } + private static final byte[] CRLF = new byte[] {0x0D, 0x0A}; + public void sendResponseHeaders (int rCode, long contentLen) throws IOException { @@ -215,10 +217,11 @@ class ExchangeImpl { throw new IOException ("headers already sent"); } this.rcode = rCode; - String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n"; + String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode); ByteArrayOutputStream tmpout = new ByteArrayOutputStream(); PlaceholderOutputStream o = getPlaceholderResponseBody(); - tmpout.write (bytes(statusLine, 0), 0, statusLine.length()); + tmpout.write (bytes(statusLine, false, 0), 0, statusLine.length()); + tmpout.write (CRLF); boolean noContentToSend = false; // assume there is content boolean noContentLengthHeader = false; // must not send Content-length is set rspHdrs.set("Date", FORMATTER.format(Instant.now())); @@ -305,11 +308,11 @@ class ExchangeImpl { List values = entry.getValue(); for (String val : values) { int i = key.length(); - buf = bytes (key, 2); + buf = bytes (key, true, 2); buf[i++] = ':'; buf[i++] = ' '; os.write (buf, 0, i); - buf = bytes (val, 2); + buf = bytes (val, false, 2); i = val.length(); buf[i++] = '\r'; buf[i++] = '\n'; @@ -327,8 +330,14 @@ class ExchangeImpl { * Make sure that at least "extra" bytes are free at end * of rspbuf. Reallocate rspbuf if not big enough. * caller must check return value to see if rspbuf moved + * + * Header values are supposed to be limited to 7-bit ASCII + * but 8-bit has to be allowed (for ISO_8859_1). For efficiency + * we just down cast 16 bit Java chars to byte. We don't allow + * any character that can't be encoded in 8 bits. */ - private byte[] bytes (String s, int extra) { + private byte[] bytes (String s, boolean isKey, int extra) throws IOException { + Utils.checkHeader(s, !isKey); int slen = s.length(); if (slen+extra > rspbuf.length) { int diff = slen + extra - rspbuf.length; diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java index 43dadb84a90..41834172f27 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java @@ -88,4 +88,37 @@ public class Utils { } return true; } + + /* Throw IAE if illegal character found. isValue is true if String is + * a value. Otherwise it is header name + */ + public static void checkHeader(String str, boolean isValue) { + int len = str.length(); + for (int i=0; i= len - 2) { + throw new IllegalArgumentException("Illegal CR found in header"); + } + char c1 = str.charAt(i+1); + char c2 = str.charAt(i+2); + if (c1 != '\n') { + throw new IllegalArgumentException("Illegal char found after CR in header"); + } + if (c2 != ' ' && c2 != '\t') { + throw new IllegalArgumentException("No whitespace found after CRLF in header"); + } + i+=2; + } else if (c == '\n') { + throw new IllegalArgumentException("Illegal LF found in header"); + } else if (c > 255) { + throw new IllegalArgumentException("Illegal character found in header"); + } + } + } + } diff --git a/test/jdk/java/awt/Desktop/BrowseTest.java b/test/jdk/java/awt/Desktop/BrowseTest.java index 33de1ecdca7..28e08fe16c7 100644 --- a/test/jdk/java/awt/Desktop/BrowseTest.java +++ b/test/jdk/java/awt/Desktop/BrowseTest.java @@ -40,10 +40,27 @@ import jtreg.SkippedException; public class BrowseTest extends JPanel { static final String INSTRUCTIONS = """ - This test could launch default file manager to open user's home - directory, and default web browser to show the URL of java vendor. - After test execution close the native file manager and web browser + Set your default browser as per the test platform. + macOS - Safari + windows - MS Edge + linux - Firefox + + This test checks 2 cases: + + 1) Directory URI: + On macOS and windows, verify that a browser window opens and + EITHER the browser OR native file manager shows the user's + home directory. + + On Linux verify that the user's home directory is shown by the + default file manager. + + 2) Web URI: + Verify that the Web URI (URL of java vendor) opens in the browser. + + After test execution close the native file manager and any web browser windows if they were launched by test. + Also check output for any unexpected EXCEPTIONS, if you see any failure messages press Fail otherwise press Pass. """; @@ -53,7 +70,7 @@ public class BrowseTest extends JPanel { URI dirURI = new File(System.getProperty("user.home")).toURI(); URI webURI = URI.create(System.getProperty("java.vendor.url", "http://www.java.com")); - boolean failed = false; + PassFailJFrame.log("Testing 1st case: Directory URI ..."); try { PassFailJFrame.log("Try to browse " + dirURI + " ..."); desktop.browse(dirURI); @@ -62,6 +79,7 @@ public class BrowseTest extends JPanel { PassFailJFrame.log("EXCEPTION: " + e.getMessage()); } + PassFailJFrame.log("Testing 2nd case: Web URI ..."); try { PassFailJFrame.log("Try to browse " + webURI + " ..."); desktop.browse(webURI); diff --git a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java index b2d7ef28df1..77d86ceb42a 100644 --- a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java +++ b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java @@ -48,7 +48,7 @@ public class EditAndPrintTest extends JPanel { This test tries to edit and print a directory, which will expectedly raise IOException. Then this test would edit and print a .txt file, which should be successful. After test execution close the editor if it was launched by test. - If you see any EXCEPTION messages in the output press FAIL. + If you see any EXCEPTION messages in case of .txt file in the output press FAIL. """; static Desktop desktop; diff --git a/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java b/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java new file mode 100644 index 00000000000..7700eda6cd6 --- /dev/null +++ b/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025, 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 8365058 + * @summary Check basic correctness of de-serialization + * @run junit SerializationTest + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SerializationTest { + + // Ensure basic serialization round trip correctness + @ParameterizedTest + @MethodSource + void roundTripTest(CopyOnWriteArraySet expected) { + var bytes = ser(expected); + var actual = deSer(bytes); + assertEquals(CopyOnWriteArraySet.class, actual.getClass()); + assertEquals(expected, actual); + } + + private static Stream> roundTripTest() { + return Stream.of( + new CopyOnWriteArraySet<>(), + new CopyOnWriteArraySet<>(List.of(1, 2, 3)), + new CopyOnWriteArraySet<>(Set.of("Foo", "Bar", "Baz")) + ); + } + + private static byte[] ser(Object obj) { + return assertDoesNotThrow(() -> { + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream)) { + oos.writeObject(obj); + return byteArrayOutputStream.toByteArray(); + } + }, "Unexpected error during serialization"); + } + + private static Object deSer(byte[] bytes) { + return assertDoesNotThrow(() -> { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream)) { + return ois.readObject(); + } + }, "Unexpected error during de-serialization"); + } +} diff --git a/test/jdk/javax/management/security/SecurityTest.java b/test/jdk/javax/management/security/SecurityTest.java index 7212aea883f..b46bbfa759f 100644 --- a/test/jdk/javax/management/security/SecurityTest.java +++ b/test/jdk/javax/management/security/SecurityTest.java @@ -402,6 +402,8 @@ public class SecurityTest { opts.add(JDKToolFinder.getJDKTool("java")); opts.addAll(Arrays.asList(jdk.test.lib.Utils.getTestJavaOpts())); + opts.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); + // We need to forward some properties to the client side opts.add("-Dtest.src=" + System.getProperty("test.src")); diff --git a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java index 3aa7a98c394..7616b112e39 100644 --- a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java +++ b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java @@ -137,6 +137,7 @@ public class SSLSocketParametersTest extends SSLContextTemplate { } public static void main(String[] args) throws Exception { + System.setProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "false"); SSLSocketParametersTest test = new SSLSocketParametersTest(); test.runTest(Integer.parseInt(args[0])); } diff --git a/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java b/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java deleted file mode 100644 index 94318492bd9..00000000000 --- a/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2022, 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 5074006 - * @key headful - * @library /java/awt/regtesthelpers - * @build PassFailJFrame - * @summary Swing JOptionPane shows tag as a string after newline - * @run main/manual TestJOptionHTMLTag -*/ - -import javax.swing.JDialog; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; - -public class TestJOptionHTMLTag { - static String instructions - = """ - INSTRUCTIONS: - A dialog will be shown. - If it does not contain string, press Pass else press Fail. - """; - static PassFailJFrame passFailJFrame; - - public static void main(String[] args) throws Exception { - - SwingUtilities.invokeAndWait(() -> { - try { - String message = "" + "This is a test\n" + ""; - JOptionPane optionPane = new JOptionPane(); - optionPane.setMessage(message); - optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE); - JDialog dialog = new JDialog(); - dialog.setContentPane(optionPane); - dialog.pack(); - dialog.setVisible(true); - - passFailJFrame = new PassFailJFrame(instructions); - PassFailJFrame.addTestWindow(dialog); - PassFailJFrame.positionTestWindow(dialog, PassFailJFrame.Position.HORIZONTAL); - } catch (Exception e) { - e.printStackTrace(); - } - }); - passFailJFrame.awaitAndCheck(); - } -} - diff --git a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java index 61359084297..1f4707fab3b 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, Red Hat Inc + * Copyright (c) 2025, 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 @@ -205,6 +206,7 @@ public class JMXInterfaceBindingTest { // This is needed for testing on loopback args.add("-Djava.rmi.server.hostname=" + address); if (useSSL) { + args.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); args.add("-Dcom.sun.management.jmxremote.registry.ssl=true"); args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC); args.add("-Djavax.net.ssl.trustStore=" + TRUSTSTORE_LOC); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java index 2a99c5e7d52..b62d3f1bbd7 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, 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 @@ -169,6 +169,7 @@ public class RmiBootstrapTest extends RmiTestBase { final List credentialFiles = prepareTestFiles(args[0]); Security.setProperty("jdk.tls.disabledAlgorithms", ""); + System.setProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "false"); try { MAX_GET_FREE_PORT_TRIES = Integer.parseInt(System.getProperty("test.getfreeport.max.tries", "10")); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java index 1aa20937962..b4ef5e224f8 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java @@ -179,6 +179,7 @@ public class RmiRegistrySslTest { initTestEnvironment(); List command = new ArrayList<>(); + command.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); command.add("-Dtest.src=" + TEST_SRC); command.add("-Dtest.rmi.port=" + port); command.addAll(Arrays.asList(args)); diff --git a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java index cabb225bf1c..5491d7b0d7a 100644 --- a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java +++ b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java @@ -47,6 +47,7 @@ import com.sun.net.httpserver.*; import java.io.*; import java.math.BigInteger; import java.net.InetSocketAddress; +import java.security.Security; import java.security.cert.*; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -69,6 +70,7 @@ public class AIACertTimeout { private static X509Certificate eeCert; public static void main(String[] args) throws Exception { + Security.setProperty("com.sun.security.allowedAIALocations", "any"); int servTimeoutMsec = (args != null && args.length >= 1) ? Integer.parseInt(args[0]) : -1; boolean expectedPass = args != null && args.length >= 2 && diff --git a/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java b/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java index 3b598d78d9f..2214e9256c3 100644 --- a/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java +++ b/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, 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 @@ -38,6 +38,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; +import java.security.Security; import java.security.cert.CertPath; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; @@ -47,7 +48,6 @@ import java.security.cert.PKIXParameters; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -67,25 +67,27 @@ public class ExtensionsWithLDAP { * Not After : Jan 17 18:03:59 2043 GMT * Subject: CN=Root */ - private static final String CA_CERT = "" - + "-----BEGIN CERTIFICATE-----\n" - + "MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV\n" - + "BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD\n" - + "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm\n" - + "bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH\n" - + "V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv\n" - + "BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b\n" - + "QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t\n" - + "svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6\n" - + "bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb\n" - + "QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB\n" - + "/zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp\n" - + "3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES\n" - + "q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ\n" - + "nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj\n" - + "jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc\n" - + "IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg==\n" - + "-----END CERTIFICATE-----"; + private static final String CA_CERT = + """ + -----BEGIN CERTIFICATE----- + MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV + BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD + VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm + bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH + V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv + BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b + QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t + svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6 + bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb + QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB + /zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp + 3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES + q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ + nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj + jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc + IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg== + -----END CERTIFICATE----- + """; /* * Certificate: @@ -106,39 +108,41 @@ public class ExtensionsWithLDAP { * Authority Information Access: * CA Issuers - URI:ldap://ldap.host.for.aia/dc=Root?cACertificate */ - private static final String EE_CERT = "" - + "-----BEGIN CERTIFICATE-----\n" - + "MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290\n" - + "MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw\n" - + "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi\n" - + "T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK\n" - + "mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz\n" - + "7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR\n" - + "v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM\n" - + "ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19\n" - + "AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m\n" - + "b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s\n" - + "ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G\n" - + "CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/\n" - + "gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc\n" - + "iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn\n" - + "ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1\n" - + "/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL\n" - + "hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY\n" - + "-----END CERTIFICATE-----"; + private static final String EE_CERT = + """ + -----BEGIN CERTIFICATE----- + MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290 + MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw + ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi + T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK + mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz + 7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR + v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM + ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19 + AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m + b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s + ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G + CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/ + gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc + iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn + ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1 + /a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL + hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY + -----END CERTIFICATE-----"""; public static void main(String[] args) throws Exception { String extension = args[0]; String targetHost = args[1]; - + Security.setProperty("com.sun.security.allowedAIALocations", + "ldap://" + targetHost + "/dc=Root"); X509Certificate trustedCert = loadCertificate(CA_CERT); X509Certificate eeCert = loadCertificate(EE_CERT); Set trustedCertsSet = new HashSet<>(); trustedCertsSet.add(new TrustAnchor(trustedCert, null)); - CertPath cp = (CertPath) CertificateFactory.getInstance("X509") - .generateCertPath(Arrays.asList(eeCert)); + CertPath cp = CertificateFactory.getInstance("X509") + .generateCertPath(List.of(eeCert)); // CertPath validator should try to parse CRLDP and AIA extensions, // and load CRLs/certs which they point to. @@ -151,7 +155,7 @@ public class ExtensionsWithLDAP { = (InetSocketAddress) socket.getRemoteSocketAddress(); hosts.add(remoteAddress.getHostName()); }; - try (SocksProxy proxy = SocksProxy.startProxy(socketConsumer)) { + try (SocksProxy _ = SocksProxy.startProxy(socketConsumer)) { CertPathValidator.getInstance("PKIX").validate(cp, new PKIXParameters(trustedCertsSet)); throw new RuntimeException("CertPathValidatorException not thrown");