¿Cómo puedo saber si se agotó el tiempo de espera de mi privilegio sudoer?

20

Estoy trabajando en un script que ejecuta un comando como sudo y hace eco de una línea de texto SOLO si mis privilegios de sudo se han agotado, por lo que solo si ejecutar un comando con sudo requeriría que mi usuario (no root) vuelva a escribir su contraseña.

¿Cómo verifico eso? Tenga en cuenta que $(id -u)incluso cuando se ejecuta como sudo devolverá mi identificación de usuario actual, por lo que no se puede verificar que coincida con 0 ...

Necesito un método que verifique esto en silencio.

TonyMorello
fuente

Respuestas:

28

Use la opción -npara verificar si todavía tiene privilegios; de man sudo:

-n , --no interactivo

Evite pedirle al usuario que ingrese cualquier tipo. Si se requiere una contraseña para que se ejecute el comando, sudo mostrará un mensaje de error y saldrá.

Por ejemplo,

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

Tenga en cuenta que es posible que los privilegios caduquen entre consultarlos sudo -n truey usarlos. Es posible que desee probar directamente con sudo -n command...y, en caso de falla, mostrar un mensaje y posiblemente volver a intentar la ejecución sudointeractiva.

Editar: Ver también el comentario de ruakh a continuación.

AlexP
fuente
Gracias. He intentado algo similar antes, pero no pude hacerlo funcionar como quería.
TonyMorello
3
Re: "Tenga en cuenta que es posible que los privilegios caduquen entre consultarlos sudo -n truey usarlos realmente": la documentación es un poco vaga en este punto, pero creo que ejecutar un sudocomando, incluso solo sudo -n true, restablecerá el tiempo de espera reloj. De cualquier manera, -vestá documentado explícitamente como tal y, de todos modos , sudo -n -ves probablemente más apropiado que sudo -n truepara este propósito.
ruakh
Si bien esto será hecho en silencio, se registro de errores a diario del sistema si no hay privilegios: hostname sudo[8870]: username : a password is required ; TTY=pts/0 ; PWD=/home/username ; USER=root ; COMMAND=/usr/bin/true. Si lo usa en bash prompt, por ejemplo, generará muchos mensajes de error.
Rogach
Y si hay privilegios, habrá un registro de depuración de pam_unix y la ejecución del comando sudo.
Rogach
8

Correr:

sudo -nv

Si sus privilegios de sudo han expirado, esto saldrá con un código de salida de 1 y generará:

sudo: a password is required

Si tiene credenciales en caché válidas, este comando tendrá éxito y no generará nada.

Entonces, para ponerlo todo junto, aquí hay un scriptlet que comprobará en silencio si tiene credenciales válidas en caché:

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

Como se mencionó en otras respuestas / comentarios, la -vopción ("validar") para sudo renueva silenciosamente las credenciales almacenadas en caché si hay alguna otra solicitud de autenticación para generar credenciales almacenadas en caché, y la -nopción ("no interactiva") evita que sudo genere cualquier solicitud interactiva, como la solicitud de autenticación.

jayhendren
fuente
Esta es una buena solución ... Lo intenté antes, pero la respuesta de AlexP hace exactamente lo que necesitaba ... Creo que no
entendí bien el
1

sudo -nvfunciona bien, pero contamina los registros del sistema con errores de sudo e información de autenticación de pam. Necesitaba verificar los privilegios de sudo para mi solicitud de bash, por lo que se ejecutó con bastante frecuencia y mis registros consistían casi solo en este ruido.

Es posible analizar el archivo de marca de tiempo de sudo directamente: escribí una pequeña utilidad C para él:

/* 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;
}
Rogach
fuente