feat: add a setting for sudo over ssh
This commit is contained in:
1 parent
658adb7da7
commit
d8bbefed8a
22 files changed
+1367
-1320
No files matched your search
@@ -50,6 +50,7 @@ Settings Settings::load(const QString &path)
|
||||
s.maxFailures = kv.integer(QStringLiteral("MaxFailures"), s.maxFailures, 1, 20);
|
||||
s.lockoutMinutes = kv.integer(QStringLiteral("LockoutMinutes"), s.lockoutMinutes, 1, 24 * 60);
|
||||
s.skipLidClosed = kv.boolean(QStringLiteral("SkipLidClosed"), s.skipLidClosed);
|
||||
s.sshSessions = kv.boolean(QStringLiteral("SshSessions"), s.sshSessions);
|
||||
s.adapt = kv.boolean(QStringLiteral("Adapt"), s.adapt);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ struct Settings {
|
||||
int lockoutMinutes = 15;
|
||||
// A laptop with the lid shut has its camera looking at the keyboard.
|
||||
bool skipLidClosed = true;
|
||||
// sudo and admin prompts in an SSH session. Off by default: whoever sits
|
||||
// in front of the camera need not be whoever types. On, that person still
|
||||
// has to be at the machine (see the PAM module).
|
||||
bool sshSessions = false;
|
||||
// Take in a little of each confident unlock, so a new haircut or a pair
|
||||
// of glasses does not need a new setup. Face ID does the same.
|
||||
bool adapt = true;
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
// hello version
|
||||
// status [user] faces, lockout, camera, settings
|
||||
// cameras every camera and which one is in use
|
||||
// verify user purpose [verbose]
|
||||
// verify user purpose [verbose] [remote]
|
||||
// scan for that user. Events: started, face,
|
||||
// hint, frame (verbose only), result
|
||||
// hint, frame (verbose only), result. remote:
|
||||
// the PAM module asks for an SSH session
|
||||
// enroll [name] set up a face for the caller. Asks polkit
|
||||
// first. Events: authorizing, authorized,
|
||||
// started, frame, pose, hint, captured, result.
|
||||
@@ -434,12 +435,17 @@ void Server::handleVerify(Client *client, const QJsonObject &request)
|
||||
if (!targetUser(client, request, &uid)) {
|
||||
return;
|
||||
}
|
||||
const Settings s = settings();
|
||||
// Whoever sits in front of the camera need not be whoever types.
|
||||
if (request.value(u"remote").toBool() && !s.sshSessions) {
|
||||
fail(client, QStringLiteral("remote"));
|
||||
return;
|
||||
}
|
||||
if (m_job) {
|
||||
fail(client, QStringLiteral("busy"));
|
||||
return;
|
||||
}
|
||||
|
||||
const Settings s = settings();
|
||||
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
||||
const UserState state = UserState::load(m_options.stateDir, uid);
|
||||
if (const qint64 left = state.lockoutLeft(now)) {
|
||||
|
||||
@@ -309,6 +309,7 @@ FU_SETTINGS=(
|
||||
"pam|sudo|bool|no|sudo in a terminal"
|
||||
"pam|polkit-1|bool|no|Admin prompts"
|
||||
"pam|lockscreens|bool|yes|Lock screens|||lockers"
|
||||
"sys|SshSessions|bool|no|In SSH sessions"
|
||||
"group|Recognition"
|
||||
"sys|Liveness|choice|light|Photo check|off,light,heavy"
|
||||
"sys|Strictness|choice|normal|How closely the face has to match|relaxed,normal,strict"
|
||||
@@ -348,6 +349,7 @@ fu_setting_help() {
|
||||
LockBlur:*) fu_msg "Blurs the picture behind face-unlock's own lock screen." ;;
|
||||
lockscreens:*) fu_msg "The lock screen takes your face in its password check. Press Enter on the empty field to scan. Found here: %s." "$(fu_pam_lockers_list)" ;;
|
||||
polkit-1:*) fu_msg "The password windows of your desktop and apps, for example when you install software. No match: you type the password." ;;
|
||||
SshSessions:*) fu_msg "sudo in an SSH session takes your face too, if you sit at this computer. Off by default: the person at the camera may not be the one typing." ;;
|
||||
Liveness:heavy) fu_msg "You have to blink or turn your head a little. This also stops printed photos." ;;
|
||||
Liveness:off) fu_msg "No check at all. Only for trying out a camera." ;;
|
||||
Liveness:*) fu_msg "Stops photos on a phone, a tablet or glossy paper. You do not have to blink. A matte printed photo can get through." ;;
|
||||
|
||||
@@ -28,6 +28,7 @@ declare -A FU_SYS_VALID=(
|
||||
[ScanSeconds]='^([2-9]|1[0-5])$'
|
||||
[Adapt]='^(yes|no)$'
|
||||
[SkipLidClosed]='^(yes|no)$'
|
||||
[SshSessions]='^(yes|no)$'
|
||||
[MaxFailures]='^([1-9]|1[0-9]|20)$'
|
||||
[LockoutMinutes]='^[1-9][0-9]{0,3}$'
|
||||
)
|
||||
|
||||
+10
-12
@@ -10,10 +10,11 @@
|
||||
// It refuses to be useful in exactly the places where a camera is the wrong
|
||||
// witness:
|
||||
// - a remote login (SSH, a remote host in PAM_RHOST). Whoever is in front
|
||||
// of the camera is not the person typing.
|
||||
// of the camera need not be the person typing. The daemon refuses it
|
||||
// unless SshSessions is on in the system settings.
|
||||
// - a user who is not sitting at the machine right now, with an active
|
||||
// session on a seat.
|
||||
// In both cases it returns PAM_IGNORE without touching the camera.
|
||||
// session on a seat. Then it returns PAM_IGNORE without touching the
|
||||
// camera.
|
||||
//
|
||||
// Options:
|
||||
// purpose=sudo|polkit|other what the bubble says (default: from the
|
||||
@@ -291,12 +292,7 @@ __attribute__((visibility("default"))) PAM_EXTERN int pam_sm_authenticate(pam_ha
|
||||
}
|
||||
return PAM_IGNORE;
|
||||
}
|
||||
if (is_remote(pamh)) {
|
||||
if (o.debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "remote session, not scanning for %s", user);
|
||||
}
|
||||
return PAM_IGNORE;
|
||||
}
|
||||
const bool remote = is_remote(pamh);
|
||||
if (!is_at_seat(user)) {
|
||||
if (o.debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "%s is not at the machine, not scanning", user);
|
||||
@@ -310,7 +306,8 @@ __attribute__((visibility("default"))) PAM_EXTERN int pam_sm_authenticate(pam_ha
|
||||
}
|
||||
|
||||
char request[512];
|
||||
const int len = snprintf(request, sizeof(request), "{\"cmd\":\"verify\",\"user\":\"%s\",\"purpose\":\"%s\"}\n", user, purpose);
|
||||
const int len = snprintf(request, sizeof(request), "{\"cmd\":\"verify\",\"user\":\"%s\",\"purpose\":\"%s\",\"remote\":%s}\n", user, purpose,
|
||||
remote ? "true" : "false");
|
||||
if (len <= 0 || (size_t)len >= sizeof(request) || strpbrk(user, "\"\\") || write(fd, request, (size_t)len) != len) {
|
||||
close(fd);
|
||||
return PAM_IGNORE;
|
||||
@@ -380,8 +377,9 @@ __attribute__((visibility("default"))) PAM_EXTERN int pam_sm_authenticate(pam_ha
|
||||
} else if (strcmp(value, "no-face") == 0 || strcmp(value, "attention") == 0 || strcmp(value, "quality") == 0) {
|
||||
rc = PAM_AUTH_ERR;
|
||||
} else {
|
||||
// Not set up, no camera, lid shut, busy: face unlock is
|
||||
// simply not available right now.
|
||||
// Not set up, no camera, the camera in a video call,
|
||||
// lid shut, busy, SSH: face unlock is simply not
|
||||
// available right now.
|
||||
rc = PAM_AUTHINFO_UNAVAIL;
|
||||
if (o.debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "face unlock unavailable for %s: %s", user, value);
|
||||
|
||||
Reference in new issue
Block a user