Domanda

Sto cercando di creare attacchi alla libreria dei fusibili usando Jna, ma ho colpito un ostacolo lungo la strada. Ho minimizzato il codice il più possibile per renderlo digeribile qui.

La libreria dei fusibili viene fornito con alcuni filesystem di esempio scritti in C. Il più semplice è hello.c. Di seguito è riportata una versione minimizzata del suo codice per semplicemente alcune stampe nelle funzioni del filesystem:

hello.c:

/*
  FUSE: Filesystem in Userspace
  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>

  This program can be distributed under the terms of the GNU GPL.
  See the file COPYING.

  gcc -Wall hello.c -o hello `pkg-config fuse --cflags --libs`
*/
#define FUSE_USE_VERSION 26

#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

static int hello_getattr(const char *path, struct stat *stbuf)
{
    printf("getattr was called\n");
    return 0;
}

static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
    printf("readdir was called\n");
    return 0;
}

static int hello_open(const char *path, struct fuse_file_info *fi)
{
    printf("open was called\n");
    return 0;
}

static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
    printf("read was called\n");
    return 0;
}

static struct fuse_operations hello_oper = {
    .getattr    = hello_getattr,
    .readdir    = hello_readdir,
    .open       = hello_open,
    .read       = hello_read,
};

int main(int argc, char *argv[])
{
    return fuse_main_real(argc, argv, &hello_oper, sizeof(hello_oper), NULL);
}

Questo può essere compilato usando gcc -Wall hello.c -o hello -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse -pthread -lfuse -lrt -ldl

E invocato con ./hello.c -f /some/mount/point

Il -f La bandiera è renderlo in primo piano in modo da poter vedere il printf()funziona.

Tutto questo funziona bene, puoi vedere il printf()è eseguito correttamente. Sto cercando di replicare la stessa cosa in Java usando JNA. Ecco cosa mi è venuto in mente:

FuseTemp.java:

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;

public class FuseTemp
{
    public static interface Fuse extends Library
    {
        int fuse_main_real(int argc, String[] argv, StructFuseOperations op, long size, Pointer user_data);
    }

    @SuppressWarnings("unused")
    public static class StructFuseOperations extends Structure
    {
        public static class ByReference extends StructFuseOperations implements Structure.ByReference
        {
        }

        public Callback getattr = new Callback()
        {
            public int callback(final String path, final Pointer stat)
            {
                System.out.println("getattr was called");
                return 0;
            }
        };
        public Callback readlink = null;
        public Callback mknod = null;
        public Callback mkdir = null;
        public Callback unlink = null;
        public Callback rmdir = null;
        public Callback symlink = null;
        public Callback rename = null;
        public Callback link = null;
        public Callback chmod = null;
        public Callback chown = null;
        public Callback truncate = null;
        public Callback utime = null;
        public Callback open = new Callback()
        {
            public int callback(final String path, final Pointer info)
            {
                System.out.println("open was called");
                return 0;
            }
        };
        public Callback read = new Callback()
        {
            public int callback(final String path, final Pointer buffer, final long size, final long offset, final Pointer fi)
            {
                System.out.println("read was called");
                return 0;
            }
        };
        public Callback write = null;
        public Callback statfs = null;
        public Callback flush = null;
        public Callback release = null;
        public Callback fsync = null;
        public Callback setxattr = null;
        public Callback getxattr = null;
        public Callback listxattr = null;
        public Callback removexattr = null;
        public Callback opendir = null;
        public Callback readdir = new Callback()
        {
            public int callback(final String path, final Pointer buffer, final Pointer filler, final long offset,
                    final Pointer fi)
            {
                System.out.println("readdir was called");
                return 0;
            }
        };
        public Callback releasedir = null;
        public Callback fsyncdir = null;
        public Callback init = null;
        public Callback destroy = null;
        public Callback access = null;
        public Callback create = null;
        public Callback ftruncate = null;
        public Callback fgetattr = null;
        public Callback lock = null;
        public Callback utimens = null;
        public Callback bmap = null;
        public int flag_nullpath_ok;
        public int flag_reserved;
        public Callback ioctl = null;
        public Callback poll = null;
    }

