Est-il possible de simuler un chemin spécifique pour un processus?

9

J'essaie d'exécuter ADB sur un serveur Linux avec plusieurs utilisateurs où je ne suis pas root (pour jouer avec mon émulateur Android). Le démon adb écrit ses journaux dans le fichier /tmp/adb.logqui semble malheureusement être codé en dur dans ADB et cette situation ne va pas changer .

Donc, adb ne parvient pas à terme, donnant l'erreur évidente: cannot open '/tmp/adb.log': Permission denied. Ce fichier est créé par un autre utilisateur et /tmppossède un bit collant. Si je démarre adb en le adb nodaemon serverfaisant écrire sur stdout, aucune erreur ne se produit (j'ai également configuré son port sur une valeur unique pour éviter les conflits).

Ma question est: existe-t-il un moyen de faire en sorte que la BAD écrive dans un autre fichier que /tmp/adb.log? Plus généralement, existe-t-il un moyen de créer une sorte de lien symbolique spécifique au processus? Je veux rediriger tous les accès aux fichiers /tmp/adb.logvers, en disant, un fichier ~/tmp/adb.log.

Encore une fois, je ne suis pas root sur le serveur, donc chroot, mount -o rbindet ce chmodne sont pas des options valides. Si possible, je ne voudrais pas modifier les sources ADB, mais sûrement s'il n'y a pas d'autres solutions, je le ferai.

PS Pour le cas ADB spécifique, je peux recourir à la redirection adb nodaemon serveravec nohupet avec la sortie, mais la question générale est toujours d'actualité.

gluk47
la source
2
Oui. vous pouvez placer votre processus dans un espace de noms de montage privé, et monter un autre fichier /tmp/adb.log, ou même monter son propre /tmptout à fait privé . faire man unshareet man namespaceset man nsenter.
mikeserv
1
@mikeserv super, cela semble être exactement ce dont j'ai besoin, merci! Si vous reformatez votre commentaire en tant que réponse, je pourrai le définir comme accepté.
gluk47
Ou il y a des LD_PRELOADastuces, mais ce serait plus compliqué.
thrig
@thrig ouais, je pensais à LD_PRELOAD, mais franchement, il serait plus facile de coder en dur /home/$USER/tmp/adb.loget de reconstruire adb :)
gluk47

Réponses:

5

Voici un exemple très simple d'utilisation de util-linux's unsharepour placer un processus dans un espace de noms de montage privé et lui donner une vue différente du même système de fichiers que son parent possède actuellement:

{   cd /tmp                      #usually a safe place for this stuff
    echo hey   >file             #some
    echo there >file2            #evidence
    sudo unshare -m sh -c '      #unshare requires root by default
         mount -B file2 file     #bind mount there over hey
         cat file                #show it
         kill -TSTP "$$"         #suspend root shell and switch back to parent
         umount file             #unbind there
         cat file'               #show it
    cat file                     #root shell just suspended
    fg                           #bring it back
    cat file2                    #round it off
}

there                            #root shell
hey                              #root shell suspended
hey                              #root shell restored
there                            #rounded

Vous pouvez donner à un processus une vue privée de son système de fichiers avec l' unshareutilitaire sur les systèmes Linux à jour, bien que la fonction d'espace de noms de montage elle-même soit assez mature pour toute la série de noyaux 3.x. Vous pouvez entrer des espaces de noms préexistants de toutes sortes avec l' nsenterutilitaire du même package, et vous pouvez en savoir plus avec man.

