Wie finde ich heraus, ob mein Sudoer-Privileg abgelaufen ist?

20

Ich arbeite an einem Skript, das einen Befehl als sudo ausführt und eine Textzeile NUR dann wiedergibt, wenn meine sudo-Berechtigungen abgelaufen sind. Wenn ein Befehl mit sudo ausgeführt wird, muss mein Benutzer (nicht root) das Kennwort erneut eingeben.

Wie überprüfe ich das? Beachten Sie, dass $(id -u)meine aktuelle Benutzer-ID auch dann zurückgegeben wird, wenn sie als sudo ausgeführt wird, sodass nicht überprüft werden kann, ob sie mit 0 übereinstimmt ...

Ich brauche eine Methode, die dies leise überprüfen würde.

TonyMorello
quelle

Antworten:

28

Verwenden Sie die Option -n, um zu überprüfen, ob Sie noch Berechtigungen haben. von man sudo:

-n , --nicht interaktiv

Vermeiden Sie es, den Benutzer zur Eingabe jeglicher Art aufzufordern. Wenn für die Ausführung des Befehls ein Kennwort erforderlich ist, zeigt sudo eine Fehlermeldung an und beendet das Programm.

Beispielsweise,

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

Beachten Sie, dass die Berechtigungen zwischen der Überprüfung sudo -n trueund der tatsächlichen Verwendung verfallen können. Möglicherweise möchten Sie direkt versuchen sudo -n command..., eine Meldung anzuzeigen und im Fehlerfall die sudointeraktive Ausführung zu wiederholen .

Bearbeiten: Siehe auch den Kommentar von ruakh unten.

AlexP
quelle
Vielen Dank, dass ich etwas Ähnliches versucht habe, aber ich konnte es einfach nicht so zum Laufen bringen, wie ich es wollte.
TonyMorello
3
Betreff: "Seien Sie sich bewusst, dass die Berechtigungen zwischen der Überprüfung sudo -n trueund der tatsächlichen Verwendung verfallen können.": Die Dokumentation ist in diesem Punkt etwas vage, aber ich denke, dass das Ausführen eines sudoBefehls, auch nur sudo -n true, das Timeout zurücksetzt Uhr. In beiden Fällen -vwird dies ausdrücklich dokumentiert und sudo -n -vist wahrscheinlich sudo -n truesowieso geeigneter als für diesen Zweck.
Ruakh
Während dies in der Tat schweigen, wird es Fehler auf Systemjournal einzuloggen , wenn es keine Privilegien sind: hostname sudo[8870]: username : a password is required ; TTY=pts/0 ; PWD=/home/username ; USER=root ; COMMAND=/usr/bin/true. Wenn Sie es beispielsweise in der Bash-Eingabeaufforderung verwenden, werden viele Fehlermeldungen ausgegeben.
Rogach
Und wenn es Berechtigungen gibt, wird eine Debug-Protokollierung von der Ausführung von pam_unix- und sudo-Befehlen durchgeführt.
Rogach
8

Lauf:

sudo -nv

Wenn Ihre sudo-Berechtigungen abgelaufen sind, wird dies mit einem Exit-Code von 1 beendet und ausgegeben:

sudo: a password is required

Wenn Sie gültige zwischengespeicherte Anmeldeinformationen haben, ist dieser Befehl erfolgreich und gibt nichts aus.

Also, sie alle zusammen zu setzen, ist hier ein scriptlet das wird still , wenn Sie gültige Cache gespeicherten Anmeldeinformationen verfügen:

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

Wie in anderen Antworten / Kommentaren erwähnt, -verneuert die Option ("validieren") zu sudo im Hintergrund die zwischengespeicherten Anmeldeinformationen, wenn zwischengespeicherte Anmeldeinformationen vorhanden sind, oder fordert zur Authentifizierung auf, und die -nOption ("nicht interaktiv") verhindert, dass sudo generiert Interaktive Eingabeaufforderungen, z. B. die Authentifizierungsaufforderung.

Jayhendren
quelle
Dies ist eine gute Lösung ... Ich habe es bereits versucht, aber die Antwort von AlexP erfüllt genau das, was ich brauchte ... Ich glaube, ich habe den Parameter -n schon einmal falsch verstanden
TonyMorello
1

sudo -nvFunktioniert einwandfrei, verschmutzt jedoch die Systemprotokolle mit Sudo-Fehlern und Informationen zur Pam-Authentifizierung. Ich musste die sudo-Berechtigungen für meine Bash-Eingabeaufforderung überprüfen, daher wurde sie ziemlich oft ausgeführt und meine Protokolle bestanden fast nur aus diesem Rauschen.

Es ist möglich, die sudo-Timestamp-Datei direkt zu analysieren - ich habe ein kleines C util dafür geschrieben:

/* 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
quelle