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