Linux lhjmq-records 5.15.0-118-generic #128-Ubuntu SMP Fri Jul 5 09:28:59 UTC 2024 x86_64
Your IP : 18.117.232.108
from binascii import hexlify
from enum import Enum
import os
from typing import Optional
def make_auth_external() -> bytes:
"""Prepare an AUTH command line with the current effective user ID.
This is the preferred authentication method for typical D-Bus connections
over a Unix domain socket.
"""
hex_uid = hexlify(str(os.geteuid()).encode('ascii'))
return b'AUTH EXTERNAL %b\r\n' % hex_uid
def make_auth_anonymous() -> bytes:
"""Format an AUTH command line for the ANONYMOUS mechanism
Jeepney's higher-level wrappers don't currently use this mechanism,
but third-party code may choose to.
See <https://tools.ietf.org/html/rfc4505> for details.
"""
from . import __version__
trace = hexlify(('Jeepney %s' % __version__).encode('ascii'))
return b'AUTH ANONYMOUS %s\r\n' % trace
BEGIN = b'BEGIN\r\n'
NEGOTIATE_UNIX_FD = b'NEGOTIATE_UNIX_FD\r\n'
class ClientState(Enum):
# States from the D-Bus spec (plus 'Success'). Not all used in Jeepney.
WaitingForData = 1
WaitingForOk = 2
WaitingForReject = 3
WaitingForAgreeUnixFD = 4
Success = 5
class AuthenticationError(ValueError):
"""Raised when DBus authentication fails"""
def __init__(self, data, msg="Authentication failed"):
self.msg = msg
self.data = data
def __str__(self):
return f"{self.msg}. Bus sent: {self.data!r}"
class FDNegotiationError(AuthenticationError):
"""Raised when file descriptor support is requested but not available"""
def __init__(self, data):
super().__init__(data, msg="File descriptor support not available")
class Authenticator:
"""Process data for the SASL authentication conversation
If enable_fds is True, this includes negotiating support for passing
file descriptors.
"""
def __init__(self, enable_fds=False):
self.enable_fds = enable_fds
self.buffer = bytearray()
self._to_send = b'\0' + make_auth_external()
self.state = ClientState.WaitingForOk
self.error = None
@property
def authenticated(self):
return self.state is ClientState.Success
def __iter__(self):
return iter(self.data_to_send, None)
def data_to_send(self) -> Optional[bytes]:
"""Get a line of data to send to the server
The data returned should be sent before waiting to receive data.
Returns empty bytes if waiting for more data from the server, and None
if authentication is finished (success or error).
Iterating over the Authenticator object will also yield these lines;
:meth:`feed` should be called with received data inside the loop.
"""
if self.authenticated or self.error:
return None
self._to_send, to_send = b'', self._to_send
return to_send
def process_line(self, line):
if self.state is ClientState.WaitingForOk:
if line.startswith(b'OK '):
if self.enable_fds:
return NEGOTIATE_UNIX_FD, ClientState.WaitingForAgreeUnixFD
else:
return BEGIN, ClientState.Success
# We only support EXTERNAL authentication, but if we allow others,
# 'REJECTED <mechs>' would tell us to try another one.
elif self.state is ClientState.WaitingForAgreeUnixFD:
if line.startswith(b'AGREE_UNIX_FD'):
return BEGIN, ClientState.Success
# The protocol allows us to continue if FD passing is rejected,
# but Jeepney assumes that if you enable FD support you need it,
# so we fail rather
self.error = line
raise FDNegotiationError(line)
self.error = line
raise AuthenticationError(line)
def feed(self, data: bytes):
"""Process received data
Raises AuthenticationError if the incoming data is not as expected for
successful authentication. The connection should then be abandoned.
"""
self.buffer += data
if b'\r\n' in self.buffer:
line, self.buffer = self.buffer.split(b'\r\n', 1)
if self.buffer:
# We only expect one line before we reply
raise AuthenticationError(self.buffer, "Unexpected data received")
self._to_send, self.state = self.process_line(line)
# Avoid consuming lots of memory if the server is not sending what we
# expect. There doesn't appear to be a specified maximum line length,
# but 8192 bytes leaves a sizeable margin over all the examples in the
# spec (all < 100 bytes per line).
elif len(self.buffer) > 8192:
raise AuthenticationError(
self.buffer, "Too much data received without line ending"
)
# Old name (behaviour on errors has changed, but should work for standard case)
SASLParser = Authenticator
|