1 /*
   2  * Copyright (c) 1997, 2002, 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 sun.security.x509;
  27 
  28 import java.io.IOException;
  29 import java.lang.Integer;
  30 import java.net.InetAddress;
  31 import java.util.Arrays;
  32 import sun.security.util.HexDumpEncoder;
  33 import sun.security.util.BitArray;
  34 import sun.security.util.DerOutputStream;
  35 import sun.security.util.DerValue;
  36 
  37 /**
  38  * This class implements the IPAddressName as required by the GeneralNames
  39  * ASN.1 object.  Both IPv4 and IPv6 addresses are supported using the
  40  * formats specified in IETF PKIX RFC2459.
  41  * <p>
  42  * [RFC2459 4.2.1.7 Subject Alternative Name]
  43  * When the subjectAltName extension contains a iPAddress, the address
  44  * MUST be stored in the octet string in "network byte order," as
  45  * specified in RFC 791. The least significant bit (LSB) of
  46  * each octet is the LSB of the corresponding byte in the network
  47  * address. For IP Version 4, as specified in RFC 791, the octet string
  48  * MUST contain exactly four octets.  For IP Version 6, as specified in
  49  * RFC 1883, the octet string MUST contain exactly sixteen octets.
  50  * <p>
  51  * [RFC2459 4.2.1.11 Name Constraints]
  52  * The syntax of iPAddress MUST be as described in section 4.2.1.7 with
  53  * the following additions specifically for Name Constraints.  For IPv4
  54  * addresses, the ipAddress field of generalName MUST contain eight (8)
  55  * octets, encoded in the style of RFC 1519 (CIDR) to represent an
  56  * address range.[RFC 1519]  For IPv6 addresses, the ipAddress field
  57  * MUST contain 32 octets similarly encoded.  For example, a name
  58  * constraint for "class C" subnet 10.9.8.0 shall be represented as the
  59  * octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation
  60  * 10.9.8.0/255.255.255.0.
  61  * <p>
  62  * @see GeneralName
  63  * @see GeneralNameInterface
  64  * @see GeneralNames
  65  *
  66  *
  67  * @author Amit Kapoor
  68  * @author Hemma Prafullchandra
  69  */
  70 public class IPAddressName implements GeneralNameInterface {
  71     private byte[] address;
  72     private boolean isIPv4;
  73     private String name;
  74 
  75     /**
  76      * Create the IPAddressName object from the passed encoded Der value.
  77      *
  78      * @param derValue the encoded DER IPAddressName.
  79      * @exception IOException on error.
  80      */
  81     public IPAddressName(DerValue derValue) throws IOException {
  82         this(derValue.getOctetString());
  83     }
  84 
  85     /**
  86      * Create the IPAddressName object with the specified octets.
  87      *
  88      * @param address the IP address
  89      * @throws IOException if address is not a valid IPv4 or IPv6 address
  90      */
  91     public IPAddressName(byte[] address) throws IOException {
  92         /*
  93          * A valid address must consist of 4 bytes of address and
  94          * optional 4 bytes of 4 bytes of mask, or 16 bytes of address
  95          * and optional 16 bytes of mask.
  96          */
  97         if (address.length == 4 || address.length == 8) {
  98             isIPv4 = true;
  99         } else if (address.length == 16 || address.length == 32) {
 100             isIPv4 = false;
 101         } else {
 102             throw new IOException("Invalid IPAddressName");
 103         }
 104         this.address = address;
 105     }
 106 
 107     /**
 108      * Create an IPAddressName from a String.
 109      * [IETF RFC1338 Supernetting {@literal &} IETF RFC1519 Classless Inter-Domain
 110      * Routing (CIDR)] For IPv4 addresses, the forms are
 111      * "b1.b2.b3.b4" or "b1.b2.b3.b4/m1.m2.m3.m4", where b1 - b4 are decimal
 112      * byte values 0-255 and m1 - m4 are decimal mask values
 113      * 0 - 255.
 114      * <p>
 115      * [IETF RFC2373 IP Version 6 Addressing Architecture]
 116      * For IPv6 addresses, the forms are "a1:a2:...:a8" or "a1:a2:...:a8/n",
 117      * where a1-a8 are hexadecimal values representing the eight 16-bit pieces
 118      * of the address. If /n is used, n is a decimal number indicating how many
 119      * of the leftmost contiguous bits of the address comprise the prefix for
 120      * this subnet. Internally, a mask value is created using the prefix length.
 121      *
 122      * @param name String form of IPAddressName
 123      * @throws IOException if name can not be converted to a valid IPv4 or IPv6
 124      *     address
 125      */
 126     public IPAddressName(String name) throws IOException {
 127 
 128         if (name == null || name.length() == 0) {
 129             throw new IOException("IPAddress cannot be null or empty");
 130         }
 131         if (name.charAt(name.length() - 1) == '/') {
 132             throw new IOException("Invalid IPAddress: " + name);
 133         }
 134 
 135         if (name.indexOf(':') >= 0) {
 136             // name is IPv6: uses colons as value separators
 137             // Parse name into byte-value address components and optional
 138             // prefix
 139             parseIPv6(name);
 140             isIPv4 = false;
 141         } else if (name.indexOf('.') >= 0) {
 142             //name is IPv4: uses dots as value separators
 143             parseIPv4(name);
 144             isIPv4 = true;
 145         } else {
 146             throw new IOException("Invalid IPAddress: " + name);
 147         }
 148     }
 149 
 150     /**
 151      * Parse an IPv4 address.
 152      *
 153      * @param name IPv4 address with optional mask values
 154      * @throws IOException on error
 155      */
 156     private void parseIPv4(String name) throws IOException {
 157 
 158         // Parse name into byte-value address components
 159         int slashNdx = name.indexOf('/');
 160         if (slashNdx == -1) {
 161             address = InetAddress.getByName(name).getAddress();
 162         } else {
 163             address = new byte[8];
 164 
 165             // parse mask
 166             byte[] mask = InetAddress.getByName
 167                 (name.substring(slashNdx+1)).getAddress();
 168 
 169             // parse base address
 170             byte[] host = InetAddress.getByName
 171                 (name.substring(0, slashNdx)).getAddress();
 172 
 173             System.arraycopy(host, 0, address, 0, 4);
 174             System.arraycopy(mask, 0, address, 4, 4);
 175         }
 176     }
 177 
 178     /**
 179      * Parse an IPv6 address.
 180      *
 181      * @param name String IPv6 address with optional /<prefix length>
 182      *             If /<prefix length> is present, address[] array will
 183      *             be 32 bytes long, otherwise 16.
 184      * @throws IOException on error
 185      */
 186     private static final int MASKSIZE = 16;
 187     private void parseIPv6(String name) throws IOException {
 188 
 189         int slashNdx = name.indexOf('/');
 190         if (slashNdx == -1) {
 191             address = InetAddress.getByName(name).getAddress();
 192         } else {
 193             address = new byte[32];
 194             byte[] base = InetAddress.getByName
 195                 (name.substring(0, slashNdx)).getAddress();
 196             System.arraycopy(base, 0, address, 0, 16);
 197 
 198             // append a mask corresponding to the num of prefix bits specified
 199             int prefixLen = Integer.parseInt(name.substring(slashNdx+1));
 200             if (prefixLen < 0 || prefixLen > 128) {
 201                 throw new IOException("IPv6Address prefix length (" +
 202                         prefixLen + ") in out of valid range [0,128]");
 203             }
 204 
 205             // create new bit array initialized to zeros
 206             BitArray bitArray = new BitArray(MASKSIZE * 8);
 207 
 208             // set all most significant bits up to prefix length
 209             for (int i = 0; i < prefixLen; i++)
 210                 bitArray.set(i, true);
 211             byte[] maskArray = bitArray.toByteArray();
 212 
 213             // copy mask bytes into mask portion of address
 214             for (int i = 0; i < MASKSIZE; i++)
 215                 address[MASKSIZE+i] = maskArray[i];
 216         }
 217     }
 218 
 219     /**
 220      * Return the type of the GeneralName.
 221      */
 222     public int getType() {
 223         return NAME_IP;
 224     }
 225 
 226     /**
 227      * Encode the IPAddress name into the DerOutputStream.
 228      *
 229      * @param out the DER stream to encode the IPAddressName to.
 230      * @exception IOException on encoding errors.
 231      */
 232     public void encode(DerOutputStream out) throws IOException {
 233         out.putOctetString(address);
 234     }
 235 
 236     /**
 237      * Return a printable string of IPaddress
 238      */
 239     public String toString() {
 240         try {
 241             return "IPAddress: " + getName();
 242         } catch (IOException ioe) {
 243             // dump out hex rep for debugging purposes
 244             HexDumpEncoder enc = new HexDumpEncoder();
 245             return "IPAddress: " + enc.encodeBuffer(address);
 246         }
 247     }
 248 
 249     /**
 250      * Return a standard String representation of IPAddress.
 251      * See IPAddressName(String) for the formats used for IPv4
 252      * and IPv6 addresses.
 253      *
 254      * @throws IOException if the IPAddress cannot be converted to a String
 255      */
 256     public String getName() throws IOException {
 257         if (name != null)
 258             return name;
 259 
 260         if (isIPv4) {
 261             //IPv4 address or subdomain
 262             byte[] host = new byte[4];
 263             System.arraycopy(address, 0, host, 0, 4);
 264             name = InetAddress.getByAddress(host).getHostAddress();
 265             if (address.length == 8) {
 266                 byte[] mask = new byte[4];
 267                 System.arraycopy(address, 4, mask, 0, 4);
 268                 name = name + '/' +
 269                        InetAddress.getByAddress(mask).getHostAddress();
 270             }
 271         } else {
 272             //IPv6 address or subdomain
 273             byte[] host = new byte[16];
 274             System.arraycopy(address, 0, host, 0, 16);
 275             name = InetAddress.getByAddress(host).getHostAddress();
 276             if (address.length == 32) {
 277                 // IPv6 subdomain: display prefix length
 278 
 279                 // copy subdomain into new array and convert to BitArray
 280                 byte[] maskBytes = new byte[16];
 281                 for (int i=16; i < 32; i++)
 282                     maskBytes[i-16] = address[i];
 283                 BitArray ba = new BitArray(16*8, maskBytes);
 284                 // Find first zero bit
 285                 int i=0;
 286                 for (; i < 16*8; i++) {
 287                     if (!ba.get(i))
 288                         break;
 289                 }
 290                 name = name + '/' + i;
 291                 // Verify remaining bits 0
 292                 for (; i < 16*8; i++) {
 293                     if (ba.get(i)) {
 294                         throw new IOException("Invalid IPv6 subdomain - set " +
 295                             "bit " + i + " not contiguous");
 296                     }
 297                 }
 298             }
 299         }
 300         return name;
 301     }
 302 
 303     /**
 304      * Returns this IPAddress name as a byte array.
 305      */
 306     public byte[] getBytes() {
 307         return address.clone();
 308     }
 309 
 310     /**
 311      * Compares this name with another, for equality.
 312      *
 313      * @return true iff the names are identical.
 314      */
 315     public boolean equals(Object obj) {
 316         if (this == obj)
 317             return true;
 318 
 319         if (!(obj instanceof IPAddressName))
 320             return false;
 321 
 322         IPAddressName otherName = (IPAddressName)obj;
 323         byte[] other = otherName.address;
 324 
 325         if (other.length != address.length)
 326             return false;
 327 
 328         if (address.length == 8 || address.length == 32) {
 329             // Two subnet addresses
 330             // Mask each and compare masked values
 331             int maskLen = address.length/2;
 332             for (int i=0; i < maskLen; i++) {
 333                 byte maskedThis = (byte)(address[i] & address[i+maskLen]);
 334                 byte maskedOther = (byte)(other[i] & other[i+maskLen]);
 335                 if (maskedThis != maskedOther) {
 336                     return false;
 337                 }
 338             }
 339             // Now compare masks
 340             for (int i=maskLen; i < address.length; i++)
 341                 if (address[i] != other[i])
 342                     return false;
 343             return true;
 344         } else {
 345             // Two IPv4 host addresses or two IPv6 host addresses
 346             // Compare bytes
 347             return Arrays.equals(other, address);
 348         }
 349     }
 350 
 351     /**
 352      * Returns the hash code value for this object.
 353      *
 354      * @return a hash code value for this object.
 355      */
 356     public int hashCode() {
 357         int retval = 0;
 358 
 359         for (int i=0; i<address.length; i++)
 360             retval += address[i] * i;
 361 
 362         return retval;
 363     }
 364 
 365     /**
 366      * Return type of constraint inputName places on this name:<ul>
 367      *   <li>NAME_DIFF_TYPE = -1: input name is different type from name
 368      *       (i.e. does not constrain).
 369      *   <li>NAME_MATCH = 0: input name matches name.
 370      *   <li>NAME_NARROWS = 1: input name narrows name (is lower in the naming
 371      *       subtree)
 372      *   <li>NAME_WIDENS = 2: input name widens name (is higher in the naming
 373      *       subtree)
 374      *   <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but
 375      *       is same type.
 376      * </ul>.  These results are used in checking NameConstraints during
 377      * certification path verification.
 378      * <p>
 379      * [RFC2459] The syntax of iPAddress MUST be as described in section
 380      * 4.2.1.7 with the following additions specifically for Name Constraints.
 381      * For IPv4 addresses, the ipAddress field of generalName MUST contain
 382      * eight (8) octets, encoded in the style of RFC 1519 (CIDR) to represent an
 383      * address range.[RFC 1519]  For IPv6 addresses, the ipAddress field
 384      * MUST contain 32 octets similarly encoded.  For example, a name
 385      * constraint for "class C" subnet 10.9.8.0 shall be represented as the
 386      * octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation
 387      * 10.9.8.0/255.255.255.0.
 388      *
 389      * @param inputName to be checked for being constrained
 390      * @return constraint type above
 391      * @throws UnsupportedOperationException if name is not exact match, but
 392      * narrowing and widening are not supported for this name type.
 393      */
 394     public int constrains(GeneralNameInterface inputName)
 395     throws UnsupportedOperationException {
 396         int constraintType;
 397         if (inputName == null)
 398             constraintType = NAME_DIFF_TYPE;
 399         else if (inputName.getType() != NAME_IP)
 400             constraintType = NAME_DIFF_TYPE;
 401         else if (((IPAddressName)inputName).equals(this))
 402             constraintType = NAME_MATCH;
 403         else {
 404             IPAddressName otherName = (IPAddressName)inputName;
 405             byte[] otherAddress = otherName.address;
 406             if (otherAddress.length == 4 && address.length == 4)
 407                 // Two host addresses
 408                 constraintType = NAME_SAME_TYPE;
 409             else if ((otherAddress.length == 8 && address.length == 8) ||
 410                      (otherAddress.length == 32 && address.length == 32)) {
 411                 // Two subnet addresses
 412                 // See if one address fully encloses the other address
 413                 boolean otherSubsetOfThis = true;
 414                 boolean thisSubsetOfOther = true;
 415                 boolean thisEmpty = false;
 416                 boolean otherEmpty = false;
 417                 int maskOffset = address.length/2;
 418                 for (int i=0; i < maskOffset; i++) {
 419                     if ((byte)(address[i] & address[i+maskOffset]) != address[i])
 420                         thisEmpty=true;
 421                     if ((byte)(otherAddress[i] & otherAddress[i+maskOffset]) != otherAddress[i])
 422                         otherEmpty=true;
 423                     if (!(((byte)(address[i+maskOffset] & otherAddress[i+maskOffset]) == address[i+maskOffset]) &&
 424                           ((byte)(address[i]   & address[i+maskOffset])      == (byte)(otherAddress[i] & address[i+maskOffset])))) {
 425                         otherSubsetOfThis = false;
 426                     }
 427                     if (!(((byte)(otherAddress[i+maskOffset] & address[i+maskOffset])      == otherAddress[i+maskOffset]) &&
 428                           ((byte)(otherAddress[i]   & otherAddress[i+maskOffset]) == (byte)(address[i] & otherAddress[i+maskOffset])))) {
 429                         thisSubsetOfOther = false;
 430                     }
 431                 }
 432                 if (thisEmpty || otherEmpty) {
 433                     if (thisEmpty && otherEmpty)
 434                         constraintType = NAME_MATCH;
 435                     else if (thisEmpty)
 436                         constraintType = NAME_WIDENS;
 437                     else
 438                         constraintType = NAME_NARROWS;
 439                 } else if (otherSubsetOfThis)
 440                     constraintType = NAME_NARROWS;
 441                 else if (thisSubsetOfOther)
 442                     constraintType = NAME_WIDENS;
 443                 else
 444                     constraintType = NAME_SAME_TYPE;
 445             } else if (otherAddress.length == 8 || otherAddress.length == 32) {
 446                 //Other is a subnet, this is a host address
 447                 int i = 0;
 448                 int maskOffset = otherAddress.length/2;
 449                 for (; i < maskOffset; i++) {
 450                     // Mask this address by other address mask and compare to other address
 451                     // If all match, then this address is in other address subnet
 452                     if ((address[i] & otherAddress[i+maskOffset]) != otherAddress[i])
 453                         break;
 454                 }
 455                 if (i == maskOffset)
 456                     constraintType = NAME_WIDENS;
 457                 else
 458                     constraintType = NAME_SAME_TYPE;
 459             } else if (address.length == 8 || address.length == 32) {
 460                 //This is a subnet, other is a host address
 461                 int i = 0;
 462                 int maskOffset = address.length/2;
 463                 for (; i < maskOffset; i++) {
 464                     // Mask other address by this address mask and compare to this address
 465                     if ((otherAddress[i] & address[i+maskOffset]) != address[i])
 466                         break;
 467                 }
 468                 if (i == maskOffset)
 469                     constraintType = NAME_NARROWS;
 470                 else
 471                     constraintType = NAME_SAME_TYPE;
 472             } else {
 473                 constraintType = NAME_SAME_TYPE;
 474             }
 475         }
 476         return constraintType;
 477     }
 478 
 479     /**
 480      * Return subtree depth of this name for purposes of determining
 481      * NameConstraints minimum and maximum bounds and for calculating
 482      * path lengths in name subtrees.
 483      *
 484      * @return distance of name from root
 485      * @throws UnsupportedOperationException if not supported for this name type
 486      */
 487     public int subtreeDepth() throws UnsupportedOperationException {
 488         throw new UnsupportedOperationException
 489             ("subtreeDepth() not defined for IPAddressName");
 490     }
 491 }