2003-05-15 14:03:24

by David Howells

[permalink] [raw]
Subject: Alternative to PAGs


Hi Linus,

Okay, starting with a clean slate and writing PAGs off as a bad idea, how
about trying to work out what is required first? As I see it:

THE REQUIREMENTS
================

(1) Credentials/tokens/keys/whatever are held in a "keyring".

(2) A keyring is destroyed when the last reference goes away (kernel
resources are precious, though it may be possible to store credentials
in swapspace somehow).

(3) Every user has to have a default keyring that is created the first time
they log in [Linus demands this].

(4) A user has to be able to override the default keyring, such that they
can, for instance, perform operations on a remote filesystem with a
different credential.

(5) A user has to be able to run a program with a reduced set of
credentials.

(6) A process must be able to pass a subset of its credentials to a second,
already running process so that the second process can perform some
service on its behalf.

This gets tricky if the service process is performing services for a
number of processes simultaneously, each of which has its own set of
credentials.

(7) A process should be able to discard any credential it has access to,
particularly in conjunction with (6).

(8) It must be possible to withdraw a credential.

(9) The credentials governing access to a file must be associated with a
struct file, not with the process context currently accessing a file.

(10) A struct file will gain its credentials at the time it is opened.


POSSIBLE EXTENSIONS
===================

(11) Threads should perhaps share a common set of credentials, but be able to
adjust them on a per-thread basis (see (6)).

(12) A SUID process should add the credentials it gains from its new user ID
to those it inherited from its original context.

(13) There's one place Win32 has an advantage, I think: calls for setting up
handles (files, mutexes, etc) take security context parameters.


I think "groups" specific keyrings and arbitrary joinable keyrings are
superfluous (that's what ACLs are for) and a systems maintenance nightmare, so
I haven't included them.


SUGGESTED IMPLEMENTATION
========================

As far as implementation goes, perhaps each task_struct and each file should
point to a stack of keyrings:


+------+
| |
+--------------------->| USER |
| | |
| +------+
|
+------+ +------+ +------+ +------+
| | | | | | | |
| TASK |----->| SUID |---->| PRIV |---->| USER |
| | | | | | | |
+------+ +------+ +------+ +------+
^ ^
+------+ | |
| | | |
| FILE |---------+ |
| | |
+------+ |
|
+------+ |
| | |
| FILE |----------------------+
| | ^
+------+ |
|
+------+ +------+ +------+
| | | | | |
| TASK |----->| FILE |----------------->| USER |
| | | | | |
+------+ +------+ +------+

The keyrings in the stack would then be refcounted, and the next pointers
would be immutable.

struct keyring {
struct keyring *next;
struct keyring *conjunction;
struct list_head keys;
atomic_t usage;
int type;
#define KEYRING_USER 0
#define KEYRING_PRIVATE 1
#define KEYRING_SUID 2
#define KEYRING_FD 3
};

struct key {
struct list_head link;
atomic_t usage;
int type;
#define KEY_POSITIVE 0
#define KEY_NEGATIVE 1
void *credential;
};

And then add the following syscalls:

(*) auth_clear_stack(int completely)

Totally clear a process's stack (either completely or of everything but
the user credentials).

(*) auth_add_cred(const char *fs, const char *domain, void *data);

Add a new credential to the TOS keyring. The key would be negative if
data is NULL.

(*) auth_push_empty()

Push an empty keyring onto this process's stack.

(*) auth_push_fdcreds(int fd)

Push the credentials associated with fd onto the stack as a preferred
alternative.

(*) auth_pop()

Pop the top credential off of the stack.


David


2003-05-15 16:41:10

by Linus Torvalds

[permalink] [raw]
Subject: Re: Alternative to PAGs


On Thu, 15 May 2003, David Howells wrote:
>
> (1) Credentials/tokens/keys/whatever are held in a "keyring".

