apparmor: Allow cups-browsed to change nice value (CAP_SYS_NICE)

Bug #1897369 reported by Paul Menzel
46
This bug affects 8 people
Affects Status Importance Assigned to Milestone
cups-filters (Debian)
New
Unknown
cups-filters (Ubuntu)
Triaged
Low
Till Kamppeter

Bug Description

In Ubuntu 20.04.1 with *cups-browsed* 1.27.4-1, apparmor prevents `/usr/sbin/cups-browsed` to change its nice value.

    $ sudo dmesg | grep apparmor
    [541870.509461] audit: type=1400 audit(1600898428.089:60): apparmor="DENIED" operation="capable" profile="/usr/sbin/cups-browsed" pid=62030 comm="cups-browsed" capability=23 capname="sys_nice"
    [628298.779668] audit: type=1400 audit(1600984854.115:61): apparmor="DENIED" operation="capable" profile="/usr/sbin/cups-browsed" pid=66850 comm="cups-browsed" capability=23 capname="sys_nice"
    [714667.424963] audit: type=1400 audit(1601071220.527:62): apparmor="DENIED" operation="capable" profile="/usr/sbin/cups-browsed" pid=76828 comm="cups-browsed" capability=23 capname="sys_nice"

Revision history for this message
Paul Menzel (paulmenzel) wrote :

From the manual page capabilities(7):

       CAP_SYS_NICE
              * Lower the process nice value (nice(2), setpriority(2)) and
                change the nice value for arbitrary processes;
              * set real-time scheduling policies for calling process, and set
                scheduling policies and priorities for arbitrary processes
                (sched_setscheduler(2), sched_setparam(2), sched_setattr(2));
              * set CPU affinity for arbitrary processes (sched_setaffin‐
                ity(2));
              * set I/O scheduling class and priority for arbitrary processes
                (ioprio_set(2));
              * apply migrate_pages(2) to arbitrary processes and allow pro‐
                cesses to be migrated to arbitrary nodes;
              * apply move_pages(2) to arbitrary processes;
              * use the MPOL_MF_MOVE_ALL flag with mbind(2) and move_pages(2)

No idea, if cups-browsed should be allowed to change the nice value of *arbitrary* processes.

Revision history for this message
Launchpad Janitor (janitor) wrote :

Status changed to 'Confirmed' because the bug affects multiple users.

Changed in cups (Ubuntu):
status: New → Confirmed
Revision history for this message
B. C. Schmerker (bcschmerker) wrote :

On my system, I have a consistent Deny on cups-browsed --capable, one example from 281300Z November 2020 being:

Nov 28 13:00:24 hotrodgpc-desktop kernel: [ 52.928672] audit: type=1400 audit(1606597224.111:54): apparmor="DENIED" operation="capable" profile="/usr/sbin/cups-browsed" pid=1496 comm="cups-browsed" capability=23 capname="sys_nice"

I have set cups-browsed to complain mode in AppArmor pending the bugfix.

---

ubuntu® 20.04.1-LTS AMD64
AuthenticAMD® Athlon64® 3500+ / 3.3 GiB memory
AuthenticAMD® RS780 GPU
GNOME 3.36.3

Revision history for this message
Sebastien Bacher (seb128) wrote :

Thank you for your bug report

Till, do you know what impact that priority change failing has? Could you check with the security team if that call should be allowed by default in the cups profile?

Changed in cups (Ubuntu):
assignee: nobody → Till Kamppeter (till-kamppeter)
Revision history for this message
Till Kamppeter (till-kamppeter) wrote :

I did not have anything to control the priority in the source code of cups-browsed, I also did not find anything in the packaging of cups-filters. I also do not see any security risk in priority changing, it can only make the system faster or slower.

Perhaps systemd does the nice level change? How do I investigate this?

Revision history for this message
Till Kamppeter (till-kamppeter) wrote :

Anyone of the security team, does allowing the "sys_nice" capability for cups-browsed cause any possible security risk?

Revision history for this message
Jamie Strandboge (jdstrand) wrote :

Till, it allows quite a few things (from man capabilities):

