qrexec.policy.parser – Qubes RPC policy parser¶
Qrexec policy format is available as separate specification: Multifile policy.
Representing domain names¶
- class qrexec.policy.parser.VMToken(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
A domain specification
Wherever policy evaluation needs to represent a VM or a
@somethingtoken, instances of this class (and subclasses) are used. Each@tokenhas its own dedicated class.- There are 4 such contexts:
Not all
@tokenscan be used everywhere. Where they can be used is specified by inheritance.All tokens are also instances of
strand can be compared to other strings.
- class qrexec.policy.parser.Source(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
- class qrexec.policy.parser.Target(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
- class qrexec.policy.parser.Redirect(value: str | None, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
- class qrexec.policy.parser.IntendedTarget(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
- verify(*, system_info: FullSystemInfo) VMToken | None[source]¶
Check if given value names valid target
This function check if given value is not only syntactically correct, but also if names valid service call target (existing domain, or valid
'@dispvm'like keyword). If the domain does not exist, returns a DefaultVM.- Parameters:
system_info – information about the system
- Returns:
for successful verification
- Return type:
- Raises:
qrexec.exc.AccessDenied – for failed verification
The classes should be instantiated using either VMToken or the context
that expects the token.
>>> type(VMToken('@adminvm'))
<class 'qrexec.policy.parser.AdminVM'>
>>> type(Target('@adminvm'))
<class 'qrexec.policy.parser.AdminVM'>
The latter has the advantage that tokens inappropriate for the context are rejected:
>>> Redirect('@tag:tag1')
Traceback (most recent call last):
...
qrexec.exc.PolicySyntaxError: <unknown>:None: invalid redirect token: '@tag:tag1'
The tokens are as follows. EXACT means the token should match exactly. PREFIX
means anything goes after the prefix. When two different prefixes match
('@dispvm:'/'@dispvm:@tag:'), the longer one is chosen.
- class qrexec.policy.parser.AdminVM(value: str | None, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
Bases:
Source,Target,Redirect,IntendedTarget- EXACT = '@adminvm'¶
- class qrexec.policy.parser.AnyVM(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
-
- EXACT = '@anyvm'¶
- class qrexec.policy.parser.DefaultVM(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
Bases:
Target,IntendedTarget- EXACT = '@default'¶
- class qrexec.policy.parser.TypeVM(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
-
- PREFIX = '@type:'¶
- class qrexec.policy.parser.TagVM(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
-
- PREFIX = '@tag:'¶
- class qrexec.policy.parser.DispVM(value: str | None, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
Bases:
Target,Redirect,IntendedTarget- EXACT = '@dispvm'¶
- static get_dispvm_template(source: str, *, system_info: FullSystemInfo) DispVMTemplate | None[source]¶
Given source, get appropriate template for DispVM. Maybe None.
- class qrexec.policy.parser.DispVMTemplate(value: str | None, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
Bases:
Source,Target,Redirect,IntendedTarget- PREFIX = '@dispvm:'¶
- class qrexec.policy.parser.DispVMTag(token: str, *, filepath: Path | None = None, lineno: int | None = None)[source]¶
-
- PREFIX = '@dispvm:@tag:'¶
There is a helper metaclass for this, do not use it elsewhere:
Request object¶
- class qrexec.policy.parser.Request(service: str | None, argument: str, source: str, target: str, *, system_info: FullSystemInfo, allow_resolution_type: Type[AllowResolution] = <class 'qrexec.policy.parser.AllowResolution'>, ask_resolution_type: Type[AskResolution] = <class 'qrexec.policy.parser.AskResolution'>, requested_source: str | None = '')[source]¶
Qrexec request
A request object keeps what is searched for in the policy. It keeps the principal quadruple: service, argument, source and target that are parameters of the qrexec call. There is also system_info, which represents current state of the system, incl. the list of all domains in the system and their respective properties that are relevant to policy.
- Parameters:
service (str or None) – Service name.
argument (str) – The argument. Must start with
'+'.source (str) – name of source qube
target (str) – target designation
system_info (dict) – as returned from
qrexec.utils.system_info()allow_resolution_type (type) – a child of
AllowResolutionask_resolution_type (type) – a child of
AskResolution
- service¶
the service that is being requested
- argument¶
argument for the service
- requested_source¶
requested source qube name (from qrexec-client-vm)
- target¶
target (qube or token) as requested by source qube
- system_info¶
system info
- allow_resolution_type¶
factory for allow resolution
- ask_resolution_type¶
factory for ask resolution
- source¶
source qube name
Actions and resolutions¶
There are two things that represent “what to do” when there is a match in
policy: actions and resolutions. Action is part of a Rule, it means
what this rule prescripts. In contrast, a resolution is something that happens
after a Rule was actually matched to Request.
- class qrexec.policy.parser.ActionType(rule: Rule)[source]¶
Base class for actions
Children of this class are types of objects representing action in policy rule (
Rule.action). Not to be confused withAbstractResolution, which happens when particular rule is matched to aRequest.Keyword arguments to __init__ are taken from parsing params in the rule, so this defines, what params are valid for which action.
- actual_target(intended_target: VMToken) IntendedTarget[source]¶
If action has redirect, it is it. Otherwise, the rule’s own target
- Parameters:
intended_target (IntendedTarget) –
Request.target- Returns:
- either
target, if not None, or intended_target
- either
- Return type:
- static allow_no_autostart(target: str, system_info: FullSystemInfo) bool[source]¶
Should we allow this target when autostart is disabled
- abstractmethod evaluate(request: Request) AbstractResolution[source]¶
Evaluate the request.
Depending on action and possibly user’s decision either return a resolution or raise exception.
- Parameters:
request (Request) – the request that was matched to the rule
- Returns:
for successful requests
- Return type:
- Raises:
qrexec.exc.AccessDenied – for denied requests
- rule¶
the rule that holds this action
- class qrexec.policy.parser.Allow(rule: Rule, *, target: str | None = None, user: str | None = None, notify: bool = False, autostart: bool = True)[source]¶
- evaluate(request: Request) AllowResolution[source]¶
- Returns:
for successful requests
- Return type:
- Raises:
qrexec.exc.AccessDenied – for invalid requests
- class qrexec.policy.parser.Deny(rule: Rule, *, notify: bool | None = None)[source]¶
- class qrexec.policy.parser.Ask(rule: Rule, *, target: str | None = None, default_target: str | None = None, user: str | None = None, notify: bool = False, autostart: bool = True)[source]¶
- evaluate(request: Request) AskResolution[source]¶
- Returns:
AskResolution
- Raises:
qrexec.exc.AccessDenied – for invalid requests
- class qrexec.policy.parser.AbstractResolution(rule: Rule, request: Request, *, user: str | None)[source]¶
Object representing positive policy evaluation result - either ask or allow action
- abstractmethod async execute() str[source]¶
Execute the action. For allow, this runs the qrexec. For ask, it asks user and then (depending on verdict) runs the call.
- Parameters:
caller_ident (str) – Service caller ident (
process_ident,source_name, source_id)
- request¶
request
- rule¶
policy rule from which this action is derived
- user¶
the user to run command as, or None for default
- class qrexec.policy.parser.AllowResolution(rule: Rule, request: Request, *, user: str | None, target: str, autostart: bool)[source]¶
Resolution returned for
RulewithAllow.- classmethod from_ask_resolution(ask_resolution: AskResolution, *, target: str) AllowResolution[source]¶
This happens after user manually approved the call
- target¶
target domain the service should be connected to
- class qrexec.policy.parser.AskResolution(rule: Rule, request: Request, *, targets_for_ask: Sequence[str], default_target: str | None, autostart: bool, user: str | None)[source]¶
Resolution returned for
RulewithAsk.This base class is a dummy implementation which behaves as if user always denied the call. The programmer is expected to inherit from this class and overload
execute()to display the question to the user by appropriate means. User should have choice amongtargets_for_ask. Ifdefault_targetis notNone, that should be the default. Otherwise there should be no default. After querying the user,handle_user_response()should be called. For negative answers, raisingqrexec.exc.AccessDeniedis also enough.The child class should be supplied as part of
Request.- async execute() NoReturn[source]¶
Ask the user for permission.
This method should be overloaded in children classes. This implementation always denies the request.
- Raises:
qrexec.exc.AccessDenied – always
- handle_invalid_response() NoReturn[source]¶
Handle invalid response for the ‘ask’ action. Throws AccessDenied.
- handle_user_response(response: bool, target: str) AllowResolution[source]¶
Handle user response for the ‘ask’ action. Children class’
execute()is supposed to call this method to report the user’s verdict.- Parameters:
- Returns:
for positive answer
- Return type:
- Raises:
qrexec.exc.AccessDenied – for negative answer
Parsers¶
- class qrexec.policy.parser.Rule(service: str, argument: str, source: str, target: str, action: str, params: List[str], *, policy: AbstractPolicy, filepath: Path, lineno: int | None)[source]¶
A single line of policy file
Avoid instantiating manually, use either
from_line()orfrom_line_service().- policy¶
the parser that this rule belongs to
- filepath¶
the file path
- lineno¶
the line number
- service¶
the qrexec service
- argument¶
the argument to the service
- source¶
source specification
- target¶
target specification
- classmethod from_line(policy, line, *, filepath, lineno)[source]¶
Load a single line of qrexec policy and check its syntax. Do not verify existence of named objects.
- Parameters:
line – a single line of actual qrexec policy (not a comment, empty line or
@include)filepath (pathlib.Path) – Path of the file from which this line is loaded
lineno – line number from which this line is loaded
- Raises:
PolicySyntaxError – when syntax error is found
- classmethod from_line_service(policy, service, argument, line, *, filepath, lineno)[source]¶
Load a single line in old format.
- Parameters:
service – the service for which this line applies
argument – argument for the service
line (str) – the line to be parsed
filepath (pathlib.Path) – the file from which this line was taken
lineno (int) – the line number
- Raises:
PolicySyntaxError – when syntax error is found
- is_match(request: Request) bool[source]¶
Check if given request matches this line.
- Parameters:
request – request to check against
- Returns:
True or False
- is_match_but_target(request: Request) bool[source]¶
Check if given (service, argument source) matches this line.
Target is ignored. This is used for
collect_targets_for_ask().- Parameters:
system_info – information about the system - available VMs, their types, labels, tags etc. as returned by
app_to_system_info()service – name of the service
argument – the argument
source – name of the source VM
target – name of the target VM, or None if not specified
system_info – the context
- Returns:
True or False
- class qrexec.policy.parser.AbstractParser[source]¶
A minimal, pluggable, validating policy parser
- load_policy_file_service(service, argument, file, filepath)[source]¶
Parse a policy file from
!include-service
- abstractmethod handle_include(included_path: PurePosixPath, *, filepath, lineno)[source]¶
Handle
!includeline when encountered inpolicy_load_file().This method is to be provided by subclass.
- abstractmethod handle_include_dir(included_path: PurePosixPath, *, filepath, lineno)[source]¶
Handle
!include-dirline when encountered inpolicy_load_file().This method is to be provided by subclass.
- abstractmethod handle_include_service(service, argument, included_path: PurePosixPath, *, filepath, lineno)[source]¶
Handle
!include-serviceline when encountered inpolicy_load_file().This method is to be provided by subclass.
- abstractmethod handle_rule(rule, *, filepath, lineno)[source]¶
Handle a line with a rule.
This method is to be provided by subclass.
- class qrexec.policy.parser.AbstractPolicy(*args, **kwds)[source]¶
This class is a parser that accumulates the rules to form policy.
- handle_rule(rule, *, filepath, lineno)[source]¶
Handle a line with a rule.
This method is to be provided by subclass.
- evaluate(request)[source]¶
Evaluate policy
- Returns:
For allow or ask resolutions.
- Return type:
- Raises:
AccessDenied – when action should be denied unconditionally
- class qrexec.policy.parser.AbstractFileLoader[source]¶
Parser that loads next files on
!include[-service]directivesThis class uses regular files as accessed by
pathlib.Path, but it is possible to overload those functions and use file-like objects.- resolve_path(included_path: PurePosixPath) Path[source]¶
Resolve path from
!include*topathlib.Path
- resolve_filepath(included_path: PurePosixPath, *, filepath, lineno) Tuple[TextIO, PurePath][source]¶
Resolve
!include[-service]to open file and filepathThe callee is responsible for closing the file descriptor.
- Raises:
qrexec.exc.PolicySyntaxError – when the path does not point to a file
- handle_include(included_path: PurePosixPath, *, filepath, lineno)[source]¶
Handle
!includeline when encountered inpolicy_load_file().This method is to be provided by subclass.
- handle_include_service(service, argument, included_path: PurePosixPath, *, filepath, lineno)[source]¶
Handle
!include-serviceline when encountered inpolicy_load_file().This method is to be provided by subclass.
- class qrexec.policy.parser.AbstractDirectoryLoader[source]¶
Parser that loads next files on
!include-dirdirectives- resolve_dirpath(included_path: PurePosixPath, *, filepath, lineno) Path[source]¶
Resolve
!include-dirto directory path- Return type:
- Raises:
qrexec.exc.PolicySyntaxError – when the path does not point to a directory
- handle_include_dir(included_path: PurePosixPath, *, filepath, lineno)[source]¶
Handle
!include-dirline when encountered inpolicy_load_file().This method is to be provided by subclass.
- load_policy_dir(dirpath)[source]¶
Load all files in the directory (
!include-dir)- Parameters:
dirpath (pathlib.Path) – the directory to load
- Raises:
OSError – for problems in opening files or directories
- class qrexec.policy.parser.AbstractFileSystemLoader(*, policy_path: None | PurePath | Iterable[PurePath] = None)[source]¶
This class is used when policy is stored as regular files in a directory.
- Parameters:
policy_path – Load these directories. Paths given to
!includeetc. directives in a file are interpreted relative to the path from which the file was loaded.
- resolve_path(included_path: PurePosixPath) Path[source]¶
Resolve path from
!include*topathlib.Path
- class qrexec.policy.parser.FilePolicy(*, policy_path: None | PurePath | Iterable[PurePath] = None)[source]¶
Full policy loaded from files.
Usage:
>>> policy = qrexec.policy.parser.FilePolicy() >>> request = Request( ... 'qrexec.Service', '+argument', 'source-name', 'target-name', ... system_info=qrexec.utils.get_system_info()) >>> resolution = policy.evaluate(request) >>> await resolution.execute('process-ident') # asynchroneous method
Miscellaneous and test facilities¶
- class qrexec.policy.parser.ValidateParser(*, overrides: Dict[Path, str | None], policy_path: None | PurePath | Iterable[PurePath] = None)[source]¶
A parser that validates the policy directory along with proposed changes.
Pass files to be overriden in the
overridesdictionary, with either new content, or None if the file is to be deleted.- load_policy_dir(dirpath: Path) None[source]¶
Load all files in the directory (
!include-dir)- Parameters:
dirpath (pathlib.Path) – the directory to load
- Raises:
OSError – for problems in opening files or directories
- resolve_filepath(included_path: PurePosixPath, *, filepath, lineno) Tuple[TextIO, PurePath][source]¶
Resolve
!include[-service]to open file and filepathThe callee is responsible for closing the file descriptor.
- Raises:
qrexec.exc.PolicySyntaxError – when the path does not point to a file
- class qrexec.policy.parser.StringLoader(*args, policy, **kwds)[source]¶
An in-memory loader used for tests
- Parameters:
policy (dict or str) – policy dictionary. The keys are filenames to be included. It should contain
'__main__'key which is loaded. If the argument isstr, it behaves as it was dict’s'__main__'.
- resolve_filepath(included_path, *, filepath, lineno) Tuple[TextIO, PurePath][source]¶
- Raises:
qrexec.exc.PolicySyntaxError – when wrong path is included
- handle_include_dir(included_path: PurePosixPath, *, filepath, lineno)[source]¶
Handle
!include-dirline when encountered inpolicy_load_file().This method is to be provided by subclass.
- class qrexec.policy.parser.StringPolicy(*, policy, policy_compat=None, **kwds)[source]¶
String policy, used for tests and loading single files as policy. It can be used to test most of the code paths used in policy parsing.
>>> testpolicy = StringPolicy(policy={ ... '__main__': '!include policy2' ... 'policy2': '* * @anyvm @anyvm allow'})
Helper functions¶
- qrexec.policy.parser.get_invalid_characters(s: str, allowed: FrozenSet[str] = frozenset({'+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}), disallowed: Iterable[str] = '') Sequence[str][source]¶
Return characters contained in disallowed and/or not int allowed
- qrexec.policy.parser.parse_service_and_argument(rpcname: str | PurePath, *, no_arg: str = '+') Tuple[str, str][source]¶
Parse service and argument string.
Parse
SERVICE+ARGUMENT. Argument may be empty (single+at the end) or omitted (no+at all). If no argument is given, no_arg is returned instead. By default this returns'+', as if argument is empty.A Path from
pathlibis also accepted, in which case the filename is parsed.
- qrexec.policy.parser.validate_service_and_argument(service: str | None, argument: str | None, *, filepath: Path, lineno: int | None) Tuple[str | None, str | None][source]¶
Check service name and argument
This is intended as policy syntax checker to discard obviously invalid service names and arguments. There are some cases for which this function will not signal a problem, but the call still would be invalid. One of those cases is too long total call name.
- Parameters:
service (str) – the service as appeared in policy file
argument (str) – the argument as appeared in policy file
filepath (pathlib.Path) – the file path
lineno (int) – the line in the file
- Returns:
service and argument
- Return type:
- Raises:
qrexec.exc.PolicySyntaxError – for a number of forbidden cases
- qrexec.policy.parser.filter_filepaths(filepaths: Iterable[Path]) List[Path][source]¶
Check if files should be considered by policy.
The file name should contain only allowed characters (latin lowercase, digits, underscore, full stop and dash). It should not start with the dot.
Only the file name is considered, not the directories on path that leads to it.
- Parameters:
filepaths – the file paths
- Returns:
sorted list of paths, without ignored ones
- Return type:
list of pathlib.Path
- Raises:
qrexec.exc.AccessDenied – for invalid path which is not ignored