ssh: action script: Require user credentials when editing ssh keys

This change prevents the plinth user to set the ssh-keys without
knowing the user password.

- Debian: added new dependency python3-pampy to authenticate users.
- Added additional required parameter --auth-user to the
  'actions/ssh set-keys' command. A password should be
  provided through STDIN.

Tests performed:
- running 'actions/ssh set-keys' with empty or wrong admin credentials
  fails.
- running 'actions/ssh set-keys' with correct admin credentials
  succeeds.
- running 'actions/ssh set-keys' with correct non-admin credentials
  succeeds if the --username is the same user.
- running 'actions/ssh set-keys' with correct non-admin credentials
  fails if the --username is a different user.

Signed-off-by: Veiko Aasa <veiko17@disroot.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Veiko Aasa 2020-08-28 11:47:04 +03:00 committed by Sunil Mohan Adapa
parent 98f9d59ef1
commit 6b61ca2f18
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
3 changed files with 39 additions and 1 deletions

View File

@ -5,6 +5,7 @@ Configuration helper for SSH server.
"""
import argparse
import grp
import os
import pwd
import shutil
@ -14,7 +15,7 @@ import sys
import augeas
from plinth import action_utils
from plinth import action_utils, utils
def parse_arguments():
@ -32,6 +33,7 @@ def parse_arguments():
help='Set SSH authorized keys')
set_keys.add_argument('--username', required=True, type=_managed_user)
set_keys.add_argument('--keys', required=True)
set_keys.add_argument('--auth-user', required=True)
subparsers.add_parser('get-password-config',
help='Get SSH password auth configuration')
@ -44,6 +46,24 @@ def parse_arguments():
return parser.parse_args()
def _validate_user(username, must_be_admin=True):
"""Validate a user."""
if must_be_admin:
try:
admins = grp.getgrnam('admin').gr_mem
except KeyError:
admins = []
if username not in admins:
msg = '"{}" is not authorized to perform this action'.format(
username)
raise argparse.ArgumentTypeError(msg)
password = _read_password()
if not utils.is_authenticated_user(username, password):
raise argparse.ArgumentTypeError("Invalid credentials")
def _managed_user(username):
"""Raise an error if the user is root."""
if pwd.getpwnam(username).pw_gid == 0:
@ -52,6 +72,11 @@ def _managed_user(username):
return username
def _read_password():
"""Read the password from stdin."""
return ''.join(sys.stdin)
def subcommand_setup(arguments):
"""Setup Open SSH server.
@ -91,6 +116,10 @@ def subcommand_get_keys(arguments):
def subcommand_set_keys(arguments):
"""Set SSH authorized keys."""
user = arguments.username
auth_user = arguments.auth_user
must_be_admin = user != auth_user
_validate_user(auth_user, must_be_admin=must_be_admin)
ssh_folder = os.path.join(get_user_homedir(user), '.ssh')
key_file_path = os.path.join(ssh_folder, 'authorized_keys')

2
debian/control vendored
View File

@ -32,6 +32,7 @@ Build-Depends:
python3-flake8,
python3-gi,
python3-markupsafe,
python3-pampy,
python3-paramiko,
python3-psutil,
python3-pytest,
@ -104,6 +105,7 @@ Depends:
python3-django-stronghold,
python3-gi,
python3-markupsafe,
python3-pampy,
python3-paramiko,
python3-psutil,
python3-requests,

View File

@ -12,6 +12,7 @@ import string
from distutils.version import LooseVersion
import markupsafe
import pam
import ruamel.yaml
from django.utils.functional import lazy
@ -156,3 +157,9 @@ def is_axes_old():
return False
return LooseVersion(version) < LooseVersion('5.0')
def is_authenticated_user(username, password):
"""Return true if the user authentication succeeds."""
pam_authenticator = pam.pam()
return bool(pam_authenticator.authenticate(username, password))