CAP_SYS_NICE
       * Raise process nice value (nice(2), setpriority(2)) and change the
         nice value for arbitrary processes;
       * set real-time scheduling policies for calling process, and set
         scheduling policies and priorities for arbitrary processes
         (sched_setscheduler(2), sched_setparam(2), sched_setattr(2));
       * set CPU affinity for arbitrary processes (sched_setaffinity(2));
       * set I/O scheduling class and priority for arbitrary processes (io‐
         prio_set(2));
       * apply migrate_pages(2) to arbitrary processes and allow processes
         to be migrated to arbitrary nodes;
       * apply move_pages(2) to arbitrary processes;
       * use the MPOL_MF_MOVE_ALL flag with mbind(2) and move_pages(2).

cups-browsed is probably just trying to renice itself, which isn't terrible for it to try, but it probably fails gracefully with this just being noise. If it does fail gracefully, you could consider an explicit deny rule to silence the log. Eg:

  deny capability sys_nice,

That said, we've normally allowed system policy (ie, those shipped in debs) to use sys_nice if they have a legitimate use case for it.

Revision history for this message
Till Kamppeter (till-kamppeter) wrote :

I have searched the code of cups-browsed and libcupsfilters and did not find any call of the mentioned functions which require CAP_SYS_NICE. Most probably some of the library functions cups-browsed is using contains such calls.

As cups-browsed works correctly I suggest to add the "deny capability sys_nice," to silence the log messages.

Changed in cups (Ubuntu):
importance: Undecided → Low
Revision history for this message
Seth Arnold (seth-arnold) wrote :

It may also be an option to set the desired scheduling parameters via systemd.exec(5) parameters instead of asking the daemon to do the changes itself.

Thanks

Revision history for this message
Bernard Stafford (bernard010) wrote :
Revision history for this message
Bernard Stafford (bernard010) wrote :

I have 33 denied messages that increase in number every time I reboot.

Revision history for this message
Bernard Stafford (bernard010) wrote :

Apparmor audit message.

Revision history for this message
Joel Holveck (joelh) wrote :

This message doesn't seem to affect anything, from what I can tell. Here's a technical analysis.

The system call, sched_setattr, is being made in glib's g_system_thread_get_scheduler_settings. It gets the current scheduling settings, and then tests to make sure it can set them on the same thread. This call is performed when the thread pool is being created, so that the initial scheduler settings can be recorded and applied to future threads. (If that's not possible, then glib has a different mechanism that it will use instead.)

In the Linux kernel, __sched_setscheduler (which does the work for sched_setattr) tests to see if the user is trying to do anything that requires the SYS_NICE capability. There are several tests it runs, all wrapped together:

/* Simplified version of the relevant kernel code */
if (!capable(CAP_SYS_NICE)) {
  if (new_nice < old_nice)
    return -EPERM;
  if (is_rt_policy(new_policy)) {
    if (new_policy != old_policy)
      return -EPERM;
    if (new_priority > old_priority && new_priority > rlim_rtprio)
      return -EPERM;
  }
  if (is_dl_policy(new_policy))
    return -EPERM;
  /* and so on */
}

