1 /* 2 * Copyright (c) 1997, 2006, 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 private static int getMask(String action) { 471 472 if (action == null) { 473 throw new NullPointerException("action can't be null"); 474 } 475 476 if (action.equals("")) { 477 throw new IllegalArgumentException("action can't be empty"); 478 } 479 480 int mask = NONE; 481 482 // Check against use of constants (used heavily within the JDK) 483 if (action == SecurityConstants.SOCKET_RESOLVE_ACTION) { 484 return RESOLVE; 485 } else if (action == SecurityConstants.SOCKET_CONNECT_ACTION) { 486 return CONNECT; 487 } else if (action == SecurityConstants.SOCKET_LISTEN_ACTION) { 488 return LISTEN; 489 } else if (action == SecurityConstants.SOCKET_ACCEPT_ACTION) { 490 return ACCEPT; 491 } else if (action == SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION) { 492 return CONNECT|ACCEPT; 493 } 494 495 char[] a = action.toCharArray(); 496 497 int i = a.length - 1; 498 if (i < 0) 499 return mask; 500 501 while (i != -1) { 502 char c; 503 504 // skip whitespace 505 while ((i!=-1) && ((c = a[i]) == ' ' || 506 c == '\r' || 507 c == '\n' || 508 c == '\f' || 509 c == '\t')) 510 i--; 511 512 // check for the known strings 513 int matchlen; 514 515 if (i >= 6 && (a[i-6] == 'c' || a[i-6] == 'C') && 516 (a[i-5] == 'o' || a[i-5] == 'O') && 517 (a[i-4] == 'n' || a[i-4] == 'N') && 518 (a[i-3] == 'n' || a[i-3] == 'N') && 519 (a[i-2] == 'e' || a[i-2] == 'E') && 520 (a[i-1] == 'c' || a[i-1] == 'C') && 521 (a[i] == 't' || a[i] == 'T')) 522 { 523 matchlen = 7; 524 mask |= CONNECT; 525 526 } else if (i >= 6 && (a[i-6] == 'r' || a[i-6] == 'R') && 527 (a[i-5] == 'e' || a[i-5] == 'E') && 528 (a[i-4] == 's' || a[i-4] == 'S') && 529 (a[i-3] == 'o' || a[i-3] == 'O') && 530 (a[i-2] == 'l' || a[i-2] == 'L') && 531 (a[i-1] == 'v' || a[i-1] == 'V') && 532 (a[i] == 'e' || a[i] == 'E')) 533 { 534 matchlen = 7; 535 mask |= RESOLVE; 536 537 } else if (i >= 5 && (a[i-5] == 'l' || a[i-5] == 'L') && 538 (a[i-4] == 'i' || a[i-4] == 'I') && 539 (a[i-3] == 's' || a[i-3] == 'S') && 540 (a[i-2] == 't' || a[i-2] == 'T') && 541 (a[i-1] == 'e' || a[i-1] == 'E') && 542 (a[i] == 'n' || a[i] == 'N')) 543 { 544 matchlen = 6; 545 mask |= LISTEN; 546 547 } else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') && 548 (a[i-4] == 'c' || a[i-4] == 'C') && 549 (a[i-3] == 'c' || a[i-3] == 'C') && 550 (a[i-2] == 'e' || a[i-2] == 'E') && 551 (a[i-1] == 'p' || a[i-1] == 'P') && 552 (a[i] == 't' || a[i] == 'T')) 553 { 554 matchlen = 6; 555 mask |= ACCEPT; 556 557 } else { 558 // parse error 559 throw new IllegalArgumentException( 560 "invalid permission: " + action); 561 } 562 563 // make sure we didn't just match the tail of a word 564 // like "ackbarfaccept". Also, skip to the comma. 565 boolean seencomma = false; 566 while (i >= matchlen && !seencomma) { 567 switch(a[i-matchlen]) { 568 case ',': 569 seencomma = true; 570 /*FALLTHROUGH*/ 571 case ' ': case '\r': case '\n': 572 case '\f': case '\t': 573 break; 574 default: 575 throw new IllegalArgumentException( 576 "invalid permission: " + action); 577 } 578 i--; 579 } 580 581 // point i at the location of the comma minus one (or -1). 582 i -= matchlen; 583 } 584 585 return mask; 586 } 587 588 private boolean isUntrusted() 589 throws UnknownHostException 590 { 591 if (trusted) return false; 592 if (invalid || untrusted) return true; 593 try { 594 if (!trustNameService && (defaultDeny || 595 sun.net.www.URLConnection.isProxiedHost(hostname))) { 596 if (this.cname == null) { 597 this.getCanonName(); 598 } 599 if (!match(cname, hostname)) { 600 // Last chance 601 if (!authorized(hostname, addresses[0].getAddress())) { 602 untrusted = true; 603 Debug debug = getDebug(); 604 if (debug != null && Debug.isOn("failure")) { 605 debug.println("socket access restriction: proxied host " + "(" + addresses[0] + ")" + " does not match " + cname + " from reverse lookup"); 606 } 607 return true; 608 } 609 } 610 trusted = true; 611 } 612 } catch (UnknownHostException uhe) { 613 invalid = true; 614 throw uhe; 615 } 616 return false; 617 } 618 619 /** 620 * attempt to get the fully qualified domain name 621 * 622 */ 623 void getCanonName() 624 throws UnknownHostException 625 { 626 if (cname != null || invalid || untrusted) return; 627 628 // attempt to get the canonical name 629 630 try { 631 // first get the IP addresses if we don't have them yet 632 // this is because we need the IP address to then get 633 // FQDN. 634 if (addresses == null) { 635 getIP(); 636 } 637 638 // we have to do this check, otherwise we might not 639 // get the fully qualified domain name 640 if (init_with_ip) { 641 cname = addresses[0].getHostName(false).toLowerCase(); 642 } else { 643 cname = InetAddress.getByName(addresses[0].getHostAddress()). 644 getHostName(false).toLowerCase(); 645 } 646 } catch (UnknownHostException uhe) { 647 invalid = true; 648 throw uhe; 649 } 650 } 651 652 private transient String cdomain, hdomain; 653 654 private boolean match(String cname, String hname) { 655 String a = cname.toLowerCase(); 656 String b = hname.toLowerCase(); 657 if (a.startsWith(b) && 658 ((a.length() == b.length()) || (a.charAt(b.length()) == '.'))) 659 return true; 660 if (cdomain == null) { 661 cdomain = RegisteredDomain.getRegisteredDomain(a); 662 } 663 if (hdomain == null) { 664 hdomain = RegisteredDomain.getRegisteredDomain(b); 665 } 666 667 return cdomain.length() != 0 && hdomain.length() != 0 668 && cdomain.equals(hdomain); 669 } 670 671 private boolean authorized(String cname, byte[] addr) { 672 if (addr.length == 4) 673 return authorizedIPv4(cname, addr); 674 else if (addr.length == 16) 675 return authorizedIPv6(cname, addr); 676 else 677 return false; 678 } 679 680 private boolean authorizedIPv4(String cname, byte[] addr) { 681 String authHost = ""; 682 InetAddress auth; 683 684 try { 685 authHost = "auth." + 686 (addr[3] & 0xff) + "." + (addr[2] & 0xff) + "." + 687 (addr[1] & 0xff) + "." + (addr[0] & 0xff) + 688 ".in-addr.arpa"; 689 // Following check seems unnecessary 690 // auth = InetAddress.getAllByName0(authHost, false)[0]; 691 authHost = hostname + '.' + authHost; 692 auth = InetAddress.getAllByName0(authHost, false)[0]; 693 if (auth.equals(InetAddress.getByAddress(addr))) { 694 return true; 695 } 696 Debug debug = getDebug(); 697 if (debug != null && Debug.isOn("failure")) { 698 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr)); 699 } 700 } catch (UnknownHostException uhe) { 701 Debug debug = getDebug(); 702 if (debug != null && Debug.isOn("failure")) { 703 debug.println("socket access restriction: forward lookup failed for " + authHost); 704 } 705 } 706 return false; 707 } 708 709 private boolean authorizedIPv6(String cname, byte[] addr) { 710 String authHost = ""; 711 InetAddress auth; 712 713 try { 714 StringBuffer sb = new StringBuffer(39); 715 716 for (int i = 15; i >= 0; i--) { 717 sb.append(Integer.toHexString(((addr[i]) & 0x0f))); 718 sb.append('.'); 719 sb.append(Integer.toHexString(((addr[i] >> 4) & 0x0f))); 720 sb.append('.'); 721 } 722 authHost = "auth." + sb.toString() + "IP6.ARPA"; 723 //auth = InetAddress.getAllByName0(authHost, false)[0]; 724 authHost = hostname + '.' + authHost; 725 auth = InetAddress.getAllByName0(authHost, false)[0]; 726 if (auth.equals(InetAddress.getByAddress(addr))) 727 return true; 728 Debug debug = getDebug(); 729 if (debug != null && Debug.isOn("failure")) { 730 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr)); 731 } 732 } catch (UnknownHostException uhe) { 733 Debug debug = getDebug(); 734 if (debug != null && Debug.isOn("failure")) { 735 debug.println("socket access restriction: forward lookup failed for " + authHost); 736 } 737 } 738 return false; 739 } 740 741 742 /** 743 * get IP addresses. Sets invalid to true if we can't get them. 744 * 745 */ 746 void getIP() 747 throws UnknownHostException 748 { 749 if (addresses != null || wildcard || invalid) return; 750 751 try { 752 // now get all the IP addresses 753 String host; 754 if (getName().charAt(0) == '[') { 755 // Literal IPv6 address 756 host = getName().substring(1, getName().indexOf(']')); 757 } else { 758 int i = getName().indexOf(":"); 759 if (i == -1) 760 host = getName(); 761 else { 762 host = getName().substring(0,i); 763 } 764 } 765 766 addresses = 767 new InetAddress[] {InetAddress.getAllByName0(host, false)[0]}; 768 769 } catch (UnknownHostException uhe) { 770 invalid = true; 771 throw uhe; 772 } catch (IndexOutOfBoundsException iobe) { 773 invalid = true; 774 throw new UnknownHostException(getName()); 775 } 776 } 777 778 /** 779 * Checks if this socket permission object "implies" the 780 * specified permission. 781 * <P> 782 * More specifically, this method first ensures that all of the following 783 * are true (and returns false if any of them are not):<p> 784 * <ul> 785 * <li> <i>p</i> is an instanceof SocketPermission,<p> 786 * <li> <i>p</i>'s actions are a proper subset of this 787 * object's actions, and<p> 788 * <li> <i>p</i>'s port range is included in this port range. Note: 789 * port range is ignored when p only contains the action, 'resolve'.<p> 790 * </ul> 791 * 792 * Then <code>implies</code> checks each of the following, in order, 793 * and for each returns true if the stated condition is true:<p> 794 * <ul> 795 * <li> If this object was initialized with a single IP address and one of <i>p</i>'s 796 * IP addresses is equal to this object's IP address.<p> 797 * <li>If this object is a wildcard domain (such as *.sun.com), and 798 * <i>p</i>'s canonical name (the name without any preceding *) 799 * ends with this object's canonical host name. For example, *.sun.com 800 * implies *.eng.sun.com..<p> 801 * <li>If this object was not initialized with a single IP address, and one of this 802 * object's IP addresses equals one of <i>p</i>'s IP addresses.<p> 803 * <li>If this canonical name equals <i>p</i>'s canonical name.<p> 804 * </ul> 805 * 806 * If none of the above are true, <code>implies</code> returns false. 807 * @param p the permission to check against. 808 * 809 * @return true if the specified permission is implied by this object, 810 * false if not. 811 */ 812 813 public boolean implies(Permission p) { 814 int i,j; 815 816 if (!(p instanceof SocketPermission)) 817 return false; 818 819 if (p == this) 820 return true; 821 822 SocketPermission that = (SocketPermission) p; 823 824 return ((this.mask & that.mask) == that.mask) && 825 impliesIgnoreMask(that); 826 } 827 828 /** 829 * Checks if the incoming Permission's action are a proper subset of 830 * the this object's actions. 831 * <P> 832 * Check, in the following order: 833 * <ul> 834 * <li> Checks that "p" is an instanceof a SocketPermission 835 * <li> Checks that "p"'s actions are a proper subset of the 836 * current object's actions. 837 * <li> Checks that "p"'s port range is included in this port range 838 * <li> If this object was initialized with an IP address, checks that 839 * one of "p"'s IP addresses is equal to this object's IP address. 840 * <li> If either object is a wildcard domain (i.e., "*.sun.com"), 841 * attempt to match based on the wildcard. 842 * <li> If this object was not initialized with an IP address, attempt 843 * to find a match based on the IP addresses in both objects. 844 * <li> Attempt to match on the canonical hostnames of both objects. 845 * </ul> 846 * @param p the incoming permission request 847 * 848 * @return true if "permission" is a proper subset of the current object, 849 * false if not. 850 */ 851 852 boolean impliesIgnoreMask(SocketPermission that) { 853 int i,j; 854 855 if ((that.mask & RESOLVE) != that.mask) { 856 // check port range 857 if ((that.portrange[0] < this.portrange[0]) || 858 (that.portrange[1] > this.portrange[1])) { 859 return false; 860 } 861 } 862 863 // allow a "*" wildcard to always match anything 864 if (this.wildcard && "".equals(this.cname)) 865 return true; 866 867 // return if either one of these NetPerm objects are invalid... 868 if (this.invalid || that.invalid) { 869 return compareHostnames(that); 870 } 871 872 try { 873 if (this.init_with_ip) { // we only check IP addresses 874 if (that.wildcard) 875 return false; 876 877 if (that.init_with_ip) { 878 return (this.addresses[0].equals(that.addresses[0])); 879 } else { 880 if (that.addresses == null) { 881 that.getIP(); 882 } 883 for (i=0; i < that.addresses.length; i++) { 884 if (this.addresses[0].equals(that.addresses[i])) 885 return true; 886 } 887 } 888 // since "this" was initialized with an IP address, we 889 // don't check any other cases 890 return false; 891 } 892 893 // check and see if we have any wildcards... 894 if (this.wildcard || that.wildcard) { 895 // if they are both wildcards, return true iff 896 // that's cname ends with this cname (i.e., *.sun.com 897 // implies *.eng.sun.com) 898 if (this.wildcard && that.wildcard) 899 return (that.cname.endsWith(this.cname)); 900 901 // a non-wildcard can't imply a wildcard 902 if (that.wildcard) 903 return false; 904 905 // this is a wildcard, lets see if that's cname ends with 906 // it... 907 if (that.cname == null) { 908 that.getCanonName(); 909 } 910 return (that.cname.endsWith(this.cname)); 911 } 912 913 // comapare IP addresses 914 if (this.addresses == null) { 915 this.getIP(); 916 } 917 918 if (that.addresses == null) { 919 that.getIP(); 920 } 921 922 if (!(that.init_with_ip && this.isUntrusted())) { 923 for (j = 0; j < this.addresses.length; j++) { 924 for (i=0; i < that.addresses.length; i++) { 925 if (this.addresses[j].equals(that.addresses[i])) 926 return true; 927 } 928 } 929 930 // XXX: if all else fails, compare hostnames? 931 // Do we really want this? 932 if (this.cname == null) { 933 this.getCanonName(); 934 } 935 936 if (that.cname == null) { 937 that.getCanonName(); 938 } 939 940 return (this.cname.equalsIgnoreCase(that.cname)); 941 } 942 943 } catch (UnknownHostException uhe) { 944 return compareHostnames(that); 945 } 946 947 // make sure the first thing that is done here is to return 948 // false. If not, uncomment the return false in the above catch. 949 950 return false; 951 } 952 953 private boolean compareHostnames(SocketPermission that) { 954 // we see if the original names/IPs passed in were equal. 955 956 String thisHost = hostname; 957 String thatHost = that.hostname; 958 959 if (thisHost == null) { 960 return false; 961 } else if (this.wildcard) { 962 final int cnameLength = this.cname.length(); 963 return thatHost.regionMatches(true, 964 (thatHost.length() - cnameLength), 965 this.cname, 0, cnameLength); 966 } else { 967 return thisHost.equalsIgnoreCase(thatHost); 968 } 969 } 970 971 /** 972 * Checks two SocketPermission objects for equality. 973 * <P> 974 * @param obj the object to test for equality with this object. 975 * 976 * @return true if <i>obj</i> is a SocketPermission, and has the 977 * same hostname, port range, and actions as this 978 * SocketPermission object. However, port range will be ignored 979 * in the comparison if <i>obj</i> only contains the action, 'resolve'. 980 */ 981 public boolean equals(Object obj) { 982 if (obj == this) 983 return true; 984 985 if (! (obj instanceof SocketPermission)) 986 return false; 987 988 SocketPermission that = (SocketPermission) obj; 989 990 //this is (overly?) complex!!! 991 992 // check the mask first 993 if (this.mask != that.mask) return false; 994 995 if ((that.mask & RESOLVE) != that.mask) { 996 // now check the port range... 997 if ((this.portrange[0] != that.portrange[0]) || 998 (this.portrange[1] != that.portrange[1])) { 999 return false; 1000 } 1001 } 1002 1003 // short cut. This catches: 1004 // "crypto" equal to "crypto", or 1005 // "1.2.3.4" equal to "1.2.3.4.", or 1006 // "*.edu" equal to "*.edu", but it 1007 // does not catch "crypto" equal to 1008 // "crypto.eng.sun.com". 1009 1010 if (this.getName().equalsIgnoreCase(that.getName())) { 1011 return true; 1012 } 1013 1014 // we now attempt to get the Canonical (FQDN) name and 1015 // compare that. If this fails, about all we can do is return 1016 // false. 1017 1018 try { 1019 this.getCanonName(); 1020 that.getCanonName(); 1021 } catch (UnknownHostException uhe) { 1022 return false; 1023 } 1024 1025 if (this.invalid || that.invalid) 1026 return false; 1027 1028 if (this.cname != null) { 1029 return this.cname.equalsIgnoreCase(that.cname); 1030 } 1031 1032 return false; 1033 } 1034 1035 /** 1036 * Returns the hash code value for this object. 1037 * 1038 * @return a hash code value for this object. 1039 */ 1040 1041 public int hashCode() { 1042 /* 1043 * If this SocketPermission was initialized with an IP address 1044 * or a wildcard, use getName().hashCode(), otherwise use 1045 * the hashCode() of the host name returned from 1046 * java.net.InetAddress.getHostName method. 1047 */ 1048 1049 if (init_with_ip || wildcard) { 1050 return this.getName().hashCode(); 1051 } 1052 1053 try { 1054 getCanonName(); 1055 } catch (UnknownHostException uhe) { 1056 1057 } 1058 1059 if (invalid || cname == null) 1060 return this.getName().hashCode(); 1061 else 1062 return this.cname.hashCode(); 1063 } 1064 1065 /** 1066 * Return the current action mask. 1067 * 1068 * @return the actions mask. 1069 */ 1070 1071 int getMask() { 1072 return mask; 1073 } 1074 1075 /** 1076 * Returns the "canonical string representation" of the actions in the 1077 * specified mask. 1078 * Always returns present actions in the following order: 1079 * connect, listen, accept, resolve. 1080 * 1081 * @param mask a specific integer action mask to translate into a string 1082 * @return the canonical string representation of the actions 1083 */ 1084 private static String getActions(int mask) 1085 { 1086 StringBuilder sb = new StringBuilder(); 1087 boolean comma = false; 1088 1089 if ((mask & CONNECT) == CONNECT) { 1090 comma = true; 1091 sb.append("connect"); 1092 } 1093 1094 if ((mask & LISTEN) == LISTEN) { 1095 if (comma) sb.append(','); 1096 else comma = true; 1097 sb.append("listen"); 1098 } 1099 1100 if ((mask & ACCEPT) == ACCEPT) { 1101 if (comma) sb.append(','); 1102 else comma = true; 1103 sb.append("accept"); 1104 } 1105 1106 1107 if ((mask & RESOLVE) == RESOLVE) { 1108 if (comma) sb.append(','); 1109 else comma = true; 1110 sb.append("resolve"); 1111 } 1112 1113 return sb.toString(); 1114 } 1115 1116 /** 1117 * Returns the canonical string representation of the actions. 1118 * Always returns present actions in the following order: 1119 * connect, listen, accept, resolve. 1120 * 1121 * @return the canonical string representation of the actions. 1122 */ 1123 public String getActions() 1124 { 1125 if (actions == null) 1126 actions = getActions(this.mask); 1127 1128 return actions; 1129 } 1130 1131 /** 1132 * Returns a new PermissionCollection object for storing SocketPermission 1133 * objects. 1134 * <p> 1135 * SocketPermission objects must be stored in a manner that allows them 1136 * to be inserted into the collection in any order, but that also enables the 1137 * PermissionCollection <code>implies</code> 1138 * method to be implemented in an efficient (and consistent) manner. 1139 * 1140 * @return a new PermissionCollection object suitable for storing SocketPermissions. 1141 */ 1142 1143 public PermissionCollection newPermissionCollection() { 1144 return new SocketPermissionCollection(); 1145 } 1146 1147 /** 1148 * WriteObject is called to save the state of the SocketPermission 1149 * to a stream. The actions are serialized, and the superclass 1150 * takes care of the name. 1151 */ 1152 private synchronized void writeObject(java.io.ObjectOutputStream s) 1153 throws IOException 1154 { 1155 // Write out the actions. The superclass takes care of the name 1156 // call getActions to make sure actions field is initialized 1157 if (actions == null) 1158 getActions(); 1159 s.defaultWriteObject(); 1160 } 1161 1162 /** 1163 * readObject is called to restore the state of the SocketPermission from 1164 * a stream. 1165 */ 1166 private synchronized void readObject(java.io.ObjectInputStream s) 1167 throws IOException, ClassNotFoundException 1168 { 1169 // Read in the action, then initialize the rest 1170 s.defaultReadObject(); 1171 init(getName(),getMask(actions)); 1172 } 1173 1174 /* 1175 public String toString() 1176 { 1177 StringBuffer s = new StringBuffer(super.toString() + "\n" + 1178 "cname = " + cname + "\n" + 1179 "wildcard = " + wildcard + "\n" + 1180 "invalid = " + invalid + "\n" + 1181 "portrange = " + portrange[0] + "," + portrange[1] + "\n"); 1182 if (addresses != null) for (int i=0; i<addresses.length; i++) { 1183 s.append( addresses[i].getHostAddress()); 1184 s.append("\n"); 1185 } else { 1186 s.append("(no addresses)\n"); 1187 } 1188 1189 return s.toString(); 1190 } 1191 1192 public static void main(String args[]) throws Exception { 1193 SocketPermission this_ = new SocketPermission(args[0], "connect"); 1194 SocketPermission that_ = new SocketPermission(args[1], "connect"); 1195 System.out.println("-----\n"); 1196 System.out.println("this.implies(that) = " + this_.implies(that_)); 1197 System.out.println("-----\n"); 1198 System.out.println("this = "+this_); 1199 System.out.println("-----\n"); 1200 System.out.println("that = "+that_); 1201 System.out.println("-----\n"); 1202 1203 SocketPermissionCollection nps = new SocketPermissionCollection(); 1204 nps.add(this_); 1205 nps.add(new SocketPermission("www-leland.stanford.edu","connect")); 1206 nps.add(new SocketPermission("www-sun.com","connect")); 1207 System.out.println("nps.implies(that) = " + nps.implies(that_)); 1208 System.out.println("-----\n"); 1209 } 1210 */ 1211 } 1212 1213 /** 1214 1215 if (init'd with IP, key is IP as string) 1216 if wildcard, its the wild card 1217 else its the cname? 1218 1219 * 1220 * @see java.security.Permission 1221 * @see java.security.Permissions 1222 * @see java.security.PermissionCollection 1223 * 1224 * 1225 * @author Roland Schemers 1226 * 1227 * @serial include 1228 */ 1229 1230 final class SocketPermissionCollection extends PermissionCollection 1231 implements Serializable 1232 { 1233 // Not serialized; see serialization section at end of class 1234 private transient List perms; 1235 1236 /** 1237 * Create an empty SocketPermissions object. 1238 * 1239 */ 1240 1241 public SocketPermissionCollection() { 1242 perms = new ArrayList(); 1243 } 1244 1245 /** 1246 * Adds a permission to the SocketPermissions. The key for the hash is 1247 * the name in the case of wildcards, or all the IP addresses. 1248 * 1249 * @param permission the Permission object to add. 1250 * 1251 * @exception IllegalArgumentException - if the permission is not a 1252 * SocketPermission 1253 * 1254 * @exception SecurityException - if this SocketPermissionCollection object 1255 * has been marked readonly 1256 */ 1257 1258 public void add(Permission permission) 1259 { 1260 if (! (permission instanceof SocketPermission)) 1261 throw new IllegalArgumentException("invalid permission: "+ 1262 permission); 1263 if (isReadOnly()) 1264 throw new SecurityException( 1265 "attempt to add a Permission to a readonly PermissionCollection"); 1266 1267 // optimization to ensure perms most likely to be tested 1268 // show up early (4301064) 1269 synchronized (this) { 1270 perms.add(0, permission); 1271 } 1272 } 1273 1274 /** 1275 * Check and see if this collection of permissions implies the permissions 1276 * expressed in "permission". 1277 * 1278 * @param p the Permission object to compare 1279 * 1280 * @return true if "permission" is a proper subset of a permission in 1281 * the collection, false if not. 1282 */ 1283 1284 public boolean implies(Permission permission) 1285 { 1286 if (! (permission instanceof SocketPermission)) 1287 return false; 1288 1289 SocketPermission np = (SocketPermission) permission; 1290 1291 int desired = np.getMask(); 1292 int effective = 0; 1293 int needed = desired; 1294 1295 synchronized (this) { 1296 int len = perms.size(); 1297 //System.out.println("implies "+np); 1298 for (int i = 0; i < len; i++) { 1299 SocketPermission x = (SocketPermission) perms.get(i); 1300 //System.out.println(" trying "+x); 1301 if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) { 1302 effective |= x.getMask(); 1303 if ((effective & desired) == desired) 1304 return true; 1305 needed = (desired ^ effective); 1306 } 1307 } 1308 } 1309 return false; 1310 } 1311 1312 /** 1313 * Returns an enumeration of all the SocketPermission objects in the 1314 * container. 1315 * 1316 * @return an enumeration of all the SocketPermission objects. 1317 */ 1318 1319 public Enumeration elements() { 1320 // Convert Iterator into Enumeration 1321 synchronized (this) { 1322 return Collections.enumeration(perms); 1323 } 1324 } 1325 1326 private static final long serialVersionUID = 2787186408602843674L; 1327 1328 // Need to maintain serialization interoperability with earlier releases, 1329 // which had the serializable field: 1330 1331 // 1332 // The SocketPermissions for this set. 1333 // @serial 1334 // 1335 // private Vector permissions; 1336 1337 /** 1338 * @serialField permissions java.util.Vector 1339 * A list of the SocketPermissions for this set. 1340 */ 1341 private static final ObjectStreamField[] serialPersistentFields = { 1342 new ObjectStreamField("permissions", Vector.class), 1343 }; 1344 1345 /** 1346 * @serialData "permissions" field (a Vector containing the SocketPermissions). 1347 */ 1348 /* 1349 * Writes the contents of the perms field out as a Vector for 1350 * serialization compatibility with earlier releases. 1351 */ 1352 private void writeObject(ObjectOutputStream out) throws IOException { 1353 // Don't call out.defaultWriteObject() 1354 1355 // Write out Vector 1356 Vector permissions = new Vector(perms.size()); 1357 1358 synchronized (this) { 1359 permissions.addAll(perms); 1360 } 1361 1362 ObjectOutputStream.PutField pfields = out.putFields(); 1363 pfields.put("permissions", permissions); 1364 out.writeFields(); 1365 } 1366 1367 /* 1368 * Reads in a Vector of SocketPermissions and saves them in the perms field. 1369 */ 1370 private void readObject(ObjectInputStream in) throws IOException, 1371 ClassNotFoundException { 1372 // Don't call in.defaultReadObject() 1373 1374 // Read in serialized fields 1375 ObjectInputStream.GetField gfields = in.readFields(); 1376 1377 // Get the one we want 1378 Vector permissions = (Vector)gfields.get("permissions", null); 1379 perms = new ArrayList(permissions.size()); 1380 perms.addAll(permissions); 1381 } 1382 } --- EOF ---