Credentials Process Groups (CPG) for Solaris a.k.a. PAGs/Keyrings BACKGROUND ---------- Today the Solaris NFS client can only associate cryptographic credentials with all of each users' processes. Other operating systems can associate cryptographic credentials with users' login sessions, which is desirable in some cases and trivially enables destruction of cryptographic credentials when the last reference vanishes. Our solution is based on grouping processes through cred_t, with an extensible notion of process groups. This solution is generic generic and will enable solutions to other problems as described below. ARCHITECTURE SUMMARY -------------------- Credentials Process Groups provide a new process grouping facility that tracks group membership through cred_t. New group types can be added with a simple system call. PAM modules and applications ensure that new login sessions are placed into new CPGs (or joined to existing ones) as necessary and associate with those CPGs whatever state is appropriate. Initially this will be used to associate Kerberos V credentials with user login sessions' processes and to ensure their destruction when the user's last process and open file reference with a given CPG vanish (credentials destruction is handled by a per-{CPG type, zone} service. But it can also be used to associate specific devices with virtualized device nodes (e.g., a /dev/audio equivalent of /dev/tty). Users will not have to be aware of CPGs at all, though pcred(1) will be extended to allow for observability and for changing CPG memberships. kinit(1) too will be extended to support the use of CPGs for recording the location of a user's Kerberos V credentials. To see all the new interfaces and changed interfaces look at the manpages linked to from the webrev: - man/cpg_change.txt - man/pam_unix_cred-diffs - man/pam_krb5-diffs - man/ucred_get-diffs - man/gssd-diffs - man/gss_acquire_cred_ucred.txt - man/kernel_APIs The remainder of this README is mostly design review material. INTRODUCTION ------------ Credentials Process Groups (CPGs) allow for grouping the processes that make up a login session together, much like other process grouping schemes in Solaris, but with two crucial differences: a) new CPG types can be added trivially (with a system call), with extensible semantics (so we need not do this again and again), b) group membership is represented in cred_t. The initial consumer of CPGs will be the Solaris Kerberos stack, and will benefit the secure NFS client, ssh, and other kerberized applications, as well as cronjobs that need Kerberos V credentials. But there is also strong interest from the Solaris Boomer (sound) i-team, such that Boomer may be the first consumer. Other potential uses include management of ssh-agents, PKCS#11 tokens, and devices for login seats (consoles, non-console seats, remote seats such as Sun Ray), etcetera. It may be possible to migrate audit context into a CPG, and it may be possible to migrate the FGAP (fine grained access permissions) to use CPGs as well. In all these cases there's a need to associate processes with resources related to the login sessions that spawned the processes. Often we use environment variables for this, but, for reasons that shall become clear below, environment variables are not always sufficient. CPGs will be visible through cred_t, ucred, and proc(1), and, therefore, useful in any kernel and user context where a cred_t or ucred is available. One common use of credentials process groups will be to help kernel sub-systems and user-land helper daemons find resources that are to be used only for processes belonging to a particular login session. Environment variables are not easily accessible from kernel and IPC peer contests. Making them so is not an obvious project. As it is, system calls and helper daemons such as nscd(1M), gssd(1M), keyserv(1M), etcetera, don't have much more information about a process' logon session's resources than they can glean from the process' cred_t/ucred. A solution to this problem that extends cred_t/ucred would fit in naturally into Solaris. Additionally, if such a solution could track the number of references to a login session and notify interested parties that all processes in some session have exited and all related open file references have be relinquished, then automatic release (and revocation of access to) such resources would also be possible. At this point it may be useful to see an brief overview of AFS' PAG concept and the Linux kernel keyring facility. See README.PAGs and README.Linux-keyrings, respectively. We have considered re-using existing process grouping facilities in Solaris: - System V process groups - SVR4 session IDs - task IDs - audit IDs - process contracts None of these are suitable, for a variety of reasons, for use as a Solaris equivalent of AFS PAGs, though task IDs come pretty close. Only audit IDs are visible through cred_t/ucred, and then in severely constrained ways (requires PRIV_PROC_AUDIT, a non-basic privilege and requires that auditing have been enabled). And though task IDs could be made visible through cred_t/ucred, multiplexing multiple uses of a process grouping scheme onto a single group membership results in significant complexity in user-land[2]. Plus, all of these are 32-bit IDs, which means we'd need to take additional measures to avoid reuse. Moreover, every time we need a new process grouping scheme we tend to create ad-hoc interfaces when often the semantics of AFS PAGs, or slight variants thereof would suffice. Therefore we think a new process grouping facility is needed for Solaris that will allow us to group processes by login session, and will support sufficient a multiplicity of uses. We also seek to keep the new interfaces as simple as possible, such that users and developers rarely ever need use them. PROPOSAL -------- We propose a feature that is somewhat more generic (and therefore, slightly more complicated) than the AFS PAG concept, but also significantly simpler than the Linux kernel keyring concept. Where AFS PAGs do not provide for multiplicity of uses CPGs will. And where Linux keyrings provide a complex search order, CPG will not as we cannot find a current need for that feature (CPG search order can be added later or layered on top of the proposed CPG facility). Similarly, our approach will differ from Linux keyrings with respect to multiplicity (more on this below). Finally, CPGs are not just about cryptographic keys and cryptographic credentials; CPGs can be used for other purposes (and some possible future uses are indicated below), therefore the name of the facility is itself more generic: neither the words "keyring" nor "authentication" seem appropriate. Although there is a fairly large number of system calls, CPGs will mostly be transparent to user applications, with most explicit uses of CPGs buried in system software such as PAM modules, GSS-API mechanisms, pcred(1) and third party tools like AFS' pagsh(1). Application developers who are nonetheless interested in using CPGs explicitly will need to make use of two or three functions at most. CPG consists of: - A struct cred field to track CPG membership of any process with that cred_t in zero, one or more PAG-like groups, with kernel-land accessors and ucred_get(3C) user-land accessors. - New CPG types can be registered early at boot time in the global zone - Each CPG type can have varying semantics represented as a set of flags. - Several system calls to access and manage these CPG memberships. - DDI interfaces to associate arbitrary data, and a callback function for CPG emptiness reporting, with CPGs. - pcred(1) extensions for observability. - pam_unix_cred(5) changes to create CPGs (not CPG _types_) as needed, such that most users and applications need never use any of these interfaces in order to benefit from CPG. - Each CPG type will specify, as a semantics flag, whether pam_unix_cred(5) is to create a new one for regular logins, regular logins with "seats," and/or su(1)-type PAM services - It will be the responsibility of other PAM modules, such as pam_krb5(5), or PAM applications, such as gdm(1M) to associate resources with CPGs created by pam_unix_cred(5). By using PAM as the nexus for CPG management most users and applications will be able to avoid use of new interfaces. Existing interfaces, such as klist(1), will be able to find the resources they require associated with the caller's CPG if available. - One or more SMF services to register each CPG type and service doors for the kernel to upcall when the last reference to a CPG goes away. svc:/system/cpg/krb5:default ... COMPARISON TO AFS PAGs AND LINUX KERNEL KEYRINGS ------------------------------------------------ Solaris CPG interfaces are conceptually very similar to AFS PAG interfaces, but CPGs differ enough from PAGs that the interfaces must differ in detail. The primary differences between this facility and AFS' PAG facility are: - Solaris will allow the creation of more than one type of CPG, each with potentially different semantics represented as a set of semantics flags. - Solaris CPGs will have locally unique (since boot) 64-bit IDs (instead of 32-bit IDs, as in AFS). - CPGs will be addressable by CPG ID, but also by {PID, group type name} tuples. The latter makes it very easy to use CPGs in some contexts. The two ways of addressing CPG objects accounts for about half the number of CPG system call functions. - The Solaris equivalent of PAGs will allow users to associate small strings with each PAG (e.g., the name of a Kerberos V credentials store), much like Linux keyrings. We considered going so far as to allow many user data items to be associated with a PAG, much like environment variables. However, the point of the user data is to be an optimization when using IPC -- it appears in ucred, thus it needn't be looked up by PAG ID. And ucred is a fixed max size blob. Thus environment variables would have been difficult to fit in. Environment variables can always be layered on top of this facility. - Solaris will allow kernel-land callers to associate private data and cleanup callback functions with CPGs. Such data will not be visible in user-land, except via mdb -k. - Solaris will provide a facility for reporting CPG emptiness events so that cryptographic credentials can be deleted when the last process and open file reference to a CPG disappear. Along with this Solaris will provide a generic door upcall facility for kernel users of CPGs. - Because of all these differences the CPG APIs will look substantially different than AFS' PAG APIs (getpag(2)/setpag(2)). As for Solaris CPGs vs. Linux Kernel Keyrings: - We leave out any support for the Linux keyring notion of per-{thread, process, user} keyrings. CPGs are the rough equivalent only of per-session keyrings. A CPG equivalent of per-thread keyrings is precluded by the fact that Solaris does not allow threads to have cred_ts independent of the process' cred_t. Per-process and per-user keyrings can be emulated with CPGs. We looked at how the Linux secure NFSv4 client uses keyrings, and we found that it uses per-session keyrings exclusively. MIT krb5 does support the use of user, session, process and thread keyrings, but we don't know of actual uses of per-process and per-thread keyrings in that context -- "[t]he default is to put [ticket session keys] at the session keyring level." - We do include a facility for upcalling to get arbitrary data from user-land, but we use Solaris doors for this. (Linux kernel keyrings cause the kernel to create a new user process and exec a program that then processes the request). - We don't aim to for storing cryptographic credentials in the kernel. Kernel modules certainly may do so, of course, using the ability to associate arbitrary data with CPGs and the ability to upcall user-land services associated with each {CPG type, zone}. Also CPG user data can be flagged as private or secret on a per-CPG type basis. Note that the Linux secure NFSv4 client only stores Kerberos credential cache names in session keyrings, not cryptographic key material. The Solaris NFSv4 client (and CIFS client), likewise does not need to store cryptographic credentials in kernel-land. MIT krb5 does support storing the ticket keys in Linux keyrings, but stores the tickets themselves in a file. It's not clear what benefit is derived from doing this. If the goal were to not store keys in cleartext in files, even on tmpfs, then one could simply store a single master key in the keyring and then encrypt the individual ticket keys in the master key so as to store them in encrypted form. The MIT krb5 approach means that as a ccache grows in size so does the keyring in the kernel -- keyrings should have to be subject to resource controls! By avoiding the storage of arbitrarily large objects in a keyring, in kernel memory, we avoid the need to impose resource controls on CPGs. Roughly, user may have no more CPGs than processes, and CPGs are roughly fixed in size, and we already have resource controls for that. In contrast with Linux keyrings we aim merely to address the need for per-login session grouping of processes, associating resources with such groups by convention, and reference counting such resources. INTERFACES ---------- Systemcalls ----------- See: - man/cpg_change.txt. - usr/src/head/cpgroup.h - usr/src/uts/common/sys/cpgroup.h Kernel-land Interfaces ---------------------- - cred.h interfaces: - crgetcpg() - crsetcpg() See usr/src/uts/common/sys/cred.h - CPG interfaces: - cpg_hold(); - cpg_rele(); - cpg_getid(); - cpg_getreg(); - cpg_getowner(); - cpg_setowner(); - cpg_getudata(); - cpg_setudata(); - cpg_getkdata(); - cpg_setkdata(); - cpg_getdoor(); - cpg_set_hook(); See usr/src/uts/common/sys/cpgroup_kernel.h ucred_get(3C) Extensions ------------------------ uint64_t ucred_getcpgid(const ucred_t *ucred, const char *cpg_name); const char *ucred_getcpgudata(const ucred_t *ucred, const char *cpg_name); See usr/src/head/ucred.h. proc(4)/libproc/proc(1) extensions ---------------------------------- There's no need for a proc(4) or libproc interface since the system calls take an optional pid_t argument. proc(4)/libproc applications should, however keep a proc(4) file for the victim open so as to prevent PID re-use while examining the CPGs of a victim process. pcred(1) will be extended to have an option to show/set CPGs. GSS-API Extensions ------------------ We'll add two functions so that the application can give libgss the CPG information that can help GSS-API mechanisms find user credentials: - gss_acquire_cred_with_ucred() - gss_add_cred_with_ucred() These take a ucred_t, in addition to all the normal arguments for gss_acquire_cred() and gss_add_cred(). That ucred_t contains CPG information, accessible via ucred_get(3C) accessors (see above). gssd changes ------------ Assumption: gssd will run as the user triggering the upcall, likely as a per-user gssd daemon. - gssd will replace references to GSS_C_NO_CREDENTIAL with credential handles returned by gss_acquire_cred_with_ucred(). - gssd will call gss_acquire/add_cred_with_ucred() when servicing the corresponding RPCs. SMF Services for CPGs --------------------- CPG types will be registered in the global zone by an SMF service per-CPG type. These services will also be able to register a door (in the global zone and in non-global zones) that will receive upcalls indicating CPG emptiness events. These services should have FMRIs like: svc:/system/cpg/:default and may be transient (just CPG type registration) or not (if CPG emptiness reporting is needed for the given CPG type). For Solaris Kerberos there will be a service that registers a door and which will kdestroy user Kerberos credentials associated with a CPG when the last reference to that CPG disappears. PAM module/application extensions --------------------------------- The following PAM modules will be changed initially: - pam_unix_cred(5) The pam_sm_setcred(3PAM) entry point of pam_unix_cred(5), when called with the PAM_ESTABLISH_CRED flag, will call cpg_change(CPG_ALL, -1, CPG_F_LOGIN); But when called with the PAM_SERVICE "su" or if a module argument "sulike" is given then it will call: cpg_change(CPG_ALL, -1, CPG_F_SU); - pam_krb5(5) The pam_sm_setcred(3PAM) entry point of pam_krb5(5), when called with the PAM_ESTABLISH_CRED flag, will call: cpg_setuserdata("krb5creds", ); where is the name of a ccache containing the TGT obtained for the user. Additionally, pam_krb5(5) will no longer use pam_setenv(3PAM) to set the KRB5CCNAME environment variable. (Note that Kerberos applications and libraries will still honor KRB5CCNAME if set.) When refreshing/renewing/ destroying credentials pam_krb5(5) will find the relevant ccache by calling: cpg_getuserdata("krb5creds", sizeof (ccname), &ccname, &ccname_size); In the future PAM applications such as gdm(1M) and login(1) may be modified to associate devices with the login session process group created by pam_unix_cred(5) (see above). Initially these applications will be left unmodified. Other changes ------------- The Solaris Kerberos library (mech_krb5, a.k.a., libkrb5) will look for an find credentials caches in this order: 1) use KRB5CCNAME, if set; 2) use the caller's "krb5creds" CPG's ID and/or user data, if any; 3) use the old default (/tmp/krb5cc_). The format of krb5creds CPG user data will be a semi-colon-separated list of: c= k= r= p= If a ccache is not named it will default to /tmp/krb5cc_cpg_. If a keytab is not named it will default to the current default (/etc/krb5/krb5.keytab). If a replay cache is not named it will default to the current default. If a principal name is not given it will default to $USER@. POSSIBLE FUTURE PROJECTS ------------------------ - Use CPGs to track ssh-agents instead of environment variables. - Use CPGs to track XDISPLAY/XAUTHORITY. - Use CPGs to track other devices, real and virtual, associated with "seats," that are not already associated with X11 displays (e.g., audio devices). - Modify mech_dh/keylogin/pam_dhkeys to make use of CPGs, just like Solaris Kerberos. - Use CPGs to track audit ID, mask, termid. This would allow us to extend audit context items without making additional changes to cred_t and crget*() implementations. - Convert FGAP klp policy daemon door facility into a CPG. - Move project(4) and task IDs into CPGs. This would allow us to implement resource controls in contexts where there is only a cred_t reference available but no proc_t. REFERENCES ---------- [0] PAG references See README.PAGs. [1] Linux keyring references: See README.Linux-keyrings.