On Sun, 1 May 2005, Mike Smith wrote:
>
> On Apr 30, 2005, at 8:11 AM, John Dalgliesh wrote:
>
> > You can trust the daemon when it is root, because root can do
> > anything.
>
> That's not trust, but it highlights exactly what's *wrong* with
> running as root.
... later ...
> Right again. Because a degree of trust is implicit in running as root,
> and the system only wants things that are trusted running as root.
Surely you see there is a contradiction there? I agree with the second
statement not the first. Running something as root implies that it is
trusted. And sometimes you will require something to be run as root to
prove its trustworthiness to the system.
> If you have a daemon helper for your kext, it doesn't have to run as
> root.
>
> That way, the system does not have to trust it. Instead, it's forced
> to trust
> your kext even though it does not want to.
I'm glad at least that you view running a daemon as root to be the same as
putting code in a kext. Now we can drop the whole 'don't do that in the
kernel' thread of the argument and focus on the trust issue.
I see a major problem with making a kext depend on anything external. The
more complicated it is, the worse the dependency. The problem is that
having an external dependency _extends your trust to that dependency_.
Even when it is just giving you data, you have to trust that data. The
tracking of tainted data in perl is a concrete example of this thinking.
I'm not sure I understand your comment about being forced to trust my
kext. Of course my kext is trusted; the user indicated so by installing
it. All of my driver is trusted. If I make it have external dependencies,
I'd better make sure they are trusted too, or else I just opened a
security hole.
There's no point trying to split a driver up into trusted and untrusted
parts, if that weakens the security of the whole by depending on (and
thereby trusting to some extent) the so-called 'untrusted' part.
> > From a quick look, the NVRAM property setter appears to be a good
> > example.
> > If you're not root, you can only set a few well-known user-settable
> > properties. If you are root, you can set any property. The kernel
> > has no
> > way of knowing what affect setting it to the given data will have - it
> > trusts calls from root to give it data that isn't bogus. And the type
> > checking that it does perform is equally available to kernel
> > callers as
> > it is to userspace callers.
>
> Right again. Unless the system trusts you to become root, you can't
> do much in the way of silly things.
Surely you see the correspondence between the NVRAM module which can write
system-controlling data, and a driver for a PCI card that runs firmware
on a chip with full access to the PCI bus?
They both have to be able to trust the source of that data. Thus they
check that the provider is running as root.
> Again, this is why as much code as possible should run untrusted, and
> why you should do your file I/O in a daemon that is *not* running as
> root.
So how do you explain the design of the NVRAM module then?
You might as well argue that kextload should not be run as root since it
does file I/O.
> >> There are many good reasons not to instigate file I/O inside the
> >> kernel; some
> >> more are:
> >>
> >> - There's rarely a need for it. The basic principle; when in doubt,
> >> leave it out.
> >> - Deadlock opportunities abound.
> >> - The I/O subsystem is not *designed* to handle I/O requests from
> >> inside
> >> the kernel.
> >>
> >
> > Those are all good reasons. I totally agree that as a general rule you
> > don't want to do file I/O - or anything else for that matter -
> > inside the
> > kernel. But when there is an interaction between userspace and the
> > kernel
> > you have to be able to validate that interaction. The security
> > issues of
> > relying on a process external to the kernel are far worse than
> > those of
> > relying on a regular file external to the kernel, not to mention the
> > general badness of an increase in complexity.
>
> On the contrary. The process is subject to all the normal rules
> regarding
> file access, as well as having all the benefits that are conferred on a
> piece of code playing by the rules.
I don't see how that response addresses my point about importing the
security of the process into the kernel.
> > And surely the funky virtual memory and kernel threads that make up
> > the
> > Darwin kernel environment alleviate some of the deadlock/design
> > concerns?
>
> That's a great design approach: "it's complicated, surely it must
> work". 8)
My point was not that it was complicated. My point was that the kernel
environment is very much like the process environment. So the arguments of
potential deadlock and 'not designed for it' are pretty weak. Unless
you're calling it at primary interrupt time or something, it won't be any
different to calling it via a system call.
> > What about the pager and all the other places that do in-kernel IO?
>
> The pager has a user-space application (dynamic_pager) specifically
> so that it can create the files it uses for paging.
I'm talking about reading existing files not creating files. Like I said
earlier, I understand that creating a file is an involved process and am
happy to stay away from it. But with the KPI reading a file just got
really easy:
vnode_open( char * name, mode, &ptr );
vnode_unlock( ptr );
vn_rdwr( READ, ptr, buf, size, offset, flags );
You don't even need to fiddle around with nameidata any more!
> There are several pieces of code inside the kernel that know how to go
> about creating files safely. Those pieces of code export APIs that
> allow
> other pieces of code to create files; those APIs are called "system
> calls".
Then there is kern_acct which writes out process accounting info when the
process exits; kern_audit which writes out auditing log files; kern_core
which writes out core files; kern_exec reads for execve; nfs for its
lockfiles (nfs even has a daemon already!), and a handful of other places.
They are not all exported via syscalls. The corefile in particular could
easily be done with a daemon running as the user. The point is that there
are in-kernel clients of the file system.
But anyway this is the boring part of the thread. Doing the file I/O in
the kernel or doing it in a daemon isn't a big deal (except for being a
major pain to sync up).
Not being allowed to run your daemon as root is the huge and important
issue.
> >> - It's rare that you really need to read/write as root, and it is
> >> hard to get
> >> correct permissions enforcement for an externally-instigated
> >> operation
> >> if you're requesting it inside the kernel.
> >
> > That one is a bit of a worry, and comes back to the trust issue.
> > Even if I
> > was to make a daemon running outside the kernel, then running it as
> > root
> > would be frowned upon? I really _really_ don't want any old user to be
> > able to send me the firmware that runs on my PCI card.
>
> So validate the firmware. There's nothing preventing "any old user"
> from
> switching the firmware file out from under your firmware loader, and no
> amount of running it as root will save you there.
Of course there is! It's called file access permissions. The firmware file
is writeable only by root, so any user can not just up and replace it.
If we had a daemon running as root also, then we could trust that it was
reading and sending us the right file; a file approved by an
administrator.
This idea of validation is exactly the problem. You can't dynamically
validate the firmware, any more than you can validate a kext before you
load it. In both cases, you get the user to do the 'validation' by
requiring an admin password in order to install it. Once installed, it
cannot be tampered with except by an admin.
Validating by hashing the firmware contents would be a poor solution for
me, if that is what you are suggesting. Users of my drivers get their
firmware from the website of the manufacturer. These firmwares can, and
do, change regularly. I have no desire to update my drivers every time a
manufacturer makes some small change to their firmware.
If it comes to it, then I'd much rather only update them when one
manufacturer makes a change to their kernel :)
> = Mike
Another problem with not running as root: What if the driver has to run
before any user logs in? One of the uses of my drivers is for internet
access via satellite. I can easily imagine a scenario with one machine
providing the access for a small network. A user should not be required to
log on to start up network services on that machine.
And anyway, which user should it be if not root?
A startup item is not appropriate for hot pluggable drivers such those
for PCMCIA cards (and USB/FireWire boxes, but they don't have quite the
same trust issues).
{P^/
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-drivers mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/darwin-drivers/email@hidden
This email sent to email@hidden