Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 12 Nov 2001 18:17:42 -0800
From:      Terry Lambert <tlambert2@mindspring.com>
To:        Robert Watson <rwatson@FreeBSD.org>
Cc:        freebsd-arch@FreeBSD.org
Subject:   Re: cur{thread/proc}, or not.
Message-ID:  <3BF082C6.BA7CA05D@mindspring.com>
References:  <Pine.NEB.3.96L.1011112183454.36592A-100000@fledge.watson.org>

next in thread | previous in thread | raw e-mail | index | archive | help
Robert Watson wrote:
> > I think that the majority of the netinet code can be handled by using
> > the socket credential, instead of the process credential.
> 
> The majority, yes, but not all.  In particular, there are a number of
> desirable behaviors where you *do* want to use the process credential.  In
> particular, relating to binding activities, where current semantics permit
> a 'privileged process' to create and bind sockets such that they have
> access to otherwise restricted ports, transfer them to unprivileged
> processes, but not grant the full scope of privilege to those processes. A
> primary example of this in use in practice might be a situation where an
> I/O socket is handed off from a network daemon to an unprivileged process,
> such as inetd handing off to fingerd: fingerd should not retain inetd's
> privileges regarding many aspects of the socket's behavior.  This argument
> might be seen more convincingly from the perspective of UDP sockets.  Yes,
> it is true that in most cases use of the socket credential is desirable,
> but in a number of important cases, it is not.

I think that this case implies that the socket creation and
binding are seperated, or that it's possible to re-bind a
socket, once bound.

I think the model needs to be "reliquish priviledges"; in other
words, there is an explicit handoff, at which point this is an
allowable thing.

Putting bits like "may bind to privileged port" on unbound
sockets is, I think, a bad thing.

The easiest way to deal with this is to replace the socket
credential when the handoff takes place.

However, I think that in most cases, the priviledge handoff
associated with the handdof of a priviledged object is _intentional_,
in order to have a process with full privileges (e.g. "root") hand
off only partial privileges to another, otherwise unprivileged
process.  Specifically, it's a workaround for not having high
granularity control over privileges and/or a capabilities model
(capabilities models are, by definition, impossible to initialize
without invoking some implicit privilege, so we can ignore them as
academic curiousities for now).

If I had off access to something by handing off a descriptor,
rather than handing off a reference and forcing you to create
your own descriptor, then my handoff of rights is intentional,
and not something which needs to be blocked.


> There are some related cases in VFS, where we consider a per-jail
> securelevel based on the acting process, not the file-opening process.

I don't like these, but I accept that they must exist for jail
code to function.

> Similarly, there are some ioctl's on tty devices that are subject
> (process)  credential authorized: these are in general present to handle
> the case where descriptors to these objects are (and must be) inherited.

I think this and the previous case can be folded together as
"user option", similar to not being able to have simultaneous
use of your X server, or the ability to load kernel modules, and
secure level 2 at the same time: it's a trade off, and it is a
conscious one make at user discretion.


> There are some related cases, such as fd passing via unix domain sockets,
> where the same properties can prove very useful: the ability to transfer
> access to sockets/files via LPC as 'rights' rather than delegating all
> rights.

The read/write rights for object opened by another process, or
opened in an SUID case, with a subsequent relinquishing of the
credentials that permitted the operation in the first place are
the interesting cases, I think.  The others fall into exception
and administrative fiat.


> > > With the eventual addition
> > > of td->td_ucred, it will be desirable to use the credential for the
> > > current thread, rather than the proc, which will require locking to use.
> >
> > I think locking credential instances is bad.
> 
> That is not what we're talking about.  We're talking about locking the
> process structure.  No one is suggesting this.

I think locking the process structure/thread structure is bad,
particularly when you are only doing it to get at the credential,
and it's probably the wrong credential anyway.


> > There is a lot of this type of fuzzy thinking, asking "how can I
> > propagate the process credential that I used to use for this operation
> > down to the underlying code?", when the real question should be "what is
> > the appropriate credential to use for this operation, and is the process
> > credential really what I want to use in this case?".
> 
> I agree there has been a lot of fuzzy thinking.  I also agree that, in
> every case, we need to carefully consider the credential used.  In
> particular, this is true in the 'new world order' of td_ucred, where we'll
> now often have three credentials to decide from:
> 
> (1) Mutable p_ucred (requires proc lock)
> (2) Cached td_ucred (requires no lock)
> (3) Cached so->so_cred, file->f_cred, et al.
> 
> In most cases, (2) or (3) will be appropriate.  In some situations,
> particularly when it comes to credential update, (1) will be appropriate.

I consider caching of mutable data harmful.  Here, you inply that
there will be cached mutable data in scope at the time that the
decision to use the mutable data must be made.

