mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
Initial checkin
This commit is contained in:
parent
f9df421131
commit
704e1e4d36
7 changed files with 568 additions and 0 deletions
1
changehat/tomcat_apparmor/tomcat_5_0/Manifest
Normal file
1
changehat/tomcat_apparmor/tomcat_5_0/Manifest
Normal file
|
@ -0,0 +1 @@
|
|||
Main-Class: ChangeHatValve
|
195
changehat/tomcat_apparmor/tomcat_5_0/README
Normal file
195
changehat/tomcat_apparmor/tomcat_5_0/README
Normal file
|
@ -0,0 +1,195 @@
|
|||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2002-2006 Novell/SUSE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
# License published by the Free Software Foundation.
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
----------------------
|
||||
|
||||
1. Overview
|
||||
2. Requirements
|
||||
3. Compiling the code
|
||||
4. Installation
|
||||
5. Generating a basic Tomcat profile
|
||||
6. Implementation Notes
|
||||
7. Profile Generation Tools and change_hat
|
||||
8. Feedback/Resources
|
||||
|
||||
-----------------------
|
||||
|
||||
|
||||
1. Overview
|
||||
--------
|
||||
|
||||
This package provides an implementation of a Tomcat 5.0 Valve that calls out
|
||||
to the change_hat(2) function provided by libapparmor to allow a process to
|
||||
change its security context. This code is intended as a proof of concept plugin
|
||||
for AppArmor and Tomcat. Any feedback is greatly appreciated.
|
||||
|
||||
|
||||
2. Requirements
|
||||
------------
|
||||
|
||||
AppArmor version 1.2 or later
|
||||
Tomcat version 5.0.X
|
||||
JDK 1.4.2 or later
|
||||
|
||||
|
||||
3. Compiling the Code
|
||||
-----------------
|
||||
|
||||
From the top level directory execute:
|
||||
ant jar jni_so
|
||||
|
||||
|
||||
This will create a jar file and a shared library (for the JNI interface to the
|
||||
libapparmor library) and place them under dist/.
|
||||
|
||||
|
||||
4. Installation
|
||||
------------
|
||||
|
||||
- Copy the jar file to $TOMCAT_HOME/server/lib:
|
||||
|
||||
[SLES10 example]
|
||||
|
||||
cp dist/changeHatValve.jar /usr/share/tomcat5/server/lib
|
||||
|
||||
|
||||
- Copy the shared library to somehere in your library search path:
|
||||
|
||||
cp dist/libJNIChangeHat.so /usr/local/lib
|
||||
|
||||
[Note: you must ensure that the target directory is in your search path or set
|
||||
the env variable LD_LIBRARY_PATH to include this directory so that tomcat can
|
||||
find this library at startup]
|
||||
|
||||
|
||||
- Configure the Tomcat server to use ChangeHatValve:
|
||||
|
||||
Place the configuration directive below in your server.xml file. The valve
|
||||
definition should be the initial configuration option declared in the
|
||||
top-level container in the container hierarchy.
|
||||
|
||||
<Valve className="com.novell.apparmor.catalina.valves.ChangeHatValve"
|
||||
mediationType="ServletPath"/>
|
||||
|
||||
[Note: The mediationType attribute may be set to ServletPath or URI depending
|
||||
on the granularity of containers that you wish to create. URI will
|
||||
prompt the user to create containers for every URI it processes. This
|
||||
is not recommended for most deployment scenarios and so the default
|
||||
"ServletPath" should be used. This maps to containers identified by
|
||||
the ServletPath header defined in the HttpRequest.]
|
||||
|
||||
|
||||
- Defining a default and required hat for the tomcat profile
|
||||
|
||||
Edit the file /etc/apparmor/logprof.conf and add the following line to the
|
||||
section [required_hats]:
|
||||
^.+/catalina.sh$ = DEFAULT
|
||||
|
||||
Edit the file /etc/apparmor/logprof.conf and add the following line to the
|
||||
section [default_hat]:
|
||||
^.+/catalina.sh$ = DEFAULT
|
||||
|
||||
|
||||
|
||||
|
||||
5. Generating a basic Tomcat profile
|
||||
-------------------------------
|
||||
|
||||
Once the installation steps above have been started you are ready to begin
|
||||
creating a profile for your application. The profile creation tool genprof will
|
||||
guide you through generating a profile and its support for change_hat will
|
||||
prompt you create discrete hats as requested byt the changeHatValve during
|
||||
tomcat execution.
|
||||
|
||||
1. Create a basic profile for the tomcat server.
|
||||
|
||||
- Run the command "genprof PATH_TO_CATALINA.SH"
|
||||
- In a seperate window start tomcat and then stop tomcat
|
||||
- In the genprof window press "S" to scan for events
|
||||
- Answer the questions about the initial profile for tomcat
|
||||
|
||||
|
||||
2. Extending the profile to include containers for your web-app
|
||||
|
||||
- Stop the tomcat server
|
||||
- Deploy your WAR file or equivalent files under the container.
|
||||
- execute "genprof PATH_TO_CATALINA.SH"
|
||||
- In a seperate window start tomcat and then exercise your web application
|
||||
- In the genprof window press "S" to scan for events
|
||||
During the prompting you will be asked questions similar to:
|
||||
|
||||
-----------------------------------------------
|
||||
|
||||
Profile: /usr/share/tomcat5/bin/catalina.sh
|
||||
Default Hat: DEFAULT
|
||||
Requested Hat: /servlet/CookieExample
|
||||
|
||||
(A)dd Requested Hat / (U)se Default Hat / (D)eny / Abo(r)t / (F)inish
|
||||
|
||||
------------------------------------------------
|
||||
|
||||
This example shows the tomcat valve for changehat attempting to change to
|
||||
the hat "/servlet/CookieExample". You can choose to create this hat, and
|
||||
subsequently fill it with resources, use the Default hat, named "DEFAULT"
|
||||
and is the default hat for request processing.
|
||||
|
||||
|
||||
|
||||
6. Implementation Notes
|
||||
--------------------
|
||||
|
||||
- Selecting the hat during request processing
|
||||
|
||||
This implementation follows the following pattern to decide what hat to execute in for an incoming request:
|
||||
|
||||
Try #1: Request data (URI or ServletInfo)
|
||||
if #1 fails (because the hat does not exist) then try #2
|
||||
Try #2: DEFAULT - this is the default hat for request processing
|
||||
if #2 fails (because of any error) then..
|
||||
Error: report that change_hat calls failed and remain in current security context.
|
||||
|
||||
- Java 1.4.2 Notes
|
||||
|
||||
This library uses java.security.SecureRandom to generate random numbers used as cookies in
|
||||
the change_hat(2) interface. This class on java version 1.4.2 uses /dev/random which is a
|
||||
blocking call and can adversely effect performance. Java can be configured to use
|
||||
/dev/urandom instead. For details:
|
||||
|
||||
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4705093
|
||||
|
||||
|
||||
|
||||
7. Profile Tools and change_hat
|
||||
----------------------------
|
||||
|
||||
When using the profile generation tool, genprof, you will be prompted to add a
|
||||
new hat when you exercise your program and requests are processed by the
|
||||
changeHatValve. You can choose to Add the hat or use the Default hat.
|
||||
If you choose to add the requested hat: genprof will create the hat and then
|
||||
all subsequent resource requests will be mediated in this hew hat (or security
|
||||
context).
|
||||
If you choose to use the default hat: genprof will mediate all resource
|
||||
requests in the default hat for the duration of processing this request.
|
||||
When the request processng is complete the valve will change_hat back to the
|
||||
parent context.
|
||||
|
||||
|
||||
|
||||
8. Feedback/Resources
|
||||
-----------------
|
||||
|
||||
To provide feedback or ask questions please contact the
|
||||
apparmor-dev@forge.novell.com mail list. This is the development list for the
|
||||
AppArmor team.
|
||||
|
||||
|
||||
|
||||
|
71
changehat/tomcat_apparmor/tomcat_5_0/build.xml
Normal file
71
changehat/tomcat_apparmor/tomcat_5_0/build.xml
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="changeHatValve" default="jar" basedir=".">
|
||||
<property name="src" location="src"/>
|
||||
<property name="jni_src" location="src/jni_src"/>
|
||||
<property name="build" location="build"/>
|
||||
<property name="lib" location="lib"/>
|
||||
<property name="dist" location="dist"/>
|
||||
<property name="jarfile" location="${dist}/${ant.project.name}.jar"/>
|
||||
<property name="compile.debug" value="true"/>
|
||||
|
||||
<fileset id="lib.jars" dir="${lib}">
|
||||
<include name="**/*.jar"/>
|
||||
</fileset>
|
||||
|
||||
<fileset id="tomcat.jars" dir="/usr/share/tomcat5/server/lib">
|
||||
<include name="**/*.jar"/>
|
||||
</fileset>
|
||||
<fileset id="servlet.jars" dir="/usr/share/tomcat5/common/lib">
|
||||
<include name="**/*.jar"/>
|
||||
</fileset>
|
||||
|
||||
<path id="lib.path">
|
||||
<fileset refid="lib.jars"/>
|
||||
<fileset refid="servlet.jars"/>
|
||||
<fileset refid="tomcat.jars"/>
|
||||
</path>
|
||||
<path id="servlet.path">
|
||||
<fileset refid="servlet.jars"/>
|
||||
</path>
|
||||
<path id="tomcat.path">
|
||||
<fileset refid="tomcat.jars"/>
|
||||
</path>
|
||||
|
||||
<target name="compile" description="Compile code">
|
||||
<mkdir dir="${build}"/>
|
||||
<mkdir dir="${lib}"/>
|
||||
<javac srcdir="${src}" destdir="${build}" includeAntRuntime="no"
|
||||
classpathref="lib.path" debug="${compile.debug}">
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!--
|
||||
<target name="jni_header" depends="compile" description="Generate JNI headers">
|
||||
<javah destdir="${jni_src}" class="com.novell.apparmor.JNIChangeHat"/>
|
||||
</target>
|
||||
-->
|
||||
<target name="jni_so" depends="compile" description="Build JNI library">
|
||||
<mkdir dir="${dist}"/>
|
||||
<exec dir="${jni_src}" executable="/usr/bin/make"/>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile" description="Build jar">
|
||||
<mkdir dir="${dist}"/>
|
||||
<jar jarfile="${jarfile}" basedir="${build}" manifest="Manifest">
|
||||
<!-- Merge library jars into final jar file -->
|
||||
<zipgroupfileset refid="lib.jars"/>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="run" depends="jar" description="Run jar file">
|
||||
<java jar="${jarfile}" fork="yes" failonerror="true"/>
|
||||
</target>
|
||||
|
||||
<target name="clean" description="Remove build and dist directories">
|
||||
<delete dir="${build}"/>
|
||||
<delete dir="${dist}"/>
|
||||
<exec dir="${jni_src}" executable="/usr/bin/make">
|
||||
<arg value="clean"/>
|
||||
</exec>
|
||||
</target>
|
||||
</project>
|
|
@ -0,0 +1,38 @@
|
|||
/* ------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (C) 2002-2005 Novell/SUSE
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* ------------------------------------------------------------------ */
|
||||
package com.novell.apparmor;
|
||||
|
||||
import java.nio.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* JNI interface to AppArmor change_hat(2) call
|
||||
*
|
||||
**/
|
||||
public class JNIChangeHat
|
||||
{
|
||||
public static int ENOMEM = 12;
|
||||
public static int EACCES = 13;
|
||||
public static int EFAULT = 14;
|
||||
|
||||
// Native 'c' function delcaration
|
||||
public native int changehat_in(String subdomain, int magic_token);
|
||||
|
||||
// Native 'c' function delcaration
|
||||
public native int changehat_out(int magic_token);
|
||||
|
||||
static
|
||||
{
|
||||
// The runtime system executes a class's static initializer
|
||||
// when it loads the class.
|
||||
System.loadLibrary("JNIChangeHat");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/* ------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (C) 2002-2005 Novell/SUSE
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
package com.novell.apparmor.catalina.valves;
|
||||
|
||||
import com.novell.apparmor.JNIChangeHat;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.catalina.HttpRequest;
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.HttpResponse;
|
||||
import org.apache.catalina.valves.ValveBase;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
|
||||
public final class ChangeHatValve extends ValveBase {
|
||||
// JNI interface class for AppArmor change_hat
|
||||
private static JNIChangeHat changehat_wrapper = new JNIChangeHat();
|
||||
private static SecureRandom randomNumberGenerator = null;
|
||||
private static String DEFAULT_HAT = "DEFAULT";
|
||||
private static int SERVLET_PATH_MEDIATION = 0;
|
||||
private static int URI_MEDIATION = 1;
|
||||
|
||||
private int mediationType = ChangeHatValve.SERVLET_PATH_MEDIATION;
|
||||
|
||||
/*
|
||||
*
|
||||
* Property setter called during the parsing of the server.xml.
|
||||
* If the <code>mediationType</code> is an attribute of the
|
||||
* Valve definition for
|
||||
* <code>com.novell.apparmor.catalina.valves.ChangeHatValve</code>
|
||||
* then this setter will be called to set the value for this property.
|
||||
*
|
||||
* @param type <b>URI|ServletPath<b>
|
||||
*
|
||||
* Controls what granularity of security confinement when used with
|
||||
* AppArmor change_hat(2). Either based upon <code>getServletPath()</code>
|
||||
* or <code>getRequestURI()</code> called against on the request.
|
||||
*
|
||||
*/
|
||||
public void setMediationType( String type ) {
|
||||
if ( type.equalsIgnoreCase("URI") ) {
|
||||
this.mediationType = ChangeHatValve.URI_MEDIATION;
|
||||
} else if ( type.equalsIgnoreCase("servletPath") ) {
|
||||
this.mediationType = ChangeHatValve.SERVLET_PATH_MEDIATION;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Return an int value representing the currently configured
|
||||
* <code>mediationType</code> for this instance.
|
||||
*
|
||||
*/
|
||||
int getMediationType() {
|
||||
return this.mediationType;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Return an instance of <code>SecureRandom</code> creating one if necessary
|
||||
*
|
||||
*/
|
||||
SecureRandom getRndGen() {
|
||||
if ( ChangeHatValve.randomNumberGenerator == null) {
|
||||
ChangeHatValve.randomNumberGenerator = new java.security.SecureRandom();
|
||||
}
|
||||
return ChangeHatValve.randomNumberGenerator;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Call to return a random cookie from the <code>SecureRandom</code> PRNG
|
||||
*
|
||||
*/
|
||||
int getCookie() {
|
||||
SecureRandom rnd = getRndGen();
|
||||
if ( rnd == null ) {
|
||||
this.getContainer().getLogger().log( "[APPARMOR] can't initialize SecureRandom for cookie generation for change_hat() call.", container.getLogger().ERROR);
|
||||
return 0;
|
||||
}
|
||||
return rnd.nextInt();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Call out to AppArmor change_hat(2) to change the security
|
||||
* context for the processing of the request by subsequent valves.
|
||||
* Returns to the current security context when processing is complete.
|
||||
* The security context that is chosen is govern by the
|
||||
* <code>mediationType</code> property - which can be set in the
|
||||
* <code>server.xml</code> file.
|
||||
*
|
||||
* @param request Request being processed
|
||||
* @param response Response being processed
|
||||
* @param context The valve context used to invoke the next valve
|
||||
* in the current processing pipeline
|
||||
*
|
||||
* @exception IOException if an input/output error has occurred
|
||||
* @exception ServletException if a servlet error has occurred
|
||||
*
|
||||
*/
|
||||
public void invoke( org.apache.catalina.Request request,
|
||||
org.apache.catalina.Response response,
|
||||
org.apache.catalina.ValveContext context )
|
||||
throws IOException, ServletException {
|
||||
|
||||
Container container = this.getContainer();
|
||||
int cookie, result;
|
||||
boolean inSubHat = false;
|
||||
|
||||
container.getLogger().log(this.getClass().toString() +
|
||||
"[APPARMOR] Request received [" + request.getInfo()
|
||||
+ "]", container.getLogger().DEBUG);
|
||||
|
||||
if ( !( request instanceof HttpRequest)
|
||||
|| !(response instanceof HttpResponse) ) {
|
||||
container.getLogger().log(this.getClass().toString()
|
||||
+ "[APPARMOR] Non HttpRequest received. Not changing context. ["
|
||||
+ request.getInfo() + "]", container.getLogger().ERROR);
|
||||
context.invokeNext(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequest httpRequest = (HttpRequest) request;
|
||||
HttpServletRequest servletRequest = (HttpServletRequest) httpRequest.getRequest();
|
||||
|
||||
String hatname = ChangeHatValve.DEFAULT_HAT;;
|
||||
if ( getMediationType() == ChangeHatValve.SERVLET_PATH_MEDIATION ) {
|
||||
hatname = servletRequest.getServletPath();
|
||||
} else if ( getMediationType() == ChangeHatValve.URI_MEDIATION ) {
|
||||
hatname = servletRequest.getRequestURI();
|
||||
}
|
||||
|
||||
/*
|
||||
* Select the AppArmor container for this request:
|
||||
*
|
||||
* 1. try hat name from either URI or ServletPath (based on configuration)
|
||||
*
|
||||
* 2. try hat name of the defined DEFAULT_HAT
|
||||
*
|
||||
* 3. run in the current AppArmor context
|
||||
*/
|
||||
|
||||
cookie = getCookie();
|
||||
container.getLogger().log("[APPARMOR] ChangeHat to [" + hatname
|
||||
+ "] cookie [" + cookie + "]", container.getLogger().DEBUG);
|
||||
result = changehat_wrapper.changehat_in(hatname, cookie);
|
||||
if ( result == JNIChangeHat.EACCES ) {
|
||||
changehat_wrapper.changehat_out(cookie);
|
||||
result = changehat_wrapper.changehat_in(ChangeHatValve.DEFAULT_HAT, cookie);
|
||||
if ( result != 0 ) {
|
||||
changehat_wrapper.changehat_out(cookie);
|
||||
container.getLogger().log("[APPARMOR] ChangeHat to [" + hatname
|
||||
+ "] failed. Running in parent context.",
|
||||
container.getLogger().ERROR);
|
||||
} else {
|
||||
inSubHat = true;
|
||||
}
|
||||
} else if ( result != 0 ) {
|
||||
changehat_wrapper.changehat_out(cookie);
|
||||
container.getLogger().log("[APPARMOR] ChangeHat to [" + hatname
|
||||
+ "] failed. Running in parent context.",
|
||||
container.getLogger().ERROR);
|
||||
} else {
|
||||
inSubHat = true;
|
||||
}
|
||||
context.invokeNext(request, response);
|
||||
if ( inSubHat ) changehat_wrapper.changehat_out(cookie);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
------------------------------------------------------------------
|
||||
|
||||
Copyright (C) 2002-2005 Novell/SUSE
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of version 2 of the GNU General Public
|
||||
License published by the Free Software Foundation.
|
||||
|
||||
------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "jni.h"
|
||||
#include <errno.h>
|
||||
#include "sys/apparmor.h"
|
||||
#include "com_novell_apparmor_JNIChangeHat.h"
|
||||
|
||||
/* c intermediate lib call for Java -> JNI -> c library execution of the change_hat call */
|
||||
|
||||
JNIEXPORT jint Java_com_novell_apparmor_JNIChangeHat_changehat_1in
|
||||
(JNIEnv *env, jobject obj, jstring hatnameUTF, jint token)
|
||||
{
|
||||
|
||||
int len = (*env)->GetStringLength(env, hatnameUTF);
|
||||
jint result = 0;
|
||||
if ( len > 0 ) {
|
||||
if ( len > 128 ) {
|
||||
len = 128;
|
||||
}
|
||||
char hatname[128];
|
||||
(*env)->GetStringUTFRegion(env, hatnameUTF, 0, len, hatname);
|
||||
result = (jint) change_hat(hatname, (unsigned int) token);
|
||||
if ( result ) {
|
||||
return errno;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return (jint) result;
|
||||
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_novell_apparmor_JNIChangeHat_changehat_1out
|
||||
(JNIEnv *env, jobject obj, jint token)
|
||||
{
|
||||
|
||||
jint result = 0;
|
||||
result = (jint) change_hat(NULL, (unsigned int) token);
|
||||
if ( result ) {
|
||||
return errno;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
21
changehat/tomcat_apparmor/tomcat_5_0/src/jni_src/Makefile
Normal file
21
changehat/tomcat_apparmor/tomcat_5_0/src/jni_src/Makefile
Normal file
|
@ -0,0 +1,21 @@
|
|||
INCLUDE=/usr/lib/java/include
|
||||
TOP=../..
|
||||
CLASSPATH=${TOP}/build
|
||||
CFLAGS=-g -O2 -Wall -Wstrict-prototypes -pipe -fpic -D_REENTRANT
|
||||
INCLUDES=-I$(INCLUDE) -I$(INCLUDE)/linux
|
||||
CLASSFILE=${CLASSPATH}/com/novell/apparmor/JNIChangeHat.class
|
||||
|
||||
all:
|
||||
make jnichangehat
|
||||
|
||||
jnichangehat: libJNIChangehat.so
|
||||
|
||||
clean:
|
||||
rm -f ${TOP}/dist/*.so JNIChangeHat.java com_novell_apparmor_JNIChangeHat.h
|
||||
|
||||
|
||||
JNIChangeHat.java com_novell_apparmor_JNIChangeHat.h: ${CLASSFILE}
|
||||
javah -jni -classpath ${CLASSPATH} com.novell.apparmor.JNIChangeHat
|
||||
|
||||
libJNIChangehat.so: JNIChangeHat.c JNIChangeHat.java com_novell_apparmor_JNIChangeHat.h
|
||||
gcc ${INCLUDES} ${CFLAGS} -shared -o ${TOP}/dist/libJNIChangeHat.so JNIChangeHat.c -lapparmor
|
Loading…
Add table
Reference in a new issue