On 12/6/18 3:34 PM, Daniel P. Berrangé wrote:
On Thu, Dec 06, 2018 at 03:17:47PM +0100, Michal Privoznik wrote:
> On 12/6/18 12:38 PM, Daniel P. Berrangé wrote:
>> On Thu, Nov 29, 2018 at 02:52:18PM +0100, Michal Privoznik wrote:
>>> This file implements wrappers over XATTR getter/setter. It
>>> ensures the proper XATTR namespace is used.
>>>
>>> Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
>>> ---
>>> src/security/Makefile.inc.am | 2 +
>>> src/security/security_util.c | 226 +++++++++++++++++++++++++++++++++++
>>> src/security/security_util.h | 32 +++++
>>> 3 files changed, 260 insertions(+)
>>> create mode 100644 src/security/security_util.c
>>> create mode 100644 src/security/security_util.h
>>> +int
>>> +virSecuritySetRememberedLabel(const char *name,
>>> + const char *path,
>>> + const char *label)
>>> +{
>>> + char *ref_name = NULL;
>>> + char *attr_name = NULL;
>>> + char *value = NULL;
>>> + unsigned int refcount = 0;
>>> + int ret = -1;
>>> +
>>> + if (!(ref_name = virSecurityGetRefCountAttrName(name)))
>>> + goto cleanup;
>>> +
>>> + if (virFileGetXAtrr(path, ref_name, &value) < 0) {
>>> + if (errno == ENOSYS || errno == ENOTSUP) {
>>> + ret = 0;
>>> + goto cleanup;
>>> + } else if (errno != ENODATA) {
>>> + virReportSystemError(errno,
>>> + _("Unable to get XATTR %s on
%s"),
>>> + ref_name,
>>> + path);
>>> + goto cleanup;
>>> + }
>>> + }
>>> +
>>> + if (value &&
>>> + virStrToLong_ui(value, NULL, 10, &refcount) < 0) {
>>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>>> + _("malformed refcount %s on %s"),
>>> + value, path);
>>> + goto cleanup;
>>> + }
>>> +
>>> + VIR_FREE(value);
>>> +
>>> + refcount++;
>>> +
>>> + if (refcount == 1) {
>>> + if (!(attr_name = virSecurityGetAttrName(name)))
>>> + goto cleanup;
>>> +
>>> + if (virFileSetXAtrr(path, attr_name, label) < 0)
>>> + goto cleanup;
>>> + }
>>
>> Do we need to have a
>>
>> } else {
>> .... check the currently remember label matches
>> what was passed into this method ?
>> }
>>
>> if not, can you add API docs for this method explainng the
>> intended semantics when label is already remembered.
>
> I don't think that's a good idea because that sets us off onto a path
> where we'd have to determine whether a file is accessible or not. I
> mean, if the current owner is UID_A:GID_A (and qemu has access through
> both) and this new label passed into here is UID_B:GID_B that doesn't
> necessarily mean that qemu loses the access (UID_A can be a member of
> GID_B).
I wasn't actually suggesting checking accessibility.
IMHO if you are going to have 2 guests both accessing the same file,
we should simply declare that they *MUST* both use the same label.
Simply reject the idea that the 2nd guest can change the label, using
a GID_B that magically still allows guest A to access it. Such scenarios
are way more likely to be an admin screw up than an intentional decision.
As an admin - the guest A might set svirt_image_t:s0:c12,c35. The guest B
then comes along and sets 'svirt_image_t:s0' which grants access to
all guests. This is a clearly a mistake because the first guests' label
was an exclusive access label, while the second guests label was a shared
access label. The lock manager should have blocked this, but I still
think we should also block it here, as the lock manager won't block it
until after you've already set the labels.
IOW we're justified in requiring strict equality of labels for all
guests, even if they technically might prevent something the kernel would
otherwise allow.
Okay then, but this is not the correct place for that check then. In the
XATTRs there is stored the original owner, not the current one. If domA
is already running, then domB doesn't need to check if its seclabel ==
original owner rather than if its seclabel == current seclabel.
What I can do is to have virSecuritySetRememberedLabel() return the
updated value of the ref counter and if it is greater than 1 require the
seclabel to be the one that @path currently has (in the caller). I mean,
these virSecurity{Get,Set}RememberedLabel APIs really treat seclabels as
an opaque data. I rather not have them understand seclabels.
> Sure, this doesn't prevent us from the following scenario:
>
> 1) set a seclabel for domA on path X
> 2) user tries to start domB with a different seclabel which also
> requires path X
> 3) while starting domB, libvirt overwrites seclabel on X effectively
> cutting of domA
> 4) imagine domB can't be started (or is destroyed later or whatever),
> libvirt starts restore, but since X's refcount = 1, the original owner
> is not restored (domA is still cut off)
>
> But I don't see any easy solution to that. I mean:
> a) As I say we don't want to check in 3) if the new seclabel would cut
> off domA. That's for kernel to decide.
Simply don't try to decide that. Require identical labels for all
guests sharing the disk.
> b) If we would want to rollback in 4) and set domA seclabel on X we
> would need a very clever algorithm. We would need to store an array of
> seclabels in XATTR and match them with domains that are doing the
> restore. For instance: there are three domains domA, domB and domC and
> assume they all share path X and have their own seclabels defined
> (A:UID, B:UID, C:UID). Lets start them all and the XATTR on X would look
> like this:
>
> {[original owner],[A:UID],[B:UID]} (and the X is currently owned by C:UID)
>
> Now, domA is shut off. How would I know what item in the array to
> remove? Note that [A:UID] was added when starting domB, and in fact
> [original owner] was added by domA.
If we block guest B from starting unless its label is identical to that
originally used by guest A, we've nothing needing to rollback for guest
B.
Fair enough.
Michal