Not "a keyring". At least to me, a "keyring" implies a "bunch of keys",
with no structure to it. That's not what you want from a maintenance
perspective.

You want to have multiple keyrings, with the ability to say "I am now
done with those keys I got from source X". That doesn't mean that you drop
all your credentials, it only means that you have stopped one "session"
(and clearly a session should be able to have many keys).

In particular, one "session" is your default one, and again, clearly that
is not necessarily just a single key.

Also, the fact that you have a default one MUST NOT preclude you from
having access to _another_ keyring too. But accessing that other keyring
does not mean that the keys from that should be added to your default
keyring: you're sharing the default keyring with other processes, so
modifying your default one to add capabilities to _one_ process is clearly
the wrong thing to do.

In other words, you want to not maintain individual keys, you always want
to maintain a _group_ of keys. You don't get "a key" by default. You get
"a group of keys" by default. The group may, of course, contain just a
single key.

So what I think you want is a hierarchy:

- "my credentials" is a "set of keyrings"
- a "keyring" is a "set of keys" aka a "session" (when used as a AFS
session, this is a PAG. Other users might have other naming conventions
for their sets of keys)
- a "key" is an indivisible blob, as far as the base kernel is concerned
(and likely most users of the key too, for that matter).

A key must be on a ring, ie all keys are part of a "session". But a
session may be associated with an arbitrary number of processes, and a
process must be able to maintain multiple sessions at once.

> (4) A user has to be able to override the default keyring, such that they
> can, for instance, perform operations on a remote filesystem with a
> different credential.

I disagree. Your (4) comes from your (1) - inability to have multiple
keyrings. If you have multiple keyrings, you don't "override" anything.
You just have a stackign order (ie the credentials is a _sorted_ list of
keyrings), and you search the credentials for valid keys in order.

> (6) A process must be able to pass a subset of its credentials to a second,
> already running process so that the second process can perform some
> service on its behalf.

A "subset of credentials" _is_ a keyring. You only ever pass keyrings
around. You don't pass individual keys around, but you also aren't forced
to pass off _all_ of your credentials at the same time.

> (8) It must be possible to withdraw a credential.

I think you have a few (different) cases of invalidation:

- you "change the lock" (ie the old key that gave you something now
becomes useless)

- you remove access to a group of keys (ie you remove a keyring, aka
"terminate a session" for one user). Other processes in that session
still have access to the keyring.

- you maintain a session/keyring (add or remove keys from it, and all
processes usign that session will see the changes to the session).

So there are at least three (very different) set of operations that
withdraw a credential and work on different levels.

> (9) The credentials governing access to a file must be associated with a
> struct file, not with the process context currently accessing a file.

This does beg the question: which level of credentials does the file get
associated with. Does it get _all_ the credentials that the opener had (ie
access to every keyring)? Or does it get associated with the one session
that the open() decided was relevant? Or does it get associated with the
single key that was used for the open?

I suspect that the open file should be associated with one "session".

> (10) A struct file will gain its credentials at the time it is opened.

Agreed.

> (12) A SUID process should add the credentials it gains from its new user ID
> to those it inherited from its original context.

But it has to keep them _separate_: it needs to be able to drop it's own
extra keys at some point, and revert to "non-suid-ness". This is why it's
so important to consider the "set of credentials" to be more than just a
"bunch of keys". It has to be bunched up some way.

And my suggestion is that the keyring is that "bunch", and that a suid
application is nothign more than one that got a special keyring at
execve() time. A keyring that it can choose to drop.

Linus

2003-05-15 23:01:53

by Garance A Drosihn

[permalink] [raw]
Subject: [OpenAFS-devel] Re: Alternative to PAGs

