-
-
Notifications
You must be signed in to change notification settings - Fork 117
Use ProtocolError on invalid requests #751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
a8cd75b to
479ac3d
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #751 +/- ##
=======================================
Coverage 70.17% 70.17%
=======================================
Files 61 61
Lines 13977 14073 +96
=======================================
+ Hits 9808 9876 +68
- Misses 4169 4197 +28
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
479ac3d to
6be57f3
Compare
| Client programming errors. The client made a request that it should have | ||
| known better than to send in the first place. Includes things like passing | ||
| an argument or payload to a service documented to not take one. The only | ||
| way that a correctly behaving client can get ProtocolError is qubesd is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| way that a correctly behaving client can get ProtocolError is qubesd is | |
| way that a correctly behaving client can get ProtocolError is qubesd is if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
qubes/exc.py
Outdated
|
|
||
| This does not provide any useful information to the client making the | ||
| request. Therefore, it should only be raised if there is a client | ||
| *programming* error, such as passing an argument to a request that does not | ||
| take an argument. It should not be used to reject requests that are valid, | ||
| but which qubesd is refusing to process. Instead, raise a subclass of | ||
| :py:class:`QubesException` with a useful error message. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| This does not provide any useful information to the client making the | |
| request. Therefore, it should only be raised if there is a client | |
| *programming* error, such as passing an argument to a request that does not | |
| take an argument. It should not be used to reject requests that are valid, | |
| but which qubesd is refusing to process. Instead, raise a subclass of | |
| :py:class:`QubesException` with a useful error message. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this deleting the paragraph? Github interface doesn't make it obvious.
I tried to make sure that only sanitized and already trusted information, as well as non private information that the caller doesn't know, is sent in the exception message. There are various cases where this could go wrong, especially in the part about revealing more information to the caller than what it should have, but a proper error message should be done any way to avoid hard to debug errors such as Got empty response from qubesd.
| class QubesVolumeRevisionNotFoundError(KeyError): | ||
| """Specified revision not found in qube volume.""" | ||
|
|
||
|
|
||
| class QubesPoolNotFoundError(KeyError): | ||
| """Pool does not exist.""" | ||
|
|
||
|
|
||
| class QubesVolumeNotFoundError(KeyError): | ||
| """Pool does not exist.""" | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catching all of these requires catching KeyError, which seems a bit weird to me. Would StoragePoolException be a better superclass?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And then make class StoragePoolException(QubesException, KeyError)? If yes, I agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would
StoragePoolExceptionbe a better superclass?
No because it is a superclass of QubesException. Search for comments above each of these exceptions in qubes/api/*, I mention that it may review existence of revisions, volumes and pools.
The issue is that we are doing checks to pass sanitized data to fire_event_for_permission for the qubes.ext.admin, this can revealinformation about the system that the admin-extension could want to block. The current state is that Qrexec policy may not be restrictive enough, and the admin-extension is something to be a more precise ACL, as it is already in qubesd, and not on qrexec level.
| self.enforce( | ||
| b"\n" not in passphrase, | ||
| reason="Passphrase must not contain newline character", | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there also be a check for NUL, or does qrexec do this implicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested, it allows null, QubesVM.run_service, which run_service_for_stdio uses, has filter_esc=False as default. If set to True, it substitutes by _ which would become an invalid password if user has other prohibited characters in the UTF-8 set. This is a hard one if we want to allow UTF-8 characters in passphrases... switching it off now might break existing workflows, maybe allow the safe to print set you did on qrexec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scrypt allows NUL and escape character as a passphrase. Tested with reading the passphrase from a file, to not have to deal with bash's handling. scrypt does prohibits \n though.
The qubes.backup.Backup.backup_do, encodes the passphrase in UTF-8, I don't know if conversion from higher sets to a lower set always works. Later, on launch_scrypt, does write the passphrase to the pty.
5858529 to
9c79152
Compare
|
PipelineRetryFailed |
The exceptions ProtocolError and PermissionDenied are both raised by qubesd to indicate various problems with requests. However, both cause clients to print the extremely unhelpful "Got empty response from qubesd" message. ProtocolError must be used only for client *programming* errors (bugs), not for problems that the client could not have detected before making the request. PermissionDenied must be used only for service authorization denials. Therefore, a API handler can be arranged as: - Validation: ProtocolError otherwise - fire_event_for_permission(): PermissionDenied if unauthorized - Action Ideally, validation that may leak existence of a system property should be done after asking for administrative permission, but then it would not be possible to pass only safe values to "admin-api:" events. If we are already leaking existence of a property, it makes sense to provide a useful exception class for it. Fixes: QubesOS/qubes-issues#10345
4ccd46d to
cadacc5
Compare
cadacc5 to
389be8e
Compare
|
This PR got bigger than what I anticipated. I will try to restrict myself to:
For future PRs, somethings to tackle would be improving API security: QubesOS/qubes-doc@753fa4e, which, if no GSoC applicant takes it, might be assigned directly to me. I think this becomes more important when the non-dom0 GUI domain is streamlined. |
Now the client is able to understand what failed. Fixes: QubesOS/qubes-issues#10345
If a required object is absent prior to admin-permission, it allows knowing if object exists before the admin extension can potentially hide it with PermissionDenied. - The comment that there was a leak on CloneTo and CloneFrom is not applicable, the token is already set to a volume, it is just checking if things are alright, volume pool still exists etc; - The existence leak is sometimes not possible to solved in the admin/api.py, because it would limit the sanitization made before the fire_event_for_permission(); - When the leak was avoided without compromising sanitization, it makes the logic a bit strange, some validation being done past fire_event_for_permission().
389be8e to
1a16799
Compare
|
Demi did a PR in jan/2025 that I didn't see before... #645, will take a look at it and add things that are relevant. |
The exceptions ProtocolError and PermissionDenied are both raised by qubesd to indicate various problems with requests. However, both cause clients to print the extremely unhelpful "Got empty response from qubesd" message.
ProtocolError must be used only for client programming errors (bugs), not for problems that the client could not have detected before making the request.
PermissionDenied must be used only for service authorization denials.
Therefore, a API handler can be arranged as:
Ideally, validation that may leak existence of a system property should be done after asking for administrative permission, but then it would not be possible to pass only safe values to "admin-api:" events.
If we are already leaking existence of a property, it makes sense to provide a useful exception class for it.
Fixes: QubesOS/qubes-issues#10345
It is a draft, just posting here for CI. It is more like an idea than a finished version. Logging messages should be okay now, it is only from trusted input.
From some open TODOs:
It seems that most checks that are done before
admin-permissionevents may leak some data but not sure of its sensitiveness.revisionis a datevolumesare always the samepoolandlabelshave a default that most people usetemplateandqubeexistence, not so niceBut without these checks, there is no good error message...