I think this is incredibly messy, and will only lead to mistakes
about what's being used.

I think that if a right is granted, it's granted, and only if you
define a specific revocation protocol that can be procedurally
linked so as to notify those people who need to make the assumption
of non-mutability for performance reasons, is it OK to change it.

I would be very tempted to:

1)	const the credentials that are non-mutable; this is hard,
	but manageable through a cast after the reference count
	adjustment.

2)	leave all unnecessary credentials out of scope, so that
	the decision as to which to use is obvious.

3)	Discourage the implementation of a revocation protocol.

I realize that this is a tradeoff between explicit and implicit,
and that it results in irrevokable grant of priviledges, in so
far as the credential reference granted grants such priviledge,
but the cases where this is bas are incredible exceptions, such
as revocation of a clearance to someone formerly having clearance
on a machine where you are going to trust their processes to
continue to run, at the lowered clearance level.

Continuing to let the code run in this situation will probably
happen when hell freezes over.


> > I think it's possible to get rid of most of the process credential
> > references -- and therefore, most of the proc references -- at all
> > points below the /sys/kern/uipc_socket*.c level.
> 
> No, it's not, in a number of very important cases, of which I've
> identified at least three above.

I disagree with two of them (see above), and thingk the third is
an incredible exception.  If you don't think so, then perhaps it's
time we rethink the underlying problem being solved, and change
the solution to be more rational so as to not require that.

The problem here is that you are trying to do something as an
afterthought (add security features not previously present), and
avoid some of the redesign that should happen, at the cost of a
performance penalty.


> Structuring code to have a notion of "but the kernel asked" vs. "but a
> user asked" is difficult, and something I'm not sure we have a grasp on
> how to approach.  Sometimes, for example, FSCRED or NOCRED is used as a
> "special-case" credential to say "do it anyway".  This is often broken
> when it comes to distributed file systems where a client system may not
> simply be able to assert "because I said so", and probably reflects
> unclear thinking on the topic.

Most distributed FS's have this issue.  You're not going to resolve
it by fiat, since it's impossible to do that without an enforcible
distributed cache coherency protocol,. such that when the cached
data gets to the client, it can be forecefully updated by the server,
should it become necessary.

I think you are concentrating too much on the revocation of granted
rights issue, rather than on the grant of nonrevokable right issue,
which is what I think should be the tack taken.


> > I would much rather that the credentials be object referenced off of
> > non-process, non-thread objects, based on whatever the correct scoping
> > really is, for the security model you want to enforce.  My "accept"
> > example is only one of a class of changes that could facilitate this.
> 
> I think everyone agrees that the 'cached credential' model is the right
> approach for many of these cases, but I think it's over-reaching to claim
> it's appropriate in all cases.  The question then becomes, how do we
> access the relevant 'subject' credential to authorize the operation: is it
> something that is passed down via the call stack (possibly via 'struct
> thread *td'), or is it something implicit to the run-time environmenta
> ('curproc'/'curthread'), which is precisely the question I was trying to
> resolve through my post.  If 'curproc'/'curthread' is truly undesirable,
> then we can simply eliminate its use, and replace that with almost
> universal passing of 'struct thread' (for the purposes of authorization,
> but also for other purposes: target of copyin/copyout/aio, scheduling,
> ktrace, ...).  If it is acceptable to maintain the use of curproc, we may
> want to change some of our primitives to represent it being available.

I think it's truly undesirable, since it limits the scalability
of number of CPUs, and the ability to create clusters resonably,
by putting a lot of bus contention into operations which should
not involve inter-CPU cache coherency issues in the first place.

I don't believe you will be able to grant priviledge on one node
of a NUMA cluster, translate the process to another node, and then
revoke the privilege on a third node, and have that revocation
take effect without leaving a race window in which the putatively
de-credentialed process is still able to act with the granted
credentials before the node on which it is running receives the
revocation.

This is exactly the X.509 certificate revocation problem, and it'd
be nice if everyone could afford to check with the certificate
authority to see the revocation list each and every time that they
wanted to invoke the privilege granted by holding the ceritificate,
but that's just not scalable to real world application.

If you want to do this, then you need to change the way you handle
it entirely; for X.509, this is generally done by providing for a
time based expiriation, and a recertification requirement.  No one
really looks at the CRLs, in practice.

In the limit, this scales by granting the rights for longer and
longer windows, as utilization increases.  It's not very satisfying.


> Right now, we're in a state of limbo: the official policy (if you will) is
> 'XXX'.  We should either eliminate it from general use, or we should use
> it where it's appropriate :-).

I definitely agree that there should be an uambiguous policy in
place... I just think I disagree wih you about what it should be.
:-).

-- Terry

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3BF082C6.BA7CA05D>