1 /*
   2  * Copyright (c) 1997, 2012, 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 
  27 package com.sun.jmx.snmp.IPAcl;
  28 
  29 
  30 
  31 // java import
  32 //
  33 import java.io.Serializable;
  34 import java.io.File;
  35 import java.io.FileInputStream;
  36 import java.io.FileNotFoundException;
  37 import java.net.InetAddress;
  38 import java.net.UnknownHostException;
  39 import java.util.Hashtable;
  40 import java.util.logging.Level;
  41 import java.util.Vector;
  42 import java.util.Enumeration;
  43 import java.util.HashSet;
  44 import java.security.acl.AclEntry;
  45 import java.security.acl.NotOwnerException;
  46 
  47 // SNMP Runtime import
  48 //
  49 import static com.sun.jmx.defaults.JmxProperties.SNMP_LOGGER;
  50 import com.sun.jmx.snmp.InetAddressAcl;
  51 
  52 /**
  53  * Defines an implementation of the {@link com.sun.jmx.snmp.InetAddressAcl InetAddressAcl} interface.
  54  * <p>
  55  * In this implementation the ACL information is stored on a flat file and
  56  * its default location is "$JRE/lib/snmp.acl" - See
  57  * {@link #getDefaultAclFileName()}
  58  * <p>
  59  * <OL>
  60   *
  61  * <p><b>This API is a Sun Microsystems internal API  and is subject
  62  * to change without notice.</b></p>
  63  */
  64 
  65 public class SnmpAcl implements InetAddressAcl, Serializable {
  66     private static final long serialVersionUID = -6702287103824397063L;
  67 
  68     static final PermissionImpl READ  = new PermissionImpl("READ");
  69     static final PermissionImpl WRITE = new PermissionImpl("WRITE");
  70 
  71     /**
  72      * Constructs the Java Dynamic Management(TM) Access Control List
  73      * based on IP addresses. The ACL will take the given owner name.
  74      * The current IP address will be the owner of the ACL.
  75      *
  76      * @param Owner The name of the ACL Owner.
  77      *
  78      * @exception UnknownHostException If the local host is unknown.
  79      * @exception IllegalArgumentException If the ACL file doesn't exist.
  80      */
  81     public SnmpAcl(String Owner)
  82         throws UnknownHostException, IllegalArgumentException {
  83         this(Owner,null);
  84     }
  85 
  86     /**
  87      * Constructs the Java Dynamic Management(TM) Access Control List
  88      * based on IP addresses. The ACL will take the given owner name.
  89      * The current IP address will be the owner of the ACL.
  90      *
  91      * @param Owner The name of the ACL Owner.
  92      * @param aclFileName The name of the ACL File.
  93      *
  94      * @exception UnknownHostException If the local host is unknown.
  95      * @exception IllegalArgumentException If the ACL file doesn't exist.
  96      */
  97     public SnmpAcl(String Owner, String aclFileName)
  98         throws UnknownHostException, IllegalArgumentException {
  99         trapDestList= new Hashtable<InetAddress, Vector<String>>();
 100         informDestList= new Hashtable<InetAddress, Vector<String>>();
 101 
 102         // PrincipalImpl() take the current host as entry
 103         owner = new PrincipalImpl();
 104         try {
 105             acl = new AclImpl(owner,Owner);
 106             AclEntry ownEntry = new AclEntryImpl(owner);
 107             ownEntry.addPermission(READ);
 108             ownEntry.addPermission(WRITE);
 109             acl.addEntry(owner,ownEntry);
 110         } catch (NotOwnerException ex) {
 111             if (SNMP_LOGGER.isLoggable(Level.FINEST)) {
 112                 SNMP_LOGGER.logp(Level.FINEST, SnmpAcl.class.getName(),
 113                     "SnmpAcl(String,String)",
 114                     "Should never get NotOwnerException as the owner " +
 115                     "is built in this constructor");
 116             }
 117         }
 118         if (aclFileName == null) setDefaultFileName();
 119         else setAuthorizedListFile(aclFileName);
 120         readAuthorizedListFile();
 121     }
 122 
 123     /**
 124      * Returns an enumeration of the entries in this ACL. Each element in the
 125      * enumeration is of type <CODE>java.security.acl.AclEntry</CODE>.
 126      *
 127      * @return An enumeration of the entries in this ACL.
 128      */
 129     public Enumeration<AclEntry> entries() {
 130         return acl.entries();
 131     }
 132 
 133     /**
 134      * Returns ann enumeration of community strings. Community strings are returned as String.
 135      * @return The enumeration of community strings.
 136      */
 137     public Enumeration<String> communities() {
 138         HashSet<String> set = new HashSet<String>();
 139         Vector<String> res = new Vector<String>();
 140         for (Enumeration<AclEntry> e = acl.entries() ; e.hasMoreElements() ;) {
 141             AclEntryImpl entry = (AclEntryImpl) e.nextElement();
 142             for (Enumeration<String> cs = entry.communities();
 143                  cs.hasMoreElements() ;) {
 144                 set.add(cs.nextElement());
 145             }
 146         }
 147         String[] objs = set.toArray(new String[0]);
 148         for(int i = 0; i < objs.length; i++)
 149             res.addElement(objs[i]);
 150 
 151         return res.elements();
 152     }
 153 
 154     /**
 155      * Returns the name of the ACL.
 156      *
 157      * @return The name of the ACL.
 158      */
 159     public String getName() {
 160         return acl.getName();
 161     }
 162 
 163     /**
 164      * Returns the read permission instance used.
 165      *
 166      * @return The read permission instance.
 167      */
 168     static public PermissionImpl getREAD() {
 169         return READ;
 170     }
 171 
 172     /**
 173      * Returns the write permission instance used.
 174      *
 175      * @return  The write permission instance.
 176      */
 177     static public PermissionImpl getWRITE() {
 178         return WRITE;
 179     }
 180 
 181     /**
 182      * Get the default name for the ACL file.
 183      * In this implementation this is "$JRE/lib/snmp.acl"
 184      * @return The default name for the ACL file.
 185      **/
 186     public static String getDefaultAclFileName() {
 187         final String fileSeparator =
 188             System.getProperty("file.separator");
 189         final StringBuilder defaultAclName =
 190             new StringBuilder(System.getProperty("java.home")).
 191             append(fileSeparator).append("lib").append(fileSeparator).
 192             append("snmp.acl");
 193         return defaultAclName.toString();
 194     }
 195 
 196     /**
 197      * Sets the full path of the file containing the ACL information.
 198      *
 199      * @param filename The full path of the file containing the ACL information.
 200      * @throws IllegalArgumentException If the passed ACL file doesn't exist.
 201      */
 202     public void setAuthorizedListFile(String filename)
 203         throws IllegalArgumentException {
 204         File file = new File(filename);
 205         if (!file.isFile() ) {
 206             if (SNMP_LOGGER.isLoggable(Level.FINEST)) {
 207                 SNMP_LOGGER.logp(Level.FINEST, SnmpAcl.class.getName(),
 208                     "setAuthorizedListFile", "ACL file not found: " + filename);
 209             }
 210             throw new
 211                 IllegalArgumentException("The specified file ["+file+"] "+
 212                                          "doesn't exist or is not a file, "+
 213                                          "no configuration loaded");
 214         }
 215         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 216             SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 217                 "setAuthorizedListFile", "Default file set to " + filename);
 218         }
 219         authorizedListFile = filename;
 220     }
 221 
 222     /**
 223      * Resets this ACL to the values contained in the configuration file.
 224      *
 225      * @exception NotOwnerException If the principal attempting the reset is not an owner of this ACL.
 226      * @exception UnknownHostException If IP addresses for hosts contained in the ACL file couldn't be found.
 227      */
 228     public void rereadTheFile() throws NotOwnerException, UnknownHostException {
 229         alwaysAuthorized = false;
 230         acl.removeAll(owner);
 231         trapDestList.clear();
 232         informDestList.clear();
 233         AclEntry ownEntry = new AclEntryImpl(owner);
 234         ownEntry.addPermission(READ);
 235         ownEntry.addPermission(WRITE);
 236         acl.addEntry(owner,ownEntry);
 237         readAuthorizedListFile();
 238     }
 239 
 240     /**
 241      * Returns the full path of the file used to get ACL information.
 242      *
 243      * @return The full path of the file used to get ACL information.
 244      */
 245     public String getAuthorizedListFile() {
 246         return authorizedListFile;
 247     }
 248 
 249     /**
 250      * Checks whether or not the specified host has <CODE>READ</CODE> access.
 251      *
 252      * @param address The host address to check.
 253      *
 254      * @return <CODE>true</CODE> if the host has read permission, <CODE>false</CODE> otherwise.
 255      */
 256     public boolean checkReadPermission(InetAddress address) {
 257         if (alwaysAuthorized) return ( true );
 258         PrincipalImpl p = new PrincipalImpl(address);
 259         return acl.checkPermission(p, READ);
 260     }
 261 
 262     /**
 263      * Checks whether or not the specified host and community have <CODE>READ</CODE> access.
 264      *
 265      * @param address The host address to check.
 266      * @param community The community associated with the host.
 267      *
 268      * @return <CODE>true</CODE> if the pair (host, community) has read permission, <CODE>false</CODE> otherwise.
 269      */
 270     public boolean checkReadPermission(InetAddress address, String community) {
 271         if (alwaysAuthorized) return ( true );
 272         PrincipalImpl p = new PrincipalImpl(address);
 273         return acl.checkPermission(p, community, READ);
 274     }
 275 
 276     /**
 277      * Checks whether or not a community string is defined.
 278      *
 279      * @param community The community to check.
 280      *
 281      * @return <CODE>true</CODE> if the community is known, <CODE>false</CODE> otherwise.
 282      */
 283     public boolean checkCommunity(String community) {
 284         return acl.checkCommunity(community);
 285     }
 286 
 287     /**
 288      * Checks whether or not the specified host has <CODE>WRITE</CODE> access.
 289      *
 290      * @param address The host address to check.
 291      *
 292      * @return <CODE>true</CODE> if the host has write permission, <CODE>false</CODE> otherwise.
 293      */
 294     public boolean checkWritePermission(InetAddress address) {
 295         if (alwaysAuthorized) return ( true );
 296         PrincipalImpl p = new PrincipalImpl(address);
 297         return acl.checkPermission(p, WRITE);
 298     }
 299 
 300     /**
 301      * Checks whether or not the specified host and community have <CODE>WRITE</CODE> access.
 302      *
 303      * @param address The host address to check.
 304      * @param community The community associated with the host.
 305      *
 306      * @return <CODE>true</CODE> if the pair (host, community) has write permission, <CODE>false</CODE> otherwise.
 307      */
 308     public boolean checkWritePermission(InetAddress address, String community) {
 309         if (alwaysAuthorized) return ( true );
 310         PrincipalImpl p = new PrincipalImpl(address);
 311         return acl.checkPermission(p, community, WRITE);
 312     }
 313 
 314     /**
 315      * Returns an enumeration of trap destinations.
 316      *
 317      * @return An enumeration of the trap destinations (enumeration of <CODE>InetAddress</CODE>).
 318      */
 319     public Enumeration<InetAddress> getTrapDestinations() {
 320         return trapDestList.keys();
 321     }
 322 
 323     /**
 324      * Returns an enumeration of trap communities for a given host.
 325      *
 326      * @param i The address of the host.
 327      *
 328      * @return An enumeration of trap communities for a given host (enumeration of <CODE>String</CODE>).
 329      */
 330     public Enumeration<String> getTrapCommunities(InetAddress i) {
 331         Vector<String> list = null;
 332         if ((list = trapDestList.get(i)) != null ) {
 333             if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 334                 SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 335                     "getTrapCommunities", "["+i.toString()+"] is in list");
 336             }
 337             return list.elements();
 338         } else {
 339             list = new Vector<>();
 340             if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 341                 SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 342                     "getTrapCommunities", "["+i.toString()+"] is not in list");
 343             }
 344             return list.elements();
 345         }
 346     }
 347 
 348     /**
 349      * Returns an enumeration of inform destinations.
 350      *
 351      * @return An enumeration of the inform destinations (enumeration of <CODE>InetAddress</CODE>).
 352      */
 353     public Enumeration<InetAddress> getInformDestinations() {
 354         return informDestList.keys();
 355     }
 356 
 357     /**
 358      * Returns an enumeration of inform communities for a given host.
 359      *
 360      * @param i The address of the host.
 361      *
 362      * @return An enumeration of inform communities for a given host (enumeration of <CODE>String</CODE>).
 363      */
 364     public Enumeration<String> getInformCommunities(InetAddress i) {
 365         Vector<String> list = null;
 366         if ((list = informDestList.get(i)) != null ) {
 367             if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 368                 SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 369                     "getInformCommunities", "["+i.toString()+"] is in list");
 370             }
 371             return list.elements();
 372         } else {
 373             list = new Vector<>();
 374             if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 375                 SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 376                     "getInformCommunities", "["+i.toString()+"] is not in list");
 377             }
 378             return list.elements();
 379         }
 380     }
 381 
 382     /**
 383      * Converts the input configuration file into ACL.
 384      */
 385     private void readAuthorizedListFile() {
 386 
 387         alwaysAuthorized = false;
 388 
 389         if (authorizedListFile == null) {
 390             if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 391                 SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 392                     "readAuthorizedListFile", "alwaysAuthorized set to true");
 393             }
 394             alwaysAuthorized = true ;
 395         } else {
 396             // Read the file content
 397             Parser parser = null;
 398             try {
 399                 parser= new Parser(new FileInputStream(getAuthorizedListFile()));
 400             } catch (FileNotFoundException e) {
 401                 if (SNMP_LOGGER.isLoggable(Level.FINEST)) {
 402                     SNMP_LOGGER.logp(Level.FINEST, SnmpAcl.class.getName(),
 403                             "readAuthorizedListFile",
 404                             "The specified file was not found, authorize everybody");
 405                 }
 406                 alwaysAuthorized = true ;
 407                 return;
 408             }
 409 
 410             try {
 411                 JDMSecurityDefs n = parser.SecurityDefs();
 412                 n.buildAclEntries(owner, acl);
 413                 n.buildTrapEntries(trapDestList);
 414                 n.buildInformEntries(informDestList);
 415             } catch (ParseException e) {
 416                 if (SNMP_LOGGER.isLoggable(Level.FINEST)) {
 417                     SNMP_LOGGER.logp(Level.FINEST, SnmpAcl.class.getName(),
 418                         "readAuthorizedListFile", "Got parsing exception", e);
 419                 }
 420                 throw new IllegalArgumentException(e.getMessage());
 421             } catch (Error err) {
 422                 if (SNMP_LOGGER.isLoggable(Level.FINEST)) {
 423                     SNMP_LOGGER.logp(Level.FINEST, SnmpAcl.class.getName(),
 424                         "readAuthorizedListFile", "Got unexpected error", err);
 425                 }
 426                 throw new IllegalArgumentException(err.getMessage());
 427             }
 428 
 429             for(Enumeration<AclEntry> e = acl.entries(); e.hasMoreElements();) {
 430                 AclEntryImpl aa = (AclEntryImpl) e.nextElement();
 431                 if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 432                     SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 433                             "readAuthorizedListFile",
 434                             "===> " + aa.getPrincipal().toString());
 435                 }
 436                 for (Enumeration<java.security.acl.Permission> eee = aa.permissions();eee.hasMoreElements();) {
 437                     java.security.acl.Permission perm = eee.nextElement();
 438                     if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 439                         SNMP_LOGGER.logp(Level.FINER, SnmpAcl.class.getName(),
 440                                 "readAuthorizedListFile", "perm = " + perm);
 441                     }
 442                 }
 443             }
 444         }
 445     }
 446 
 447     /**
 448      * Set the default full path for "snmp.acl" input file.
 449      * Do not complain if the file does not exists.
 450      */
 451     private void setDefaultFileName() {
 452         try {
 453             setAuthorizedListFile(getDefaultAclFileName());
 454         } catch (IllegalArgumentException x) {
 455             // OK...
 456         }
 457     }
 458 
 459 
 460     // PRIVATE VARIABLES
 461     //------------------
 462 
 463     /**
 464      * Represents the Access Control List.
 465      */
 466     private AclImpl acl = null;
 467     /**
 468      * Flag indicating whether the access is always authorized.
 469      * <BR>This is the case if there is no flat file defined.
 470      */
 471     private boolean alwaysAuthorized = false;
 472     /**
 473      * Represents the Access Control List flat file.
 474      */
 475     private String authorizedListFile = null;
 476     /**
 477      * Contains the hosts list for trap destination.
 478      */
 479     private Hashtable<InetAddress, Vector<String>> trapDestList = null;
 480     /**
 481      * Contains the hosts list for inform destination.
 482      */
 483     private Hashtable<InetAddress, Vector<String>> informDestList = null;
 484 
 485     private PrincipalImpl owner = null;
 486 }