Да! Вот и все.
Я думал об этом вчера днем, и я выбрал то же решение. Я опубликую здесь свой код для тех, кому было бы интересно это.
1) NativeRMISecurityManager
2) код C (вы должны сгенерировать .h с помощью javah
(примечание: я не буду переводить это на английский, так как есть множество французских комментариев)
package rmi;
import java.rmi.RMISecurityManager;
/**
* <p> Ce SecurityManager, qui herite de RMISecurityManager,
* implemente une verification supplementaire des droits
* d'acces aux fichiers.
* A la creation du SecurityManager et lors de la creation
* de nouveaux threads, on renseigne ThreadLocal du nom du
* _user_ du thread.
* <p>Ainsi, lors des checkRead() et checkWrite()
* notre SecurityManager appelle une methode native (JNI)
* qui va verifier directement si le user a les droits
* d'acces a la ressource.
* <p><b>Warning : NE PAS OUBLIER DE FAIRE APPEL A
* setCurrentUser() DANS CHAQUE THREAD CREE.</b>
* <p> <b>Remarque :</b> Pour les informations sur la compilation
* et l'execution de la lib ecrite en C, cf. le fichier README.
* @author a_po
*/
public class NativeRMISecurityManager extends RMISecurityManager
{
private boolean unix;
protected ThreadLocal user = new ThreadLocal();
/**
* Constructeur par defaut.
* <p><b>ATTENTION :</b> Bien faire appel a la methode setCurrentUser(String) !
* Sinon le SecurityManager se comportera comme un RMISecurityManager classique.
* @see public void setCurrentUser(String userName)
*/
public NativeRMISecurityManager()
{
super();
String OS = System.getProperty("os.name").toLowerCase();
unix = (OS.compareTo("windows") != 0); /* Si le systeme
* n'EST PAS windows,
* alors c'est UNIX...
*
* Pas tres rigoureux,
* mais sinon il faut tester
* Systeme V, Linux, *BSD,
* Sun OS, ...
*/
/*
* User du ThreadLocal : Chaque thread est considere comme ayant des
* droits d'acces au systeme potentiellement differents.
*/
this.user.set(user);
if (!unix)
{
System.out.println("Systeme : "+OS);
}
}
/**
* Verification en lecture.
* <p>
* Dans le cas ou l'on est sur une plateforme POSIX,
* on souhaite verifier que le _user_ du Thread a le droit
* de lecture sur le fichier.
* <p>
* De plus, dans le cas ou user est null, cela signifie
* OBLIGATOIREMENT que le thread a ete cree "automatiquement"
* et que le thread courant n'est pas un thread de "tache a executer".
* <p>
* En effet, le user est recupere dans le ThreadLocal
* et on force l'initialisation de cette variable a l'instanciation
* du SecurityManager (en mettant le constructeur par defaut prive) ou
* en faisant appel a setCurrentUser(String)
* @see void rmi.NativeRMISecurityManager.setCurrentUser(String user)
*/
public void checkRead(String file)
{
super.checkRead(file);
String str_user = (String)this.user.get();
if (unix && str_user != null)
{
if (file == null)
{
throw new SecurityException("file = NULL !!!");
}
int ret = c_checkRead(file, str_user);
if (ret != 0)
{
throw new SecurityException("Erreur d'acces au fichier : " + file);
}
}
}
/**
* Verification d'acces en ecriture sur un fichier.
* @see void rmi.NativeRMISecurityManager.checkRead(String file)
*/
public void checkWrite(String file)
{
super.checkWrite(file);
String str_user = (String)this.user.get();
if (unix && str_user != null)
{
if (file == null)
{
throw new SecurityException("file = NULL !!!");
}
int ret = c_checkWrite(file, str_user);
if (ret != 0)
{
throw new SecurityException("Erreur d'acces au fichier : " + file);
}
}
}
/**
* Configure le thread courant pour que le user soit pris en compte
* dans les verifications d'acces aux fichiers.
* @param user
*/
public void setCurrentUser(String userName)
{
this.user = new ThreadLocal();
this.user.set(userName);
}
public String getCurrentUser()
{
if (user!=null){
return (String)user.get();
}
else return null;
}
/**
* Methode native a implementer en C.
* @param file
* @param user
* @return 0 si ok <p> -1 sinon
*/
public native int c_checkRead(String file, String user);
/**
* Idem que pour c_checkRead
* @param file
* @param user
* @return
* @see int rmi.NativeRMISecurityManager.c_checkRead(String file, String user)
*/
public native int c_checkWrite(String file, String user);
/**
* Chargement de la bibliotheque JNI.
*/
static
{
System.loadLibrary("rmi_NativeRMISecurityManager");
}
}
И библиотека C:
#include <stdio.h>
#include <jni.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <stdlib.h>
#include <grp.h>
#include <string.h>
#include "rmi_NativeRMISecurityManager.h"
/* Droits en lecture / ecriture / execution */
#define R_RIGHT 4
#define X_RIGHT 1
#define W_RIGHT 2
JNIEXPORT jint JNICALL Java_rmi_NativeRMISecurityManager_c_1checkRead
(JNIEnv *env, jobject obj, jstring file, jstring user)
{
int ret = check_permission(env, obj, file, user);
/**
* La permission d'acces a un fichier vaut ceci :
* 1 pour l'execution
* 2 pour l'ecriture
* 4 pour la lecture.
* Donc :
* * Droit en lecture : 4, 5, 6, 7
* * Droit en ecriture : 2, 3, 6, 7
* * Droit en execution : 1, 3, 5, 7.
*/
if (ret == R_RIGHT || ret == R_RIGHT + W_RIGHT ||
ret == R_RIGHT + X_RIGHT || ret == R_RIGHT + W_RIGHT + X_RIGHT)
{
return 0;
}
else
return -1;
}
JNIEXPORT jint JNICALL Java_rmi_NativeRMISecurityManager_c_1checkWrite
(JNIEnv *env, jobject obj, jstring file, jstring user)
{
int ret = check_permission(env, obj, file, user);
/**
* La permission d'acces a un fichier vaut ceci :
* 1 pour l'execution
* 2 pour l'ecriture
* 4 pour la lecture.
* Donc :
* * Droit en lecture : 4, 5, 6, 7
* * Droit en ecriture : 2, 3, 6, 7
* * Droit en execution : 1, 3, 5, 7.
*/
if (ret == W_RIGHT || ret == W_RIGHT + R_RIGHT ||
ret == W_RIGHT + X_RIGHT || ret == W_RIGHT + R_RIGHT + X_RIGHT)
{
return 0;
}
else
return -1;
}
int check_permission(JNIEnv *env, jobject obj, jstring file, jstring user)
{
struct stat pstat;
const char* pzcfile = (*env)->GetStringUTFChars(env, file, 0);
const char* pzcuser = (*env)->GetStringUTFChars(env, user, 0);
struct passwd* puserInfo;
int bisOwner = 0;
int bisGroup = 0;
struct group* pgroupInfo;
int i;
int droits = 0;
/* recuperer les informations relatives au fichier */
if(lstat(pzcfile, &pstat)<0)
{
fprintf(stderr,"* Le fichier %s n'exite pas.\n", pzcfile);
(*env)->ReleaseStringUTFChars(env, file, pzcfile);
(*env)->ReleaseStringUTFChars(env, user, pzcuser);
return -1;
}
/* recuperer l'identifiant du user */
puserInfo = getpwnam(pzcuser);
if(puserInfo == NULL)
{
fprintf(stderr,"* L'utilisateur %s n'est pas connu du systeme.\n", pzcuser);
(*env)->ReleaseStringUTFChars(env, file, pzcfile);
(*env)->ReleaseStringUTFChars(env, user, pzcuser);
return -2;
}
/* regarder si le user est proprietaire du fichier */
if(puserInfo->pw_uid == pstat.st_uid)
{
bisOwner = 1;
}
/* si le user n'est pas proprietaire, verifier s'il est membre du groupe */
if(!bisOwner)
{
/* recuperer les informations relatives au groupe */
pgroupInfo = getgrgid(pstat.st_gid);
/* parcourir la liste des membres du groupe a la recherche du user */
for(i=0;;i++)
{
if(pgroupInfo->gr_mem[i] == NULL)
{
break;
}
if(strcmp(pgroupInfo->gr_mem[i],pzcuser) == 0)
{
bisGroup = 1;
break;
}
}
}
/* recuperer les droits correspondants au user */
if(bisOwner)
{
droits = (pstat.st_mode & S_IRWXU) >> 6;
}
else if(bisGroup)
{
droits = (pstat.st_mode & S_IRWXG) >> 3;
}
else
{
droits = pstat.st_mode & S_IRWXO;
}
/* liberer les espaces memoire alloues */
(*env)->ReleaseStringUTFChars(env, file, pzcfile);
(*env)->ReleaseStringUTFChars(env, user, pzcuser);
return droits;
}
Большое спасибо, Грег Кейс. Это меня утешает, потому что мы нашли одно и то же решение. :)