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