User: oleg    
  Date: 01/01/09 17:24:12

  Added:       src/main/org/jboss/security/plugins
                        AbstractServerLoginModule.java
                        DatabaseServerLoginModule.java
                        JaasSecurityManager.java
                        JaasSecurityManagerService.java
                        JaasSecurityManagerServiceMBean.java package.html
  Log:
  Package structure for security stuff improved.
  Classes from "system" package moved to "security" package.
  Added "security/plugins" and "security/plugins/samples" packages.
  Added JaasServerLoginModule and AbstractServerLoginModule classes
  by Edward Kenworthy <[EMAIL PROTECTED]>
  (file based implementation for JAAS security).
  
  Revision  Changes    Path
  1.1                  
jboss/src/main/org/jboss/security/plugins/AbstractServerLoginModule.java
  
  Index: AbstractServerLoginModule.java
  ===================================================================
  /*
   * JBoss, the OpenSource EJB server
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  
  package org.jboss.security.plugins;
  
  import java.util.*;
  import java.io.*;
  
  import javax.security.auth.Subject;
  import javax.security.auth.callback.Callback;
  import javax.security.auth.callback.CallbackHandler;
  import javax.security.auth.callback.NameCallback;
  import javax.security.auth.callback.PasswordCallback;
  import javax.security.auth.callback.UnsupportedCallbackException;
  import javax.security.auth.login.LoginException;
  import javax.security.auth.login.FailedLoginException;
  import javax.security.auth.spi.LoginModule;
  
  /**
   * AbstractServerLoginModule
   * written by: Edward Kenworthy 12th Dec 2000
   *
   * This class implements the common functionality required for a JAAS 
ServerLoginModule.
   * To implement your own implementation you need to add just the user/password/roles 
lookup
   * functionality.
   *
   * It attaches the roles to the subject as public credentials.
   * According to the JAAS spec it requires privileged access to do this, and as I 
don't
   * explicilty code that privilege I assume I must have it by virtue of being a 
ServerLoginModule.
   *
   * As a minimum you must implement:
   *
   *    protected String getUsersRoles(); // returns a csv list of the users roles
   *    protected String getUsersPassword(); // returns the users password
   *
   * You may also wish to override
   *
   *    public void initialize(Subject subject, CallbackHandler callbackHandler, Map 
sharedState, Map options)
   *
   * In which case the first line of your initialize() method should be 
super.initialize(subject, callbackHandler, sharedState, options);
   *
   * You may also wish to override
   *
   *    public boolean login() throws LoginException
   *
   * In which case the last line of your login() method should be return super.login();
   *
   * @author <a href="[EMAIL PROTECTED]">Edward Kenworthy</a>
   */
  public abstract class AbstractServerLoginModule implements LoginModule
  {
      private Subject _subject;
      private CallbackHandler _callbackHandler;
  
      // username and password
      private String _username;
      protected String getUsername() {return _username;}
      private char[] _password;
  
      abstract protected Enumeration getUsersRoles();
      abstract protected String getUsersPassword();
  
      public void initialize(Subject subject, CallbackHandler callbackHandler, Map 
sharedState, Map options)
      {
          _subject = subject;
          _callbackHandler = callbackHandler;
      }
  
      public boolean login() throws LoginException
      {
          Callback[] callbacks = new Callback[2];
          // prompt for a username and password
          if (_callbackHandler == null)
          {
              throw new LoginException("Error: no CallbackHandler available " +
                                   "to garner authentication information from the 
user");
          }
          callbacks[0] = new NameCallback("User name: ", "guest");
          callbacks[1] = new PasswordCallback("Password: ", false);
          try
          {
              _callbackHandler.handle(callbacks);
              _username = ((NameCallback)callbacks[0]).getName();
              char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword();
              if (tmpPassword != null)
              {
                  _password = new char[tmpPassword.length];
                  System.arraycopy(tmpPassword, 0, _password, 0, tmpPassword.length);
                  ((PasswordCallback)callbacks[1]).clearPassword();
              }
          }
          catch (java.io.IOException ioe)
          {
              throw new LoginException(ioe.toString());
          }
          catch (UnsupportedCallbackException uce)
          {
              throw new LoginException("Error: " + uce.getCallback().toString() +
                      " not available to garner authentication information " +
                      "from the user");
          }
          String userPassword = getUsersPassword();
          if (_password == null || userPassword == null || !(new 
String(_password)).equals(userPassword))
          {
              System.out.print("[JAASSecurity] Bad password.\n");
              throw new FailedLoginException("Password Incorrect/Password Required");
          }
          System.out.print("[JAASSecurity] User '" + _username + "' authenticated.\n");
          return true;
      }
  
      /**
       * Method to commit the authentication process (phase 2).
       */
      public boolean commit() throws LoginException
      {
          Set roles = _subject.getPublicCredentials();
          Enumeration roleList = getUsersRoles();
          if (roleList != null) {
              while (roleList.hasMoreElements()) {
                  roles.add(roleList.nextElement());
              }
          }
          return true;
      }
  
      /**
       * Method to abort the authentication process (phase 2).
       */
      public boolean abort() throws LoginException
      {
          _username = null;
          if (_password != null)
          {
              for (int i = 0; i < _password.length; i++)
              _password[i] = ' ';
              _password = null;
          }
          return true;
      }
  
      public boolean logout() throws LoginException
      {
          return true;
      }
  }
  
  
  
  
  
  
  1.1                  
jboss/src/main/org/jboss/security/plugins/DatabaseServerLoginModule.java
  
  Index: DatabaseServerLoginModule.java
  ===================================================================
  /*
   * JBoss, the OpenSource EJB server
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
   
  package org.jboss.security.plugins;
  
  
  import java.util.Map;
  import java.util.Set;
  import java.util.Arrays;
  import java.sql.Connection;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import javax.naming.InitialContext;
  import javax.naming.NamingException;
  import javax.sql.DataSource;
  import javax.security.auth.Subject;
  import javax.security.auth.callback.Callback;
  import javax.security.auth.callback.CallbackHandler;
  import javax.security.auth.callback.NameCallback;
  import javax.security.auth.callback.PasswordCallback;
  import javax.security.auth.callback.UnsupportedCallbackException;
  import javax.security.auth.login.LoginException;
  import javax.security.auth.login.FailedLoginException;
  import javax.security.auth.spi.LoginModule;
  
  public class DatabaseServerLoginModule implements LoginModule {
      private String _db;
      private String _table;
      private String _nameCol;
      private String _pswCol;
      private Subject _subject;
      private CallbackHandler _callbackHandler;
      private String _username;
  
      /**
       * Initialize this LoginModule.
       */
      public void initialize(Subject subject, CallbackHandler callbackHandler,
              Map sharedState, Map options) {
          _subject = subject;
          _callbackHandler = callbackHandler;
          _db = (String) options.get("db");
          _table = (String) options.get("table");
          _nameCol = (String) options.get("name");
          _pswCol = (String) options.get("password");
      }
  
      /**
       * Method to authenticate a Subject (phase 1).
       */
      public boolean login() throws LoginException {
          Callback[] callbacks = new Callback[2];
          char[] password;
          char[] tmpPassword;
          InitialContext initial;
          DataSource ds;
          Connection conn = null;
          PreparedStatement ps;
          ResultSet rs;
          Object psw;
          boolean ok;
        
          try {
              // prompt for a username and password
              if (_callbackHandler == null) {
                  throw new LoginException("Error: no CallbackHandler available " +
                                           "to garner authentication information from 
the user");
              }
  
              callbacks[0] = new NameCallback("User name: ", "guest");
              callbacks[1] = new PasswordCallback("Password: ", false);
              _callbackHandler.handle(callbacks);
              _username = ((NameCallback)callbacks[0]).getName();
              tmpPassword = ((PasswordCallback)callbacks[1]).getPassword();
              if (tmpPassword == null) {
                  password = null;
              } else {
                  password = new char[tmpPassword.length];
                  System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
                  ((PasswordCallback)callbacks[1]).clearPassword();
              }
  
              // password authorization
              if (_pswCol != null) {
                  initial = new InitialContext();
                  ds = (DataSource) initial.lookup( _db );
                  conn = ds.getConnection();
                  ps = conn.prepareStatement("SELECT " + _pswCol + " FROM " + _table +
                                             " WHERE " + _nameCol + "=?");
                  ps.setString(1, _username);
                  rs = ps.executeQuery();
                  if (!rs.next()) {
                      throw new FailedLoginException("Incorrect user name");
                  }
                  psw = rs.getObject(1);
                  if (password == null || psw == null) {
                      ok = (password == psw);
                  } else if (psw instanceof byte[]) {
                      byte[] bpsw;
                      int len;
                      char[] cpsw;
                      bpsw = (byte[]) psw;
                      // trim zero bytes
                      for (len = bpsw.length; len>0; len--) {
                          if (bpsw[len - 1] != 0) {
                              break;
                          }
                      }
                      cpsw = new char[len];
                      for (int i = 0; i < len; i++) {
                          cpsw[i] = (char) bpsw[i];
                      }
                      ok = Arrays.equals(password, cpsw);
                  } else if (psw instanceof String) {
                      // trim spaces
                      ok = (new String(password)).equals(((String) psw).trim());
                  } else {
                      throw new LoginException("Unsupported SQL type of password 
column");
                  }
                  if (!ok) {
                      throw new FailedLoginException("Incorrect password");
                  }                        
              }
          } catch (NamingException ex) {
              throw new LoginException(ex.toString());
          } catch (java.io.IOException ex) {
              throw new LoginException(ex.toString());
          } catch (SQLException ex) {
              throw new LoginException(ex.toString());
          } catch (UnsupportedCallbackException uce) {
              throw new LoginException("Error: " + uce.getCallback().toString() +
                      " not available to garner authentication information " +
                      "from the user");
          } finally {
              if (conn != null) {
                  try {
                      conn.close();
                  } catch (Exception ex) {
                  }
              }
          }
          return true;
      }
            
      /**
       * Method to commit the authentication process (phase 2).
       */
      public boolean commit() throws LoginException {
          return true;
      }    
            
      /**
       * Method to abort the authentication process (phase 2).
       */
      public boolean abort() throws LoginException {
          _username = null;
          return true;
      }
  
      public boolean logout() throws LoginException {
          return true;
      }
  }
  
  
  
  1.1                  
jboss/src/main/org/jboss/security/plugins/JaasSecurityManager.java
  
  Index: JaasSecurityManager.java
  ===================================================================
  /*
   * JBoss, the OpenSource EJB server
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
   
  package org.jboss.security.plugins;
  
  import java.io.File;
  import java.io.Serializable;
  import java.net.URL;
  import java.rmi.server.UnicastRemoteObject;
  import java.rmi.RemoteException;
  import java.rmi.ServerException;
  import java.util.Set;
  import java.util.HashMap;
  import java.util.Arrays;
  import java.util.Iterator;
  
  import java.security.Principal;
  import javax.security.auth.Subject;
  import javax.security.auth.login.Configuration;
  import javax.security.auth.login.LoginContext;
  import javax.security.auth.callback.CallbackHandler;
  import javax.security.auth.callback.Callback;
  import javax.security.auth.callback.NameCallback;
  import javax.security.auth.callback.PasswordCallback;
  import javax.security.auth.callback.UnsupportedCallbackException;
  import com.sun.security.auth.login.ConfigFile;
  
  import javax.naming.InitialContext;
  import javax.naming.Context;
  import javax.naming.Reference;
  import javax.naming.Name;
  import javax.naming.spi.ObjectFactory;
  
  import javax.management.MBeanServer;
  import javax.management.ObjectName;
  
  import javax.transaction.TransactionManager;
  
  import org.jboss.logging.Log;
  import org.jboss.util.ServiceMBeanSupport;
  
  import org.jboss.security.EJBSecurityManager;
  import org.jboss.security.RealmMapping;
  
  /**
   * The JaasSecurityManager is responsible both for validating credentials
   * associated with principals and for role mapping.
   *
   * @author <a href="[EMAIL PROTECTED]">Oleg Nitz</a>
   */
  public class JaasSecurityManager
          implements EJBSecurityManager, RealmMapping, Serializable {
      
      /**
       * Security manager name.
       */
      private final String _smName;
      
      /**
       * Maps an original principal to authenticated client credential, aka password.
       * Should be a time or size limited cache for better scalability.
       * This is a master cache, when a principal is removed from this cache,
       * the related entries from all other caches should be removed too.
       */
      private final HashMap _passwords = new HashMap();
  
      /**
       * Maps original principal to principal for the bean.
       */
      private final HashMap _principals = new HashMap();
      
      /**
       * Maps original principal to Set of roles for the bean.
       */
      private final HashMap _roles = new HashMap();
  
      /**
       * @param smName The name of the security manager
       */
      public JaasSecurityManager(String smName) {
          _smName = smName;
      }
      
      public boolean isValid(Principal principal, Object credential) {
          boolean ok;
          char[] authenticated;
  
          authenticated = (char[]) _passwords.get(principal);
          if (authenticated == null) {
              return authenticate(_smName, principal, credential);
          } else  {
              if ((credential instanceof char[]) &&
                      Arrays.equals(authenticated, (char[]) credential)) {
                  return true;
              } else {
                  // the password may have changed - reauthenticate
                  return authenticate(_smName, principal, credential);
              }
          }
      }
      
      public Principal getPrincipal(Principal principal) {
          Principal result;
          result = (Principal) _principals.get(principal);
          if (result == null) {
              if (authenticate(_smName, principal, null)) {
                  result = (Principal) _principals.get(principal);
              }
          }
          return result;
      }
  
      public boolean doesUserHaveRole(Principal principal, Set roleNames)
      {
          Set roles;
          Iterator it;
  
          if (roleNames == null)
              return true;
          roles = (Set) _roles.get(principal);
          if (roles == null) {
              if (!authenticate(_smName, principal, null)) {
                  return false;
              }
          } 
          it = roleNames.iterator();
          while (it.hasNext()) {
              if (roles.contains(it.next())) {
                  return true;
              }
          }
          return false;
      }
      
      
      /**
       * @param bean The bean name
       * @param userName The user name
       * @param password The password
       * @return false on failure, true on success.
       */
      private boolean authenticate(String beanName, Principal principal, Object 
credential) {
          LoginContext lc;
          Subject subj;
          final String userName = principal.getName();
          final char[] password = (char[]) credential;
          Iterator it;
          Principal beanPrincipal;
  
          try {
              lc = new LoginContext(beanName, new CallbackHandler() {
                  public void handle(Callback[] callbacks) throws 
UnsupportedCallbackException {
                      for (int i = 0; i < callbacks.length; i++) {
                          if (callbacks[i] instanceof NameCallback) {
                              ((NameCallback) callbacks[i]).setName(userName);
                          } else if (callbacks[i] instanceof PasswordCallback) {
                              ((PasswordCallback) callbacks[i]).setPassword(password);
                          } else {
                              throw new UnsupportedCallbackException(callbacks[i], 
"Unrecognized Callback");
                          }
                      }
                  }
              });
              lc.login();
              _passwords.put(principal, password);
              subj = lc.getSubject();
              beanPrincipal = principal;
              it = subj.getPrincipals().iterator();
              if (it.hasNext()) {
                  beanPrincipal = (Principal) it.next();
              }
              _principals.put(principal, beanPrincipal);
              _roles.put(principal, subj.getPublicCredentials());
              return true;
          } catch (Exception ex) {
              ex.printStackTrace();
              return false;
          } 
      }    
  }
  
  
  
  
  1.1                  
jboss/src/main/org/jboss/security/plugins/JaasSecurityManagerService.java
  
  Index: JaasSecurityManagerService.java
  ===================================================================
  /*
   * JBoss, the OpenSource EJB server
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
   
  package org.jboss.security.plugins;
  
  import java.io.File;
  import java.net.URL;
  import java.rmi.server.UnicastRemoteObject;
  import java.rmi.RemoteException;
  import java.rmi.ServerException;
  import java.util.Hashtable;
  import java.util.ArrayList;
  import java.util.Iterator;
  
  import javax.naming.InitialContext;
  import javax.naming.Context;
  import javax.naming.Reference;
  import javax.naming.RefAddr;
  import javax.naming.StringRefAddr;
  import javax.naming.Name;
  import javax.naming.spi.ObjectFactory;
  import javax.naming.spi.NamingManager;
  import javax.naming.CommunicationException;
  import javax.naming.CannotProceedException;
  
  import javax.management.MBeanServer;
  import javax.management.ObjectName;
  
  import javax.security.auth.login.Configuration;
  
  import org.jboss.logging.Log;
  import org.jboss.util.ServiceMBeanSupport;
  
  import org.jboss.security.EJBSecurityManager;
  
  import org.jnp.server.NamingServer;
  import org.jnp.interfaces.NamingContext;
  
  /**
   *   This is a JMX service which manages JaasSecurityManagers.
   *    JaasSecurityManagers are responsible for validating credentials
   *    associated with principals.
   *      
   *   @see JaasSecurityManager
   *   @author <a href="[EMAIL PROTECTED]">Oleg Nitz</a>
   *   @author <a href="[EMAIL PROTECTED]">Rickard Oberg</a>
   */
  public class JaasSecurityManagerService
          extends ServiceMBeanSupport
          implements JaasSecurityManagerServiceMBean, ObjectFactory {
  
     MBeanServer server;
     
     static NamingServer srv;
     static Hashtable jsmMap = new Hashtable();
  
     public String getName()
     {
        return "JAAS Security Manager";
     }
  
     protected ObjectName getObjectName(MBeanServer server, ObjectName name)
        throws javax.management.MalformedObjectNameException
     {
        this.server = server;
        return new ObjectName(OBJECT_NAME);
     }
  
     protected void startService() throws Exception
     {
        srv = new NamingServer();
     
        InitialContext ic = new InitialContext();
  
        // Bind reference to SM subcontext in JNDI
        // Uses JNDI federation to handle the "java:jaas" context ourselves
        RefAddr refAddr = new StringRefAddr("nns", "JSM");
        Reference jsmsRef = new Reference("javax.naming.Context", 
refAddr,getClass().getName(), null);
        Context ctx = (Context)new InitialContext();
        ctx.rebind("java:/jaas", jsmsRef);
     }
  
     protected void stopService()
     {
        InitialContext ic;
        try
        {
           ic = new InitialContext();
           ic.unbind("java:/jaas");
        } catch (CommunicationException e)
        {
           // Do nothing, the naming services is already stopped   
        } catch (Exception e)
        {
           log.exception(e);
        }
     }
  
     // ObjectFactory implementation ----------------------------------
  
        /**
         * Object factory implementation. This method is a bit tricky as it is called 
twice for each
      * JSM lookup. Let's say the lookup is for "java:jaas/MySecurity". Then this will 
first be 
      * called as JNDI starts the "jaas" federation. In that call we make sure that 
the next call
      * will go through, i.e. we check that the "MySecurity" binding is availble. Then 
we return 
      * the implementation of the "jaas" context. Then, when the "MySecurity" is 
dereferenced we 
      * look up the JSM from an internal static hash table.
      *
      * Note: it is possible to break this by doing the lookup in two phases: first 
lookup "java:jaas" 
      * and then do a second lookup of "MySecurity". If that is done then the first 
lookup has no way of
      * knowing what name to check (i.e. it can't make sure that "MySecurity" is 
available in the 
      * "java:jaas" context!
         *
         * @param   obj  
         * @param   name  
         * @param   nameCtx  
         * @param   environment  
         * @return     
         * @exception   Exception  
         */
     public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable 
environment)
        throws Exception
     {
        if (name != null)
        {
           // Handle JaasSecurityManager lookup
           if (name.size() == 0)
              return nameCtx;
        
           return jsmMap.get(name);
        } else
        {
           // Handle "java:jaas" context
           CannotProceedException cpe = 
(CannotProceedException)environment.get(NamingManager.CPE);
           Name remainingName = cpe.getRemainingName();
           
           Context ctx = new NamingContext(environment, null, srv);
           
           // Make sure that JSM is available
           try
           {
              srv.lookup(remainingName);
           } catch (Exception e)
           {
              // Not found - add reference to JNDI, and a real JSM to a map
              Reference jsmRef = new Reference(JaasSecurityManager.class.getName(), 
getClass().getName(), null);
              ctx.rebind(remainingName, jsmRef);
              jsmMap.put(remainingName, new 
JaasSecurityManager(remainingName.toString()));
           }
           
           return ctx;
        }
     }
  }
  
  
  
  
  
  1.1                  
jboss/src/main/org/jboss/security/plugins/JaasSecurityManagerServiceMBean.java
  
  Index: JaasSecurityManagerServiceMBean.java
  ===================================================================
  /*
   * JBoss, the OpenSource EJB server
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  
  package org.jboss.security.plugins;
  
  public interface JaasSecurityManagerServiceMBean
      extends org.jboss.util.ServiceMBean
  {
     // Constants -----------------------------------------------------
     public static final String OBJECT_NAME = ":service=JaasSecurityManager";
      
     // Public --------------------------------------------------------
  }
  
  
  
  1.1                  jboss/src/main/org/jboss/security/plugins/package.html
  
  Index: package.html
  ===================================================================
  Security plugins: implementations for use in the real life.
  
  

Reply via email to