    public static void main(final String[] args)
    {
        final String[] actualArgs = { "-f", "/some/mount/point" };
        final Fuse fuse = (Fuse) Native.loadLibrary("fuse", Fuse.class);
        final StructFuseOperations.ByReference operations = new StructFuseOperations.ByReference();
        System.out.println("Mounting");
        final int result = fuse.fuse_main_real(actualArgs.length, actualArgs, operations, operations.size(), null);
        System.out.println("Result: " + result);
        System.out.println("Mounted");
    }
}

La definizione del fuse_operations La struttura può essere trovata qui.

Questo può essere compilato usando: javac -cp path/to/jna.jar FuseTemp.java

E invocato usando java -cp path/to/jna.jar:. FuseTemp

jna.jar è disponibile qui.

L'errore che viene visualizzato è: fusermount: failed to access mountpoint /some/mount/point: Permission denied.

Sto eseguendo entrambi i programmi come lo stesso utente con le stesse autorizzazioni nella stessa cartella MountPoint e sono in fuse gruppo. Sto usando:

  • Kernel Linux 3.0.0
  • Fuse 2.8.4
  • OpenJDK 1.6.0_23
  • JNA 3.4.0

Quindi la mia domanda è: ciò che è esattamente diverso tra questi due programmi (hello.c e FuseTemp.java) e come farli fare la stessa cosa?

Grazie in anticipo.

Modificare: Ecco alcune informazioni aggiuntive.

Iniziale stat del Mountpoint:

  File: `/some/mount/point'
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: 803h/2051d      Inode: 540652      Links: 2
Access: (0777/drwxrwxrwx)  Uid: ( 1000/ myusername)   Gid: ( 1000/ myusername)

Output ottengo dall'esecuzione del programma Java come utente normale:

Mounting
fusermount: failed to access mountpoint /some/mount/point: Permission denied
Result: 1
Mounted
(program exits with return code 0)

Dopo questo, cercando di eseguire stat fornisce il seguente messaggio di errore:

stat: cannot stat/alcuni/mount/point ': l'endpoint di trasporto non è collegato`

Questo perché il programma Java non funziona più, quindi Fuse non può chiamare i suoi callback. Per sconcertare, se provo fusermount -u /some/mount/point, Ottengo:

fusermount: entry for /some/mountpoint not found in /etc/mtab

E se provo sudo fusermount -u /some/mount/point, il mountpoint è non montato con successo e non c'è output da fusermount. /etc/mtab è chmod'd 644 (-rw-r--r--) Quindi il mio utente può leggerlo, ma non contiene /some/mount/point. Dopo una sconosciuta riuscita, il Mountpoint è tornato alle sue vecchie autorizzazioni (directory 777).

Ora, eseguendo il programma Java come root:

Mounting
Result: 1
Mounted
(program exits with return code 0)

Dopo di che, stating /some/mount/point Gli spettacoli che non sono stati modificati, cioè è ancora una directory 777.

Ho anche riscritto FuseTemp.java per includere tutto Callbacks come Callbacks invece di PointerS. Il comportamento è lo stesso, tuttavia.

Ho esaminato il codice sorgente di Fuse e il codice di errore 1 può essere restituito in più punti durante l'esecuzione. Individerò dove non riesce esattamente al lato del fusibile e riferirò qui.

Ora per hello.c: eseguirlo come utente normale, a partire dalle stesse autorizzazioni su /some/mount/point e passandolo gli argomenti -f e /some/mount/point, il programma non sta eliminando all'inizio alcun output ma continua a funzionare. Quando si corre stat Sul Mountpoint, il programma stampato

getattr was called

Come dovrebbe. stat restituisce un errore, ma è semplicemente perché hello.c'S getattr La funzione non gli fornisce alcuna informazione, quindi nessun problema lì. Dopo l'esecuzione fusermount -u /some/mount/point Come utente normale, il programma esce con il codice di ritorno 0 e la disombra ha esito positivo.

Eseguendolo come root, iniziando con le stesse autorizzazioni /some/mount/point e passandolo gli argomenti -f e /some/mount/point, il programma non sta eliminando all'inizio alcun output ma continua a funzionare. Quando si corre stat Sul Mountpoint, ricevo un errore di autorizzazione perché non sono root. Quando si corre stat su di esso come root, il programma stampe

getattr was called

Come dovrebbe. Esecuzione fusermount -u /some/mount/point Come rese regolari dell'utente

