¿Es posible falsificar una ruta específica para un proceso?

9

Estoy tratando de ejecutar ADB en un servidor Linux con múltiples usuarios donde no soy root (para jugar con mi emulador de Android). El adb daemon escribe sus registros en el archivo /tmp/adb.logque desafortunadamente parece estar codificado en ADB y esta situación no va a cambiar .

Por lo tanto, el BAD está fallando a plazo, dando el error obvio: cannot open '/tmp/adb.log': Permission denied. Este archivo es creado por otro usuario y /tmptiene un bit fijo. Si empiezo adb adb nodaemon serverhaciendo que escriba en stdout, no se producen errores (también configuro su puerto en un valor único para evitar conflictos).

Mi pregunta es: ¿hay alguna forma de hacer que ADB escriba en otro archivo que no sea /tmp/adb.log? En términos más generales, ¿hay alguna manera de crear una especie de enlace simbólico específico del proceso? Quiero redirigir todos los accesos a /tmp/adb.logun archivo, por ejemplo ~/tmp/adb.log.

Nuevamente, no soy root en el servidor, por lo tanto chroot, mount -o rbindy chmodno son opciones válidas. Si es posible, me gustaría no modificar las fuentes ADB, pero seguramente si no hay otras soluciones, lo haré.

PD: Para el caso específico de ADB que se puede recurrir a correr adb nodaemon servercon nohupy redirección de la salida, pero la cuestión general sigue siendo relevante.

gluk47
fuente
2
si. puede poner su proceso en un espacio de nombres de montaje privado y montar algún otro archivo /tmp/adb.log, o incluso montar su propio archivo privado /tmp. hacer man unsharey man namespacesy man nsenter.
mikeserv
1
@mikeserv genial, eso parece ser exactamente lo que necesito, ¡gracias! Si reformatea su comentario como respuesta, podré configurarlo como aceptado.
gluk47
O hay LD_PRELOADtrucos, aunque eso sería más complicado.
thrig
@thrig sí, pensé en LD_PRELOAD, pero, francamente, sería más fácil codificar /home/$USER/tmp/adb.logy reconstruir adb :)
gluk47

Respuestas:

5

Aquí hay un ejemplo muy simple de cómo usar util-linux's unsharepara poner un proceso en un espacio de nombres de montaje privado y darle una vista diferente del mismo sistema de archivos que tiene actualmente su padre:

{   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

Puede proporcionar a un proceso una vista privada de su sistema de archivos con la unshareutilidad en sistemas Linux actualizados, aunque la instalación de espacio de nombres de montaje en sí ha sido bastante madura para toda la serie de kernel 3.x. Puede ingresar espacios de nombres preexistentes de todo tipo con la nsenterutilidad desde el mismo paquete, y puede obtener más información con man.

mikeserv
fuente
Solo una pregunta: ¿soy yo o es una solución perfecta pero solo para el usuario root?
gluk47
@ gluk47: no tiene que ser así. puede hacer unsharetodo tipo de espacios de nombres, para incluir el espacio de nombres de usuario. y para que su usuario pueda ejecutar un espacio de nombres en el que tenga acceso a la raíz y cualquier cosa que haga dentro de lo que un usuario raíz pueda arruinar no afecta el espacio de nombres principal. en otras palabras, se puede incrustar un espacio de nombres de montaje dentro de un espacio de nombres de usuario. Realmente necesitas leer esas manpáginas. Se hace profundo. esto es precisamente cómo dockery cómo sytemd-nspawnfunciona.
mikeserv
He leído esas páginas de manual y ejemplos de Internet) Parece que necesito leerlas más, solo gracias por señalar esta tecnología, de alguna manera no estaba al tanto de ello.
gluk47
@ gluk47: no acepte respuestas por lealtad. Si bien el sentimiento es apreciado, ese tipo de cosas frustra el propósito de este lugar. acepta la respuesta que usas . Si esa no es esta, no acepte esta respuesta. Por cierto, el hecho de que un proceso se inicie como raíz no significa que deba seguir siendo un proceso raíz. existe la runuserutilidad con la que se puede usar unshare, y si de todos modos está abierto a escribir programas compilados, no hay razón para que no pueda usar unshare()syscall para hacer lo mismo, o incluso solo system()con suid binary.
mikeserv
Seguro que no aceptaría la respuesta si no fuera útil. Encuentro ambas respuestas relevantes y útiles, por lo que el sentimiento es la única razón distintiva para verificar una de estas respuestas :)
gluk47
11

LD_PRELOAD no es demasiado difícil y no necesitas ser root. Interponga su propia rutina C que se llama en lugar de la real open()en la biblioteca C. Su rutina verifica si el archivo a abrir es "/tmp/adb.log" y llama a la apertura real con un nombre de archivo diferente. Aquí está tu 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);
}

Compílelo gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.cy pruébelo poniendo algo en /tmp/myadb.logfuncionamiento LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log. Luego intente con LD_PRELOAD en adb.

meuh
fuente
Bueno, de hecho, su solución es la única que logré hacer que el trabajo fuera un usuario no root. No hice frente a unshare ( Operation not permitted). Espero que opensea ​​suficiente para manejarlo, pero finalmente, agregar unlinka este controlador no es difícil.
gluk47
Aww. Qué pena que no pueda verificar dos respuestas. Le prometí a mikeserv que verificaría su solución como respuesta, y de hecho es viable.
gluk47
2
no importa. ¡También aprendí sobre unshareeso, así que todos ganamos!
meuh
Después de un tiempo, gracias nuevamente por la muestra LD_PRELOAD. Desde que probé su código, estoy usando LD_PRELOAD en varias situaciones en las que ni siquiera lo pienso. Mi vida ha cambiado a mejor :)
gluk47
2
@ gluk47 Eso es lo maravilloso de Gnu / Linux: ¡nunca debes dejar de explorar! Hay tantas cosas buenas para descubrir y compartir.
meuh