Skip to content

Commit

Permalink
vendor: update to the latest version of ferny
Browse files Browse the repository at this point in the history
This has some breaking API changes, so make adjustments as necessary.

One slightly non-obvious change comes from the removal of
InteractionAgent.add_handlers(), which had the potential to be racy in
case a message arrived before its handler was registered.  We now have
to specify all handlers up-front, which means that the block for doing
that needs to move up to before the transport is connected.  Of course,
we can only write the stage1 bootloader after the transport is online,
so we need to keep that in its current location.
  • Loading branch information
allisonkarlitskaya committed Nov 8, 2023
1 parent f1fe2a1 commit d220099
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 23 deletions.
5 changes: 2 additions & 3 deletions src/cockpit/beiboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def shutdown(self) -> None:
self.peer.close()


class AuthorizeResponder(ferny.InteractionResponder):
class AuthorizeResponder(ferny.AskpassHandler):
commands = ('ferny.askpass', 'cockpit.report-exists')
router: Router

Expand Down Expand Up @@ -184,8 +184,7 @@ def __init__(self, router: Router, destination: str, args: argparse.Namespace):
async def do_connect_transport(self) -> None:
beiboot_helper = BridgeBeibootHelper(self)

agent = ferny.InteractionAgent(AuthorizeResponder(self.router))
agent.add_handler(beiboot_helper)
agent = ferny.InteractionAgent([AuthorizeResponder(self.router), beiboot_helper])

# We want to run a python interpreter somewhere...
cmd: Sequence[str] = ('python3', '-ic', '# cockpit-bridge')
Expand Down
6 changes: 3 additions & 3 deletions src/cockpit/polkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import pwd
from typing import Dict, List, Sequence, Tuple

from cockpit._vendor.ferny import InteractionResponder
from cockpit._vendor.ferny import AskpassHandler
from cockpit._vendor.systemd_ctypes import Variant, bus

# that path is valid on at least Debian, Fedora/RHEL, and Arch
Expand All @@ -43,7 +43,7 @@
# mapping, but that method is not available for Python 3.6 yet.

class org_freedesktop_PolicyKit1_AuthenticationAgent(bus.Object):
def __init__(self, responder: InteractionResponder):
def __init__(self, responder: AskpassHandler):
super().__init__()
self.responder = responder

