История изменений
Исправление firkax, (текущая версия) :
как оказалось, если в скрипте делать exec /bin/bash, то оно также откатывает setuid, даже если в самом скрипте указано -p
Думаю надо exec /bin/bash -p
чтоб исправить это. Если не поможет или не хочешь, то способ ниже, но он длиннее.
пришлось прописать setuid(0); как мне теперь на C узнать какой юзер запустил программу-обёртку и как передать это в качестве аргумента скрипту?
Например вот:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
int main(void) {
uid_t u1,u2,u3,u4,u5;
u1 = getuid();
u2 = geteuid();
getresuid(&u3,&u4,&u5);
printf("getuid() = %ld (caller uid)\n", (long)u1);
printf("geteuid() = %ld (effective uid from suid flag)\n", (long)u2);
printf("getresuid() = %ld %ld %ld (caller, effective, saved)\n", (long)u3, (long)u4, (long)u5);
return 0;
}
geteuid() - это текущий действующий uid
третье число из getresuid() - это uid, на который было сделано переключение setuid-битом (он нужен для того чтоб setuid прога могла сначала понизить себе права до вызывателя а затем опять активировать повышенные, которые в нём запомнены)
setuid(), вызванный рутом, меняет все три числа. Чтобы менять только второе (не затирая информацию о setuid-бинарнике) есть сисколл seteuid().
Тебе проще всего вызвать getuid() а затем сделать setuid(0).
Передать аргументы, которых больше чем было изначально - немного муторно, т.к. придётся делать новый массив для них (в старом места под ещё один нет). Можно так:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
char ** merge_args(int argc, char **argv, char const *new_progname, size_t add_argc, char **add_argv) {
size_t a, b;
int i;
char **args;
i = a = ((size_t)argc) + add_argc + 2;
if(argc<0 || i<=0 || a!=(size_t)i || a<2 || a-2<add_argc || (int)(a-2-add_argc)!=argc) { fprintf(stderr, "bad number of arguments (hack attempt)\n"); return NULL; }
b = a*sizeof(char*);
if(b/sizeof(char*)!=a || !(args = malloc(b))) { fprintf(stderr, "out of memory (too many arguments?)\n"); return NULL; }
args[0] = (char*)new_progname;
b = 1;
if(add_argc) {
memcpy(args+b, add_argv, add_argc*sizeof(char*));
b += add_argc;
}
if(argc>=2) {
memcpy(args+b, argv+1, ((size_t)(argc-1))*sizeof(char*));
b += (argc-1);
}
args[b] = NULL;
return args;
}
extern char **environ;
int main(int argc, char **argv) {
char uidstr[50];
char *add_argv[1];
char **args;
snprintf(uidstr, sizeof(uidstr), "%ld", (long)getuid());
setuid(0);
add_argv[0] = uidstr;
if(!(args = merge_args(argc, argv, "/usr/local/sbin/wrapped_script.sh", 1, add_argv))) return -1;
execve("/usr/local/sbin/wrapped_script.sh", args, environ);
fprintf(stderr, "execve(/usr/local/sbin/wrapped_script.sh) error %d (%s)\n", errno, strerror(errno));
return -1;
}
но программа не хочет запускаться от user, пока я не дам others права на чтение и исполнение, почему так?
По хорошему достаточно ---S--x---
(я проверил).
Наверно потому что тот, кто пытается запустить, не состоит в группе «user». На всякий случай: настройки групп из /etc/passwd и /etc/group применяются только при логине и программами типа su, которые вручную их читают и применяют. Если ты переключался на юзера с помощью setuid-бинарника или с помощью сисколла setuid() то группы у него остаются те же что были до переключения. Если хочешь вручную прописывать группы то это делается через setgid() для основной и setgroups() для списка дополнительных.
Исходная версия firkax, :
как оказалось, если в скрипте делать exec /bin/bash, то оно также откатывает setuid, даже если в самом скрипте указано -p
Думаю надо exec /bin/bash -p
чтоб исправить это. Если не поможет или не хочешь, то способ ниже, но он длиннее.
пришлось прописать setuid(0); как мне теперь на C узнать какой юзер запустил программу-обёртку и как передать это в качестве аргумента скрипту?
Например вот:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
int main(void) {
uid_t u1,u2,u3,u4,u5;
u1 = getuid();
u2 = geteuid();
getresuid(&u3,&u4,&u5);
printf("getuid() = %ld (caller uid)\n", (long)u1);
printf("geteuid() = %ld (effective uid from suid flag)\n", (long)u2);
printf("getresuid() = %ld %ld %ld (caller, effective, saved)\n", (long)u3, (long)u4, (long)u5);
return 0;
}
geteuid() - это текущий действующий uid
третье число из getresuid() - это uid, на который было сделано переключение setuid-битом (он нужен для того чтоб setuid прога могла сначала понизить себе права до вызывателя а затем опять активировать повышенные, которые в нём запомнены)
setuid(), вызванный рутом, меняет все три числа. Чтобы менять только второе (не затирая информацию о setuid-бинарнике) есть сисколл seteuid().
Тебе проще всего вызвать getuid() а затем сделать setuid(0).
Передать аргументы, которых больше чем было изначально - немного муторно, т.к. придётся делать новый массив для них (в старом места под ещё один нет). Можно так:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
char ** merge_args(int argc, char **argv, char const *new_progname, size_t add_argc, char **add_argv) {
size_t a, b;
int i;
char **args;
i = a = ((size_t)argc) + add_argc + 2;
if(argc<0 || i<=0 || a!=(size_t)i || a<2 || a-2<add_argc || (int)(a-2-add_argc)!=argc) { fprintf(stderr, "bad number of arguments (hack attempt)\n"); return NULL; }
b = a*sizeof(char*);
if(b/sizeof(char*)!=a || !(args = malloc(b))) { fprintf(stderr, "out of memory (too many arguments?)\n"); return NULL; }
args[0] = (char*)new_progname;
b = 1;
if(add_argc) {
memcpy(args+b, add_argv, add_argc*sizeof(char*));
b += add_argc;
}
if(argc>=2) {
memcpy(args+b, argv+1, ((size_t)(argc-1))*sizeof(char*));
b += (argc-1);
}
args[b] = NULL;
return args;
}
extern char **environ;
int main(int argc, char **argv) {
char uidstr[50];
char *add_argv[1];
char **args;
snprintf(uidstr, sizeof(uidstr), "%ld", (long)getuid());
setuid(0);
add_argv[0] = uidstr;
if(!(args = merge_args(argc, argv, "/usr/local/sbin/wrapped_script.sh", 1, add_argv))) return -1;
execve("/usr/local/sbin/wrapped_script.sh", args, environ);
fprintf(stderr, "execve(/usr/local/sbin/wrapped_script.sh) error %d (%s)\n", errno, strerror(errno));
return -1;
}
но программа не хочет запускаться от user, пока я не дам others права на чтение и исполнение, почему так?
По хорошему достаточно ---S--x---
(я проверил).
Наверно потому что тот, кто пытается запустить, не состоит в группе «user». На всякий случай: настройки групп из /etc/passwd и /etc/group применяются только при логине и программами типа su, которые вручную их читают и применяют. Если ты переключался на юзера с помощью setuid-бинарника или с помощью сисколла setuid() то группы у него остаются те же что были до переключения.