fusermount: entry for /some/mount/point not found in /etc/mtab

Esecuzione fusermount Come root, il programma esce con il codice di ritorno 0 e il disombra ha esito positivo.

È stato utile?

Soluzione

Trovato. Mentre l'errore è stato davvero sciocco a posteriori, non è stato facile da individuare.

La soluzione: Fuse's fuse_main_real Il primo argomento del metodo è un elenco di argomenti. In questo elenco, si aspetta che l'argomento 0 sia il nome del filesystem o un nome di programma significativo. Quindi, anziché

final String[] actualArgs = { "-f", "/some/mount/point" };

Sarebbe dovuto essere

final String[] actualArgs = { "programName", "-f", "/some/mount/point" };

Questo significa anche che non puoi usare l'elenco degli argomenti che Java ti dà nel tuo main Metodo, poiché ciò non include neanche il nome del programma.

Perchè importa: Fuse effettivamente fa la propria argomentazione e le chiamate /bin/mount Passandolo i seguenti argomenti:

--no-canonicalize -i -f -t fuse.(arg 0) -o (options) (mountpoints) ...

In quanto tale, se dai se -f /some/mount/point Come elenco degli argomenti, Fuse proverà a eseguire:

/bin/mount --no-canonicalize -i -f -t fuse.-f -o rw,nosuid,nodev /some/mount/point

E mount Non piace "fuse.-f"E lamenterà.

Come è stato trovato: Aggiungendo un mucchio di printf() Il codice sorgente di Inside Fuse per capire dove stavano esattamente le cose: in /lib/mount_util.c alla riga 82:

execl("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
      "-f", "-t", type, "-o", opts, fsname, mnt, NULL);

Mi scuso per aver assunto che l'errore fosse dovuto al fatto che era correlato a Java o correlato a JNA o legato alle autorizzazioni. Modificherò il titolo e i tag della domanda per riflettere questo. (In mia difesa, il fusibile di errore stava tornando ("autorizzazione negata") certamente non era utile!)

Grazie per la tua assistenza EE. E Technomage, e di nuovo mi scuso per aver portato via una parte del tuo tempo a causa di quello che si è rivelato essere un errore sciocco.

Altri suggerimenti

Per quanto riguarda il problema negato dell'autorizzazione durante l'esecuzione del barattolo ... Sono sicuro che si tratta di Java Security Permission che sta succedendo qui per spiegare perché nessuna eccezione viene catturata quando si esegue in modalità Superuser, ma l'autorizzazione viene catturata l'eccezione quando si esegue in modalità non superuser .

Da quello che posso capire, Java ha un livello di sicurezza a differenza del programma C standard (ad eccezione di alcune librerie C che possono includere controlli di sicurezza, proprio come le librerie C ++ gestite .NET). Anche se provengono le funzioni di manipolazione dei file libfuse.so, può anche chiamare le chiamate del kernel di sistema Linux che possono essere eseguite all'interno dello spazio di memoria del kernel di sistema. Dal momento che ora è in esecuzione tramite Java dove Java deve caricare/mappare tutte le funzioni della libreria, comprese le chiamate di sistema in memoria. Se Java scopre che la mappa della memoria si verifica nello spazio di memoria del kernel di sistema anziché nello spazio di memoria dell'utente durante l'esecuzione, indirizzerà il proprio gestore di sicurezza per verificare lo stato utente corrente del programma Java.

Altrimenti, l'errore negato dell'autorizzazione può effettivamente provenire dal fusibile che cerca di accedere a un punto di montaggio che è limitato dall'utente normale che è un comportamento previsto. Quindi, questo non ha nulla a che fare con Java. Tuttavia, questo errore si verificherà anche nel programma C. Ma, dal tuo post e dai commenti, non lo dice molto.

Tuttavia, l'esecuzione del programma come root non ha causato l'errore. Ahimè, non sembrava fare nulla: ha appena detto "montaggio" e "montato" all'istante. Quindi passa al completamento, ma la chiamata Fuse_Main_Real ritorna all'istante. Il numero che restituisce è 1. Questo è alcuni progressi, ma il programma deve essere eseguito come utente normale come Hello.c Can.

D'altra parte, in base al tuo recente commento sopra, sembra che i campi del tuo puntatore funzione (callback) StructFuseOperations La struttura non funziona per "accendere" qualsiasi evento di fusibili che il fusibile può invocare.

