LINUX.ORG.RU

История изменений

Исправление 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;
}
getuid() - это uid вызывателя

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;
}
getuid() - это uid вызывателя

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() то группы у него остаются те же что были до переключения.