Frage

Ich Anpassung ein wenig rmi Client-Server-Anwendung. Ich habe mehrere Dinge geschrieben:

HelloInterface -> A Hello World interface for RMI
Server -> The server app'
Client -> The client app'

Nichts Besonderes, aber ... Ich habe meine Hände in einem neuen RMISecurityManager setzen, die eine JNI-Methode aufruft und überprüft die Berechtigung für einen separaten Benutzer:

package rmi;
import java.rmi.RMISecurityManager;
import java.io.*;

public class NativeRMISecurityManager extends RMISecurityManager
{
    private boolean unix;
    protected static ThreadLocal user = new ThreadLocal();

    /*
     * On interdit l'utilisation du constructeur par defaut
     * pour obliger l'utilisation du constructeur avec user. 
     */
    private NativeRMISecurityManager()
    {
        super();
    }

    public NativeRMISecurityManager (final String user)
    {
        super();
        String OS = System.getProperty("os.name").toLowerCase();
        unix = (OS.compareTo("windows") != 0); /* Assume that if not 
                            * windows, then "UNIX/POSIX" 
                            */
        /*
         * ThreadLocal's user : Each thread is considered 
         * to have different access rights to the machine
         */

        NativeRMISecurityManager.user.set(user);

        if (!unix)
        {
            System.out.println("Systeme : "+OS);
        }
    }

    public void checkRead(String file)
    {
        super.checkRead(file);
        /*
         * If we are on a **IX platform we want to check that 
         * the _user_ has access rights.
         */
        if (unix)
        {
            String str_user = (String)NativeRMISecurityManager.user.get();

            if (file == null)
            {
                throw new SecurityException("file = NULL !!!");
            }
        if (str_user == null)
            {
                throw new SecurityException("user = NULL in the ThreadLocal!!!");
            }

            int ret = c_checkRead(
                    file, 
                    str_user
                    );
            if (ret != 0)
            {
                throw new SecurityException("Access error: " + file);
            }
        }
    }

    public native int c_checkRead(String file, String user);
}

In der Server-Klasse ich tue, dass:

String user = "my_user";
System.setSecurityManager(new NativeRMISecurityManager(user));

Diese Klasse scheint in der Server-Hauptthread zu arbeiten. Das Problem ist jetzt, wenn ich versuche und zu dieser Server-Klasse verbinden und die Registry Nachschlag. Ich erhalte diese Ausnahme:

Exception in thread "RMI TCP Connection(1)-192.168.42.207"     java.lang.ExceptionInInitializerError
        at sun.rmi.transport.StreamRemoteCall.getInputStream(StreamRemoteCall.java:111)
        at sun.rmi.transport.Transport.serviceCall(Transport.java:118)
        at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:466)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:707)
        at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.SecurityException: user = NULL dans le ThreadLocal!!!
        at rmi.NativeRMISecurityManager.checkRead(NativeRMISecurityManager.java:62)
        at java.io.File.exists(File.java:700)
        at java.lang.ClassLoader$3.run(ClassLoader.java:1689)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1686)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1668)
        at java.lang.Runtime.loadLibrary0(Runtime.java:822)
        at java.lang.System.loadLibrary(System.java:993)
        at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.rmi.server.MarshalInputStream.<clinit>(MarshalInputStream.java:97)
        ... 5 more

IMHO die Bedeutung davon ist, dass ein Thread ist (implizit) erstellt und erhält den NativeRMISecurityManager als Standard-Securitymanager.

Würde jemand einen Rat in Bezug auf das?

War es hilfreich?

Lösung

  

IMHO die Bedeutung davon ist, dass ein Thread ist (implizit) erstellt und erhält den NativeRMISecurityManager als Standard-Securitymanager.

