É possível falsificar um caminho específico para um processo?

9

Estou tentando executar o ADB em um servidor Linux com vários usuários onde não sou root (para jogar com o meu emulador Android). O daemon adb grava seus logs no arquivo /tmp/adb.logque infelizmente parece estar codificado no ADB e essa situação não vai mudar .

Então, adb não está a prazo, dando o erro óbvio: cannot open '/tmp/adb.log': Permission denied. Este arquivo é criado por outro usuário e /tmppossui um adesivo. Se eu iniciar o adb adb nodaemon serverfazendo com que ele escreva para stdout, nenhum erro ocorrerá (eu também configurei sua porta com um valor exclusivo para evitar conflitos).

Minha pergunta é: existe alguma maneira de fazer o ADB gravar em outro arquivo /tmp/adb.log? De maneira mais geral, existe uma maneira de criar uma espécie de link simbólico específico do processo? Quero redirecionar todos os acessos de arquivos /tmp/adb.logpara, digamos, um arquivo ~/tmp/adb.log.

Novamente, eu não sou raiz no servidor, por isso chroot, mount -o rbinde chmodnão são opções válidas. Se possível, gostaria de não modificar as fontes do ADB, mas certamente se não houver outras soluções, farei isso.

PS Para o caso específico ADB posso recorrer a correr adb nodaemon servercom nohupe redirecionamento de saída, mas a questão geral ainda é relevante.

gluk47
fonte
2
sim. você pode colocar seu processo em um espaço para nome de montagem privado e montar outro arquivo /tmp/adb.logou até montar seu próprio privado /tmp. faça man unsharee man namespacese man nsenter.
mikeserv
1
@ MikeServ ótimo, que parece ser exatamente o que eu preciso, obrigado! Se você reformatar seu comentário como resposta, poderei defini-lo como aceito.
gluk47
Ou há LD_PRELOADtruques, embora isso seja mais complicado.
thrig
@thrig sim, eu pensei sobre LD_PRELOAD, mas, francamente, seria mais fácil para codificar /home/$USER/tmp/adb.loge reconstruir adb :)
gluk47

Respostas:

5

Aqui está um exemplo muito simples de como usar util-linux's unsharepara colocar um processo em um namespace de montagem privada e fornecer uma visão diferente do mesmo sistema de arquivos que seu pai atualmente possui:

{   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

Você pode dar a um processo uma visão privada de seu sistema de arquivos com o unshareutilitário em sistemas linux atualizados, embora o próprio recurso de namespace mount tenha sido bastante maduro para toda a série do kernel 3.x. Você pode inserir namespaces pré-existentes de todos os tipos com o nsenterutilitário do mesmo pacote e descobrir mais com man.

mikeserv
fonte
Apenas uma pergunta: sou eu ou é uma solução perfeita, mas apenas para o usuário root?
gluk47
@ gluk47 - não precisa ser. você pode unsharetodos os tipos de espaços para nome - para incluir o espaço para nome do usuário. e, assim, seu usuário pode executar um espaço para nome no qual possui acesso root e tudo o que fizer dentro do qual um usuário raiz possa estragar não afeta o espaço para nome pai. em outras palavras, um espaço para nome de montagem pode ser incorporado a um espaço para nome de usuário. você realmente precisa ler essas manpáginas. fica profundo. é exatamente assim dockere sytemd-nspawnfunciona.
mikeserv
Eu li essas páginas de manual e exemplos da Internet) Parece que preciso lê-los mais, apenas obrigado por apontar para essa tecnologia, de alguma forma não estava ciente disso.
gluk47
@ gluk47 - não aceite respostas por uma questão de lealdade. enquanto o sentimento é apreciado, esse tipo de coisa derrota o propósito desse lugar. aceite a resposta que você usa . se esse não for este, por favor não aceite esta resposta. a propósito, apenas porque um processo é iniciado como raiz, não significa que ele deve permanecer um processo raiz. existe o runuserutilitário que pode ser usado e unshare, se você estiver aberto para escrever programas compilados, não há razão para não usar o unshare()syscall para fazer o mesmo, ou mesmo apenas system()com o suid binário.
mikeserv
Eu certamente não aceitaria a resposta se não fosse útil. Acho que ambas as respostas relevantes e úteis, de modo que o sentimento é a única razão distinguindo para verificar uma dessas respostas :)
gluk47
11

LD_PRELOAD não é muito difícil e você não precisa ser root. Interponha sua própria rotina C, que é chamada em vez do real open()na biblioteca C. Sua rotina verifica se o arquivo a ser aberto é "/tmp/adb.log" e chama a abertura real com um nome de arquivo diferente. Aqui está o seu 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);
}

Compile-o gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.ce teste-o colocando algo em /tmp/myadb.logexecução LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log. Em seguida, tente o LD_PRELOAD no adb.

meuh
fonte
Bem, de fato, sua solução é a única que eu consegui fazer para ser um usuário não root. Não lidei com o compartilhamento ( Operation not permitted). Espero que isso openseja suficiente, mas, finalmente, adicionar unlinka esse manipulador não é difícil.
gluk47
Aww. Que pena não poder verificar duas respostas. Prometi ao mikeserv verificar sua solução como resposta, e é realmente viável.
gluk47
2
deixa pra lá. Eu também aprendi unshare, então todos nós ganhamos!
meuh
Depois de algum tempo, obrigado novamente pela amostra LD_PRELOAD. Desde que experimentei seu código, estou usando LD_PRELOAD em várias situações em que nem sequer pensava nisso. Minha vida mudou para melhor :)
gluk47
2
@ gluk47 É o que há de mais maravilhoso no Gnu / Linux: você nunca precisa parar de explorar! Há tantas coisas boas para descobrir e compartilhar.
Meuhttp