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