Nota: Presumo che il programma Java principale "erroneo" mostri "montaggio" e "montato" e nient'altro tra loro che in realtà comporta una chiamata a fuse_main_real Metodo che non attiva alcun evento di fusibili ma un codice di ritorno 1 quando si esegue il programma in modalità Superuser. Non ho provato il codice nel post poiché non ho accesso al sistema operativo Linux in questo momento.

Aggiornare: Da questo punto in poi, la discussione sull'imbottitura di callback in una struttura JNA non è più valida dopo il recente aggiornamento del post effettuato da OP: https://stackoverflow.com/revisions/e28dc30b-9b71-4d65-8f8a-cfc7a3d5231e/view-source

Basato sul link dato, FUSE_OPERAZIONI RIFERIMENTO STRUCT, ti concentri solo su alcuni campi della struttura C come segue:

static struct fuse_operations hello_oper = {
    int (getattr*)(const char *path, struct stat *stbuf);
    /** some 12 skipped callbacks in between **/
    int (open*)(const char *path, struct fuse_file_info *fi);
    int (read*)(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
    /** some 10 skipped callbacks in between **/
    int (readdir*)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi);
    /** some 11 skipped callbacks in between **/
    unsigned int flag_nullpath_ok;
    unsigned int flag_reserved;
    /** some 2 skipped callbacks in between **/
};

Tuttavia, sembra che tu stia cercando di saltare alcuni campi di callback con l'imbottitura. Pertanto, per mantenere l'ordine di come vengono disposti i campi di callback fuse_operations struttura, applichi il Pointer Digita su ogni campo di callback che hai saltato. Tuttavia, assumendo un semplice Pointer Campo per questi campi di struttura saltata, hai rimosso le informazioni vitali sul callback per ogni campo: la sua firma di callback.

Dalla panoramica dell'API JNA:

Callbacks (puntatori della funzione)

JNA supporta la fornitura di callback Java al codice nativo. È necessario definire un'interfaccia che estende l'interfaccia di callback e definire un singolo metodo di callback con una firma che corrisponde al puntatore della funzione richiesto dal codice nativo. Il nome del metodo può essere qualcosa di diverso da "callback" solo se esiste un solo metodo nell'interfaccia che estende il callback o la classe che implementa il callback. Gli argomenti e il valore di restituzione seguono le stesse regole di un'invocazione della funzione diretta.

Se il callback restituisce una stringa o una stringa [], la memoria restituita sarà valida fino a quando l'oggetto restituito non sarà GC.

Di seguito è riportato ciò che viene suggerito nella panoramica:

// Original C code
struct _functions {
  int (*open)(const char*,int);
  int (*close)(int);
};

// Equivalent JNA mapping
public class Functions extends Structure {
  public static interface OpenFunc extends Callback {
    int invoke(String name, int options);
  }
  public static interface CloseFunc extends Callback {
    int invoke(int fd);
  }
  public OpenFunc open;
  public CloseFunc close;
}
...
Functions funcs = new Functions();
lib.init(funcs);
int fd = funcs.open.invoke("myfile", 0);
funcs.close.invoke(fd);

Tuttavia, non suggerisce un modo per saltare correttamente i callback con la tecnica di imbottitura in una struttura, specialmente quando è troppo grande e non vuoi definire ogni callback a cui non sei interessato. Forse, non è garantito e può causare comportamenti indefiniti come quello che stai affrontando ...

Probabilmente, invece di Pointer Per ogni campo di callback che si desidera riporre, puoi usare Callback campo, mantenere il nome del suo campo come nella specifica. Puoi o meno inizializzarlo con il valore nullo (non ci ho provato; forse potrebbe non funzionare).

Aggiornare:

Sembra che il mio suggerimento sopra possa funzionare in base alla soluzione JNA non correlata da parte di tgdavies in C callback con JNA fa schiantare JRE dove ha imbottito quei campi di callback non era interessato semplice Callback Digitare ma i nomi dei campi di richiamata corrispondenti sono rimasti intatti in sp_session_callbacks Struttura.

Immagino, a causa dell'impropro fuse_operations struttura, fuse_main_real non è in grado di accendere l'evento del fusibile previsto a cui sei interessato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top