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