我如何确定我的sudoer权限是否超时?


20

我正在研究一个脚本,该脚本以sudo身份运行命令,并且仅在我的sudo特权超时时才回显一行文本,因此,只有在运行sudo命令时,才需要我的用户(不是root用户)再次输入其密码。

我该如何验证?请注意,$(id -u)即使以sudo的身份运行时,也会返回我当前的用户ID,因此无法检查以使其与0匹配...

我需要一种可以静静检查的方法。

Answers:


28

使用该选项-n检查您是否仍然具有特权;来自man sudo

-n-非交互式

避免提示用户输入任何形式。如果命令要求密码才能运行,sudo将显示错误消息并退出。

例如,

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

请注意,特权在检查sudo -n true和实际使用它们之间可能会过期。您可能需要直接尝试,sudo -n command...并在显示失败的情况下显示一条消息,并可能重试以sudo交互方式运行。

编辑:另请参阅以下ruakh的评论。


谢谢,我之前尝试过类似的方法,但无法按我想要的方式工作。
TonyMorello

3
回复:“请注意,在检查sudo -n true和实际使用特权之间,特权可能会过期”:关于这一点,文档有点含糊,但我认为运行sudo命令(即使只是sudo -n true)也会重新设置超时时间时钟。无论哪种方式,-v都已明确记录为这样做,并且无论如何sudo -n -v都可能比sudo -n true为此目的更合适。
ruakh

尽管这确实是静默的,但是如果没有特权,它将把错误记录到系统日志中:hostname sudo[8870]: username : a password is required ; TTY=pts/0 ; PWD=/home/username ; USER=root ; COMMAND=/usr/bin/true。例如,如果在bash提示符中使用它,将导致大量错误消息。
罗加奇

并且,如果有特权,将从pam_unix进行调试日志记录并执行sudo命令。
罗加奇

8

跑:

sudo -nv

如果您的sudo权限已超时,将以退出代码1退出并输出:

sudo: a password is required

如果您具有有效的缓存凭据,则此命令将成功并且不输出任何内容。

因此,综上所述,这是一个脚本,它将静默检查您是否具有有效的缓存凭据:

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

正如其他答案/评论所述,-v如果有任何其他提示进行身份验证以生成高速缓存的凭据,则sudo 的选项(“验证”)将以静默方式更新高速缓存的凭据,并且该-n选项(“非交互式”)可防止sudo生成任何交互式提示,例如身份验证提示。


这是一个很好的解决方案...我之前曾尝试过,但是AlexP的答案恰好满足了我的需要...我想我之前误解了-n参数
TonyMorello

1

sudo -nv可以正常工作,但是会用sudo错误和pam身份验证信息污染系统日志。我需要检查bash提示符的sudo特权,因此它执行得很频繁,并且我的日志几乎只包含这种噪音。

可以直接解析sudo时间戳文件-我为此写了一个小C util:

/* 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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.