You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/02/17 22:19:03 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/servlets AuthServlet.java DefaultErrorPage.java JSecurityCheck.java

costin      00/02/17 13:19:02

  Modified:    src/share/org/apache/tomcat/core Container.java
                        RequestImpl.java
               src/share/org/apache/tomcat/request SecurityCheck.java
                        SimpleMapper.java
               src/share/org/apache/tomcat/servlets AuthServlet.java
                        DefaultErrorPage.java JSecurityCheck.java
  Added:       src/etc  tomcat-users.xml
               src/examples/jsp/security/login login.jsp
  Removed:     src/examples/jsp/security login.jsp
  Log:
  BUGFIX: fixed various bugs in error processing, security checking, form-based
  login.
  
  ADDED:
  - simple user database ( tomcat-users.xml ) - it's very limited, but should
  work for standalone tomcat - and we want to plug an existing powerfull auth,
  not to design one ( based on Craig's security code, I simplified it a bit to
  be able to get it done )
  
  - error handler will log  the stack traces.
  
  - few more messages and traces if debug is enabled.
  
  - Removed an obvious hole - after authentication we'll save a private object
  in the session, instead of a "String username". The only way you can create the
  Credential is via SecurityCheck, that means a trojan servlet can't influence
  another application.
  
  Also: tested form login, it seems to work - fill bugs in
  jakarta.apache.org/bugs !!!
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat/src/etc/tomcat-users.xml
  
  Index: tomcat-users.xml
  ===================================================================
  <tomcat-users>
    <user name="tomcat" password="tomcat" roles="tomcat" />
  </tomcat-users>
  
  
  
  1.1                  jakarta-tomcat/src/examples/jsp/security/login/login.jsp
  
  Index: login.jsp
  ===================================================================
  <html>
  <body>
  <h1>Login page for examples</h1>
  
  <form method="POST" action="j_security_check" >
   <input type="text" name="j_username"> 
   <input type="password" name="j_password"> 
   
   <input type="submit" name="j_security_check">
  </form>
  
  </body>
  </html>
  
  
  
  1.12      +22 -1     jakarta-tomcat/src/share/org/apache/tomcat/core/Container.java
  
  Index: Container.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Container.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- Container.java	2000/02/16 05:44:33	1.11
  +++ Container.java	2000/02/17 21:19:01	1.12
  @@ -86,7 +86,7 @@
    * map via addMapping() callback. It can set private informations as
    * attributes ( the Mapper or security interceptors will probably do so ).
    */
  -public class Container {
  +public class Container implements Cloneable {
   
       // The main difference between this container and Catalina
       // is that in Catalina, Container has an invoke() method.
  @@ -242,4 +242,25 @@
       public void setRoles( String roles[] ) {
   	this.roles=roles;
       }
  +
  +    public String toString() {
  +	StringBuffer sb=new StringBuffer();
  +	sb.append( "Ct (" );
  +	if( handler!= null) sb.append( handler.toString() );
  +	if( roles!=null) {
  +	    	sb.append(" Roles: ");
  +		for( int i=0; i< roles.length; i++ )
  +		    sb.append(" ").append( roles[i]);
  +	}
  +	sb.append( " )");
  +	return sb.toString();
  +    }
  +
  +    public Container getClone() {
  +	try {
  +	    return (Container)this.clone();
  +	} catch( CloneNotSupportedException ex ) {
  +	    return this;
  +	}
  +    } 
   }
  
  
  
  1.21      +13 -5     jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java
  
  Index: RequestImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- RequestImpl.java	2000/02/17 12:15:03	1.20
  +++ RequestImpl.java	2000/02/17 21:19:01	1.21
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v 1.20 2000/02/17 12:15:03 shachor Exp $
  - * $Revision: 1.20 $
  - * $Date: 2000/02/17 12:15:03 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v 1.21 2000/02/17 21:19:01 costin Exp $
  + * $Revision: 1.21 $
  + * $Date: 2000/02/17 21:19:01 $
    *
    * ====================================================================
    *
  @@ -694,8 +694,16 @@
   
       public String toString() {
   	StringBuffer sb=new StringBuffer();
  -	sb.append( "R( " + getRequestURI() + " ");
  -	if( context!=null) sb.append( context.getPath() );
  +	sb.append( "R( ");
  +	if( context!=null) {
  +	    sb.append( context.getPath() );
  +	    if( getServletPath() != null )
  +		sb.append( " + " + getServletPath() + " + " + getPathInfo());
  +	    else
  +		sb.append( " + " + getLookupPath());
  +	} else {
  +	    sb.append(getRequestURI());
  +	}
   	sb.append(")");
   	return sb.toString();
       }
  
  
  
  1.5       +117 -16   jakarta-tomcat/src/share/org/apache/tomcat/request/SecurityCheck.java
  
  Index: SecurityCheck.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/SecurityCheck.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- SecurityCheck.java	2000/02/17 07:52:21	1.4
  +++ SecurityCheck.java	2000/02/17 21:19:02	1.5
  @@ -62,10 +62,12 @@
   
   import org.apache.tomcat.core.*;
   import org.apache.tomcat.util.*;
  +import org.apache.tomcat.util.xml.*;
   import java.io.*;
   import java.net.*;
   import java.util.*;
   import javax.servlet.http.*;
  +import org.xml.sax.*;
   
   /**
    * Will process the request and determine the session Id, and set it
  @@ -77,13 +79,24 @@
    * 
    */
   public class SecurityCheck extends  BaseInterceptor {
  -    
  +    MemoryRealm memoryRealm;
  +
       public SecurityCheck() {
       }
   	
       public void contextInit( Context ctx)
   	throws TomcatException
       {
  +	if( memoryRealm==null) {
  +	    	memoryRealm = new MemoryRealm(ctx);
  +		try {
  +		    memoryRealm.readMemoryRealm(ctx);
  +		} catch(Exception ex ) {
  +		    ex.printStackTrace();
  +		    memoryRealm=null;
  +		}
  +	}
  +	
   	if( "FORM".equals( ctx.getAuthMethod() )) {
   	    ServletWrapper jcheck=new ServletWrapper();
   	    jcheck.setContext( ctx );
  @@ -151,16 +164,25 @@
   	    HttpSession session=req.getSession( false );
   	    if( session == null )
   		return 0; // not authenticated
  -	    String username=(String)session.getAttribute("j_username");
  -	    String password=(String)session.getAttribute("j_password");
  -	    
  -	    if( ctx.getDebug() > 0 ) ctx.log( "Form Auth:  " + username + " " + password);
  -	    if( checkPassword( username, password ) ) {
  -		req.setRemoteUser( username );
  +	    Credential cr=(Credential) session.getAttribute( "tomcat.credential");
  +	    if( cr!=null ) {
  +		req.setRemoteUser( cr.username );
  +		// maybe we can set the role(s) too...
   	    } else {
  -		// wrong password
  -		errorPage( req, response );
  -	    }
  +		// it wasn't authenticated, maybe it's a new login 
  +		String username=(String)session.getAttribute("j_username");
  +		String password=(String)session.getAttribute("j_password");
  +		if( ctx.getDebug() > 0 ) ctx.log( "Form Auth:  " + username + " " + password);
  +		if( username!=null && checkPassword( username, password ) ) {
  +		    req.setRemoteUser( username );
  +		    Credential c=new Credential();
  +		    c.username=username;
  +		    session.setAttribute( "tomcat.credential", c );
  +		} else {
  +		    // wrong password
  +		    errorPage( req, response );
  +		}
  +	    } 
   	}
   
   	return 0;
  @@ -183,15 +205,15 @@
   	}
   
   	String user=req.getRemoteUser();
  +	if( ctx.getDebug() > 0 ) ctx.log( "Controled access for " + user + " " + req + " " + req.getContainer() );
   	if( user!=null ) {
  -	    if( ctx.getDebug() > 0 ) ctx.log( "Controled access for " + user );
   	    for( int i=0; i< roles.length; i++ ) {
   		if( userInRole( user, roles[i] ) )
   		    return 0;
   	    }
   	}
   
  -	if( ctx.getDebug() > 0 ) ctx.log( "Unauthorized " + user);
  +	if( ctx.getDebug() > 0 ) ctx.log( "Unauthorized " + user + " " + req.getContainer());
    	return HttpServletResponse.SC_UNAUTHORIZED;
   	// XXX check transport
       }
  @@ -240,12 +262,91 @@
       }
   
       private boolean checkPassword( String user, String pass ) {
  -	System.out.println("Checking " + user + " " + pass );
  -	return true;
  +	if( memoryRealm != null ) return memoryRealm.checkPassword( user, pass );
  +	return false;
       }
   
       private boolean userInRole( String user, String role ) {
  -	System.out.println("Checking role " + user + " " + role );
  -	return true;
  +	if( memoryRealm != null ) return memoryRealm.userInRole( user, role );
  +	return false;
  +    }
  +
  +}
  +
  +class MemoryRealm {
  +    // String user -> password
  +    Hashtable passwords=new Hashtable();
  +    // String role -> Vector users
  +    Hashtable roles=new Hashtable();
  +    Context ctx;
  +    
  +    MemoryRealm(Context ctx) {
  +	this.ctx=ctx;
  +    }
  +
  +    public void addUser(String name, String pass, String groups ) {
  +	ctx.log( "Add user " + name + " " + pass + " " + groups );
  +	passwords.put( name, pass );
  +	addRole( groups, name );
       }
  +
  +    public void addRole( String role, String user ) {
  +	Vector users=(Vector)roles.get(role);
  +	if(users==null) {
  +	    users=new Vector();
  +	    roles.put(role, users );
  +	}
  +	users.addElement( user );
  +    }
  +    
  +    public boolean checkPassword( String user, String pass ) {
  +	ctx.log( "check " + user+ " " + pass + " " + passwords.get( user ));
  +	return pass.equals( (String)passwords.get( user ) );
  +    }
  +
  +    public boolean userInRole( String user, String role ) {
  +	Vector users=(Vector)roles.get(role);
  +	ctx.log( "check role " + user+ " " + role + " "  );
  +	if(users==null) return false;
  +	return users.indexOf( user ) >=0 ;
  +    }
  +
  +    void readMemoryRealm(Context ctx) throws Exception {
  +	ContextManager cm=ctx.getContextManager();
  +	String home=cm.getHome();
  +	File f=new File( home + "/conf/tomcat-users.xml");
  +	if( ! f.exists() ) {
  +	    ctx.log( "File not found  " + f );
  +	    return;
  +	}
  +	XmlMapper xh=new XmlMapper();
  +	if( ctx.getDebug() > 5 ) xh.setDebug( 2 );
  +
  +	// call addUser using attributes as parameters 
  +	xh.addRule("tomcat-users/user",
  +		   new XmlAction() {
  +			   public void start( SaxContext ctx) throws Exception {
  +			       int top=ctx.getTagCount()-1;
  +			       MemoryRealm mr=(MemoryRealm)ctx.getRoot();
  +			       AttributeList attributes = ctx.getAttributeList( top );
  +			       String user=attributes.getValue("name");
  +			       String pass=attributes.getValue("password");
  +			       String group=attributes.getValue("roles");
  +			       mr.addUser( user, pass, group );
  +			   }
  +		       }
  +		   );
  +	
  +	xh.readXml( f, this );
  +    }
  +}
  +
  +
  +class Credential {
  +    Credential() {}
  +
  +    String username;
  +    String realm;
  +    String role;
  +
   }
  
  
  
  1.14      +25 -16    jakarta-tomcat/src/share/org/apache/tomcat/request/SimpleMapper.java
  
  Index: SimpleMapper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/SimpleMapper.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- SimpleMapper.java	2000/02/16 07:06:22	1.13
  +++ SimpleMapper.java	2000/02/17 21:19:02	1.14
  @@ -171,8 +171,6 @@
   	String ctxP=context.getPath();
   	Mappings m=(Mappings)contextPaths.get(ctxP);
   
  -	if(debug>0) context.log( "Mapping: " + req );
  -
   	container=findContainer( m, path, context, req );
   	
   	// set default container, return
  @@ -185,18 +183,16 @@
   	    req.setWrapper( m.defaultContainer.getHandler() );
   	    req.setServletPath( "" );
   	    req.setPathInfo( path);
  -	    if(debug>0) context.log("Default mapper " + "\n    " + req);
   	}  else {
   	    req.setWrapper( container.getHandler() );
  -	    
  -	    if(debug>0) context.log("Found wrapper using getMapPath " + "\n    " + req);
   	}
  +	if(debug>0) context.log("SM: Handler " + req + " " + container );
   	req.setContainer( container );
   
   	// the container already has security properties
   	// in it, no need to search again
   	if( container.getRoles() != null ) {
  -	    if(debug>0) context.log("Existing security constraint " + "\n    " + container.getRoles());
  +	    if(debug>0) context.log("Existing security constraint " +  container.toString());
   	    return OK;
   	}
   	
  @@ -210,10 +206,20 @@
   	}
   	// Merge the security info into the container
   	//
  -	if(debug>0) context.log("Found security constraing " + "\n    " + scontainer.getRoles());
  -	container.setRoles( scontainer.getRoles());
  -	container.setTransport( scontainer.getTransport());
  -	
  +	// XXX merging is a very usefull optimization, but we should do it
  +	// at init time, it's very expensive because we need to be sure it has the same
  +	// pattern !!
  +	// 	if(debug>0) context.log("Merging security constraint " + scontainer + " into " + container );
  +	// 	container.setRoles( scontainer.getRoles());
  +	// 	container.setTransport( scontainer.getTransport());
  +
  +	// until merging is implemented, we'll just create a new container with the combined
  +	// properties. This code needs optimizations ( i.e. alghoritm + data, not OptimizeIt!)
  +	Container ct=container.getClone();
  + 	ct.setRoles( scontainer.getRoles());
  +	ct.setTransport( scontainer.getTransport());
  +	req.setContainer( ct );
  +	if(debug>0) context.log("Set security constraings " + req + " " + container );
   	return OK;
       }
   
  @@ -337,7 +343,7 @@
           Container wrapper = null;
   	wrapper = (Container)m.pathMappedServlets.get(path);
   
  -	if (wrapper != null) {
  +	if (wrapper != null && wrapper.getHandler() != null ) {
   	    req.setServletPath( path );
   	    // No path info - it's an exact match
   	    if(debug>1) context.log("path match " + path );
  @@ -359,7 +365,7 @@
   	while (s.length() > 0) {
   	    // XXX we can remove /* in prefix map when we add it, so no need
   	    // for another string creation
  -	    if(debug>2) context.log( "Prefix: " + s  );
  +	    //	    if(debug>8) context.log( "Prefix: " + s  );
   	    wrapper = (Container)m.prefixMappedServlets.get(s + "/*" );
   	    //Enumeration en=m.prefixMappedServlets.keys();
   	    //while( en.hasMoreElements() ) {
  @@ -371,16 +377,17 @@
   	    else
   		break;
   	}
  -		
  +	
   	// Set servlet path and path info
  -	if( wrapper != null ) {
  +	if( wrapper != null && wrapper.getHandler() != null ) {
   	    // Found a match !
   	    req.setServletPath( s );
   	    String pathI = path.substring(s.length(), path.length());
   	    if( ! "".equals(pathI) ) 
   		req.setPathInfo(pathI);
  -	    if(debug>0) context.log("prefix match " + path );
   	}
  +	if(wrapper!= null )
  +	    if(debug>0) context.log("prefix match " + path + " " + wrapper );
   	return wrapper;
       }
   
  @@ -397,6 +404,9 @@
   	if (wrapper == null)
   	    return null;
   
  +	if(debug>0) context.log("extension match " + path );
  +
  +	if( wrapper.getHandler() ==null) return wrapper;
   	// fix paths
   	// /a/b/c.jsp/d/e
           int i = path.lastIndexOf(".");
  @@ -412,7 +422,6 @@
   	    req.setServletPath( path );
   	}
   		
  -	if(debug>0) context.log("extension match " + path );
   	return wrapper; 
       }
   
  
  
  
  1.3       +1 -1      jakarta-tomcat/src/share/org/apache/tomcat/servlets/AuthServlet.java
  
  Index: AuthServlet.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/servlets/AuthServlet.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- AuthServlet.java	2000/02/17 07:52:24	1.2
  +++ AuthServlet.java	2000/02/17 21:19:02	1.3
  @@ -100,7 +100,7 @@
   		// 		rd.include( request, response );
   
   		session.setAttribute( "tomcat.auth.originalLocation", req.getRequestURI());
  -		ctx.log("Setting orig location " + req.getRequestURI());
  +		if( ctx.getDebug() > 0 ) ctx.log("Setting orig location " + req.getRequestURI());
   		if( ! page.startsWith("/")) page="/" + page;
   		response.sendRedirect( ctx.getPath() + page );
   		return; 
  
  
  
  1.5       +1 -0      jakarta-tomcat/src/share/org/apache/tomcat/servlets/DefaultErrorPage.java
  
  Index: DefaultErrorPage.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/servlets/DefaultErrorPage.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- DefaultErrorPage.java	2000/02/17 07:52:24	1.4
  +++ DefaultErrorPage.java	2000/02/17 21:19:02	1.5
  @@ -94,6 +94,7 @@
   
   	Throwable e= (Throwable)request.getAttribute("tomcat.servlet.error.throwable");
   	if( e!=null ) {
  +	    e.printStackTrace();
   	    sendError(request, response, 500, exceptionString( e ));
   	    return;
   	}
  
  
  
  1.2       +2 -3      jakarta-tomcat/src/share/org/apache/tomcat/servlets/JSecurityCheck.java
  
  Index: JSecurityCheck.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/servlets/JSecurityCheck.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- JSecurityCheck.java	2000/02/17 07:52:24	1.1
  +++ JSecurityCheck.java	2000/02/17 21:19:02	1.2
  @@ -80,7 +80,6 @@
       {
   	Request req=((HttpServletRequestFacade)request).getRealRequest();
   	Context ctx=req.getContext();
  -	ctx.log( "In JSecurityCheck");
   	HttpSession session=req.getSession( false );
   	if( session == null ) {
   	    ctx.log("TRY TO AUTHENTICATE WITHOUT A SESSION " + req);
  @@ -88,13 +87,13 @@
   	}
   	String username=req.getFacade().getParameter( "j_username" );
   	String password=req.getFacade().getParameter( "j_password" );
  -	if( ctx.getDebug() > 0 ) ctx.log( "FORM auth " + username + " " + password );
  +	if( ctx.getDebug() > 0 ) ctx.log( "JSecurityCheck - FORM auth " + username + " " + password );
   
   	session.setAttribute( "j_username", username );
   	session.setAttribute( "j_password", password );
   
   	String origLocation=(String)session.getAttribute( "tomcat.auth.originalLocation");
  -	ctx.log("Back to orig location " + origLocation);
  +	if( ctx.getDebug() > 0) ctx.log("JSecurityCheck - Back to orig location " + origLocation);
   	response.sendRedirect( origLocation );
       }