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