At 9:53 AM -0700 5/15/03, Linus Torvalds wrote:
>On Thu, 15 May 2003, David Howells wrote:
> >
> > (4) A user has to be able to override the default keyring,
> > such that they can, for instance, perform operations
> > on a remote filesystem with a different credential.
>
>I disagree. Your (4) comes from your (1) - inability to have
>multiple keyrings. If you have multiple keyrings, you don't
>"override" anything. You just have a stacking order (ie the
>credentials is a _sorted_ list of keyrings), and you search
>the credentials for valid keys in order.

I am not completely sure what David meant. I think I agree
with what he meant, but would argue with your assumption.

A process can be both [email protected] and [email protected] at
the same moment. By being [email protected], I'm also in any
number of AFS groups at RPI (a number that I have no knowledge
of or control over).

What AFS does not want is for a single process to be [email protected]
and [email protected] at the exact same time. That is to avoid the
question of what open() should do on a file which is permitted:

drosehn rlidwka
linus none

The above says [email protected] should have absolutely no access to
the file, no matter what AFS or unix groups he is in. At the same
time, [email protected] has complete access to the file. If the
process claims to be both of those users, then what is open()
to do? In the pure-unix world, is a process both userid linus
and userid drosehn? No. (it might have different values for
userid and effectiveUserid, but it only has one value for userid
and only one value for effectiveUserid).

You could argue that open() should give access, or that it should
not give access, but my point is that the limitation against being
multiple AFS users at a single time is to avoid that question.
It was not the side-effect of some other decision.

*1 = Actually, in afs the permissions are on a directory level,
but I'd agree that they *should* be on the file level, and
thus I said it that way...

--
Garance Alistair Drosehn = [email protected]
Senior Systems Programmer or [email protected]
Rensselaer Polytechnic Institute or [email protected]

2003-05-16 00:30:32

by Garance A Drosihn

[permalink] [raw]
Subject: [OpenAFS-devel] Re: Alternative to PAGs

I can see some advantage to the ability to switch into an
already-existing pag, so let me switch sides for a moment.

If someone came to me and would pay me to implement "something
just like a PAG, except that there must be an ability to join
an already-existing PAG", I would suggest the following:

1) Creation of PAGs are handled exactly as they are now.
Lightweight creation, etc.
2) If the user of some already-existing PAG wants that
PAG to be "join-able", they run some special command
which registers the PAG with a name, and with the
details of how to authenticate to that named-PAG. One
could select from a variety of authentication methods.
3) Then from some other process (which would probably already
have a PAG) could run a special setpag command, and if
they provide the proper authentication, then that
process would switch to the already-existing pag.

This isn't quite what Linus is looking for, but it provides some
of it without adding much overhead to the most common usage of
PAGs. Step #2 should probably require that the user can not
register a name for a PAG unless they pass some authentication
test to prove that they can. Ie, you don't want me to walk up
to your terminal, gesture excitedly at some fire across the
street, and then type in the 'register pag' command while you're
not paying attention to me or your computer.

Users won't know or care about the range for PAG values, because
they will only be dealing with the "registered names" for a PAG.

I still would prefer PAGs as they are, because I think this still
opens up some issues that I'd prefer to leave closed. But I
think that would be doable.

I should probably also apologize to David here, because he's
actually doing useful work. Here I'm just butting in because it
would be so great to see PAG support as part of linux, instead
of something we have to keep sticking on the side of it. RPI
is moving our AFS cell to redhat linux servers, and the easier
it is for openafs on linux, the nicer it will be for us.

--
Garance Alistair Drosehn = [email protected]
Senior Systems Programmer or [email protected]
Rensselaer Polytechnic Institute or [email protected]

2003-05-16 00:34:05

by Russ Allbery

[permalink] [raw]
Subject: Re: [OpenAFS-devel] Re: Alternative to PAGs

Garance A Drosihn <[email protected]> writes:

> What AFS does not want is for a single process to be [email protected] and
> [email protected] at the exact same time. That is to avoid the question of
> what open() should do on a file which is permitted:

> drosehn rlidwka
> linus none

