CS 696 Emerging Technologies: Java Distributed Computing Spring Semester, 1999 Java Security Model |
||
---|---|---|
© 1999, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 08-Feb-99 |
Java Security Model
JDK 1.0 Security Model
The Sandbox Model
Language features promoting safe code
JDK 1.2 Security Model
Greatly expanded over JDK 1.1
Security checks extended to include all Java programs
Fine-grained access control
SecurityManager
JDK 1.1 Abstract class
Defines checkXXX() methods to determine if program can perform action XXX
Method
|
Determines
if code can:
|
checkAccept(String,
int)
|
accept
a connection from given a host/port
|
checkAccess(Thread)
|
modify
the given thread
|
checkAccess(ThreadGroup)
|
modify
the given thread group
|
checkAwtEventQueueAccess()
|
access
to the AWT event queue
|
checkConnect(String,
int)
|
open
a connection to a given host/port
|
checkCreateClassLoader()
|
create
a new class loader
|
checkDelete(String)
|
delete
the specified file
|
checkExec(String) |
create
a subprocss
|
checkExit(int)
|
call
System.exit()
|
checkLink(String) |
dynamic
link the library code
|
checkListen(int) |
wait
for a connection from a given host/port
|
checkMemberAccess(Class,
int)
|
access
members of class
|
checkMulticast(InetAddress) |
use
IP multicast
|
checkPackageAccess(String) |
thread
allowed to access the package
|
checkPackageDefinition(String) |
define
classes in the package
|
checkPrintJobAccess() |
initiate
a print job request
|
checkPropertiesAccess() |
access
or modify the system properties
|
checkPropertyAccess(String)
|
access
the system property with given key
|
checkRead(FileDescriptor) |
read
the given file
|
checkSecurityAccess(String) |
access
to certain operations for a security API action
|
checkSetFactory() |
set
a socket factory
|
checkSystemClipboardAccess() |
access
the system clipboard
|
checkTopLevelWindow(Object) |
bring
up the top-level window
|
checkWrite(FileDescriptor) |
Write
to a given file
|
JDK 1.2 Security Manager
Concrete class
Uses permissions from policy file to determine if program can perform action XXX.
Uses checkPermission() rather than checkXXX methods.
Keeps all JDK 1.1 checkXXX methods for backward compatability
You can force a program to use the security manager using the -Djava.security.manager flag
java -Djava.security.manager aJavaProgram
If no security manager is installed, the program has all normal access to system resources.
How the manager Works In JDK code that accesses important resources, the current SecurityManager is check to see if the program can access the resource
In java.io.File class:
public boolean canRead() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } return canRead0(); }
In constructor for Socket:
SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkConnect(address.getHostAddress(), port); }
Recall that if checkRead or checkConnect determines that the program does not have access permission it throws an exception
Permissions
Permissions are set in a java.policy file
The general format of granting a permission in a java.policy file is:
grant [signedBy "signer_names"] [, codeBase "URL" ]{ permission permission_class_name "target_name", "action" [, signedBy "signer_names"]; .... permission permission_class_name "target_name" , "action" [, signedBy "signer_names"]; };Note signedBy and codeBase will be covered later
Existing Permission Types
AllPermission
AWTPermission
FilePermission
NetPermission
PropertyPermission
ReflectPermission
RuntimePermission
SecurityPermission
SerializablePermission
SocketPermission
See http://java.sun.com/products/jdk1.2/docs/guide/security/permissions.html for complete details on these permissions
Each permission has a target name
Targets can refine the permission
AWTPermission targets:
Default Permissions
Here is the contents of the standard system wide java.policy file (minus comments).
grant codeBase "file:${java.home}/lib/ext/-" { permission java.security.AllPermission; }; grant { permission java.lang.RuntimePermission "stopThread"; permission java.net.SocketPermission "localhost:1024-", "listen"; permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; permission java.util.PropertyPermission "java.vendor.url", "read"; permission java.util.PropertyPermission "java.class.version", "read"; permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.version", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "file.separator", "read"; permission java.util.PropertyPermission "path.separator", "read"; permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission "java.vm.specification.version", "read"; permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; permission java.util.PropertyPermission "java.vm.specification.name", "read"; permission java.util.PropertyPermission "java.vm.version", "read"; permission java.util.PropertyPermission "java.vm.vendor", "read"; permission java.util.PropertyPermission "java.vm.name", "read"; };
SocketPermission
General Format:
grant { permission java.net.SocketPermission "host", "actions"; };
where:
host = (hostname | IPaddress ) [:portrange] portrange = portnumber | -portnumber | portnumber-portnumber | portnumber- actions = action | action,actions action = accept | connect | listen | resolveHost host can be:
Pattern |
Meaning |
* *.edu *.sdsu.edu |
All
machines
All machines in edu domain All machines in the sdsu.edu domain |
Port Range
Pattern |
Meaning |
-N N- N-M N |
ports
N and below
ports N and above ports N through M the port N |
Action |
Meaning |
accept connect listen resolve |
accept
connections on listed port(s)
connect to listed port(s) listen on listed port(s) resolve host names |
grant { permission java.net.SocketPermission "localhost", "connect,accept,listen"; permission java.net.SocketPermission "rohan.sdsu.edu:1024-", "connect,accept,resolve"; permission java.net.SocketPermission "rohan.sdsu.edu:-1024", "connect"; };
FilePermission
General Format:
grant { permission java.io.FilePermission "pathname", "actions"; };
Pathname can specify either a file or a directory
Pathnames
with special characters
|
|
Pathname |
Meaning |
path/ |
A
directory and all files in that directory
|
path/- |
A
directory and all files and subdirectories (recursively)
|
<<ALL
FILES>>
|
All
files
|
* |
Current
directory & all its files
|
- |
Current
directory & all its files, subdirectories recursively
|
Action |
Meaning |
read write execute delete |
Permission
to read
Permission to write Permission to execute Permission to delete |
Pathnames & Windows
On a Windows platform the pathname in the file.io.Filepermission must use two backslashes for each actual single backslash in the path. For example:
grant { permission java.io.FilePermission "C:\\users\\sam\\*", "read,write,execute"; };The JVM uses the java.io.StreamTokenizer to parse the policy file. The StreamTokenizer uses a single backslash to indicate an escape string, as in “\n”. The StreamTokenizer uses the double backslash to indicate a single backslash.
This problem can be avoided by using ${/} to indicate the current platform’s file separator character. So the above example can be rewritten as
grant { permission java.io.FilePermission "C:${/}users${/}sam${/}*", "read,write,execute"; };
Except for the fact that most other platform do not have C drives, this can be moved to other platforms.
Property Expansion in Policy Files
A string in a policy file of the format ${some.property} will be expanded to the value of the system property
permission java.io.FilePermission "${user.home}/bin/", "execute";
If this is in my java.policy on rohan it will expand to:
permission java.io.FilePermission "/home/ma/whitney/bin/", "execute";
The string ${/} expands to the platform specific file separator
On a windows machine the permission:
permission java.io.FilePermission "${user.home}${/}bin${/}", "execute";
would expand to something like:
permission java.io.FilePermission "C:\users\whitney\bin\", "execute";
Bad Properties If you use a property that can not be expanded that entry is ignored.
If the property "bar" is not defined then the following grant entry will be ignored
grant codeBase ${bar} { permission java.io.FilePermission "/usr/*", "read"; permission java.io.FilePermission "/whitney/-", "read, write"; }In the following grant block only the first permission will be ignored
grant { permission java.io.FilePermission "${bar}/*", "read"; permission java.io.FilePermission "/whitney/-", "read, write"; }
CodeBase
A grant entry can specify the location of the code the permissions are to apply
grant codeBase "URL" { permission permission_class_name "target_name", "action"; };
The codeBase is a URL which specifies a code location. The URL is either "http://etc" or "file:/etc". Actually any URL can be used, but there needs to be a way to access the code via the URL. Currently only http and local file accesses are support. Always use the forward slash (/) in the URL even on Windows machines. If no codeBase is given in a grant block, that block applies to all classes.
The exact meaning of a codeBase depends on the characters on the end.
Ending |
Matches |
/ |
All
class files, not jar files, in specified directory
|
/* |
All
files, class & jar, in specified directory
|
/- |
All
files, class & jar, in specified directory and recursively in all
subdirectories
|
grant codeBase "file:${user.home}/classes/-" { permission file.io.FilePermission "<<ALL FILES>>", "read, write, execute, delete"; };When you use property expansion in the codeBase URL on a Windows machine, and file separator character in the expanded property is changed to a “/”.
CodeBase, Packages & Jars
The following was determined by tests using JDK 1.2 (reference release) on rohan.
When using the codeBase to point to local files, the codeBase URL should point to one of:
Packages
If you have a package that is not in a jar file, the codeBase must point to the directory containing the root directory of the package and must end in "/-"
Example
Assume:
grant codeBase "file:/home/ma/whitney/test/-" { permission java.io.FilePermission "<<ALL FILES>>", "read, write"; }
CodeBase & Domains
Using codeBase we can give different classes different permissions and different levels of the same permission.
A Protection Domain (or just Domain) includes all classes whose instances are granted the same set of permissions.
The policy file below creates two domains
grant { permission java.util.PropertyPermission "user.dir", "read"; permission file.io.FilePermission "<<ALL FILES>>", "read"; }; grant codeBase "file: /home/ma/whitney/goodRoot/-" { permission java.io.FilePermission "<<ALL FILES>>","read,write"; };
Determining Permissions
When an object (or class) from one domain calls code from another domain, the question is whose permissions are used?
When code performs some activity that requires a permission of type X, the intersection of all permissions of type X of all domains that currently have code on the execution stack is used.
Domain Example
Let the following java.policy file be used:
grant { permission java.util.PropertyPermission "user.dir", "read"; permission file.io.FilePermission "<<ALL FILES>>", "read"; }; grant codeBase "file: /home/ma/whitney/goodRoot/-" { permission java.io.FilePermission "<<ALL FILES>>","read,write"; };
Let the following class be located in directory /home/ma/whitney/goodRoot/good/. Then the class Naive can write files.
package good; import sdsu.io.SimpleFile; import java.io.IOException; public class Naive { public static void main(String[] args ) throws IOException { System.setSecurityManager(new SecurityManager()); write( "test", "Hi Mom" ); } public static void write( String fileName, String contents ) throws IOException { SimpleFile aFile = new SimpleFile( fileName ); aFile.setContents( contents ); } }
Example Continued Let the following class be in the directory /home/ma/whitney/test
A security exception will be thrown when Naive.write() tries to write to the file.
Even though Naive has the permission to write files, the intersection of java.io.FilePermission’s of each domain on the stack is used to determine if Naive can actual write to a file. Since Sneaky’s method main() is on the stack, its permission is intersected with Naive's permission to determine if the write is allowed. Since Sneaky does not have permission to write, Naive is not allowed to write under these conditions.
import good.Naive; import java.io.IOException; public class Sneaky { public static void main(String[] args ) throws IOException { System.setSecurityManager(new SecurityManager()); Naive.write( "test", "I broke the rules!" ); } }
Privileged Blocks
When a privileged code is executed, the domains below the code in the execution stack are not used in determining the permissions of the code or any code called by the privileged block
Code is privileged if and only if it is:
Privileged Block
If we use the following implementation for Naive, then Sneaky can use Naive to write a file.
package good; import sdsu.io.SimpleFile; import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedAction; public class Naive { public static void main(String[] args ) throws IOException { System.setSecurityManager(new SecurityManager()); write( "test", "Hi Mom" ); } public static void write( String fileName, String contents) { AccessController.doPrivileged( new AlwaysWrite( fileName, contents )); } }
Privileged Block Continued
class AlwaysWrite implements PrivilegedAction { String fileName; String contents; public AlwaysWrite( String fileName, String contents) { this.fileName = fileName; this.contents = contents; } public Object run() { try { SimpleFile aFile = new SimpleFile( fileName); aFile.setContents( contents ); } catch (IOException error ) { System.err.println( "IOException in write"); } return null; } }
Privilaged Block Exceptions To pass the IOException thrown in the run method to the caller implement the PrivilegedExceptionAction interface, as is done below
package good; import sdsu.io.SimpleFile; import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; public class Naive { public static void write( String fileName, String contents) throws IOException { try { AccessController.doPrivileged( new AlwaysWrite( fileName, contents )); } catch( PrivilegedActionException IO) { throw (IOException) IO.getException(); } } }
class AlwaysWrite implements PrivilegedExceptionAction { String fileName; String contents; public AlwaysWrite( String fileName, String contents) { this.fileName = fileName; this.contents = contents; } public Object run() throws IOException { SimpleFile aFile = new SimpleFile( fileName); aFile.setContents( contents ); return null; } }
Encryption
Public/Private Key Encryption:
Two keys are used encrypt/decrypt messages.
The public key is something that is well known, i.e. published.
The private key is kept secret and secure
A message encrypted with the private key can only be decrypted with the corresponding public key. The public key can only decrypt messages encrypted with the corresponding private key. A message encrypted with the public key can only be decrypted with the corresponding private key. The private key can only decrypt messages encrypted with the corresponding public key.
DSA and RSA are different algorithms used to encrypt/decrypt messages with public/private keys
Public/Private Key Encryption Signatures
An entity encrypts information with its own private key.
The resulting encrypted message can come only from the holder of the private key
Anyone with access to the public key can verify that the message was created by the holder of the private key
This is a digital signature
Public/Private Key Secure Messages
An entity encrypts a message with your public key
Then only those that have access to your private key can decrypt the message
One-Way Encryption
SHA1 and MD5 are common one-way encryption algorithms
One-way encryption algorithms are not reversible
Given the output of a one-way encryption algorithm you can not compute the input that gave the output. A goal of one-way encryption algorithms is to be one-to-one. That is a given output can only be achieved by one input.
One-way encryption can be used to sort a password file. Store the one-way encrypted passwords in the file. If someone reads the password file they can not decrypt your encrypted password. When you enter your password, it can be encrypted and the result checked with the entry in the password file.
Encryption & Signing Java jar files
Java jar files can be digitally signed
You can grant permissions to code based on who signed the code
To sign a jar file you need public/private keys and a certificate validating the public key
Obtaining a Certificate & keys
Some options:
Using keytool
See the http://java.sun.com/products/jdk1.2/docs/tooldocs/solaris/keytool.html for background material and details on running the keytool
The command:
keytool -genkey -alias eli
Will produce public/private keys with certificate.
The public/private keys are given the alias "eli"
These will be placed in the file .keystore in your home directory
The following is a sample interaction with keytool
rohan 16-> keytool -genkey -alias eli
Enter keystore password: *****
What is your first and last name?
[Unknown]: Roger Whitney
What is the name of your organizational unit?
[Unknown]: Department of Mathematical and Computer Sciences in the College of Sciences
What is the name of your organization?
[Unknown]: San Diego State University
What is the name of your City or Locality?
[Unknown]: San Diego
What is the name of your State or Province?
[Unknown]: California
What is the two-letter country code for this unit?
[Unknown]: US
Is <CN=Roger Whitney, OU=Department of Mathematical and Computer Sciences in the College of Sciences, O=San Diego State University, L=San Diego, ST=California, C=US> correct?
[no]: yes
Enter key password for <eli>
(RETURN if same as keystore password): *******
The command:
keytool -list –vwill display information all the keys/certificates in your .keystore file
Here is the result of the command on the newly created .keystore file
rohan 20-> keytool -list -v
Enter keystore password: *****
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry:
Alias name: eli
Creation date: Mon Feb 08 10:11:41 PST 1999
Entry type: keyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Roger Whitney, OU=Department of Mathematical and Computer Sciences in the College of Sciences, O=San Diego State University, L=San Diego, ST=California, C=US
Issuer: CN=Roger Whitney, OU=Department of Mathematical and Computer Sciences in the College of Sciences, O=San Diego State University, L=San Diego, ST=California, C=US
Serial number: 36bf28d6
Valid from: Mon Feb 08 10:11:34 PST 1999 until: Sun May 09 11:11:34 PDT 1999
Certificate fingerprints:
MD5: D0:77:7C:64:ED:26:C5:3E:0A:D4:03:D6:4C:EA:46:47
SHA1: F4:79:48:65:F3:CC:43:80:42:43:1A:ED:E0:CD:67:CB:3B:C1:F8:4F
Creating a jar File
Use the jar command, part of JDK1.2, to create a jar file
See the http://java.sun.com/products/jdk1.2/docs/tooldocs/solaris/jar.html for more details
The command:
jar -cf fileName.jar *
will place all files in the current directory and recursively all subdirectories with their files into the jar file "fileName.jar"
When you start creating jar files, you should keep your .java files and the .class files in separate directory structures. This makes it easy to create a jar file without including the source.
Going back to the good.Naive class example. I changed directory to the parent directory of good. I then create a jar file containing the binaries for the package good.
rohan 36-> jar -cf good.jar *
rohan 37-> ll
total 4
drwx------ 2 whitney 512 Feb 8 10:18 good/
-rw------- 1 whitney 2213 Feb 8 10:22 good.jar
Signing a Jar file
Use jarsigner to sign a jar file
See the http://java.sun.com/products/jdk1.2/docs/tooldocs/solaris/jarsigner.html for more information
The command:
jarsigner -signedjar signedFile.jar existing.jar aliasName
will create sign the jar file “existing.jar” with the alias "aliasName" and name the resulting jar file "signedFile.jar"
The alias "aliasName" must be known in your .keystore file
The following shows the signing of the jar file good.jar and the contents of the new jar file.
rohan 38-> jarsigner -signedjar signedGood.jar good.jar eli
Enter Passphrase for keystore: *****
Enter key password for eli: *******
rohan 39-> ls
good/ good.jar signedGood.jar
rohan 40-> ll
total 8
drwx------ 2 whitney 512 Feb 8 10:18 good/
-rw------- 1 whitney 2213 Feb 8 10:22 good.jar
-rw------- 1 whitney 3620 Feb 8 10:23 signedGood.jar
rohan 41-> jar -tf good.jar
META-INF/
META-INF/MANIFEST.MF
good/
good/Naive.class
good/Naive$1.class
good/AlwaysWrite.class
rohan 42-> jar -tf signedGood.jar
META-INF/MANIFEST.MF
META-INF/ELI.SF
META-INF/ELI.DSA
META-INF/
good/
good/Naive.class
good/Naive$1.class
good/AlwaysWrite.class
signedBy
You can grant permissions to signed code. Your java.policy file must contain the location of your keystore file. The keystore entry is in URL format. It can be either an absolute URL or a relative URL, which will be relative to your java.policy file. The following java.policy file grants permission to read & write files to any classes signed by "eli".
keystore ".keystore"; grant { permission java.util.PropertyPermission "user.dir", "read"; }; grant signedBy "eli" { permission java.io.FilePermission "<<ALL FILES>>", "read,write"; };
You can use signedBy with codeBase in any order
keystore ".keystore"; grant { permission java.util.PropertyPermission "user.dir", "read"; }; grant codeBase "file:${user.home}/goodRoot/signedGood.jar", signedBy "eli" { permission java.io.FilePermission "<<ALL FILES>>", "read,write"; };
Why The Second Signer Field
keystore ".keystore"; grant { permission java.util.PropertyPermission "user.dir", "read"; }; grant codeBase "file:${user.home}/goodRoot/signedGood.jar", signedBy "eli" { permission java.io.FilePermission "<<ALL FILES>>", "read,write", signedBy "eli"; };
From the Java Security Architecture, section 3.3.1 Policy File Format:
"The reason for including the second signer field is to prevent spoofing when a permission class does not reside with the Java runtime installation. For example, a copy of the com.abc.TVPermission class can be downloaded as part of a remote JAR archive, and the user policy might include an entry that refers to it. Because the archive is not long-lived, the second time the com.abc.TVPermission class is downloaded, posssibly from a different web site, it is crucial that the second copy is authentic, as the presence of the permission entry in the user policy might reflect the user's confidence or belief in the first copy of the class bytecode."
Policy File
Policy files can set a number of permissions beside SocketPermission.
See http://java.sun.com/products/jdk1.2/docs/guide/security/permissions.html for more details.
System wide Policy File
java –Djava.security.policy=policyFileURL anApp
java –Djava.security.policy==policyFileURL anApploads only the specified policy file. No other policy files are loaded. Using this flag we can set a different policy file per server object.
appletviewer –J-Djava.security.policy=policyFileURL appletloads the policy file in the appletviewer.
Policy File Gotcha’s
Policy File Flag not Used
The -Djava.security.policy flag will be ignored if the "policy.allowSystemProperty" in the file {JavaHome}/jre/lib/security/java.security is set to false.
The default value is set to true
Policy Files not Checked
If a security manager is not installed in an application, the policy files will not be checked.
The java flag:
-Djava.security.manager
insures that the default security manager will be installed.
Often you use this flag when specifying a policy file option:
java -Djava.security.manager –Djava.security.policy=~whitney/rmi/policFile myApp
Copyright ©, All rights reserved.
1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 08-Feb-99    Next