From owner-freebsd-arch Mon Nov 12 18:17: 3 2001 Delivered-To: freebsd-arch@freebsd.org Received: from gull.prod.itd.earthlink.net (gull.mail.pas.earthlink.net [207.217.120.84]) by hub.freebsd.org (Postfix) with ESMTP id 542E637B416; Mon, 12 Nov 2001 18:16:54 -0800 (PST) Received: from dialup-209.247.141.234.dial1.sanjose1.level3.net ([209.247.141.234] helo=mindspring.com) by gull.prod.itd.earthlink.net with esmtp (Exim 3.33 #1) id 163T87-0001fz-00; Mon, 12 Nov 2001 18:16:52 -0800 Message-ID: <3BF082C6.BA7CA05D@mindspring.com> Date: Mon, 12 Nov 2001 18:17:42 -0800 From: Terry Lambert Reply-To: tlambert2@mindspring.com X-Mailer: Mozilla 4.7 [en]C-CCK-MCD {Sony} (Win98; U) X-Accept-Language: en MIME-Version: 1.0 To: Robert Watson Cc: freebsd-arch@FreeBSD.org Subject: Re: cur{thread/proc}, or not. References: Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Sender: owner-freebsd-arch@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG 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