Expand Down Expand Up @@ -125,7 +125,7 @@ class PolkitAgent:
Use this as a context manager to ensure that the agent gets unregistered again.
"""
def __init__(self, responder: InteractionResponder):
def __init__(self, responder: AskpassHandler):
self.responder = responder
self.agent_slot = None

Expand Down
8 changes: 4 additions & 4 deletions src/cockpit/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
logger = logging.getLogger(__name__)


class PasswordResponder(ferny.InteractionResponder):
class PasswordResponder(ferny.AskpassHandler):
PASSPHRASE_RE = re.compile(r"Enter passphrase for key '(.*)': ")

password: Optional[str]
Expand Down Expand Up @@ -104,7 +104,7 @@ async def do_connect_transport(self) -> None:
logger.debug('connecting to host %s failed: %s', host, exc)
raise PeerError('no-host', error='no-host', message=str(exc)) from exc

except ferny.HostKeyError as exc:
except ferny.SshHostKeyError as exc:
if responder.hostkeys_seen:
# If we saw a hostkey then we can issue a detailed error message
# containing the key that would need to be accepted. That will
Expand All @@ -114,7 +114,7 @@ async def do_connect_transport(self) -> None:
else:
error_args = {}

if isinstance(exc, ferny.ChangedHostKeyError):
if isinstance(exc, ferny.SshChangedHostKeyError):
error = 'invalid-hostkey'
elif self.private:
error = 'unknown-hostkey'
Expand All @@ -126,7 +126,7 @@ async def do_connect_transport(self) -> None:
type(exc), exc, self.private, responder.hostkeys_seen, error, error_args)
raise PeerError(error, error=error, auth_method_results={}, **error_args) from exc

except ferny.AuthenticationError as exc:
except ferny.SshAuthenticationError as exc:
logger.debug('authentication to host %s failed: %s', host, exc)

results = {method: 'not-provided' for method in exc.methods}
Expand Down
29 changes: 18 additions & 11 deletions src/cockpit/superuser.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@


class SuperuserPeer(ConfiguredPeer):
responder: ferny.InteractionResponder
responder: ferny.AskpassHandler

def __init__(self, router: Router, config: BridgeConfig, responder: ferny.InteractionResponder):
def __init__(self, router: Router, config: BridgeConfig, responder: ferny.AskpassHandler):
super().__init__(router, config)
self.responder = responder

Expand All @@ -54,7 +54,17 @@ async def do_connect_transport(self) -> None:
else:
logger.debug('connecting non-polkit superuser peer transport %r', self.args)

agent = ferny.InteractionAgent(self.responder)
responders: 'list[ferny.InteractionHandler]' = [self.responder]

if '# cockpit-bridge' in self.args:
logger.debug('going to beiboot superuser bridge %r', self.args)
helper = BridgeBeibootHelper(self, ['--privileged'])
responders.append(helper)
stage1 = make_bootloader(helper.steps, gadgets=ferny.BEIBOOT_GADGETS).encode()
else:
stage1 = None

agent = ferny.InteractionAgent(responders)

if 'SUDO_ASKPASS=ferny-askpass' in self.env:
tmpdir = context.enter_context(TemporaryDirectory())
Expand All @@ -65,19 +75,16 @@ async def do_connect_transport(self) -> None:

transport = await self.spawn(self.args, env, stderr=agent, start_new_session=True)

if '# cockpit-bridge' in self.args:
logger.debug('going to beiboot superuser bridge %r', self.args)
helper = BridgeBeibootHelper(self, ['--privileged'])
agent.add_handler(helper)
transport.write(make_bootloader(helper.steps, gadgets=ferny.BEIBOOT_GADGETS).encode())
if stage1 is not None:
transport.write(stage1)

try:
await agent.communicate()
except ferny.InteractionError as exc:
raise PeerError('authentication-failed', message=str(exc)) from exc


class CockpitResponder(ferny.InteractionResponder):
class CockpitResponder(ferny.AskpassHandler):
commands = ('ferny.askpass', 'cockpit.send-stderr')

async def do_custom_command(self, command: str, args: Tuple, fds: List[int], stderr: str) -> None:
Expand Down Expand Up @@ -125,7 +132,7 @@ def apply_rule(self, options: JsonObject) -> Optional[Peer]:
# superuser requested, but not active? That's an error.
raise RoutingError('access-denied')

# ferny.InteractionResponder
# ferny.AskpassHandler
async def do_askpass(self, messages: str, prompt: str, hint: str) -> Optional[str]:
assert self.pending_prompt is None
echo = hint == "confirm"
Expand Down Expand Up @@ -153,7 +160,7 @@ def peer_done(self):
self.current = 'none'
self.peer = None

async def go(self, name: str, responder: ferny.InteractionResponder) -> None:
async def go(self, name: str, responder: ferny.AskpassHandler) -> None:
if self.current != 'none':
raise bus.BusError('cockpit.Superuser.Error', 'Superuser bridge already running')

Expand Down
2 changes: 1 addition & 1 deletion test/pytest/test_beiboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class BeibootPeer(Peer):
async def do_connect_transport(self) -> None:
helper = BridgeBeibootHelper(self)
agent = ferny.InteractionAgent(helper)
agent = ferny.InteractionAgent([helper])
transport = await self.spawn([sys.executable, '-iq'], env=[], stderr=agent)
transport.write(bootloader.make_bootloader(helper.steps, gadgets=ferny.BEIBOOT_GADGETS).encode())
await agent.communicate()
Expand Down

0 comments on commit d220099

Please sign in to comment.