我正在编写一个脚本,该脚本以 sudo 身份运行命令,并且仅当我的 sudo 权限超时时才回显一行文本,因此仅当使用 sudo 运行命令需要我的用户(而不是 root)再次输入其密码时。
我如何验证这一点?请注意,$(id -u)
即使以 sudo 身份运行也会返回我当前的用户 ID,因此无法检查它是否与 0 匹配...
我需要一种可以安静地检查这一点的方法。
答案1
使用该选项-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 的评论。
答案2
跑步:
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 生成任何交互式提示,例如身份验证提示。
答案3
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, ¤t_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(×tamp_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, ¤t_time, &time_since_sudo);
found = time_since_sudo.tv_sec < TIMEOUT * 60;
break;
}
}
offset += timestamp_entry_header[1];
}
fclose(timestamp_fd);
return !found;
}