Usar o sudo é a maneira mais fácil. Mas requer a cooperação do administrador do sistema para configurar.
Se você quiser fazer isso sem privilégios especiais, você pode usar um executável setuid , mas precisa ser cuidadoso para não permitir que o chamador faça mais do que você pretendia.
A maioria das variantes do Unix proíbe scripts de shell setuid ; por exemplo, no Linux, o kernel sempre ignora o bit setuid em um script. Então você precisa de um wrapper em código nativo.
Tome cuidado para que o wrapper remova tudo o que permitir ao usuário invocá-lo para executar o código. Por exemplo, variáveis de ambiente da lista de permissões; qualquer coisa que não seja conhecida como segura (por exemplo, TERM
) deve ser usada.
Aqui está uma proposta para um wrapper setuid que retenha apenas a variável de ambiente TERM
. Cuidado com o fato de eu não ter revisado ou testado; feedback bem-vindo.
/* Compilation command:
c99 -DTARGET='"/absolute/path/to/script"' -o setuid-wrapper setuid-wrapper.c
*/
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void die(const char *argv0, const char *obj, const char *msg) {
if (msg == NULL) msg = strerror(errno);
fprintf(stderr, "%s: %s: %s\n", argv0, obj, msg);
exit(127);
}
int env_want(const char *entry) {
size_t n;
for (n = 0; entry[n]; n++) {
if (entry[n] == '=') {
break;
} else if (!(isalnum(entry[n]) || entry[n] == '_')) {
return 0;
}
}
if ((n == 7 && !strncmp(entry, "DISPLAY", n)) ||
(n == 10 && !strncmp(entry, "XAUTHORITY", n))) {
return 1;
}
if ((n == 4 && !strncmp(entry, "LANG", n)) ||
(n >= 3 && !strncmp(entry, "LC_", 3)) ||
(n == 2 && !strncmp(entry, "TZ", n))) {
return !strpbrk(entry, "/%");
}
return 0;
}
int main(int argc, char *argv[], char **environ) {
size_t i, j;
const char *program_name = argv[0];
if (program_name == NULL) program_name = "setuid-wrapper";
/* Drop privileges */
if (setgid(getegid())) die(program_name, "setgid", NULL);
/*if (setgroups(0, NULL)) die(program_name, "setgroups", NULL);*/
if (setuid(geteuid())) die(program_name, "setuid", NULL);
/* Sanitize the environment */
for (i = j = 0; environ[i]; i++) {
if (env_want(environ[i])) {
environ[j] = environ[i];
j++;
}
}
environ[j] = NULL;
/* Execute the command */
execle(TARGET, TARGET, NULL, environ);
die(program_name, TARGET, NULL);
}
Observe que o programa de destino será executado com os grupos suplementares do chamador, porque você não pode eliminar grupos suplementares sem ser root.
Uma abordagem alternativa seria executar um servidor (por exemplo, um servidor da Web) e invocar seu script desse servidor. Existem vários pequenos servidores HTTP que suportam scripts CGI. A principal vantagem dessa abordagem é que é mais fácil proteger, porque o servidor está em execução em um contexto escolhido pelo destino (a parte em cujo contexto de segurança o código é executado), não pelo cliente.
Uma variante dessa abordagem seria acionar a execução do script por meio de um sistema de arquivos FUSE . Por exemplo, monte um diretório scriptfs contendo seu script com a opção allow_other
mount. Isso requer que a opção user_allow_other
seja definida em /etc/fuse.conf
.