mikeserv
la source
Une seule question: est-ce moi ou est-ce une solution parfaite mais uniquement pour l'utilisateur root?
gluk47
@ gluk47 - ce n'est pas obligatoire. vous pouvez unsharetoutes sortes d'espaces de noms - pour inclure l'espace de noms de l'utilisateur. et ainsi votre utilisateur peut exécuter un espace de noms dans lequel il a un accès root et tout ce qu'il fait dans ce qu'un utilisateur root pourrait bousiller n'affecte pas l'espace de noms parent. en d'autres termes, un espace de noms de montage peut être incorporé dans un espace de noms d'utilisateurs. vous avez vraiment besoin de lire ces manpages. ça devient profond. c'est précisément comment dockeret sytemd-nspawntravailler.
mikeserv
J'ai lu ces pages de manuel et des exemples sur Internet) Il semble juste que je doive les lire davantage, je vous remercie simplement d'avoir signalé cette technologie, je ne le savais pas du tout.
gluk47
@ gluk47 - n'acceptez pas les réponses par souci de fidélité. alors que le sentiment est apprécié, ce genre de choses va à l'encontre de l'objectif de cet endroit. acceptez la réponse que vous utilisez . si celui-ci n'est pas celui-ci, veuillez ne pas accepter cette réponse. au fait, ce n'est pas parce qu'un processus est lancé en tant que root qu'il doit rester un processus root. il y a l' runuserutilitaire qui peut être utilisé avec unshare, et si vous êtes prêt à écrire des programmes compilés de toute façon, il n'y a aucune raison que vous ne puissiez pas utiliser le unshare()syscall pour faire la même chose, ou même simplement system()avec suid binary.
mikeserv
Je n'accepterais certainement pas la réponse si elle n'était pas utile. Je trouve les deux réponses pertinentes et utiles, donc le sentiment est la seule raison distinctive de vérifier l'une de ces réponses :)
gluk47
11

LD_PRELOAD n'est pas trop difficile et vous n'avez pas besoin d'être root. Interposez votre propre routine C qui est appelée à la place du réel open()dans la bibliothèque C. Votre routine vérifie si le fichier à ouvrir est "/tmp/adb.log" et appelle la vraie ouverture avec un nom de fichier différent. Voici votre shim_open.c:

/*
 * capture calls to a routine and replace with your code
 * gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.c
 * LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log
 */
#define _FCNTL_H 1 /* hack for open() prototype */
#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#define OLDNAME "/tmp/adb.log"
#define NEWNAME "/tmp/myadb.log"

int open(const char *pathname, int flags, mode_t mode){
    static int (*real_open)(const char *pathname, int flags, mode_t mode) = NULL;

    if (!real_open) {
        real_open = dlsym(RTLD_NEXT, "open");
        char *error = dlerror();
        if (error != NULL) {
            fprintf(stderr, "%s\n", error);
            exit(1);
        }
    }
    if (strcmp(pathname,OLDNAME)==0) pathname = NEWNAME;
    fprintf(stderr, "opening: %s\n", pathname);
    return real_open(pathname, flags, mode);
}

Compilez-le avec gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.cet testez-le en y mettant quelque chose /tmp/myadb.loget en l'exécutant LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log. Essayez ensuite LD_PRELOAD sur adb.

meuh
la source
Eh bien, en effet, votre solution est la seule que j'ai réussi à faire fonctionner en étant un utilisateur non root. Je n'ai pas fait face à unshare ( Operation not permitted). J'espère que cela opensuffit à gérer, mais enfin, ajouter unlinkà ce gestionnaire n'est pas difficile.
gluk47
Aww. Quel dommage que je ne puisse pas vérifier deux réponses. J'ai promis à mikeserv de vérifier sa solution comme réponse, et c'est effectivement une solution viable.
gluk47
2
ça ne fait rien. J'en ai aussi entendu parler unshare, alors nous gagnons tous!
meuh
Après un certain temps, merci encore pour l'exemple LD_PRELOAD. Depuis que j'ai essayé votre code, j'utilise LD_PRELOAD dans diverses situations où je n'y pensais même pas. Ma vie a changé pour le mieux :)
gluk47
2
@ gluk47 C'est ce qui est si merveilleux avec Gnu / Linux: vous n'avez jamais besoin d'arrêter d'explorer! Il y a tellement de bonnes choses à découvrir et à partager.
meuh