Como descubro se o privilégio sudoer expirou?

20

Estou trabalhando em um script que executa um comando como sudo e ecoa uma linha de texto SOMENTE se meus privilégios sudo tiverem expirado, portanto, somente se executar um comando com sudo exigiria que meu usuário (não root) digitasse senha novamente.

Como posso verificar isso? Lembre-se que $(id -u) , mesmo quando rodando como sudo, retornará meu ID de usuário atual, então não é possível checar se ele está com 0 ...

Eu preciso de um método que verifique isso tranquilamente.

    
por TonyMorello 15.12.2016 / 19:42

3 respostas

28

Use a opção -n para verificar se você ainda tem privilégios; de man sudo :

-n, --non-interactive

Avoid prompting the user for input of any kind. If a password is required for the command to run, sudo will display an error message and exit.

Por exemplo,

sudo -n true 2>/dev/null && echo Privileges active || echo Privileges inactive

Esteja ciente de que é possível que os privilégios expirem entre verificar com sudo -n true e realmente usá-los. Você pode tentar diretamente com sudo -n command... e, em caso de falha, exibir uma mensagem e possivelmente tentar novamente executando sudo interativamente.

Editar: Veja também o comentário de ruakh abaixo.

    
por 15.12.2016 / 19:57
8

Executar:

sudo -nv

Se seus privilégios sudo tiverem expirado, isso sairá com um código de saída 1 e uma saída:

sudo: a password is required

Se você tiver credenciais válidas armazenadas em cache, esse comando será bem-sucedido e não produzirá nada.

Então, para juntar tudo, aqui está um scriptlet que irá silenciosamente verificar se você tem credenciais válidas em cache:

if sudo -nv 2>/dev/null; then
  echo "no sudo password required"
else
  echo "sudo password expired"
fi

Como outras respostas / comentários mencionados, a opção -v ("validate") para sudo renova silenciosamente as credenciais em cache se houver algum ou mais prompts de autenticação para gerar credenciais armazenadas em cache, e a -n option (" non-interactive ") impede que o sudo gere quaisquer comandos interativos, como o prompt de autenticação.

    
por 15.12.2016 / 19:58
1

sudo -nv funciona bem, mas polui os logs do sistema com erros de sudo e informações de autenticação de pam. Eu precisava verificar os privilégios sudo para o meu prompt bash, então ele foi executado com bastante frequência e meus logs consistiam quase que apenas nesse ruído.

É possível analisar diretamente o arquivo sudo timestamp - eu escrevi um pequeno utilitário C para ele:

/* compile and set permissions: */
/* $ gcc checksudo.c -o checksudo -std=gnu99 -O2 */
/* $ chown root:root checksudo */
/* $ chmod +s checksudo */

#define USERNAME "replace-with-your-username"
#define TIMEOUT 5

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>

void timespec_diff(struct timespec *start, struct timespec *stop, struct timespec *result) {
    if ((stop->tv_nsec - start->tv_nsec) < 0) {
        result->tv_sec = stop->tv_sec - start->tv_sec - 1;
        result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
    } else {
        result->tv_sec = stop->tv_sec - start->tv_sec;
        result->tv_nsec = stop->tv_nsec - start->tv_nsec;
    }
    return;
}

int main(int argc, char** argv) {
  if (geteuid() != 0) {
    printf("uid is not 0 - checksudo must be owned by uid 0 and have the setuid bit set\n");
    return 2;
  }

  struct timespec current_time;
  if (clock_gettime(CLOCK_BOOTTIME, &current_time) != 0) {
    printf("Unable to get current time: %s\n", strerror(errno));
    return 2;
  }

  struct stat ttypath_stat;
  if (stat(ttyname(0), &ttypath_stat) != 0) {
    printf("Unable to stat current tty: %s\n", strerror(errno));
    return 2;
  }

  FILE* timestamp_fd = fopen("/var/run/sudo/ts/" USERNAME, "rb");
  if (timestamp_fd == NULL) {
    printf("Unable to open sudo timestamp file: %s\n", strerror(errno));
    return 2;
  }

  long offset = 0;
  int found = 0;

  while (1) {
    if (fseek(timestamp_fd, offset, SEEK_SET) != 0) {
      printf("Failed to seek timestamp file: %s\n", strerror(errno));
      return 2;
    }
    unsigned short timestamp_entry_header[4];
    if (feof(timestamp_fd)) {
      printf("matching timestamp not found\n");
      return 2;
    }
    if (fread(&timestamp_entry_header, sizeof(unsigned short), 4, timestamp_fd) < 4) {
      break;
    }
    if (ferror(timestamp_fd)) {
      printf("IO error when reading timestamp file\n");
      return 2;
    }

    // read tty device id
    if (timestamp_entry_header[2] == 2 && timestamp_entry_header[3] == 0) {
      if (fseek(timestamp_fd, offset + 32, SEEK_SET) != 0) {
        printf("Failed to seek timestamp file: %s\n", strerror(errno));
        return 2;
      }
      dev_t tty_dev_id;
      if (fread(&tty_dev_id, sizeof(dev_t), 1, timestamp_fd) < 1) {
        printf("EOF when reading tty device id\n");
        return 2;
      }
      if (tty_dev_id == ttypath_stat.st_rdev) {
        // read timestamp
        if (fseek(timestamp_fd, offset + 16, SEEK_SET) != 0) {
          printf("Failed to seek timestamp file: %s\n", strerror(errno));
          return 2;
        }
        struct timespec sudo_time;
        if (fread(&sudo_time, sizeof(struct timespec), 1, timestamp_fd) < 1) {
          printf("EOF when reading timestamp\n");
          return 2;
        }

        struct timespec time_since_sudo;
        timespec_diff(&sudo_time, &current_time, &time_since_sudo);
        found = time_since_sudo.tv_sec < TIMEOUT * 60;
        break;
      }
    }

    offset += timestamp_entry_header[1];
  }

  fclose(timestamp_fd);

  return !found;
}
    
por 22.12.2016 / 11:43

Tags