Das ist wahr, aber es ist nicht die Ursache für Ihre Fehler ist; das Problem hat mit der Verwendung von Thread zu tun. Die Schlüsseleigenschaft eines Thread ist, dass jeder Aufruf Thread seinen eigenen Wert hat. In diesem Fall „user“ wird von (und für) den Thread gesetzt, der die NativeRMISecurityManager initialisiert und setzt sie als System Security Manager (vermutlich der Hauptthread).

Doch einige andere Thread (durch den Blick von ihm die RMI Message-Handling-Thread) ruft Prueflesekommandos () - aber das Feld „Benutzer“ wurde nie für diesen Thread gesetzt! Somit ist der Wert als null zurückkommt.

Es sei denn, aus irgendeinem Grund gibt es verschiedene Threads unterschiedliche Werte zu haben - und ich kann nicht ein von Ihrem Beispiel erkennen - Ich würde empfehlen, die „user“ -Feld a) ein String machen, kein Thread und b) nicht statisch. Das sollte Ihr Wert null Problem lösen.

Jedoch vorausgesetzt, dass es sich um eine Anforderung / Design-Einschränkung ist, ist das Problem mit der aktuellen Architektur, dass nur der Thread, der die NativeRMISecurityManager instanziiert tatsächlich um einen Benutzer haben wird -. Jeder anderer Thread bekommt null

in diese mit ein wenig mehr Tiefe in einzutauchen, ich glaube, ich brauche ein besseres Verständnis des Problems Domain keine hilfreiche Vorschläge zu einer Lösung zu bieten. Des Weiteren gibt es nichts schnell oder schmutzig über Java-Sicherheitsarchitektur. Aber ich werde mein Bestes Arbeiten unter einigen Annahmen tun:

  1. Themen vom System erstellt werden angenommen vertraut werden
  2. Themen von Ihrem Code erstellt wird, muss ein Benutzer angeben
  3. Kinder Fäden sollten die Benutzer des Erschaffungs Thread
  4. erben

Mögliche Umsetzung:

public class NativeRMISecurityManager extends RMISecurityManager {

    private static final boolean UNIX;

    static {
        String OS = System.getProperty("os.name").toLowerCase();
        UNIX = (OS.compareTo("windows") != 0); /* Assume that if not 
                                                * windows, then "UNIX/POSIX" 
                                                */
    }

    protected static InheritableThreadLocal<String> user =
        new InheritableThreadLocal<String>();

    public static setThreadUser(String username) {
        user.set(username);
    }


    public NativeRMISecurityManager(String initialUser) {
        super();
        // Set the user for the thread that constructs the security manager
        // All threads created as a child of that thread will inherit the user
        // All threads not created as a child of that thread will have a 'null' user
        setThreadUser(initialUser);
    }


    public void checkRead(String file) {
        super.checkRead(file);
        /*
         * If we are on a **IX platform we want to check that 
         * the _user_ has access rights.
         */
        if (UNIX)
        {
            if (file == null)
            {
                throw new SecurityException("file = NULL !!!");
            }

            String str_user = NativeRMISecurityManager.user.get();

            if (str_user != null)
            {
                // Note: sanitize input to native method
                int ret = c_checkRead(file, str_user);

                if (ret != 0)
                {
                    throw new SecurityException("Access error: " + file);
                }
            }

            // Assume a system thread and allow access
        }
    }

    public native int c_checkRead(String file, String user);
}

Andere Tipps

Ja! Das ist es.

Ich habe über die gestern Nachmittag dachte, und ich ging auf die gleiche Lösung. Ich werde hier posten meinen Code für diejenigen, die davon neugierig sein würde. 1) Die NativeRMISecurityManager 2) Der C-Code (Sie haben die .h mit javah erzeugen

(nb: Ich will es nicht in Englisch verleumden, da es viele französisch Kommentare)

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");
    }
}

Und die C-Bibliothek:

#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;
}

Vielen Dank Greg Fall. Dies tröstet mich, weil wir die gleiche Lösung gefunden zu haben. :)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top