An even better example without an obvious answer (which in this case is
that the open should be allowed, since that ACL says that drosehn should
be able to open the file and says nothing about linus) would be if linus
had negative rights (in other words, if the ACL actively asserted that
linus should *not* be able to open the file regardless of the other ACLs).

AFS supports the notion of negative rights primarily in combination with
groups, so you can have a situation like:

Normal rights:
organization:itss rlidwka

Negative rights:
rra rlidwka

where rra is a member of organization:itss. rra will be denied access to
that directory despite the fact that his membership in organization:itss
would normally give him full rights.

--
Russ Allbery ([email protected]) <http://www.eyrie.org/~eagle/>

2003-05-16 00:44:11

by Nathan Neulinger

[permalink] [raw]
Subject: Re: [OpenAFS-devel] Re: Alternative to PAGs


> I should probably also apologize to David here, because he's
> actually doing useful work. Here I'm just butting in because it
> would be so great to see PAG support as part of linux, instead
> of something we have to keep sticking on the side of it. RPI
> is moving our AFS cell to redhat linux servers, and the easier
> it is for openafs on linux, the nicer it will be for us.

I agree, having cleanly supported PAG support in the kernel would be
great. The problem is that we can't just "keep sticking on the side of
it". We'd be perfectly happy to do so - that's the way we do it on every
other unix platform that afs has EVER supported as far as I know.