All these are guarded by one check to see if the process is allowed to make changes that require CAP_SYS_NICE. This capability check is performed regardless of whether the app actually is trying to do something that requires CAP_SYS_NICE. (In this case, it's not trying to, but the check is made regardless.)

Ok, now we've outlined the components, so I'll paint the bigger picture. glib is testing to see if it can save and restore the scheduling parameters, albeit with no changes. The kernel checks to see if the process has the SYS_NICE capability. apparmor sees that the program doesn't have that capability, denies it, and logs an audit message. But the kernel continues, and determines that the program isn't trying to do anything that needs SYS_NICE after all. The kernel tells glib that everything is fine, and glib finishes setting up the thread pool.

At no point does anything even attempt to make any actual changes to the scheduling parameters. cups_browsed doesn't want to renice itself. It's just that, as part of the startup process, the SYS_NICE capability is tested, even though it's ultimately not needed.

Personally, I like Jamie's suggestion: mute the message using a "deny" rule, since it's understood and not causing any ill effects.

If users want to do this before it gets integrated (and I suggest it gets upstreamed to Debian, where that file is introduced), then create a file named /etc/apparmor.d/local/usr.sbin.cups-browsed with the contents "deny capability sys_nice," (including the comma).

Revision history for this message
Till Kamppeter (till-kamppeter) wrote :

Joel, thank you very much for your analysis and for posting Debian bug #1016622, as the fix has to be applied in the Debian package.

affects: cups (Ubuntu) → cups-filters (Ubuntu)
Changed in cups-filters (Ubuntu):
status: Confirmed → Triaged
Changed in cups-filters (Debian):
status: Unknown → New
Revision history for this message
Paul Menzel (paulmenzel) wrote :

> All these are guarded by one check to see if the process is allowed to
> make changes that require CAP_SYS_NICE. This capability check is
> performed regardless of whether the app actually is trying to do
> something that requires CAP_SYS_NICE. (In this case, it's not trying to,
> but the check is made regardless.)

Should that be fixed in the Linux kernel, or at least reported to the developers?

Revision history for this message
Joel Holveck (joelh) wrote :

> Should that be fixed in the Linux kernel, or at least reported to the developers?

tl;dr: I don't have insight to the relevant community, but it may be worth reporting to either the kernel community or the apparmor devs.

Context: I'm just a random user who did the analysis, not an Ubuntu or Debian Developer.

I mentioned this in the Debian bug (debbugs#1016622). I can see either way that this would go; it could be a disconnect between the devs who did the capability framework vs. the devs who implemented apparmor.

Is capable() meant to be something that can have side effects? The people who wrote __sched_setscheduler clearly expected that it wouldn't, while the apparmor devs did put a side effect there (the only place they could put audit logs).

If you restructure that code to make it a bunch of `if (condition && capable(CAP_SYS_NICE))` tests, then you run the risk of not testing `capable`, or getting the order wrong. If you don't restructure that code, then you have this issue that there's no place to audit for when a capability is actually needed.

You could also extend the kernel capability API to have separate test and audit functions, so you could build that section as `if (!capable_test_only(CAP_SYS_NICE)) { ... if (condition) return capability_audit_and_return(CAP_SYS_NICE, -EPERM); ... }`, but I don't see that as particularly useful.

As I said, this seems to be a disconnect in whether or not capable() should have side effects; maybe the apparmor guys would be best positioned to engage the kernel community on the matter.

That's my thoughts, but I tried not to give recommendations. I'm not active in the Linux kernel development community, and I don't know what their preferred style is. I couldn't find any docs on the capability API (I didn't look terribly hard), so I don't know if there's documentation to discuss whether or not capable() is supposed to have side-effects.

Revision history for this message
John Johansen (jjohansen) wrote :

This is not a disconnect between the capability framework (which is integrated into the LSM), nor the devs who implemented AppArmor.

Calls to capable() can have side effects, it is an LSM hook and linux capabilities are implemented as an LSM module that is stacked with the other LSMs. So if an LSM chooses to mediate a capability, it can. AppArmor, Smack, and SELinux all mediate capable calls, which is done in addition to the regular capable subsystem check. Unless the call to capable() is marked with the option CAP_OPT_NOAUDIT, an LSM may generate audit messages.

The solution is that the caller of capable() needs to pass the option CAP_OPT_NOAUDIT if there really shouldn't be an audit message generated.

If userspace code doesn't want an audit message generated then it needs to work with the LSMs policy to make sure an audit message is not generated. AppArmor policy by default quiets auditing of capability messages if the capability is granted by policy, it also allows disabling of auditing of a message if the capability should be denied.

Revision history for this message
reliable-robin-22 (nicolasdiogo) wrote :

2023 - August

This problem persists, and it makes impossible to use a printer in Ubuntu (!)

Thanks,

Revision history for this message
Seth Arnold (seth-arnold) wrote :

@reliable-robin-22 this specific message is almost certainly unrelated to whatever problem you're facing. There's millions of people using Ubuntu and surely several of them print from time to time. (I may only print once a year, but it does work for me. :)

You should open a new bug report and populate it with details from https://wiki.ubuntu.com/DebuggingPrintingProblems

Thanks

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Duplicates of this bug

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.