Skip to content

Commit

Permalink
Allow setting the user which we drop permissions to using suid.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmv committed Feb 24, 2023
1 parent 83168cd commit bedda90
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 5 deletions.
1 change: 1 addition & 0 deletions aiosmtpd/docs/NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
Added
-----
* Unthreaded Controllers (Closes #160)
* Ability to drop permissions to a user other than ``nobody`` using ``-S``

Fixed/Improved
--------------
Expand Down
18 changes: 14 additions & 4 deletions aiosmtpd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,20 @@ def _parser() -> ArgumentParser:
default=True,
action="store_false",
help=(
"This program generally tries to setuid ``nobody``, unless this "
"This program generally uses setuid to drop permissions, unless this "
"flag is set. The setuid call will fail if this program is not "
"run as root (in which case, use this flag)."
),
)
parser.add_argument(
"-S",
"--suid-user",
dest="suid_user",
default="nobody",
help=(
"The user to change to using suid; defaults to ``nobody``."
),
)
parser.add_argument(
"-c",
"--class",
Expand Down Expand Up @@ -224,12 +233,13 @@ def main(args: Optional[Sequence[str]] = None) -> None:
file=sys.stderr,
)
sys.exit(1)
nobody = pwd.getpwnam("nobody").pw_uid
suid_user_id = pwd.getpwnam(args.suid_user).pw_uid
try:
os.setuid(nobody)
os.setuid(suid_user_id)
except PermissionError:
print(
'Cannot setuid "nobody"; try running with -n option.', file=sys.stderr
f'Cannot setuid to "{args.suid_user}"; try running with -n option.',
file=sys.stderr,
)
sys.exit(1)

Expand Down
14 changes: 13 additions & 1 deletion aiosmtpd/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

import asyncio
from collections import namedtuple
import logging
import multiprocessing as MP
import os
Expand Down Expand Up @@ -141,6 +142,17 @@ def test_setuid(self, nobody_uid, mocker):
main(args=())
mock.assert_called_with(nobody_uid)

def test_setuid_other(self, nobody_uid, mocker):
other_user = namedtuple(
"pwnam",
["pw_uid", "pw_dir", "pw_shell"],
)(42, "/", "/bin/sh")
mock_getpwnam = mocker.patch("pwd.getpwnam", return_value=other_user)
mock_suid = mocker.patch("os.setuid")
main(args=("-S", "other"))
mock_getpwnam.assert_called_with("other")
mock_suid.assert_called_with(42)

def test_setuid_permission_error(self, nobody_uid, mocker, capsys):
mock = mocker.patch("os.setuid", side_effect=PermissionError)
with pytest.raises(SystemExit) as excinfo:
Expand All @@ -149,7 +161,7 @@ def test_setuid_permission_error(self, nobody_uid, mocker, capsys):
mock.assert_called_with(nobody_uid)
assert (
capsys.readouterr().err
== 'Cannot setuid "nobody"; try running with -n option.\n'
== 'Cannot setuid to "nobody"; try running with -n option.\n'
)

def test_setuid_no_pwd_module(self, nobody_uid, mocker, capsys):
Expand Down

0 comments on commit bedda90

Please sign in to comment.