Showing the changes to java/pkg/java/net/SocketPermission.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,1376 @@ | |||
| 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 | ||
| 962 | return thisHost.equalsIgnoreCase(thatHost); | ||
| 963 | } | ||
| 964 | |||
| 965 | /** | ||
| 966 | * Checks two SocketPermission objects for equality. | ||
| 967 | * <P> | ||
| 968 | * @param obj the object to test for equality with this object. | ||
| 969 | * | ||
| 970 | * @return true if <i>obj</i> is a SocketPermission, and has the | ||
| 971 | * same hostname, port range, and actions as this | ||
| 972 | * SocketPermission object. However, port range will be ignored | ||
| 973 | * in the comparison if <i>obj</i> only contains the action, 'resolve'. | ||
| 974 | */ | ||
| 975 | public boolean equals(Object obj) { | ||
| 976 | if (obj == this) | ||
| 977 | return true; | ||
| 978 | |||
| 979 | if (! (obj instanceof SocketPermission)) | ||
| 980 | return false; | ||
| 981 | |||
| 982 | SocketPermission that = (SocketPermission) obj; | ||
| 983 | |||
| 984 | //this is (overly?) complex!!! | ||
| 985 | |||
| 986 | // check the mask first | ||
| 987 | if (this.mask != that.mask) return false; | ||
| 988 | |||
| 989 | if ((that.mask & RESOLVE) != that.mask) { | ||
| 990 | // now check the port range... | ||
| 991 | if ((this.portrange[0] != that.portrange[0]) || | ||
| 992 | (this.portrange[1] != that.portrange[1])) { | ||
| 993 | return false; | ||
| 994 | } | ||
| 995 | } | ||
| 996 | |||
| 997 | // short cut. This catches: | ||
| 998 | // "crypto" equal to "crypto", or | ||
| 999 | // "1.2.3.4" equal to "1.2.3.4.", or | ||
| 1000 | // "*.edu" equal to "*.edu", but it | ||
| 1001 | // does not catch "crypto" equal to | ||
| 1002 | // "crypto.eng.sun.com". | ||
| 1003 | |||
| 1004 | if (this.getName().equalsIgnoreCase(that.getName())) { | ||
| 1005 | return true; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | // we now attempt to get the Canonical (FQDN) name and | ||
| 1009 | // compare that. If this fails, about all we can do is return | ||
| 1010 | // false. | ||
| 1011 | |||
| 1012 | try { | ||
| 1013 | this.getCanonName(); | ||
| 1014 | that.getCanonName(); | ||
| 1015 | } catch (UnknownHostException uhe) { | ||
| 1016 | return false; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | if (this.invalid || that.invalid) | ||
| 1020 | return false; | ||
| 1021 | |||
| 1022 | if (this.cname != null) { | ||
| 1023 | return this.cname.equalsIgnoreCase(that.cname); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | return false; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | /** | ||
| 1030 | * Returns the hash code value for this object. | ||
| 1031 | * | ||
| 1032 | * @return a hash code value for this object. | ||
| 1033 | */ | ||
| 1034 | |||
| 1035 | public int hashCode() { | ||
| 1036 | /* | ||
| 1037 | * If this SocketPermission was initialized with an IP address | ||
| 1038 | * or a wildcard, use getName().hashCode(), otherwise use | ||
| 1039 | * the hashCode() of the host name returned from | ||
| 1040 | * java.net.InetAddress.getHostName method. | ||
| 1041 | */ | ||
| 1042 | |||
| 1043 | if (init_with_ip || wildcard) { | ||
| 1044 | return this.getName().hashCode(); | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | try { | ||
| 1048 | getCanonName(); | ||
| 1049 | } catch (UnknownHostException uhe) { | ||
| 1050 | |||
| 1051 | } | ||
| 1052 | |||
| 1053 | if (invalid || cname == null) | ||
| 1054 | return this.getName().hashCode(); | ||
| 1055 | else | ||
| 1056 | return this.cname.hashCode(); | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | /** | ||
| 1060 | * Return the current action mask. | ||
| 1061 | * | ||
| 1062 | * @return the actions mask. | ||
| 1063 | */ | ||
| 1064 | |||
| 1065 | int getMask() { | ||
| 1066 | return mask; | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | /** | ||
| 1070 | * Returns the "canonical string representation" of the actions in the | ||
| 1071 | * specified mask. | ||
| 1072 | * Always returns present actions in the following order: | ||
| 1073 | * connect, listen, accept, resolve. | ||
| 1074 | * | ||
| 1075 | * @param mask a specific integer action mask to translate into a string | ||
| 1076 | * @return the canonical string representation of the actions | ||
| 1077 | */ | ||
| 1078 | private static String getActions(int mask) | ||
| 1079 | { | ||
| 1080 | StringBuilder sb = new StringBuilder(); | ||
| 1081 | boolean comma = false; | ||
| 1082 | |||
| 1083 | if ((mask & CONNECT) == CONNECT) { | ||
| 1084 | comma = true; | ||
| 1085 | sb.append("connect"); | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | if ((mask & LISTEN) == LISTEN) { | ||
| 1089 | if (comma) sb.append(','); | ||
| 1090 | else comma = true; | ||
| 1091 | sb.append("listen"); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | if ((mask & ACCEPT) == ACCEPT) { | ||
| 1095 | if (comma) sb.append(','); | ||
| 1096 | else comma = true; | ||
| 1097 | sb.append("accept"); | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | |||
| 1101 | if ((mask & RESOLVE) == RESOLVE) { | ||
| 1102 | if (comma) sb.append(','); | ||
| 1103 | else comma = true; | ||
| 1104 | sb.append("resolve"); | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | return sb.toString(); | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | /** | ||
| 1111 | * Returns the canonical string representation of the actions. | ||
| 1112 | * Always returns present actions in the following order: | ||
| 1113 | * connect, listen, accept, resolve. | ||
| 1114 | * | ||
| 1115 | * @return the canonical string representation of the actions. | ||
| 1116 | */ | ||
| 1117 | public String getActions() | ||
| 1118 | { | ||
| 1119 | if (actions == null) | ||
| 1120 | actions = getActions(this.mask); | ||
| 1121 | |||
| 1122 | return actions; | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | /** | ||
| 1126 | * Returns a new PermissionCollection object for storing SocketPermission | ||
| 1127 | * objects. | ||
| 1128 | * <p> | ||
| 1129 | * SocketPermission objects must be stored in a manner that allows them | ||
| 1130 | * to be inserted into the collection in any order, but that also enables the | ||
| 1131 | * PermissionCollection <code>implies</code> | ||
| 1132 | * method to be implemented in an efficient (and consistent) manner. | ||
| 1133 | * | ||
| 1134 | * @return a new PermissionCollection object suitable for storing SocketPermissions. | ||
| 1135 | */ | ||
| 1136 | |||
| 1137 | public PermissionCollection newPermissionCollection() { | ||
| 1138 | return new SocketPermissionCollection(); | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | /** | ||
| 1142 | * WriteObject is called to save the state of the SocketPermission | ||
| 1143 | * to a stream. The actions are serialized, and the superclass | ||
| 1144 | * takes care of the name. | ||
| 1145 | */ | ||
| 1146 | private synchronized void writeObject(java.io.ObjectOutputStream s) | ||
| 1147 | throws IOException | ||
| 1148 | { | ||
| 1149 | // Write out the actions. The superclass takes care of the name | ||
| 1150 | // call getActions to make sure actions field is initialized | ||
| 1151 | if (actions == null) | ||
| 1152 | getActions(); | ||
| 1153 | s.defaultWriteObject(); | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | /** | ||
| 1157 | * readObject is called to restore the state of the SocketPermission from | ||
| 1158 | * a stream. | ||
| 1159 | */ | ||
| 1160 | private synchronized void readObject(java.io.ObjectInputStream s) | ||
| 1161 | throws IOException, ClassNotFoundException | ||
| 1162 | { | ||
| 1163 | // Read in the action, then initialize the rest | ||
| 1164 | s.defaultReadObject(); | ||
| 1165 | init(getName(),getMask(actions)); | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | /* | ||
| 1169 | public String toString() | ||
| 1170 | { | ||
| 1171 | StringBuffer s = new StringBuffer(super.toString() + "\n" + | ||
| 1172 | "cname = " + cname + "\n" + | ||
| 1173 | "wildcard = " + wildcard + "\n" + | ||
| 1174 | "invalid = " + invalid + "\n" + | ||
| 1175 | "portrange = " + portrange[0] + "," + portrange[1] + "\n"); | ||
| 1176 | if (addresses != null) for (int i=0; i<addresses.length; i++) { | ||
| 1177 | s.append( addresses[i].getHostAddress()); | ||
| 1178 | s.append("\n"); | ||
| 1179 | } else { | ||
| 1180 | s.append("(no addresses)\n"); | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | return s.toString(); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | public static void main(String args[]) throws Exception { | ||
| 1187 | SocketPermission this_ = new SocketPermission(args[0], "connect"); | ||
| 1188 | SocketPermission that_ = new SocketPermission(args[1], "connect"); | ||
| 1189 | System.out.println("-----\n"); | ||
| 1190 | System.out.println("this.implies(that) = " + this_.implies(that_)); | ||
| 1191 | System.out.println("-----\n"); | ||
| 1192 | System.out.println("this = "+this_); | ||
| 1193 | System.out.println("-----\n"); | ||
| 1194 | System.out.println("that = "+that_); | ||
| 1195 | System.out.println("-----\n"); | ||
| 1196 | |||
| 1197 | SocketPermissionCollection nps = new SocketPermissionCollection(); | ||
| 1198 | nps.add(this_); | ||
| 1199 | nps.add(new SocketPermission("www-leland.stanford.edu","connect")); | ||
| 1200 | nps.add(new SocketPermission("www-sun.com","connect")); | ||
| 1201 | System.out.println("nps.implies(that) = " + nps.implies(that_)); | ||
| 1202 | System.out.println("-----\n"); | ||
| 1203 | } | ||
| 1204 | */ | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | /** | ||
| 1208 | |||
| 1209 | if (init'd with IP, key is IP as string) | ||
| 1210 | if wildcard, its the wild card | ||
| 1211 | else its the cname? | ||
| 1212 | |||
| 1213 | * | ||
| 1214 | * @see java.security.Permission | ||
| 1215 | * @see java.security.Permissions | ||
| 1216 | * @see java.security.PermissionCollection | ||
| 1217 | * | ||
| 1218 | * | ||
| 1219 | * @author Roland Schemers | ||
| 1220 | * | ||
| 1221 | * @serial include | ||
| 1222 | */ | ||
| 1223 | |||
| 1224 | final class SocketPermissionCollection extends PermissionCollection | ||
| 1225 | implements Serializable | ||
| 1226 | { | ||
| 1227 | // Not serialized; see serialization section at end of class | ||
| 1228 | private transient List perms; | ||
| 1229 | |||
| 1230 | /** | ||
| 1231 | * Create an empty SocketPermissions object. | ||
| 1232 | * | ||
| 1233 | */ | ||
| 1234 | |||
| 1235 | public SocketPermissionCollection() { | ||
| 1236 | perms = new ArrayList(); | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | /** | ||
| 1240 | * Adds a permission to the SocketPermissions. The key for the hash is | ||
| 1241 | * the name in the case of wildcards, or all the IP addresses. | ||
| 1242 | * | ||
| 1243 | * @param permission the Permission object to add. | ||
| 1244 | * | ||
| 1245 | * @exception IllegalArgumentException - if the permission is not a | ||
| 1246 | * SocketPermission | ||
| 1247 | * | ||
| 1248 | * @exception SecurityException - if this SocketPermissionCollection object | ||
| 1249 | * has been marked readonly | ||
| 1250 | */ | ||
| 1251 | |||
| 1252 | public void add(Permission permission) | ||
| 1253 | { | ||
| 1254 | if (! (permission instanceof SocketPermission)) | ||
| 1255 | throw new IllegalArgumentException("invalid permission: "+ | ||
| 1256 | permission); | ||
| 1257 | if (isReadOnly()) | ||
| 1258 | throw new SecurityException( | ||
| 1259 | "attempt to add a Permission to a readonly PermissionCollection"); | ||
| 1260 | |||
| 1261 | // optimization to ensure perms most likely to be tested | ||
| 1262 | // show up early (4301064) | ||
| 1263 | synchronized (this) { | ||
| 1264 | perms.add(0, permission); | ||
| 1265 | } | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | /** | ||
| 1269 | * Check and see if this collection of permissions implies the permissions | ||
| 1270 | * expressed in "permission". | ||
| 1271 | * | ||
| 1272 | * @param p the Permission object to compare | ||
| 1273 | * | ||
| 1274 | * @return true if "permission" is a proper subset of a permission in | ||
| 1275 | * the collection, false if not. | ||
| 1276 | */ | ||
| 1277 | |||
| 1278 | public boolean implies(Permission permission) | ||
| 1279 | { | ||
| 1280 | if (! (permission instanceof SocketPermission)) | ||
| 1281 | return false; | ||
| 1282 | |||
| 1283 | SocketPermission np = (SocketPermission) permission; | ||
| 1284 | |||
| 1285 | int desired = np.getMask(); | ||
| 1286 | int effective = 0; | ||
| 1287 | int needed = desired; | ||
| 1288 | |||
| 1289 | synchronized (this) { | ||
| 1290 | int len = perms.size(); | ||
| 1291 | //System.out.println("implies "+np); | ||
| 1292 | for (int i = 0; i < len; i++) { | ||
| 1293 | SocketPermission x = (SocketPermission) perms.get(i); | ||
| 1294 | //System.out.println(" trying "+x); | ||
| 1295 | if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) { | ||
| 1296 | effective |= x.getMask(); | ||
| 1297 | if ((effective & desired) == desired) | ||
| 1298 | return true; | ||
| 1299 | needed = (desired ^ effective); | ||
| 1300 | } | ||
| 1301 | } | ||
| 1302 | } | ||
| 1303 | return false; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | /** | ||
| 1307 | * Returns an enumeration of all the SocketPermission objects in the | ||
| 1308 | * container. | ||
| 1309 | * | ||
| 1310 | * @return an enumeration of all the SocketPermission objects. | ||
| 1311 | */ | ||
| 1312 | |||
| 1313 | public Enumeration elements() { | ||
| 1314 | // Convert Iterator into Enumeration | ||
| 1315 | synchronized (this) { | ||
| 1316 | return Collections.enumeration(perms); | ||
| 1317 | } | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | private static final long serialVersionUID = 2787186408602843674L; | ||
| 1321 | |||
| 1322 | // Need to maintain serialization interoperability with earlier releases, | ||
| 1323 | // which had the serializable field: | ||
| 1324 | |||
| 1325 | // | ||
| 1326 | // The SocketPermissions for this set. | ||
| 1327 | // @serial | ||
| 1328 | // | ||
| 1329 | // private Vector permissions; | ||
| 1330 | |||
| 1331 | /** | ||
| 1332 | * @serialField permissions java.util.Vector | ||
| 1333 | * A list of the SocketPermissions for this set. | ||
| 1334 | */ | ||
| 1335 | private static final ObjectStreamField[] serialPersistentFields = { | ||
| 1336 | new ObjectStreamField("permissions", Vector.class), | ||
| 1337 | }; | ||
| 1338 | |||
| 1339 | /** | ||
| 1340 | * @serialData "permissions" field (a Vector containing the SocketPermissions). | ||
| 1341 | */ | ||
| 1342 | /* | ||
| 1343 | * Writes the contents of the perms field out as a Vector for | ||
| 1344 | * serialization compatibility with earlier releases. | ||
| 1345 | */ | ||
| 1346 | private void writeObject(ObjectOutputStream out) throws IOException { | ||
| 1347 | // Don't call out.defaultWriteObject() | ||
| 1348 | |||
| 1349 | // Write out Vector | ||
| 1350 | Vector permissions = new Vector(perms.size()); | ||
| 1351 | |||
| 1352 | synchronized (this) { | ||
| 1353 | permissions.addAll(perms); | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | ObjectOutputStream.PutField pfields = out.putFields(); | ||
| 1357 | pfields.put("permissions", permissions); | ||
| 1358 | out.writeFields(); | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | /* | ||
| 1362 | * Reads in a Vector of SocketPermissions and saves them in the perms field. | ||
| 1363 | */ | ||
| 1364 | private void readObject(ObjectInputStream in) throws IOException, | ||
| 1365 | ClassNotFoundException { | ||
| 1366 | // Don't call in.defaultReadObject() | ||
| 1367 | |||
| 1368 | // Read in serialized fields | ||
| 1369 | ObjectInputStream.GetField gfields = in.readFields(); | ||
| 1370 | |||
| 1371 | // Get the one we want | ||
| 1372 | Vector permissions = (Vector)gfields.get("permissions", null); | ||
| 1373 | perms = new ArrayList(permissions.size()); | ||
| 1374 | perms.addAll(permissions); | ||
| 1375 | } | ||
| 1376 | } | ||