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