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.length() > 0) { 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 private boolean match(String cname, String hname) { 679 String a = cname.toLowerCase(); 680 String b = hname.toLowerCase(); 681 if (a.startsWith(b) && 682 ((a.length() == b.length()) || (a.charAt(b.length()) == '.'))) { 683 return true; 684 } 685 if (cdomain == null) { 686 cdomain = RegisteredDomain.from(a) 687 .map(RegisteredDomain::name) 688 .orElse(a); 689 } 690 if (hdomain == null) { 691 hdomain = RegisteredDomain.from(b) 692 .map(RegisteredDomain::name) 693 .orElse(b); 694 } 695 696 return cdomain.length() != 0 && hdomain.length() != 0 697 && cdomain.equals(hdomain); 698 } 699 700 private boolean authorized(String cname, byte[] addr) { 701 if (addr.length == 4) 702 return authorizedIPv4(cname, addr); 703 else if (addr.length == 16) 704 return authorizedIPv6(cname, addr); 705 else 706 return false; 707 } 708 709 private boolean authorizedIPv4(String cname, byte[] addr) { 710 String authHost = ""; 711 InetAddress auth; 712 713 try { 714 authHost = "auth." + 715 (addr[3] & 0xff) + "." + (addr[2] & 0xff) + "." + 716 (addr[1] & 0xff) + "." + (addr[0] & 0xff) + 717 ".in-addr.arpa"; 718 // Following check seems unnecessary 719 // auth = InetAddress.getAllByName0(authHost, false)[0]; 720 authHost = hostname + '.' + authHost; 721 auth = InetAddress.getAllByName0(authHost, false)[0]; 722 if (auth.equals(InetAddress.getByAddress(addr))) { 723 return true; 724 } 725 Debug debug = getDebug(); 726 if (debug != null && Debug.isOn("failure")) { 727 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr)); 728 } 729 } catch (UnknownHostException uhe) { 730 Debug debug = getDebug(); 731 if (debug != null && Debug.isOn("failure")) { 732 debug.println("socket access restriction: forward lookup failed for " + authHost); 733 } 734 } 735 return false; 736 } 737 738 private boolean authorizedIPv6(String cname, byte[] addr) { 739 String authHost = ""; 740 InetAddress auth; 741 742 try { 743 StringBuilder sb = new StringBuilder(39); 744 745 for (int i = 15; i >= 0; i--) { 746 sb.append(Integer.toHexString(((addr[i]) & 0x0f))); 747 sb.append('.'); 748 sb.append(Integer.toHexString(((addr[i] >> 4) & 0x0f))); 749 sb.append('.'); 750 } 751 authHost = "auth." + sb.toString() + "IP6.ARPA"; 752 //auth = InetAddress.getAllByName0(authHost, false)[0]; 753 authHost = hostname + '.' + authHost; 754 auth = InetAddress.getAllByName0(authHost, false)[0]; 755 if (auth.equals(InetAddress.getByAddress(addr))) 756 return true; 757 Debug debug = getDebug(); 758 if (debug != null && Debug.isOn("failure")) { 759 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr)); 760 } 761 } catch (UnknownHostException uhe) { 762 Debug debug = getDebug(); 763 if (debug != null && Debug.isOn("failure")) { 764 debug.println("socket access restriction: forward lookup failed for " + authHost); 765 } 766 } 767 return false; 768 } 769 770 771 /** 772 * get IP addresses. Sets invalid to true if we can't get them. 773 * 774 */ 775 void getIP() 776 throws UnknownHostException 777 { 778 if (addresses != null || wildcard || invalid) return; 779 780 try { 781 // now get all the IP addresses 782 String host; 783 if (getName().charAt(0) == '[') { 784 // Literal IPv6 address 785 host = getName().substring(1, getName().indexOf(']')); 786 } else { 787 int i = getName().indexOf(':'); 788 if (i == -1) 789 host = getName(); 790 else { 791 host = getName().substring(0,i); 792 } 793 } 794 795 addresses = 796 new InetAddress[] {InetAddress.getAllByName0(host, false)[0]}; 797 798 } catch (UnknownHostException uhe) { 799 invalid = true; 800 throw uhe; 801 } catch (IndexOutOfBoundsException iobe) { 802 invalid = true; 803 throw new UnknownHostException(getName()); 804 } 805 } 806 807 /** 808 * Checks if this socket permission object "implies" the 809 * specified permission. 810 * <P> 811 * More specifically, this method first ensures that all of the following 812 * are true (and returns false if any of them are not): 813 * <ul> 814 * <li> <i>p</i> is an instanceof SocketPermission, 815 * <li> <i>p</i>'s actions are a proper subset of this 816 * object's actions, and 817 * <li> <i>p</i>'s port range is included in this port range. Note: 818 * port range is ignored when p only contains the action, 'resolve'. 819 * </ul> 820 * 821 * Then {@code implies} checks each of the following, in order, 822 * and for each returns true if the stated condition is true: 823 * <ul> 824 * <li> If this object was initialized with a single IP address and one of <i>p</i>'s 825 * IP addresses is equal to this object's IP address. 826 * <li>If this object is a wildcard domain (such as *.sun.com), and 827 * <i>p</i>'s canonical name (the name without any preceding *) 828 * ends with this object's canonical host name. For example, *.sun.com 829 * implies *.eng.sun.com. 830 * <li>If this object was not initialized with a single IP address, and one of this 831 * object's IP addresses equals one of <i>p</i>'s IP addresses. 832 * <li>If this canonical name equals <i>p</i>'s canonical name. 833 * </ul> 834 * 835 * If none of the above are true, {@code implies} returns false. 836 * @param p the permission to check against. 837 * 838 * @return true if the specified permission is implied by this object, 839 * false if not. 840 */ 841 @Override 842 public boolean implies(Permission p) { 843 int i,j; 844 845 if (!(p instanceof SocketPermission)) 846 return false; 847 848 if (p == this) 849 return true; 850 851 SocketPermission that = (SocketPermission) p; 852 853 return ((this.mask & that.mask) == that.mask) && 854 impliesIgnoreMask(that); 855 } 856 857 /** 858 * Checks if the incoming Permission's action are a proper subset of 859 * the this object's actions. 860 * <P> 861 * Check, in the following order: 862 * <ul> 863 * <li> Checks that "p" is an instanceof a SocketPermission 864 * <li> Checks that "p"'s actions are a proper subset of the 865 * current object's actions. 866 * <li> Checks that "p"'s port range is included in this port range 867 * <li> If this object was initialized with an IP address, checks that 868 * one of "p"'s IP addresses is equal to this object's IP address. 869 * <li> If either object is a wildcard domain (i.e., "*.sun.com"), 870 * attempt to match based on the wildcard. 871 * <li> If this object was not initialized with an IP address, attempt 872 * to find a match based on the IP addresses in both objects. 873 * <li> Attempt to match on the canonical hostnames of both objects. 874 * </ul> 875 * @param that the incoming permission request 876 * 877 * @return true if "permission" is a proper subset of the current object, 878 * false if not. 879 */ 880 boolean impliesIgnoreMask(SocketPermission that) { 881 int i,j; 882 883 if ((that.mask & RESOLVE) != that.mask) { 884 885 // check simple port range 886 if ((that.portrange[0] < this.portrange[0]) || 887 (that.portrange[1] > this.portrange[1])) { 888 889 // if either includes the ephemeral range, do full check 890 if (this.includesEphemerals() || that.includesEphemerals()) { 891 if (!inRange(this.portrange[0], this.portrange[1], 892 that.portrange[0], that.portrange[1])) 893 { 894 return false; 895 } 896 } else { 897 return false; 898 } 899 } 900 } 901 902 // allow a "*" wildcard to always match anything 903 if (this.wildcard && "".equals(this.cname)) 904 return true; 905 906 // return if either one of these NetPerm objects are invalid... 907 if (this.invalid || that.invalid) { 908 return compareHostnames(that); 909 } 910 911 try { 912 if (this.init_with_ip) { // we only check IP addresses 913 if (that.wildcard) 914 return false; 915 916 if (that.init_with_ip) { 917 return (this.addresses[0].equals(that.addresses[0])); 918 } else { 919 if (that.addresses == null) { 920 that.getIP(); 921 } 922 for (i=0; i < that.addresses.length; i++) { 923 if (this.addresses[0].equals(that.addresses[i])) 924 return true; 925 } 926 } 927 // since "this" was initialized with an IP address, we 928 // don't check any other cases 929 return false; 930 } 931 932 // check and see if we have any wildcards... 933 if (this.wildcard || that.wildcard) { 934 // if they are both wildcards, return true iff 935 // that's cname ends with this cname (i.e., *.sun.com 936 // implies *.eng.sun.com) 937 if (this.wildcard && that.wildcard) 938 return (that.cname.endsWith(this.cname)); 939 940 // a non-wildcard can't imply a wildcard 941 if (that.wildcard) 942 return false; 943 944 // this is a wildcard, lets see if that's cname ends with 945 // it... 946 if (that.cname == null) { 947 that.getCanonName(); 948 } 949 return (that.cname.endsWith(this.cname)); 950 } 951 952 // comapare IP addresses 953 if (this.addresses == null) { 954 this.getIP(); 955 } 956 957 if (that.addresses == null) { 958 that.getIP(); 959 } 960 961 if (!(that.init_with_ip && this.isUntrusted())) { 962 for (j = 0; j < this.addresses.length; j++) { 963 for (i=0; i < that.addresses.length; i++) { 964 if (this.addresses[j].equals(that.addresses[i])) 965 return true; 966 } 967 } 968 969 // XXX: if all else fails, compare hostnames? 970 // Do we really want this? 971 if (this.cname == null) { 972 this.getCanonName(); 973 } 974 975 if (that.cname == null) { 976 that.getCanonName(); 977 } 978 979 return (this.cname.equalsIgnoreCase(that.cname)); 980 } 981 982 } catch (UnknownHostException uhe) { 983 return compareHostnames(that); 984 } 985 986 // make sure the first thing that is done here is to return 987 // false. If not, uncomment the return false in the above catch. 988 989 return false; 990 } 991 992 private boolean compareHostnames(SocketPermission that) { 993 // we see if the original names/IPs passed in were equal. 994 995 String thisHost = hostname; 996 String thatHost = that.hostname; 997 998 if (thisHost == null) { 999 return false; 1000 } else if (this.wildcard) { 1001 final int cnameLength = this.cname.length(); 1002 return thatHost.regionMatches(true, 1003 (thatHost.length() - cnameLength), 1004 this.cname, 0, cnameLength); 1005 } else { 1006 return thisHost.equalsIgnoreCase(thatHost); 1007 } 1008 } 1009 1010 /** 1011 * Checks two SocketPermission objects for equality. 1012 * 1013 * @param obj the object to test for equality with this object. 1014 * 1015 * @return true if <i>obj</i> is a SocketPermission, and has the 1016 * same hostname, port range, and actions as this 1017 * SocketPermission object. However, port range will be ignored 1018 * in the comparison if <i>obj</i> only contains the action, 'resolve'. 1019 */ 1020 @Override 1021 public boolean equals(Object obj) { 1022 if (obj == this) 1023 return true; 1024 1025 if (! (obj instanceof SocketPermission)) 1026 return false; 1027 1028 SocketPermission that = (SocketPermission) obj; 1029 1030 //this is (overly?) complex!!! 1031 1032 // check the mask first 1033 if (this.mask != that.mask) return false; 1034 1035 if ((that.mask & RESOLVE) != that.mask) { 1036 // now check the port range... 1037 if ((this.portrange[0] != that.portrange[0]) || 1038 (this.portrange[1] != that.portrange[1])) { 1039 return false; 1040 } 1041 } 1042 1043 // short cut. This catches: 1044 // "crypto" equal to "crypto", or 1045 // "1.2.3.4" equal to "1.2.3.4.", or 1046 // "*.edu" equal to "*.edu", but it 1047 // does not catch "crypto" equal to 1048 // "crypto.eng.sun.com". 1049 1050 if (this.getName().equalsIgnoreCase(that.getName())) { 1051 return true; 1052 } 1053 1054 // we now attempt to get the Canonical (FQDN) name and 1055 // compare that. If this fails, about all we can do is return 1056 // false. 1057 1058 try { 1059 this.getCanonName(); 1060 that.getCanonName(); 1061 } catch (UnknownHostException uhe) { 1062 return false; 1063 } 1064 1065 if (this.invalid || that.invalid) 1066 return false; 1067 1068 if (this.cname != null) { 1069 return this.cname.equalsIgnoreCase(that.cname); 1070 } 1071 1072 return false; 1073 } 1074 1075 /** 1076 * Returns the hash code value for this object. 1077 * 1078 * @return a hash code value for this object. 1079 */ 1080 @Override 1081 public int hashCode() { 1082 /* 1083 * If this SocketPermission was initialized with an IP address 1084 * or a wildcard, use getName().hashCode(), otherwise use 1085 * the hashCode() of the host name returned from 1086 * java.net.InetAddress.getHostName method. 1087 */ 1088 1089 if (init_with_ip || wildcard) { 1090 return this.getName().hashCode(); 1091 } 1092 1093 try { 1094 getCanonName(); 1095 } catch (UnknownHostException uhe) { 1096 1097 } 1098 1099 if (invalid || cname == null) 1100 return this.getName().hashCode(); 1101 else 1102 return this.cname.hashCode(); 1103 } 1104 1105 /** 1106 * Return the current action mask. 1107 * 1108 * @return the actions mask. 1109 */ 1110 1111 int getMask() { 1112 return mask; 1113 } 1114 1115 /** 1116 * Returns the "canonical string representation" of the actions in the 1117 * specified mask. 1118 * Always returns present actions in the following order: 1119 * connect, listen, accept, resolve. 1120 * 1121 * @param mask a specific integer action mask to translate into a string 1122 * @return the canonical string representation of the actions 1123 */ 1124 private static String getActions(int mask) { 1125 StringJoiner sj = new StringJoiner(","); 1126 if ((mask & CONNECT) == CONNECT) { 1127 sj.add("connect"); 1128 } 1129 if ((mask & LISTEN) == LISTEN) { 1130 sj.add("listen"); 1131 } 1132 if ((mask & ACCEPT) == ACCEPT) { 1133 sj.add("accept"); 1134 } 1135 if ((mask & RESOLVE) == RESOLVE) { 1136 sj.add("resolve"); 1137 } 1138 return sj.toString(); 1139 } 1140 1141 /** 1142 * Returns the canonical string representation of the actions. 1143 * Always returns present actions in the following order: 1144 * connect, listen, accept, resolve. 1145 * 1146 * @return the canonical string representation of the actions. 1147 */ 1148 @Override 1149 public String getActions() 1150 { 1151 if (actions == null) 1152 actions = getActions(this.mask); 1153 1154 return actions; 1155 } 1156 1157 /** 1158 * Returns a new PermissionCollection object for storing SocketPermission 1159 * objects. 1160 * <p> 1161 * SocketPermission objects must be stored in a manner that allows them 1162 * to be inserted into the collection in any order, but that also enables the 1163 * PermissionCollection {@code implies} 1164 * method to be implemented in an efficient (and consistent) manner. 1165 * 1166 * @return a new PermissionCollection object suitable for storing SocketPermissions. 1167 */ 1168 @Override 1169 public PermissionCollection newPermissionCollection() { 1170 return new SocketPermissionCollection(); 1171 } 1172 1173 /** 1174 * WriteObject is called to save the state of the SocketPermission 1175 * to a stream. The actions are serialized, and the superclass 1176 * takes care of the name. 1177 */ 1178 private synchronized void writeObject(java.io.ObjectOutputStream s) 1179 throws IOException 1180 { 1181 // Write out the actions. The superclass takes care of the name 1182 // call getActions to make sure actions field is initialized 1183 if (actions == null) 1184 getActions(); 1185 s.defaultWriteObject(); 1186 } 1187 1188 /** 1189 * readObject is called to restore the state of the SocketPermission from 1190 * a stream. 1191 */ 1192 private synchronized void readObject(java.io.ObjectInputStream s) 1193 throws IOException, ClassNotFoundException 1194 { 1195 // Read in the action, then initialize the rest 1196 s.defaultReadObject(); 1197 init(getName(),getMask(actions)); 1198 } 1199 1200 /** 1201 * Check the system/security property for the ephemeral port range 1202 * for this system. The suffix is either "high" or "low" 1203 */ 1204 private static int initEphemeralPorts(String suffix, int defval) { 1205 return AccessController.doPrivileged( 1206 new PrivilegedAction<>(){ 1207 public Integer run() { 1208 int val = Integer.getInteger( 1209 "jdk.net.ephemeralPortRange."+suffix, -1 1210 ); 1211 if (val != -1) { 1212 return val; 1213 } else { 1214 return suffix.equals("low") ? 1215 PortConfig.getLower() : PortConfig.getUpper(); 1216 } 1217 } 1218 } 1219 ); 1220 } 1221 1222 /** 1223 * Check if the target range is within the policy range 1224 * together with the ephemeral range for this platform 1225 * (if policy includes ephemeral range) 1226 */ 1227 private static boolean inRange( 1228 int policyLow, int policyHigh, int targetLow, int targetHigh 1229 ) 1230 { 1231 final int ephemeralLow = EphemeralRange.low; 1232 final int ephemeralHigh = EphemeralRange.high; 1233 1234 if (targetLow == 0) { 1235 // check policy includes ephemeral range 1236 if (!inRange(policyLow, policyHigh, ephemeralLow, ephemeralHigh)) { 1237 return false; 1238 } 1239 if (targetHigh == 0) { 1240 // nothing left to do 1241 return true; 1242 } 1243 // continue check with first real port number 1244 targetLow = 1; 1245 } 1246 1247 if (policyLow == 0 && policyHigh == 0) { 1248 // ephemeral range only 1249 return targetLow >= ephemeralLow && targetHigh <= ephemeralHigh; 1250 } 1251 1252 if (policyLow != 0) { 1253 // simple check of policy only 1254 return targetLow >= policyLow && targetHigh <= policyHigh; 1255 } 1256 1257 // policyLow == 0 which means possibly two ranges to check 1258 1259 // first check if policy and ephem range overlap/contiguous 1260 1261 if (policyHigh >= ephemeralLow - 1) { 1262 return targetHigh <= ephemeralHigh; 1263 } 1264 1265 // policy and ephem range do not overlap 1266 1267 // target range must lie entirely inside policy range or eph range 1268 1269 return (targetLow <= policyHigh && targetHigh <= policyHigh) || 1270 (targetLow >= ephemeralLow && targetHigh <= ephemeralHigh); 1271 } 1272 /* 1273 public String toString() 1274 { 1275 StringBuffer s = new StringBuffer(super.toString() + "\n" + 1276 "cname = " + cname + "\n" + 1277 "wildcard = " + wildcard + "\n" + 1278 "invalid = " + invalid + "\n" + 1279 "portrange = " + portrange[0] + "," + portrange[1] + "\n"); 1280 if (addresses != null) for (int i=0; i<addresses.length; i++) { 1281 s.append( addresses[i].getHostAddress()); 1282 s.append("\n"); 1283 } else { 1284 s.append("(no addresses)\n"); 1285 } 1286 1287 return s.toString(); 1288 } 1289 1290 public static void main(String args[]) throws Exception { 1291 SocketPermission this_ = new SocketPermission(args[0], "connect"); 1292 SocketPermission that_ = new SocketPermission(args[1], "connect"); 1293 System.out.println("-----\n"); 1294 System.out.println("this.implies(that) = " + this_.implies(that_)); 1295 System.out.println("-----\n"); 1296 System.out.println("this = "+this_); 1297 System.out.println("-----\n"); 1298 System.out.println("that = "+that_); 1299 System.out.println("-----\n"); 1300 1301 SocketPermissionCollection nps = new SocketPermissionCollection(); 1302 nps.add(this_); 1303 nps.add(new SocketPermission("www-leland.stanford.edu","connect")); 1304 nps.add(new SocketPermission("www-sun.com","connect")); 1305 System.out.println("nps.implies(that) = " + nps.implies(that_)); 1306 System.out.println("-----\n"); 1307 } 1308 */ 1309 } 1310 1311 /** 1312 1313 if (init'd with IP, key is IP as string) 1314 if wildcard, its the wild card 1315 else its the cname? 1316 1317 * 1318 * @see java.security.Permission 1319 * @see java.security.Permissions 1320 * @see java.security.PermissionCollection 1321 * 1322 * 1323 * @author Roland Schemers 1324 * 1325 * @serial include 1326 */ 1327 1328 final class SocketPermissionCollection extends PermissionCollection 1329 implements Serializable 1330 { 1331 // Not serialized; see serialization section at end of class 1332 // A ConcurrentSkipListMap is used to preserve order, so that most 1333 // recently added permissions are checked first (see JDK-4301064). 1334 private transient ConcurrentSkipListMap<String, SocketPermission> perms; 1335 1336 /** 1337 * Create an empty SocketPermissions object. 1338 * 1339 */ 1340 public SocketPermissionCollection() { 1341 perms = new ConcurrentSkipListMap<>(new SPCComparator()); 1342 } 1343 1344 /** 1345 * Adds a permission to the SocketPermissions. The key for the hash is 1346 * the name in the case of wildcards, or all the IP addresses. 1347 * 1348 * @param permission the Permission object to add. 1349 * 1350 * @exception IllegalArgumentException - if the permission is not a 1351 * SocketPermission 1352 * 1353 * @exception SecurityException - if this SocketPermissionCollection object 1354 * has been marked readonly 1355 */ 1356 @Override 1357 public void add(Permission permission) { 1358 if (! (permission instanceof SocketPermission)) 1359 throw new IllegalArgumentException("invalid permission: "+ 1360 permission); 1361 if (isReadOnly()) 1362 throw new SecurityException( 1363 "attempt to add a Permission to a readonly PermissionCollection"); 1364 1365 SocketPermission sp = (SocketPermission)permission; 1366 1367 // Add permission to map if it is absent, or replace with new 1368 // permission if applicable. NOTE: cannot use lambda for 1369 // remappingFunction parameter until JDK-8076596 is fixed. 1370 perms.merge(sp.getName(), sp, 1371 new java.util.function.BiFunction<>() { 1372 @Override 1373 public SocketPermission apply(SocketPermission existingVal, 1374 SocketPermission newVal) { 1375 int oldMask = existingVal.getMask(); 1376 int newMask = newVal.getMask(); 1377 if (oldMask != newMask) { 1378 int effective = oldMask | newMask; 1379 if (effective == newMask) { 1380 return newVal; 1381 } 1382 if (effective != oldMask) { 1383 return new SocketPermission(sp.getName(), 1384 effective); 1385 } 1386 } 1387 return existingVal; 1388 } 1389 } 1390 ); 1391 } 1392 1393 /** 1394 * Check and see if this collection of permissions implies the permissions 1395 * expressed in "permission". 1396 * 1397 * @param permission the Permission object to compare 1398 * 1399 * @return true if "permission" is a proper subset of a permission in 1400 * the collection, false if not. 1401 */ 1402 @Override 1403 public boolean implies(Permission permission) 1404 { 1405 if (! (permission instanceof SocketPermission)) 1406 return false; 1407 1408 SocketPermission np = (SocketPermission) permission; 1409 1410 int desired = np.getMask(); 1411 int effective = 0; 1412 int needed = desired; 1413 1414 //System.out.println("implies "+np); 1415 for (SocketPermission x : perms.values()) { 1416 //System.out.println(" trying "+x); 1417 if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) { 1418 effective |= x.getMask(); 1419 if ((effective & desired) == desired) { 1420 return true; 1421 } 1422 needed = (desired ^ effective); 1423 } 1424 } 1425 return false; 1426 } 1427 1428 /** 1429 * Returns an enumeration of all the SocketPermission objects in the 1430 * container. 1431 * 1432 * @return an enumeration of all the SocketPermission objects. 1433 */ 1434 @Override 1435 @SuppressWarnings("unchecked") 1436 public Enumeration<Permission> elements() { 1437 return (Enumeration)Collections.enumeration(perms.values()); 1438 } 1439 1440 private static final long serialVersionUID = 2787186408602843674L; 1441 1442 // Need to maintain serialization interoperability with earlier releases, 1443 // which had the serializable field: 1444 1445 // 1446 // The SocketPermissions for this set. 1447 // @serial 1448 // 1449 // private Vector permissions; 1450 1451 /** 1452 * @serialField permissions java.util.Vector 1453 * A list of the SocketPermissions for this set. 1454 */ 1455 private static final ObjectStreamField[] serialPersistentFields = { 1456 new ObjectStreamField("permissions", Vector.class), 1457 }; 1458 1459 /** 1460 * @serialData "permissions" field (a Vector containing the SocketPermissions). 1461 */ 1462 /* 1463 * Writes the contents of the perms field out as a Vector for 1464 * serialization compatibility with earlier releases. 1465 */ 1466 private void writeObject(ObjectOutputStream out) throws IOException { 1467 // Don't call out.defaultWriteObject() 1468 1469 // Write out Vector 1470 Vector<SocketPermission> permissions = new Vector<>(perms.values()); 1471 1472 ObjectOutputStream.PutField pfields = out.putFields(); 1473 pfields.put("permissions", permissions); 1474 out.writeFields(); 1475 } 1476 1477 /* 1478 * Reads in a Vector of SocketPermissions and saves them in the perms field. 1479 */ 1480 private void readObject(ObjectInputStream in) 1481 throws IOException, ClassNotFoundException 1482 { 1483 // Don't call in.defaultReadObject() 1484 1485 // Read in serialized fields 1486 ObjectInputStream.GetField gfields = in.readFields(); 1487 1488 // Get the one we want 1489 @SuppressWarnings("unchecked") 1490 Vector<SocketPermission> permissions = (Vector<SocketPermission>)gfields.get("permissions", null); 1491 perms = new ConcurrentSkipListMap<>(new SPCComparator()); 1492 for (SocketPermission sp : permissions) { 1493 perms.put(sp.getName(), sp); 1494 } 1495 } 1496 1497 /** 1498 * A simple comparator that orders new non-equal entries at the beginning. 1499 */ 1500 private static class SPCComparator implements Comparator<String> { 1501 @Override 1502 public int compare(String s1, String s2) { 1503 if (s1.equals(s2)) { 1504 return 0; 1505 } 1506 return -1; 1507 } 1508 } 1509 }