1 /* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.net; 27 28 import java.io.IOException; 29 import java.io.ObjectInputStream; 30 import java.io.ObjectOutputStream; 31 import java.io.ObjectStreamField; 32 import java.io.Serializable; 33 import java.net.InetAddress; 34 import java.security.AccessController; 35 import java.security.Permission; 36 import java.security.PermissionCollection; 37 import java.security.PrivilegedAction; 38 import java.security.Security; 39 import java.util.Collections; 40 import java.util.Comparator; 41 import java.util.Enumeration; 42 import java.util.Vector; 43 import java.util.StringJoiner; 44 import java.util.StringTokenizer; 45 import java.util.concurrent.ConcurrentSkipListMap; 46 import sun.net.util.IPAddressUtil; 47 import sun.net.PortConfig; 48 import sun.security.util.RegisteredDomain; 49 import sun.security.util.SecurityConstants; 50 import sun.security.util.Debug; 51 52 53 /** 54 * This class represents access to a network via sockets. 55 * A SocketPermission consists of a 56 * host specification and a set of "actions" specifying ways to 57 * connect to that host. The host is specified as 58 * <pre> 59 * host = (hostname | IPv4address | iPv6reference) [:portrange] 60 * portrange = portnumber | -portnumber | portnumber-[portnumber] 61 * </pre> 62 * The host is expressed as a DNS name, as a numerical IP address, 63 * or as "localhost" (for the local machine). 64 * The wildcard "*" may be included once in a DNS name host 65 * specification. If it is included, it must be in the leftmost 66 * position, as in "*.sun.com". 67 * <p> 68 * The format of the IPv6reference should follow that specified in <a 69 * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732: Format 70 * for Literal IPv6 Addresses in URLs</i></a>: 71 * <pre> 72 * ipv6reference = "[" IPv6address "]" 73 *</pre> 74 * For example, you can construct a SocketPermission instance 75 * as the following: 76 * <pre> 77 * String hostAddress = inetaddress.getHostAddress(); 78 * if (inetaddress instanceof Inet6Address) { 79 * sp = new SocketPermission("[" + hostAddress + "]:" + port, action); 80 * } else { 81 * sp = new SocketPermission(hostAddress + ":" + port, action); 82 * } 83 * </pre> 84 * or 85 * <pre> 86 * String host = url.getHost(); 87 * sp = new SocketPermission(host + ":" + port, action); 88 * </pre> 89 * <p> 90 * The <A HREF="Inet6Address.html#lform">full uncompressed form</A> of 91 * an IPv6 literal address is also valid. 92 * <p> 93 * The port or portrange is optional. A port specification of the 94 * form "N-", where <i>N</i> is a port number, signifies all ports 95 * numbered <i>N</i> and above, while a specification of the 96 * form "-N" indicates all ports numbered <i>N</i> and below. 97 * The special port value {@code 0} refers to the entire <i>ephemeral</i> 98 * port range. This is a fixed range of ports a system may use to 99 * allocate dynamic ports from. The actual range may be system dependent. 100 * <p> 101 * The possible ways to connect to the host are 102 * <pre> 103 * accept 104 * connect 105 * listen 106 * resolve 107 * </pre> 108 * The "listen" action is only meaningful when used with "localhost" and 109 * means the ability to bind to a specified port. 110 * The "resolve" action is implied when any of the other actions are present. 111 * The action "resolve" refers to host/ip name service lookups. 112 * <P> 113 * The actions string is converted to lowercase before processing. 114 * <p>As an example of the creation and meaning of SocketPermissions, 115 * note that if the following permission: 116 * 117 * <pre> 118 * p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept"); 119 * </pre> 120 * 121 * is granted to some code, it allows that code to connect to port 7777 on 122 * {@code puffin.eng.sun.com}, and to accept connections on that port. 123 * 124 * <p>Similarly, if the following permission: 125 * 126 * <pre> 127 * p2 = new SocketPermission("localhost:1024-", "accept,connect,listen"); 128 * </pre> 129 * 130 * is granted to some code, it allows that code to 131 * accept connections on, connect to, or listen on any port between 132 * 1024 and 65535 on the local host. 133 * 134 * <p>Note: Granting code permission to accept or make connections to remote 135 * hosts may be dangerous because malevolent code can then more easily 136 * transfer and share confidential data among parties who may not 137 * otherwise have access to the data. 138 * 139 * @see java.security.Permissions 140 * @see SocketPermission 141 * 142 * 143 * @author Marianne Mueller 144 * @author Roland Schemers 145 * @since 1.2 146 * 147 * @serial exclude 148 */ 149 150 public final class SocketPermission extends Permission 151 implements java.io.Serializable 152 { 153 private static final long serialVersionUID = -7204263841984476862L; 154 155 /** 156 * Connect to host:port 157 */ 158 private static final int CONNECT = 0x1; 159 160 /** 161 * Listen on host:port 162 */ 163 private static final int LISTEN = 0x2; 164 165 /** 166 * Accept a connection from host:port 167 */ 168 private static final int ACCEPT = 0x4; 169 170 /** 171 * Resolve DNS queries 172 */ 173 private static final int RESOLVE = 0x8; 174 175 /** 176 * No actions 177 */ 178 private static final int NONE = 0x0; 179 180 /** 181 * All actions 182 */ 183 private static final int ALL = CONNECT|LISTEN|ACCEPT|RESOLVE; 184 185 // various port constants 186 private static final int PORT_MIN = 0; 187 private static final int PORT_MAX = 65535; 188 private static final int PRIV_PORT_MAX = 1023; 189 private static final int DEF_EPH_LOW = 49152; 190 191 // the actions mask 192 private transient int mask; 193 194 /** 195 * the actions string. 196 * 197 * @serial 198 */ 199 200 private String actions; // Left null as long as possible, then 201 // created and re-used in the getAction function. 202 203 // hostname part as it is passed 204 private transient String hostname; 205 206 // the canonical name of the host 207 // in the case of "*.foo.com", cname is ".foo.com". 208 209 private transient String cname; 210 211 // all the IP addresses of the host 212 private transient InetAddress[] addresses; 213 214 // true if the hostname is a wildcard (e.g. "*.sun.com") 215 private transient boolean wildcard; 216 217 // true if we were initialized with a single numeric IP address 218 private transient boolean init_with_ip; 219 220 // true if this SocketPermission represents an invalid/unknown host 221 // used for implies when the delayed lookup has already failed 222 private transient boolean invalid; 223 224 // port range on host 225 private transient int[] portrange; 226 227 private transient boolean defaultDeny = false; 228 229 // true if this SocketPermission represents a hostname 230 // that failed our reverse mapping heuristic test 231 private transient boolean untrusted; 232 private transient boolean trusted; 233 234 // true if the sun.net.trustNameService system property is set 235 private static boolean trustNameService; 236 237 private static Debug debug = null; 238 private static boolean debugInit = false; 239 240 // lazy initializer 241 private static class EphemeralRange { 242 static final int low = initEphemeralPorts("low", DEF_EPH_LOW); 243 static final int high = initEphemeralPorts("high", PORT_MAX); 244 }; 245 246 static { 247 Boolean tmp = java.security.AccessController.doPrivileged( 248 new sun.security.action.GetBooleanAction("sun.net.trustNameService")); 249 trustNameService = tmp.booleanValue(); 250 } 251 252 private static synchronized Debug getDebug() { 253 if (!debugInit) { 254 debug = Debug.getInstance("access"); 255 debugInit = true; 256 } 257 return debug; 258 } 259 260 /** 261 * Creates a new SocketPermission object with the specified actions. 262 * The host is expressed as a DNS name, or as a numerical IP address. 263 * Optionally, a port or a portrange may be supplied (separated 264 * from the DNS name or IP address by a colon). 265 * <p> 266 * To specify the local machine, use "localhost" as the <i>host</i>. 267 * Also note: An empty <i>host</i> String ("") is equivalent to "localhost". 268 * <p> 269 * The <i>actions</i> parameter contains a comma-separated list of the 270 * actions granted for the specified host (and port(s)). Possible actions are 271 * "connect", "listen", "accept", "resolve", or 272 * any combination of those. "resolve" is automatically added 273 * when any of the other three are specified. 274 * <p> 275 * Examples of SocketPermission instantiation are the following: 276 * <pre> 277 * nr = new SocketPermission("www.catalog.com", "connect"); 278 * nr = new SocketPermission("www.sun.com:80", "connect"); 279 * nr = new SocketPermission("*.sun.com", "connect"); 280 * nr = new SocketPermission("*.edu", "resolve"); 281 * nr = new SocketPermission("204.160.241.0", "connect"); 282 * nr = new SocketPermission("localhost:1024-65535", "listen"); 283 * nr = new SocketPermission("204.160.241.0:1024-65535", "connect"); 284 * </pre> 285 * 286 * @param host the hostname or IPaddress of the computer, optionally 287 * including a colon followed by a port or port range. 288 * @param action the action string. 289 */ 290 public SocketPermission(String host, String action) { 291 super(getHost(host)); 292 // name initialized to getHost(host); NPE detected in getHost() 293 init(getName(), getMask(action)); 294 } 295 296 297 SocketPermission(String host, int mask) { 298 super(getHost(host)); 299 // name initialized to getHost(host); NPE detected in getHost() 300 init(getName(), mask); 301 } 302 303 private void setDeny() { 304 defaultDeny = true; 305 } 306 307 private static String getHost(String host) { 308 if (host.equals("")) { 309 return "localhost"; 310 } else { 311 /* IPv6 literal address used in this context should follow 312 * the format specified in RFC 2732; 313 * if not, we try to solve the unambiguous case 314 */ 315 int ind; 316 if (host.charAt(0) != '[') { 317 if ((ind = host.indexOf(':')) != host.lastIndexOf(':')) { 318 /* More than one ":", meaning IPv6 address is not 319 * in RFC 2732 format; 320 * We will rectify user errors for all unambiguious cases 321 */ 322 StringTokenizer st = new StringTokenizer(host, ":"); 323 int tokens = st.countTokens(); 324 if (tokens == 9) { 325 // IPv6 address followed by port 326 ind = host.lastIndexOf(':'); 327 host = "[" + host.substring(0, ind) + "]" + 328 host.substring(ind); 329 } else if (tokens == 8 && host.indexOf("::") == -1) { 330 // IPv6 address only, not followed by port 331 host = "[" + host + "]"; 332 } else { 333 // could be ambiguous 334 throw new IllegalArgumentException("Ambiguous"+ 335 " hostport part"); 336 } 337 } 338 } 339 return host; 340 } 341 } 342 343 private int[] parsePort(String port) 344 throws Exception 345 { 346 347 if (port == null || port.equals("") || port.equals("*")) { 348 return new int[] {PORT_MIN, PORT_MAX}; 349 } 350 351 int dash = port.indexOf('-'); 352 353 if (dash == -1) { 354 int p = Integer.parseInt(port); 355 return new int[] {p, p}; 356 } else { 357 String low = port.substring(0, dash); 358 String high = port.substring(dash+1); 359 int l,h; 360 361 if (low.equals("")) { 362 l = PORT_MIN; 363 } else { 364 l = Integer.parseInt(low); 365 } 366 367 if (high.equals("")) { 368 h = PORT_MAX; 369 } else { 370 h = Integer.parseInt(high); 371 } 372 if (l < 0 || h < 0 || h<l) 373 throw new IllegalArgumentException("invalid port range"); 374 375 return new int[] {l, h}; 376 } 377 } 378 379 /** 380 * Returns true if the permission has specified zero 381 * as its value (or lower bound) signifying the ephemeral range 382 */ 383 private boolean includesEphemerals() { 384 return portrange[0] == 0; 385 } 386 387 /** 388 * Initialize the SocketPermission object. We don't do any DNS lookups 389 * as this point, instead we hold off until the implies method is 390 * called. 391 */ 392 private void init(String host, int mask) { 393 // Set the integer mask that represents the actions 394 395 if ((mask & ALL) != mask) 396 throw new IllegalArgumentException("invalid actions mask"); 397 398 // always OR in RESOLVE if we allow any of the others 399 this.mask = mask | RESOLVE; 400 401 // Parse the host name. A name has up to three components, the 402 // hostname, a port number, or two numbers representing a port 403 // range. "www.sun.com:8080-9090" is a valid host name. 404 405 // With IPv6 an address can be 2010:836B:4179::836B:4179 406 // An IPv6 address needs to be enclose in [] 407 // For ex: [2010:836B:4179::836B:4179]:8080-9090 408 // Refer to RFC 2732 for more information. 409 410 int rb = 0 ; 411 int start = 0, end = 0; 412 int sep = -1; 413 String hostport = host; 414 if (host.charAt(0) == '[') { 415 start = 1; 416 rb = host.indexOf(']'); 417 if (rb != -1) { 418 host = host.substring(start, rb); 419 } else { 420 throw new 421 IllegalArgumentException("invalid host/port: "+host); 422 } 423 sep = hostport.indexOf(':', rb+1); 424 } else { 425 start = 0; 426 sep = host.indexOf(':', rb); 427 end = sep; 428 if (sep != -1) { 429 host = host.substring(start, end); 430 } 431 } 432 433 if (sep != -1) { 434 String port = hostport.substring(sep+1); 435 try { 436 portrange = parsePort(port); 437 } catch (Exception e) { 438 throw new 439 IllegalArgumentException("invalid port range: "+port); 440 } 441 } else { 442 portrange = new int[] { PORT_MIN, PORT_MAX }; 443 } 444 445 hostname = host; 446 447 // is this a domain wildcard specification 448 if (host.lastIndexOf('*') > 0) { 449 throw new 450 IllegalArgumentException("invalid host wildcard specification"); 451 } else if (host.startsWith("*")) { 452 wildcard = true; 453 if (host.equals("*")) { 454 cname = ""; 455 } else if (host.startsWith("*.")) { 456 cname = host.substring(1).toLowerCase(); 457 } else { 458 throw new 459 IllegalArgumentException("invalid host wildcard specification"); 460 } 461 return; 462 } else { 463 if (!host.isEmpty()) { 464 // see if we are being initialized with an IP address. 465 char ch = host.charAt(0); 466 if (ch == ':' || Character.digit(ch, 16) != -1) { 467 byte ip[] = IPAddressUtil.textToNumericFormatV4(host); 468 if (ip == null) { 469 ip = IPAddressUtil.textToNumericFormatV6(host); 470 } 471 if (ip != null) { 472 try { 473 addresses = 474 new InetAddress[] 475 {InetAddress.getByAddress(ip) }; 476 init_with_ip = true; 477 } catch (UnknownHostException uhe) { 478 // this shouldn't happen 479 invalid = true; 480 } 481 } 482 } 483 } 484 } 485 } 486 487 /** 488 * Convert an action string to an integer actions mask. 489 * 490 * @param action the action string 491 * @return the action mask 492 */ 493 private static int getMask(String action) { 494 495 if (action == null) { 496 throw new NullPointerException("action can't be null"); 497 } 498 499 if (action.equals("")) { 500 throw new IllegalArgumentException("action can't be empty"); 501 } 502 503 int mask = NONE; 504 505 // Use object identity comparison against known-interned strings for 506 // performance benefit (these values are used heavily within the JDK). 507 if (action == SecurityConstants.SOCKET_RESOLVE_ACTION) { 508 return RESOLVE; 509 } else if (action == SecurityConstants.SOCKET_CONNECT_ACTION) { 510 return CONNECT; 511 } else if (action == SecurityConstants.SOCKET_LISTEN_ACTION) { 512 return LISTEN; 513 } else if (action == SecurityConstants.SOCKET_ACCEPT_ACTION) { 514 return ACCEPT; 515 } else if (action == SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION) { 516 return CONNECT|ACCEPT; 517 } 518 519 char[] a = action.toCharArray(); 520 521 int i = a.length - 1; 522 if (i < 0) 523 return mask; 524 525 while (i != -1) { 526 char c; 527 528 // skip whitespace 529 while ((i!=-1) && ((c = a[i]) == ' ' || 530 c == '\r' || 531 c == '\n' || 532 c == '\f' || 533 c == '\t')) 534 i--; 535 536 // check for the known strings 537 int matchlen; 538 539 if (i >= 6 && (a[i-6] == 'c' || a[i-6] == 'C') && 540 (a[i-5] == 'o' || a[i-5] == 'O') && 541 (a[i-4] == 'n' || a[i-4] == 'N') && 542 (a[i-3] == 'n' || a[i-3] == 'N') && 543 (a[i-2] == 'e' || a[i-2] == 'E') && 544 (a[i-1] == 'c' || a[i-1] == 'C') && 545 (a[i] == 't' || a[i] == 'T')) 546 { 547 matchlen = 7; 548 mask |= CONNECT; 549 550 } else if (i >= 6 && (a[i-6] == 'r' || a[i-6] == 'R') && 551 (a[i-5] == 'e' || a[i-5] == 'E') && 552 (a[i-4] == 's' || a[i-4] == 'S') && 553 (a[i-3] == 'o' || a[i-3] == 'O') && 554 (a[i-2] == 'l' || a[i-2] == 'L') && 555 (a[i-1] == 'v' || a[i-1] == 'V') && 556 (a[i] == 'e' || a[i] == 'E')) 557 { 558 matchlen = 7; 559 mask |= RESOLVE; 560 561 } else if (i >= 5 && (a[i-5] == 'l' || a[i-5] == 'L') && 562 (a[i-4] == 'i' || a[i-4] == 'I') && 563 (a[i-3] == 's' || a[i-3] == 'S') && 564 (a[i-2] == 't' || a[i-2] == 'T') && 565 (a[i-1] == 'e' || a[i-1] == 'E') && 566 (a[i] == 'n' || a[i] == 'N')) 567 { 568 matchlen = 6; 569 mask |= LISTEN; 570 571 } else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') && 572 (a[i-4] == 'c' || a[i-4] == 'C') && 573 (a[i-3] == 'c' || a[i-3] == 'C') && 574 (a[i-2] == 'e' || a[i-2] == 'E') && 575 (a[i-1] == 'p' || a[i-1] == 'P') && 576 (a[i] == 't' || a[i] == 'T')) 577 { 578 matchlen = 6; 579 mask |= ACCEPT; 580 581 } else { 582 // parse error 583 throw new IllegalArgumentException( 584 "invalid permission: " + action); 585 } 586 587 // make sure we didn't just match the tail of a word 588 // like "ackbarfaccept". Also, skip to the comma. 589 boolean seencomma = false; 590 while (i >= matchlen && !seencomma) { 591 switch(a[i-matchlen]) { 592 case ',': 593 seencomma = true; 594 break; 595 case ' ': case '\r': case '\n': 596 case '\f': case '\t': 597 break; 598 default: 599 throw new IllegalArgumentException( 600 "invalid permission: " + action); 601 } 602 i--; 603 } 604 605 // point i at the location of the comma minus one (or -1). 606 i -= matchlen; 607 } 608 609 return mask; 610 } 611 612 private boolean isUntrusted() 613 throws UnknownHostException 614 { 615 if (trusted) return false; 616 if (invalid || untrusted) return true; 617 try { 618 if (!trustNameService && (defaultDeny || 619 sun.net.www.URLConnection.isProxiedHost(hostname))) { 620 if (this.cname == null) { 621 this.getCanonName(); 622 } 623 if (!match(cname, hostname)) { 624 // Last chance 625 if (!authorized(hostname, addresses[0].getAddress())) { 626 untrusted = true; 627 Debug debug = getDebug(); 628 if (debug != null && Debug.isOn("failure")) { 629 debug.println("socket access restriction: proxied host " + "(" + addresses[0] + ")" + " does not match " + cname + " from reverse lookup"); 630 } 631 return true; 632 } 633 } 634 trusted = true; 635 } 636 } catch (UnknownHostException uhe) { 637 invalid = true; 638 throw uhe; 639 } 640 return false; 641 } 642 643 /** 644 * attempt to get the fully qualified domain name 645 * 646 */ 647 void getCanonName() 648 throws UnknownHostException 649 { 650 if (cname != null || invalid || untrusted) return; 651 652 // attempt to get the canonical name 653 654 try { 655 // first get the IP addresses if we don't have them yet 656 // this is because we need the IP address to then get 657 // FQDN. 658 if (addresses == null) { 659 getIP(); 660 } 661 662 // we have to do this check, otherwise we might not 663 // get the fully qualified domain name 664 if (init_with_ip) { 665 cname = addresses[0].getHostName(false).toLowerCase(); 666 } else { 667 cname = InetAddress.getByName(addresses[0].getHostAddress()). 668 getHostName(false).toLowerCase(); 669 } 670 } catch (UnknownHostException uhe) { 671 invalid = true; 672 throw uhe; 673 } 674 } 675 676 private transient String cdomain, hdomain; 677 678 /** 679 * previously we allowed domain names to be specified in IDN ACE form 680 * Need to check for that and convert to Unicode 681 */ 682 private static String checkForIDN(String name) { 683 if (name.startsWith("xn--") || name.contains(".xn--")) { 684 return IDN.toUnicode(name); 685 } else { 686 return name; 687 } 688 } 689 690 private boolean match(String cname, String hname) { 691 String a = checkForIDN(cname.toLowerCase()); 692 String b = checkForIDN(hname.toLowerCase()); 693 if (a.startsWith(b) && 694 ((a.length() == b.length()) || (a.charAt(b.length()) == '.'))) { 695 return true; 696 } 697 if (cdomain == null) { 698 cdomain = RegisteredDomain.from(a) 699 .map(RegisteredDomain::name) 700 .orElse(a); 701 } 702 if (hdomain == null) { 703 hdomain = RegisteredDomain.from(b) 704 .map(RegisteredDomain::name) 705 .orElse(b); 706 } 707 708 return !cdomain.isEmpty() && !hdomain.isEmpty() && cdomain.equals(hdomain); 709 } 710 711 private boolean authorized(String cname, byte[] addr) { 712 if (addr.length == 4) 713 return authorizedIPv4(cname, addr); 714 else if (addr.length == 16) 715 return authorizedIPv6(cname, addr); 716 else 717 return false; 718 } 719 720 private boolean authorizedIPv4(String cname, byte[] addr) { 721 String authHost = ""; 722 InetAddress auth; 723 724 try { 725 authHost = "auth." + 726 (addr[3] & 0xff) + "." + (addr[2] & 0xff) + "." + 727 (addr[1] & 0xff) + "." + (addr[0] & 0xff) + 728 ".in-addr.arpa"; 729 // Following check seems unnecessary 730 // auth = InetAddress.getAllByName0(authHost, false)[0]; 731 authHost = hostname + '.' + authHost; 732 auth = InetAddress.getAllByName0(authHost, false)[0]; 733 if (auth.equals(InetAddress.getByAddress(addr))) { 734 return true; 735 } 736 Debug debug = getDebug(); 737 if (debug != null && Debug.isOn("failure")) { 738 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr)); 739 } 740 } catch (UnknownHostException uhe) { 741 Debug debug = getDebug(); 742 if (debug != null && Debug.isOn("failure")) { 743 debug.println("socket access restriction: forward lookup failed for " + authHost); 744 } 745 } 746 return false; 747 } 748 749 private boolean authorizedIPv6(String cname, byte[] addr) { 750 String authHost = ""; 751 InetAddress auth; 752 753 try { 754 StringBuilder sb = new StringBuilder(39); 755 756 for (int i = 15; i >= 0; i--) { 757 sb.append(Integer.toHexString(((addr[i]) & 0x0f))); 758 sb.append('.'); 759 sb.append(Integer.toHexString(((addr[i] >> 4) & 0x0f))); 760 sb.append('.'); 761 } 762 authHost = "auth." + sb.toString() + "IP6.ARPA"; 763 //auth = InetAddress.getAllByName0(authHost, false)[0]; 764 authHost = hostname + '.' + authHost; 765 auth = InetAddress.getAllByName0(authHost, false)[0]; 766 if (auth.equals(InetAddress.getByAddress(addr))) 767 return true; 768 Debug debug = getDebug(); 769 if (debug != null && Debug.isOn("failure")) { 770 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr)); 771 } 772 } catch (UnknownHostException uhe) { 773 Debug debug = getDebug(); 774 if (debug != null && Debug.isOn("failure")) { 775 debug.println("socket access restriction: forward lookup failed for " + authHost); 776 } 777 } 778 return false; 779 } 780 781 782 /** 783 * get IP addresses. Sets invalid to true if we can't get them. 784 * 785 */ 786 void getIP() 787 throws UnknownHostException 788 { 789 if (addresses != null || wildcard || invalid) return; 790 791 try { 792 // now get all the IP addresses 793 String host; 794 if (getName().charAt(0) == '[') { 795 // Literal IPv6 address 796 host = getName().substring(1, getName().indexOf(']')); 797 } else { 798 int i = getName().indexOf(':'); 799 if (i == -1) 800 host = getName(); 801 else { 802 host = getName().substring(0,i); 803 } 804 } 805 806 addresses = 807 new InetAddress[] {InetAddress.getAllByName0(host, false)[0]}; 808 809 } catch (UnknownHostException uhe) { 810 invalid = true; 811 throw uhe; 812 } catch (IndexOutOfBoundsException iobe) { 813 invalid = true; 814 throw new UnknownHostException(getName()); 815 } 816 } 817 818 /** 819 * Checks if this socket permission object "implies" the 820 * specified permission. 821 * <P> 822 * More specifically, this method first ensures that all of the following 823 * are true (and returns false if any of them are not): 824 * <ul> 825 * <li> <i>p</i> is an instanceof SocketPermission, 826 * <li> <i>p</i>'s actions are a proper subset of this 827 * object's actions, and 828 * <li> <i>p</i>'s port range is included in this port range. Note: 829 * port range is ignored when p only contains the action, 'resolve'. 830 * </ul> 831 * 832 * Then {@code implies} checks each of the following, in order, 833 * and for each returns true if the stated condition is true: 834 * <ul> 835 * <li> If this object was initialized with a single IP address and one of <i>p</i>'s 836 * IP addresses is equal to this object's IP address. 837 * <li>If this object is a wildcard domain (such as *.sun.com), and 838 * <i>p</i>'s canonical name (the name without any preceding *) 839 * ends with this object's canonical host name. For example, *.sun.com 840 * implies *.eng.sun.com. 841 * <li>If this object was not initialized with a single IP address, and one of this 842 * object's IP addresses equals one of <i>p</i>'s IP addresses. 843 * <li>If this canonical name equals <i>p</i>'s canonical name. 844 * </ul> 845 * 846 * If none of the above are true, {@code implies} returns false. 847 * @param p the permission to check against. 848 * 849 * @return true if the specified permission is implied by this object, 850 * false if not. 851 */ 852 @Override 853 public boolean implies(Permission p) { 854 int i,j; 855 856 if (!(p instanceof SocketPermission)) 857 return false; 858 859 if (p == this) 860 return true; 861 862 SocketPermission that = (SocketPermission) p; 863 864 return ((this.mask & that.mask) == that.mask) && 865 impliesIgnoreMask(that); 866 } 867 868 /** 869 * Checks if the incoming Permission's action are a proper subset of 870 * the this object's actions. 871 * <P> 872 * Check, in the following order: 873 * <ul> 874 * <li> Checks that "p" is an instanceof a SocketPermission 875 * <li> Checks that "p"'s actions are a proper subset of the 876 * current object's actions. 877 * <li> Checks that "p"'s port range is included in this port range 878 * <li> If this object was initialized with an IP address, checks that 879 * one of "p"'s IP addresses is equal to this object's IP address. 880 * <li> If either object is a wildcard domain (i.e., "*.sun.com"), 881 * attempt to match based on the wildcard. 882 * <li> If this object was not initialized with an IP address, attempt 883 * to find a match based on the IP addresses in both objects. 884 * <li> Attempt to match on the canonical hostnames of both objects. 885 * </ul> 886 * @param that the incoming permission request 887 * 888 * @return true if "permission" is a proper subset of the current object, 889 * false if not. 890 */ 891 boolean impliesIgnoreMask(SocketPermission that) { 892 int i,j; 893 894 if ((that.mask & RESOLVE) != that.mask) { 895 896 // check simple port range 897 if ((that.portrange[0] < this.portrange[0]) || 898 (that.portrange[1] > this.portrange[1])) { 899 900 // if either includes the ephemeral range, do full check 901 if (this.includesEphemerals() || that.includesEphemerals()) { 902 if (!inRange(this.portrange[0], this.portrange[1], 903 that.portrange[0], that.portrange[1])) 904 { 905 return false; 906 } 907 } else { 908 return false; 909 } 910 } 911 } 912 913 // allow a "*" wildcard to always match anything 914 if (this.wildcard && "".equals(this.cname)) 915 return true; 916 917 // return if either one of these NetPerm objects are invalid... 918 if (this.invalid || that.invalid) { 919 return compareHostnames(that); 920 } 921 922 try { 923 if (this.init_with_ip) { // we only check IP addresses 924 if (that.wildcard) 925 return false; 926 927 if (that.init_with_ip) { 928 return (this.addresses[0].equals(that.addresses[0])); 929 } else { 930 if (that.addresses == null) { 931 that.getIP(); 932 } 933 for (i=0; i < that.addresses.length; i++) { 934 if (this.addresses[0].equals(that.addresses[i])) 935 return true; 936 } 937 } 938 // since "this" was initialized with an IP address, we 939 // don't check any other cases 940 return false; 941 } 942 943 // check and see if we have any wildcards... 944 if (this.wildcard || that.wildcard) { 945 // if they are both wildcards, return true iff 946 // that's cname ends with this cname (i.e., *.sun.com 947 // implies *.eng.sun.com) 948 if (this.wildcard && that.wildcard) 949 return (that.cname.endsWith(this.cname)); 950 951 // a non-wildcard can't imply a wildcard 952 if (that.wildcard) 953 return false; 954 955 // this is a wildcard, lets see if that's cname ends with 956 // it... 957 if (that.cname == null) { 958 that.getCanonName(); 959 } 960 return (that.cname.endsWith(this.cname)); 961 } 962 963 // comapare IP addresses 964 if (this.addresses == null) { 965 this.getIP(); 966 } 967 968 if (that.addresses == null) { 969 that.getIP(); 970 } 971 972 if (!(that.init_with_ip && this.isUntrusted())) { 973 for (j = 0; j < this.addresses.length; j++) { 974 for (i=0; i < that.addresses.length; i++) { 975 if (this.addresses[j].equals(that.addresses[i])) 976 return true; 977 } 978 } 979 980 // XXX: if all else fails, compare hostnames? 981 // Do we really want this? 982 if (this.cname == null) { 983 this.getCanonName(); 984 } 985 986 if (that.cname == null) { 987 that.getCanonName(); 988 } 989 990 return (this.cname.equalsIgnoreCase(that.cname)); 991 } 992 993 } catch (UnknownHostException uhe) { 994 return compareHostnames(that); 995 } 996 997 // make sure the first thing that is done here is to return 998 // false. If not, uncomment the return false in the above catch. 999 1000 return false; 1001 } 1002 1003 private boolean compareHostnames(SocketPermission that) { 1004 // we see if the original names/IPs passed in were equal. 1005 1006 String thisHost = hostname; 1007 String thatHost = that.hostname; 1008 1009 if (thisHost == null) { 1010 return false; 1011 } else if (this.wildcard) { 1012 final int cnameLength = this.cname.length(); 1013 return thatHost.regionMatches(true, 1014 (thatHost.length() - cnameLength), 1015 this.cname, 0, cnameLength); 1016 } else { 1017 return thisHost.equalsIgnoreCase(thatHost); 1018 } 1019 } 1020 1021 /** 1022 * Checks two SocketPermission objects for equality. 1023 * 1024 * @param obj the object to test for equality with this object. 1025 * 1026 * @return true if <i>obj</i> is a SocketPermission, and has the 1027 * same hostname, port range, and actions as this 1028 * SocketPermission object. However, port range will be ignored 1029 * in the comparison if <i>obj</i> only contains the action, 'resolve'. 1030 */ 1031 @Override 1032 public boolean equals(Object obj) { 1033 if (obj == this) 1034 return true; 1035 1036 if (! (obj instanceof SocketPermission)) 1037 return false; 1038 1039 SocketPermission that = (SocketPermission) obj; 1040 1041 //this is (overly?) complex!!! 1042 1043 // check the mask first 1044 if (this.mask != that.mask) return false; 1045 1046 if ((that.mask & RESOLVE) != that.mask) { 1047 // now check the port range... 1048 if ((this.portrange[0] != that.portrange[0]) || 1049 (this.portrange[1] != that.portrange[1])) { 1050 return false; 1051 } 1052 } 1053 1054 // short cut. This catches: 1055 // "crypto" equal to "crypto", or 1056 // "1.2.3.4" equal to "1.2.3.4.", or 1057 // "*.edu" equal to "*.edu", but it 1058 // does not catch "crypto" equal to 1059 // "crypto.eng.sun.com". 1060 1061 if (this.getName().equalsIgnoreCase(that.getName())) { 1062 return true; 1063 } 1064 1065 // we now attempt to get the Canonical (FQDN) name and 1066 // compare that. If this fails, about all we can do is return 1067 // false. 1068 1069 try { 1070 this.getCanonName(); 1071 that.getCanonName(); 1072 } catch (UnknownHostException uhe) { 1073 return false; 1074 } 1075 1076 if (this.invalid || that.invalid) 1077 return false; 1078 1079 if (this.cname != null) { 1080 return this.cname.equalsIgnoreCase(that.cname); 1081 } 1082 1083 return false; 1084 } 1085 1086 /** 1087 * Returns the hash code value for this object. 1088 * 1089 * @return a hash code value for this object. 1090 */ 1091 @Override 1092 public int hashCode() { 1093 /* 1094 * If this SocketPermission was initialized with an IP address 1095 * or a wildcard, use getName().hashCode(), otherwise use 1096 * the hashCode() of the host name returned from 1097 * java.net.InetAddress.getHostName method. 1098 */ 1099 1100 if (init_with_ip || wildcard) { 1101 return this.getName().hashCode(); 1102 } 1103 1104 try { 1105 getCanonName(); 1106 } catch (UnknownHostException uhe) { 1107 1108 } 1109 1110 if (invalid || cname == null) 1111 return this.getName().hashCode(); 1112 else 1113 return this.cname.hashCode(); 1114 } 1115 1116 /** 1117 * Return the current action mask. 1118 * 1119 * @return the actions mask. 1120 */ 1121 1122 int getMask() { 1123 return mask; 1124 } 1125 1126 /** 1127 * Returns the "canonical string representation" of the actions in the 1128 * specified mask. 1129 * Always returns present actions in the following order: 1130 * connect, listen, accept, resolve. 1131 * 1132 * @param mask a specific integer action mask to translate into a string 1133 * @return the canonical string representation of the actions 1134 */ 1135 private static String getActions(int mask) { 1136 StringJoiner sj = new StringJoiner(","); 1137 if ((mask & CONNECT) == CONNECT) { 1138 sj.add("connect"); 1139 } 1140 if ((mask & LISTEN) == LISTEN) { 1141 sj.add("listen"); 1142 } 1143 if ((mask & ACCEPT) == ACCEPT) { 1144 sj.add("accept"); 1145 } 1146 if ((mask & RESOLVE) == RESOLVE) { 1147 sj.add("resolve"); 1148 } 1149 return sj.toString(); 1150 } 1151 1152 /** 1153 * Returns the canonical string representation of the actions. 1154 * Always returns present actions in the following order: 1155 * connect, listen, accept, resolve. 1156 * 1157 * @return the canonical string representation of the actions. 1158 */ 1159 @Override 1160 public String getActions() 1161 { 1162 if (actions == null) 1163 actions = getActions(this.mask); 1164 1165 return actions; 1166 } 1167 1168 /** 1169 * Returns a new PermissionCollection object for storing SocketPermission 1170 * objects. 1171 * <p> 1172 * SocketPermission objects must be stored in a manner that allows them 1173 * to be inserted into the collection in any order, but that also enables the 1174 * PermissionCollection {@code implies} 1175 * method to be implemented in an efficient (and consistent) manner. 1176 * 1177 * @return a new PermissionCollection object suitable for storing SocketPermissions. 1178 */ 1179 @Override 1180 public PermissionCollection newPermissionCollection() { 1181 return new SocketPermissionCollection(); 1182 } 1183 1184 /** 1185 * WriteObject is called to save the state of the SocketPermission 1186 * to a stream. The actions are serialized, and the superclass 1187 * takes care of the name. 1188 */ 1189 private synchronized void writeObject(java.io.ObjectOutputStream s) 1190 throws IOException 1191 { 1192 // Write out the actions. The superclass takes care of the name 1193 // call getActions to make sure actions field is initialized 1194 if (actions == null) 1195 getActions(); 1196 s.defaultWriteObject(); 1197 } 1198 1199 /** 1200 * readObject is called to restore the state of the SocketPermission from 1201 * a stream. 1202 */ 1203 private synchronized void readObject(java.io.ObjectInputStream s) 1204 throws IOException, ClassNotFoundException 1205 { 1206 // Read in the action, then initialize the rest 1207 s.defaultReadObject(); 1208 init(getName(),getMask(actions)); 1209 } 1210 1211 /** 1212 * Check the system/security property for the ephemeral port range 1213 * for this system. The suffix is either "high" or "low" 1214 */ 1215 private static int initEphemeralPorts(String suffix, int defval) { 1216 return AccessController.doPrivileged( 1217 new PrivilegedAction<>(){ 1218 public Integer run() { 1219 int val = Integer.getInteger( 1220 "jdk.net.ephemeralPortRange."+suffix, -1 1221 ); 1222 if (val != -1) { 1223 return val; 1224 } else { 1225 return suffix.equals("low") ? 1226 PortConfig.getLower() : PortConfig.getUpper(); 1227 } 1228 } 1229 } 1230 ); 1231 } 1232 1233 /** 1234 * Check if the target range is within the policy range 1235 * together with the ephemeral range for this platform 1236 * (if policy includes ephemeral range) 1237 */ 1238 private static boolean inRange( 1239 int policyLow, int policyHigh, int targetLow, int targetHigh 1240 ) 1241 { 1242 final int ephemeralLow = EphemeralRange.low; 1243 final int ephemeralHigh = EphemeralRange.high; 1244 1245 if (targetLow == 0) { 1246 // check policy includes ephemeral range 1247 if (!inRange(policyLow, policyHigh, ephemeralLow, ephemeralHigh)) { 1248 return false; 1249 } 1250 if (targetHigh == 0) { 1251 // nothing left to do 1252 return true; 1253 } 1254 // continue check with first real port number 1255 targetLow = 1; 1256 } 1257 1258 if (policyLow == 0 && policyHigh == 0) { 1259 // ephemeral range only 1260 return targetLow >= ephemeralLow && targetHigh <= ephemeralHigh; 1261 } 1262 1263 if (policyLow != 0) { 1264 // simple check of policy only 1265 return targetLow >= policyLow && targetHigh <= policyHigh; 1266 } 1267 1268 // policyLow == 0 which means possibly two ranges to check 1269 1270 // first check if policy and ephem range overlap/contiguous 1271 1272 if (policyHigh >= ephemeralLow - 1) { 1273 return targetHigh <= ephemeralHigh; 1274 } 1275 1276 // policy and ephem range do not overlap 1277 1278 // target range must lie entirely inside policy range or eph range 1279 1280 return (targetLow <= policyHigh && targetHigh <= policyHigh) || 1281 (targetLow >= ephemeralLow && targetHigh <= ephemeralHigh); 1282 } 1283 /* 1284 public String toString() 1285 { 1286 StringBuffer s = new StringBuffer(super.toString() + "\n" + 1287 "cname = " + cname + "\n" + 1288 "wildcard = " + wildcard + "\n" + 1289 "invalid = " + invalid + "\n" + 1290 "portrange = " + portrange[0] + "," + portrange[1] + "\n"); 1291 if (addresses != null) for (int i=0; i<addresses.length; i++) { 1292 s.append( addresses[i].getHostAddress()); 1293 s.append("\n"); 1294 } else { 1295 s.append("(no addresses)\n"); 1296 } 1297 1298 return s.toString(); 1299 } 1300 1301 public static void main(String args[]) throws Exception { 1302 SocketPermission this_ = new SocketPermission(args[0], "connect"); 1303 SocketPermission that_ = new SocketPermission(args[1], "connect"); 1304 System.out.println("-----\n"); 1305 System.out.println("this.implies(that) = " + this_.implies(that_)); 1306 System.out.println("-----\n"); 1307 System.out.println("this = "+this_); 1308 System.out.println("-----\n"); 1309 System.out.println("that = "+that_); 1310 System.out.println("-----\n"); 1311 1312 SocketPermissionCollection nps = new SocketPermissionCollection(); 1313 nps.add(this_); 1314 nps.add(new SocketPermission("www-leland.stanford.edu","connect")); 1315 nps.add(new SocketPermission("www-sun.com","connect")); 1316 System.out.println("nps.implies(that) = " + nps.implies(that_)); 1317 System.out.println("-----\n"); 1318 } 1319 */ 1320 } 1321 1322 /** 1323 1324 if (init'd with IP, key is IP as string) 1325 if wildcard, its the wild card 1326 else its the cname? 1327 1328 * 1329 * @see java.security.Permission 1330 * @see java.security.Permissions 1331 * @see java.security.PermissionCollection 1332 * 1333 * 1334 * @author Roland Schemers 1335 * 1336 * @serial include 1337 */ 1338 1339 final class SocketPermissionCollection extends PermissionCollection 1340 implements Serializable 1341 { 1342 // Not serialized; see serialization section at end of class 1343 // A ConcurrentSkipListMap is used to preserve order, so that most 1344 // recently added permissions are checked first (see JDK-4301064). 1345 private transient ConcurrentSkipListMap<String, SocketPermission> perms; 1346 1347 /** 1348 * Create an empty SocketPermissions object. 1349 * 1350 */ 1351 public SocketPermissionCollection() { 1352 perms = new ConcurrentSkipListMap<>(new SPCComparator()); 1353 } 1354 1355 /** 1356 * Adds a permission to the SocketPermissions. The key for the hash is 1357 * the name in the case of wildcards, or all the IP addresses. 1358 * 1359 * @param permission the Permission object to add. 1360 * 1361 * @exception IllegalArgumentException - if the permission is not a 1362 * SocketPermission 1363 * 1364 * @exception SecurityException - if this SocketPermissionCollection object 1365 * has been marked readonly 1366 */ 1367 @Override 1368 public void add(Permission permission) { 1369 if (! (permission instanceof SocketPermission)) 1370 throw new IllegalArgumentException("invalid permission: "+ 1371 permission); 1372 if (isReadOnly()) 1373 throw new SecurityException( 1374 "attempt to add a Permission to a readonly PermissionCollection"); 1375 1376 SocketPermission sp = (SocketPermission)permission; 1377 1378 // Add permission to map if it is absent, or replace with new 1379 // permission if applicable. NOTE: cannot use lambda for 1380 // remappingFunction parameter until JDK-8076596 is fixed. 1381 perms.merge(sp.getName(), sp, 1382 new java.util.function.BiFunction<>() { 1383 @Override 1384 public SocketPermission apply(SocketPermission existingVal, 1385 SocketPermission newVal) { 1386 int oldMask = existingVal.getMask(); 1387 int newMask = newVal.getMask(); 1388 if (oldMask != newMask) { 1389 int effective = oldMask | newMask; 1390 if (effective == newMask) { 1391 return newVal; 1392 } 1393 if (effective != oldMask) { 1394 return new SocketPermission(sp.getName(), 1395 effective); 1396 } 1397 } 1398 return existingVal; 1399 } 1400 } 1401 ); 1402 } 1403 1404 /** 1405 * Check and see if this collection of permissions implies the permissions 1406 * expressed in "permission". 1407 * 1408 * @param permission the Permission object to compare 1409 * 1410 * @return true if "permission" is a proper subset of a permission in 1411 * the collection, false if not. 1412 */ 1413 @Override 1414 public boolean implies(Permission permission) 1415 { 1416 if (! (permission instanceof SocketPermission)) 1417 return false; 1418 1419 SocketPermission np = (SocketPermission) permission; 1420 1421 int desired = np.getMask(); 1422 int effective = 0; 1423 int needed = desired; 1424 1425 //System.out.println("implies "+np); 1426 for (SocketPermission x : perms.values()) { 1427 //System.out.println(" trying "+x); 1428 if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) { 1429 effective |= x.getMask(); 1430 if ((effective & desired) == desired) { 1431 return true; 1432 } 1433 needed = (desired ^ effective); 1434 } 1435 } 1436 return false; 1437 } 1438 1439 /** 1440 * Returns an enumeration of all the SocketPermission objects in the 1441 * container. 1442 * 1443 * @return an enumeration of all the SocketPermission objects. 1444 */ 1445 @Override 1446 @SuppressWarnings("unchecked") 1447 public Enumeration<Permission> elements() { 1448 return (Enumeration)Collections.enumeration(perms.values()); 1449 } 1450 1451 private static final long serialVersionUID = 2787186408602843674L; 1452 1453 // Need to maintain serialization interoperability with earlier releases, 1454 // which had the serializable field: 1455 1456 // 1457 // The SocketPermissions for this set. 1458 // @serial 1459 // 1460 // private Vector permissions; 1461 1462 /** 1463 * @serialField permissions java.util.Vector 1464 * A list of the SocketPermissions for this set. 1465 */ 1466 private static final ObjectStreamField[] serialPersistentFields = { 1467 new ObjectStreamField("permissions", Vector.class), 1468 }; 1469 1470 /** 1471 * @serialData "permissions" field (a Vector containing the SocketPermissions). 1472 */ 1473 /* 1474 * Writes the contents of the perms field out as a Vector for 1475 * serialization compatibility with earlier releases. 1476 */ 1477 private void writeObject(ObjectOutputStream out) throws IOException { 1478 // Don't call out.defaultWriteObject() 1479 1480 // Write out Vector 1481 Vector<SocketPermission> permissions = new Vector<>(perms.values()); 1482 1483 ObjectOutputStream.PutField pfields = out.putFields(); 1484 pfields.put("permissions", permissions); 1485 out.writeFields(); 1486 } 1487 1488 /* 1489 * Reads in a Vector of SocketPermissions and saves them in the perms field. 1490 */ 1491 private void readObject(ObjectInputStream in) 1492 throws IOException, ClassNotFoundException 1493 { 1494 // Don't call in.defaultReadObject() 1495 1496 // Read in serialized fields 1497 ObjectInputStream.GetField gfields = in.readFields(); 1498 1499 // Get the one we want 1500 @SuppressWarnings("unchecked") 1501 Vector<SocketPermission> permissions = (Vector<SocketPermission>)gfields.get("permissions", null); 1502 perms = new ConcurrentSkipListMap<>(new SPCComparator()); 1503 for (SocketPermission sp : permissions) { 1504 perms.put(sp.getName(), sp); 1505 } 1506 } 1507 1508 /** 1509 * A simple comparator that orders new non-equal entries at the beginning. 1510 */ 1511 private static class SPCComparator implements Comparator<String> { 1512 @Override 1513 public int compare(String s1, String s2) { 1514 if (s1.equals(s2)) { 1515 return 0; 1516 } 1517 return -1; 1518 } 1519 } 1520 }