質問

小さなrmiクライアント/サーバーアプリケーションを適応させています。私はいくつかのことを書きました:

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

特別なことはありませんが、... JNIメソッドを呼び出し、別のユーザーの許可を確認する新しいRMISecurityManagerに手を入れました:

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

Serverクラスでそれを実行しています:

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

このクラスは、サーバーのメインスレッドで動作するようです。問題は、そのサーバークラスに接続してレジストリを検索しようとしたときです。 私はその例外を取得します:

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

この意味は、スレッドが(暗黙的に)作成され、NativeRMISecurityManagerをデフォルトのSecurityManagerとして取得することです。

それに関して誰かアドバイスはありますか?

役に立ちましたか?

解決

  

この意味は、スレッドが(暗黙的に)作成され、NativeRMISecurityManagerをデフォルトのSecurityManagerとして取得することです。

これは事実ですが、エラーの原因ではありません。問題はThreadLocalの使用に関係しています。 ThreadLocalの重要なプロパティは、すべての呼び出しスレッドに独自の値があることです。この場合、「ユーザー」はNativeRMISecurityManagerを初期化し、システムセキュリティマネージャー(おそらくメインスレッド)として設定するスレッドによって(およびそのために)設定されています。

ただし、他のスレッド(見たところ、RMIメッセージ処理スレッド)はcheckRead()を呼び出していますが、「ユーザー」はこのスレッドにはフィールドが設定されていません!したがって、値はnullとして返されます。

異なるスレッドに異なる値を持たせる何らかの理由がない限り-そしてあなたの例からそれを見分けることはできません-私は「ユーザー」を作ることをお勧めします;フィールドa)ThreadLocalではなくString、およびb)非静的。これでnull値の問題が解決するはずです。

ただし、それが要件/設計上の制約であると仮定すると、現在のアーキテクチャの問題は、NativeRMISecurityManagerをインスタンス化するスレッドのみが実際にユーザーを持つようになることです。他のすべてのスレッドはnullになります。

これをもう少し深く掘り下げてみると、問題のドメインをよりよく理解して、修正に関して役立つ提案を提供する必要があると思います。さらに、Javaのセキュリティアーキテクチャについて、迅速なことや汚いことはありません。ただし、いくつかの仮定の下で最善を尽くします。

  1. システムによって作成されたスレッドは信頼できると見なされます
  2. コードで作成されたスレッドはユーザーを指定する必要があります
  3. 子スレッドは作成スレッドのユーザーを継承する必要があります

潜在的な実装:

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

他のヒント

はい!それだけです。

昨日の午後、そのことを考えましたが、同じ解決策を取りました。好奇心those盛な人のために、ここに私のコードを投稿します。 1)NativeRMISecurityManager 2)Cコード(javahで.hを生成する必要があります

(nb:フランス語のコメントがたくさんあるので、英語で説明しません)

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

グレッグケース、ありがとうございました。同じ解決策を見つけたので、これは私を慰めます。 :)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top