Comment savoir si mon privilège sudoer a expiré?

20

Je travaille sur un script qui exécute une commande en tant que sudo et fait écho à une ligne de texte UNIQUEMENT si mes privilèges sudo ont expiré, donc uniquement si l'exécution d'une commande avec sudo nécessiterait que mon utilisateur (et non root) saisisse à nouveau son mot de passe.

Comment vérifier cela? N'oubliez pas que $(id -u)même lors de l'exécution en tant que sudo, mon ID utilisateur actuel sera renvoyé, ce qui ne peut pas être vérifié pour le faire correspondre à 0 ...

J'ai besoin d'une méthode qui vérifierait cela tranquillement.

TonyMorello
la source

Réponses:

28

Utilisez l'option -npour vérifier si vous avez toujours des privilèges; de man sudo:

-n , - non interactif

Évitez d'inviter l'utilisateur à saisir quelque chose que ce soit. Si un mot de passe est requis pour l'exécution de la commande, sudo affichera un message d'erreur et quittera.

Par exemple,

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

Sachez qu'il est possible que les privilèges expirent entre la vérification sudo -n trueet leur utilisation effective. Vous voudrez peut-être essayer directement avec sudo -n command...et en cas d'échec, afficher un message et éventuellement réessayer de s'exécuter de manière sudointeractive.

Edit: Voir aussi le commentaire de ruakh ci-dessous.

AlexP
la source
Merci, j'ai déjà essayé quelque chose de similaire, mais je n'arrivais pas à le faire fonctionner comme je le voulais.
TonyMorello
3
Re: "Sachez qu'il est possible que les privilèges expirent entre la vérification sudo -n trueet leur utilisation": La documentation est un peu vague sur ce point, mais je pense que l'exécution d'une sudocommande, même juste sudo -n true, rétablira le délai d'attente l'horloge. Quoi qu'il en soit, cela -vest explicitement documenté comme tel, et sudo -n -vest probablement plus approprié que sudo -n truedans ce but de toute façon.
ruakh
Bien que ce sera en effet le silence, il consignera erreur journal système s'il n'y a pas de privilèges: hostname sudo[8870]: username : a password is required ; TTY=pts/0 ; PWD=/home/username ; USER=root ; COMMAND=/usr/bin/true. Si vous l'utilisez dans l'invite bash, par exemple, cela entraînera de nombreux messages d'erreur.
Rogach
Et s'il y a des privilèges, il y aura une journalisation du débogage à partir de l'exécution de la commande pam_unix et sudo.
Rogach
8

Courir:

sudo -nv

Si vos privilèges sudo ont expiré, cela se terminera avec un code de sortie de 1 et affichera:

sudo: a password is required

Si vous disposez d'informations d'identification en cache valides, cette commande réussira et ne produira rien.

Donc, pour mettre tout cela ensemble, voici un scriptlet qui vérifiera en silence si vous avez des informations d'identification mises en cache valides:

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

Comme d'autres réponses / commentaires mentionnés, l' -voption ("valider") pour sudo renouvelle silencieusement les informations d'identification mises en cache s'il existe des invites d'authentification afin de générer des informations d'identification mises en cache, et l' -noption ("non interactive") empêche sudo de générer toutes les invites interactives, telles que l'invite d'authentification.

jayhendren
la source
C'est une bonne solution ... J'ai déjà essayé mais la réponse d'AlexP fait exactement ce dont j'avais besoin ... Je pense que j'ai mal compris le paramètre -n avant
TonyMorello
1

sudo -nvfonctionne bien, mais pollue les journaux système avec des erreurs sudo et des informations d'authentification pam. J'avais besoin de vérifier les privilèges sudo pour mon invite bash, donc elle a été exécutée assez souvent et mes journaux ne comprenaient presque que ce bruit.

Il est possible d'analyser directement le fichier d'horodatage sudo - j'ai écrit un petit utilitaire C pour cela:

/* 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
la source