The problem is that certain kernel developers have specifically and
intentionally (YES, intentionally, I've seen the discussions.) gone to
efforts to prevent the AFS developers from continuing to do this. We're
just looking for an alternative that allows AFS to continue to function
at all on newer kernels than 2.4.

-- Nathan

------------------------------------------------------------
Nathan Neulinger EMail: [email protected]
University of Missouri - Rolla Phone: (573) 341-4841
Computing Services Fax: (573) 341-4216

2003-05-16 10:55:51

by David Howells

[permalink] [raw]
Subject: Re: Alternative to PAGs


> > (1) Credentials/tokens/keys/whatever are held in a "keyring".
>
> Not "a keyring". At least to me, a "keyring" implies a "bunch of keys",
> with no structure to it. That's not what you want from a maintenance
> perspective.

I don't think I was clear. I meant "keyring" as a container holding a bunch of
keys, not as in an auth session (ie "a bunch of keyrings, ordered in some
fashion").

What do you mean by "structure" anyway? You later seem to assume a
session/keyring is just an arbitrary key container holding a set of
potentially unrelated keys.

> You want to have multiple keyrings, with the ability to say "I am now done
> with those keys I got from source X". That doesn't mean that you drop all
> your credentials, it only means that you have stopped one "session" (and
> clearly a session should be able to have many keys).

I never suggested that it would mean that.

I take it you mean "session" == "keyring" in this terminology?

> Also, the fact that you have a default one MUST NOT preclude you from
> having access to _another_ keyring too.

Agreed. But you still need some way to say in any given situation the order in
which keyrings should be considered, and whether or not they should be
considered.

> But accessing that other keyring does not mean that the keys from that
> should be added to your default keyring: you're sharing the default keyring
> with other processes, so modifying your default one to add capabilities to
> _one_ process is clearly the wrong thing to do.

Okay, I can accept that.

> In other words, you want to not maintain individual keys, you always want
> to maintain a _group_ of keys. You don't get "a key" by default. You get
> "a group of keys" by default. The group may, of course, contain just a
> single key.

Are you thinking that all the keys in a keyring should come from the same
authority (for instance Redhat's Kerberos service)? If so, that might make
"labelling" them easier... On the other hand, that would then mean you'd need
sessions to be groups of rings, so maybe not.

> So what I think you want is a hierarchy:
>
> - "my credentials" is a "set of keyrings"
> - a "keyring" is a "set of keys" aka a "session" (when used as a AFS
> session, this is a PAG. Other users might have other naming conventions
> for their sets of keys)
> - a "key" is an indivisible blob, as far as the base kernel is concerned
> (and likely most users of the key too, for that matter).

This precludes anything but AFS using PAGs... All a PAG is is a unique (though
recyclable) label by which a credentials can be indexed.

> A key must be on a ring, ie all keys are part of a "session".

I take it you mean a key must be on at least one ring...

> But a session may be associated with an arbitrary number of processes, and a
> process must be able to maintain multiple sessions at once.

I think, then, there must be facilities for:

(1) listing (or iterating over) the keyrings/sessions to which a process
subscribes.

(2) listing the keys inside a keyring to which a process subscribes.

(3) creating a new keyring.

(4) linking a key from any keyring to another (like the link() function does
for files).

(5) adding a new key to a keyring.

(6) updating a key in a keyring (ie: avoiding expiration).

(7) withdrawing a key from a keyring.

(8) unsubscribing from a keyring.

(9) opening a file with reference to a particular keyring.

(10) rearrange the set of keyrings (read sequence rather than set, I suppose)
to affect the search ordering.

> > (4) A user has to be able to override the default keyring, such that they
> > can, for instance, perform operations on a remote filesystem with a
> > different credential.
>
> I disagree. Your (4) comes from your (1) - inability to have multiple
> keyrings. If you have multiple keyrings, you don't "override" anything.
> You just have a stackign order (ie the credentials is a _sorted_ list of
> keyrings), and you search the credentials for valid keys in order.

Yes, that is one way of overriding the default keyring.

> > (6) A process must be able to pass a subset of its credentials to a
> > second, already running process so that the second process can
> > perform some service on its behalf.
>
> A "subset of credentials" _is_ a keyring. You only ever pass keyrings
> around. You don't pass individual keys around, but you also aren't forced
> to pass off _all_ of your credentials at the same time.

Okay.

> I think you have a few (different) cases of invalidation:
>
> - you "change the lock" (ie the old key that gave you something now
> becomes useless)
>
> - you remove access to a group of keys (ie you remove a keyring, aka
> "terminate a session" for one user). Other processes in that session
> still have access to the keyring.
>
> - you maintain a session/keyring (add or remove keys from it, and all
> processes usign that session will see the changes to the session).
>
> So there are at least three (very different) set of operations that
> withdraw a credential and work on different levels.

I can think of a fourth: updating a key that was due to expire, though that
would best be done as a combo delete & add operation as keys ought to be
immutable once created.

> > (9) The credentials governing access to a file must be associated with a
> > struct file, not with the process context currently accessing a file.
>
> This does beg the question: which level of credentials does the file get
> associated with. Does it get _all_ the credentials that the opener had (ie
> access to every keyring)? Or does it get associated with the one session
> that the open() decided was relevant? Or does it get associated with the
> single key that was used for the open?
>
> I suspect that the open file should be associated with one "session".

I agree. If you can construct a new keyring with an arbitrary subset of all
the keys you have access to, then you can say "open with _that_ keyring"
(though the keyring in question might be the default FS op one, sort of like
fsuid).

> > (12) A SUID process should add the credentials it gains from its new user
> > ID to those it inherited from its original context.
>
> But it has to keep them _separate_: it needs to be able to drop it's own
> extra keys at some point, and revert to "non-suid-ness".

Okay, I'll grant you that. I have seen a document discussing PAGs (for Coda I
think it was) that suggested that SUID program execution places the process in
a new PAG, and that setuid(SAVED_IDS) reverts to the original.

> This is why it's so important to consider the "set of credentials" to be
> more than just a "bunch of keys". It has to be bunched up some way.

See my suggestion above

> And my suggestion is that the keyring is that "bunch", and that a suid
> application is nothign more than one that got a special keyring at
> execve() time. A keyring that it can choose to drop.

Fair enough. Should UID/GID/GROUPS[] then be a credential/key in its own
right? If that's the case, then a process should be able to select a key (or
keyring) as being the one to supply this information.

David