Pegasus Enhancement Proposal (PEP)

PEP #:  286

PEP Type:  Functional

Title:  Privilege Separation

Status:  Approved


Version History:

 

Version Date Author Change Description
1.0 2006-11-13 Michael Brasher First Draft
1.1 2006-12-16  Michael Brasher Second Draft
1.2 2006-12-19 Michael Brasher Incorporated changes suggested by Robert, John, and Keith.
1.3 2006-12-20 Michael Brasher Incorporated comments from PEP review (Robert, John, Keith, Karl, Mike).
1.4 2007-1-11 Michael Brasher / Karl Schopmeyer Update with outstanding comments from previous review and definition of session key function requested by HP
1.5 2007-1-17 Michael Brasher / Karl Schopmeyer Incorporate comments from 1.4.  Note that we are showing the color codes from the previous version (1.4) but have incorporated the changes that were color coded in 1.4 into normal text.
1.6 2007-1-23 Michael Brasher / Karl Schopmeyer Comments from Review of 1.5.  Approved version.
1.7 2007-5-11 Roger Kumpf Rework from discussion and code review by Roger Kumpf and Mike Brasher.  Specifically, these changes are made:
  • The use of session keys to ensure the CIM Server only requests operations on the behalf of authenticated users is deferred from the initial implementation
  • The dynamic policy to control which providers may be started as which users is deferred from the initial implementation
  • The executor no longer transfers ownership of the repository files to the server user.  This is the responsibility of installation and upgrade scripts.
  • cimmofl now runs in the context of the server user when privilege separation is enabled, so repository files are automatically created with the correct owner.

Approved version.

1.8 2007-8-13 Roger Kumpf Updated for Bugs 6689, 6704, and 6771:
  • The cimservermain user name is changed to "cimsrvr" and use of the PEGASUS_CIMSERVERMAIN_USER environment variable is removed.
  • The reapProviderAgent operation is removed from the Executor.  SIGCHLD signals are simply ignored in the Executor process.
  • Propose to recommend enabling Privilege Separation for debug and release builds on HP-UX and Linux.

Approved version.

 


Abstract: This PEP describes the inclusion of  privilege separation into the OpenPegasus server. Privilege separation aims to substantially reduce the amount of server code running with elevated privilege. This improves server security by limiting vulnerability to a family of client attacks, aimed at exploiting programming errors in order to perform unauthorized privileged operations. The best known example of this kind of attack is the buffer overrun attack [1]. The potential for this kind of attack can be limited by reducing the volume of code that executes with elevated privilege. 

A typical privilege separation implementation relocates all privileged code from a server into a separate executor process. Then the executor runs with elevated privilege whereas the server runs with reduced privilege. The executor carries out a limited set of privileged operations on behalf of the server. The executor defines a policy that restricts the allowable parameters to these privileged operations. Without a policy, programming errors in the server might be exploited to trick the executor into performing a privileged operation on an unauthorized object (e.g., the server might ask the executor to execute a rogue program). The executor, server, and policy are depicted in the diagram below.

	[executor] <--(requests)-- [server]
^
|
[policy]

Definition of the Problem

In the current Pegasus implementation, all Pegasus server code executes with elevated privilege, which makes the server vulnerable to security attacks aimed at exploiting programming errors in order to execute unauthorized privileged operations. The costs of identifying and fixing these flaws are prohibitive since all server source code must be audited.

Proposed Solution

The Pegasus server will be refactored, relocating all code requiring root to a separate executor process. The executor process will execute with elevated privilege (root) and will be solely responsible for executing privileged operations on behalf of the server process. The server process will be modified to run with reduced privilege (a non-root user). The executor and the server will correspond to separate Unix programs. The executor program will assume the name cimserver and will have the same apparent behavior as the present cimserver program. The server program will be renamed to cimservermain. The cimserver will create a child cimservermain process. The two processes will communicate over an anonymous Unix domain socket. The executor will be an iterative server that processes requests from the server. The server will periodically ask the executor to carry out privileged operations. The executor will check the operation parameters against a policy definition. The executor, server, and policy are depicted in the diagram below.
	+-------------------+               +----------------------+                  +-------------------------+
| policy definition |---- reads --->| executor (cimserver) |<---- socket -----| server (cimservermain) |
+-------------------+ +----------------------+ +-------------------------+
owner=root user=root user=non-root
The policy definition restricts the parameters permitted for each of the operations.

Note that while the diagram above shows the policy definition in a file, in this implementation it will be statically compiled into the executor program.

Executor Process

The executor will go through the following steps during its lifetime.
  1. Initialize the static policy definition in memory.
  2. Create a Unix domain socket pair (used to communicate with the server).
  3. Fork server process (tying standard output/error together).
  4. Read client request from socket.

Executor Operations

The executor supports the following operations:

Operation Description
startProviderAgent Run the out-of-process provider agent (cimprovagt) as the given user with the given mode.  Return a pipe descriptor for communicating with the cimprovagt.
authenticatePassword Validate the password for a given user with the "underlying authentication facilities".  This function will use one of three underlying authentication facilities:
  • Basic PAM authentication (using functions internal to the process).
  • Basic PAM authentication (using external cimservera program).
  • Password file authentication (against the Pegasus cimserver.passwd file).
validateUser Verify that a username is valid per the underlying authentication facility (the same facilities specified in the authenticatePassword operation).
challengeLocal Initiate a local authentication sequence. Creates a file only readable by the authenticating user and places a randomly generated key in that file. Produces a challenge (containing the path of the file). This challenge is sent back to the authenticating client, which must produce a response.
authenticateLocal Finalizes the local authentication sequence initiated by challengeLocal by passing in the response to the challenge.
daemonizeExecutor Tell the executor to run as a daemon (in the "background"), that is sever ties with the terminal device, stdio, stderr, and stdout, and fork once more.
openFile Opens a file owned by root for read access. Returns a file descriptor for accessing this file. Note that this was originally added so that the control providers could read system information files (containing the information they provider) but it may turn out that it is not needed since all files examined so far are readable by all users. This operation is subject to policy constraints.
renameFile Renames the given file. This operation is subject to policy constraints.
removeFile Removes the given file. This operation is subject to policy constraints.
ping Checks to see if the executor is "alive" (one of the first operations performed by the server process).
 

Details about the input and output parameters for each of these operations can be found in the pegasus/src/Executor/Messages.h file

The executor will be written from scratch, since using any of the existing Pegasus libraries would unnecessarily widen the scope of source-level security audits. In other words, the executor implementation will be self contained and will not include any existing Pegasus headers nor will it link any existing Pegasus libraries. Further, the entire executor implementation will be contained in a single source code directory.

Executor Policy Definitions

Policies will define restrictions on the parameters of the supported operations.  In general a policy consists of the operation name followed by parameter restrictions.  For example, these entries allow the Pegasus configuration files to be opened for writing:

	OpenFile("/pegasus_home/cimserver_current.conf", "w")
OpenFile("/pegasus_home/cimserver_planned.conf", "w")

Similarly, this policy allows any file in the /tmp directory to be removed:

	RemoveFile("/tmp/*")

If a policy statement cannot be located for a given request, then the request is rejected.

Policy definitions are statically compiled into the executor and cannot be configured at runtime.

Executor Logging

The executor will log the following events.
  1. Authentication failures.
  2. Authorization failures.  The only type of authorization failure that can occur in the executor is the failure to exec cimprovagt as a given user (if the user has insufficient privilege to run it). This failure will be logged as "user X failed to exec Y".
  3. Policy breaches (e.g., an attempt to exec an unknown program or to open an unknown file).

Server Process

The server program (cimservermain) will behave like the current cimserver program with the following modifications if privilege separation is enabled:
  1. Obtain the socket inherited from the executor (needed to communicate with the executor).
  2. Tell the executor to daemonize (if and when the server daemonizes).
  3. Send requests to the server over the socket using the Executor class interface.
  4. Cache client credentials long enough so that they may be passed to the run-provider-agent command (for "run as requestor" mode).

If privilege separation is not enabled, the server will operate without using the calls to the Executor to perform root privilege functions.  The external behavior is unchanged in this case.

The Server's Executor Class

An Executor class in the CIM Server implements all the CIM Server operations which require privilege.  Its interface corresponds exactly to the operations defined in the table above.  This class is the central control point at which the privilege separation behavior is differentiated.  When privilege separation is enabled, the Executor class forwards the operations to the executor process to be performed. When privilege separation is disabled, the Executor class contains direct implementations of the operations.

File Ownership and Access

This section identifies ownership and access rights for key Pegasus files.  There are two users of interest: the root user and the server user.  Files owned by the root user are essentially unchanged by the privilege separation feature, so special handling is needed in installation scripts.  Files now owned by the server user will need to be considered by packaging and upgrade scripts.

The following table defines file ownership and server user access privileges.  The executor may override the access privileges (based on policy definitions) by opening the file on behalf of the server and passing back a descriptor.

File Owner Writable by server user? Readable by server user? Executor actions
Configuration files (cimserver_current.conf and cimserver_planned.conf)

root

no yes The executor opens these files for write on behalf of the server.
CIM Server trace file (cimserver.trc) server user (Note that individual trace files are used for each cimprovagt, and their owners and permissions are unchanged by this feature) yes yes None
PID file (cimserver_start.conf) server user yes yes None
Start-up lock file (cimserver_start.lock) server user yes yes None
Message catalogues

root

no yes None
The CIM repository

server user (cimmofl automatically creates the repository files with the owner set to the server user with privilege separation enabled; installation and upgrade scripts must ensure the correct ownership of these files.)

yes yes None
Local domain socket node file server user yes yes None
Programs root no yes None
Pegasus and provider libraries

root

no yes None
SSL public certificates

root

no yes None
SSL private key root no no Executor opens the file for read on behalf of the server.
SSL trust store and CRL store root no yes Executor opens these files for write on behalf of the server
Local authentication directory root no yes Executor creates local authentication files to be read by clients.  The CIM Server does not use this directory.
Pegasus password file (if used) root no no Executor opens the file for write on behalf of the server.
 

Affected Source Files

A C implementation of the executor program will be introduced in the pegasus/src/Executor directory.

In the CIM Server itself, the following source files will contain the bulk of the changes. Minor changes are expected in a few others.

New Runtime Files

This effort introduces the following new files into the runtime environment:

Supported Platforms

The following platforms will be supported:
NOTE: Privilege separation will be targeted at POSIX platforms and will be easy to support on other POSIX platforms. However, the mechanisms used to implement privilege separation are so specific to POSIX, that supporting it on Windows would require additional effort. For example, an executor would need to be developed for Windows; whereas, the server will require minimal changes to support Windows.

Build Environment Variables

These environment variable definitions will be proposed for PEP 292 (Recommended OpenPegasus 2.7.0 Build and Configuration Options for Selected Platforms):

PEGASUS_ENABLE_PRIVILEGE_SEPARATION

Building with Privilege Separation

To build and run the tests with privilege separation enabled:

The behavior of Pegasus is unchanged when privilege separation is not built. That is:

  1. No executor program is built.
  2. The cimserver program is built as before and there is no cimservermain program.
  3. All privileged operations are performed by the Executor class in the cimserver, rather than by the executor process.

Design Considerations

These questions were considered during the design process.  The resolutions are given after each question.

  1. What is the best way for the executor to obtain the user-id and group-id that the server will run as?  The user context for the non-privileged component of the server is defined as "cimsrvr" in the build system (in config.mak).
  2. How will the executor program (cimserver) locate the server program (cimservermain)? The executor uses the constant for cimprovagt in Constants.h and then removes "cimprovagt" and appends "cimservermain".
  3. Should the server (executor and server) stop if an attack is in progress (standard practice)? And if so, how do we keep third-party watchdog processes from restarting it? No but it is logged and is easy to configure.
  4. How will legacy in-process providers that formerly required root privilege continue to work? They must be modified to use the executor or moved out-of-process. We cannot depend on providers being modified to use the executor. Most of  providers should work out-of-process automatically. The exceptions are cases where the provider was relying on a no-no, such as sharing objects in memory with another provider. In these cases, the provider must be updated to work out-of-process to work with a server using privilege separation.
  5. Does this proposal introduce a potential for collisions between a running cimserver/cimservermain and the starting of another cimserver? The current cimserver(main) program protects against these collisions, but if the executor does anything interesting before starting cimservermain, there could be a problem.  There is a function called TestCimServerProcess() in the Executor that checks to see if cimservermain is already running to avoid collisions.

Requirements

The following is the set of  function and design requirements we have defined which the executor/server components of this function must meet:
  1. The executor must implement all privileged operations performed by the Pegasus server today (see table above).
  2. The apparent external behavior of the Pegasus server must not change.
  3. No Pegasus public interfaces may be change (programming interfaces and command-line interfaces).
  4. Providers may run out-of-process as any user (including root) as was the case before this change.
  5. Without privilege separation enabled, the Pegasus server must behave as before (including the ability to run as an ordinary user).
  6. The executor process must log security diagnostics and alerts to syslog (configurable).
  7. The server user must be configurable in the build environment.
  8. The executor program must not include any Pegasus headers or link to any Pegasus libraries.
  9. The executor program must have fewer than 5000 lines of code.
  10. The executor must shutdown properly when the Pegasus server shuts down.
  11. The Pegasus server must pass all the end-to-end tests when configured to use executor.
  12. Pegasus shall continue to compile and work on all platforms (although only HP-UX and Linux will initially support privilege separation mode).
  13. Flawfinder will be run against the executor regularly to identify and fix potential problems.
  14. The executor will avoid the use of strcpy(), strncpy(), strcat(), and strncat(), and use strlcpy() and strlcat() in their place.
  15. The following authentication will be moved into the executor:
    1. Local Authentication
    2. PAM in process authentication
    3. PAM out-of-process authentication.
    4. Password file authentication.

Non-requirements

  1. Privilege separation is not required on Windows (although Windows will continue to work without privilege separation as before).
  2. Out-of-process providers are not required to perform privileged operations via the executor (as before, an out-of-process provider can be registered to run as root at the discretion of the provider writer).
  3. In-process providers will not be restricted from performing privileged operations via the executor. Security is best obtained using out-of-process providers in conjunction with the executor.
  4. Providers with Privileged UserContext are not required to run in-process.  Providers with Requestor and Designated UserContext will remain out-of-process.
  5. In-process providers will still be able to interfere with the correct operation of the executor and server.
  6. While all authentication should be in a separate process/trust-domain (to reduce risk) than the cimserver (unprivileged where possible), this PEP doesn't move all of them due to time/resource constraints.  Specifically the following authentication will not be moved into the executor:
    1. SSL Client Certificate authentication (Others are encouraged to contribute moving certificate authentication to a separate (security-reviewed, small) process)
    2. Kerberos authentication (since the developers do not have access to a Kerberos implementation)

Testing

Pegasus currently has exhaustive end-to-end test including many tests "privileged tests". Minimally, the resulting system must pass all end-to-end tests and all privileged tests as it does today. Additional tests will be written as needed to verify the implementation and to assure that the current code coverage percentages are not reduced with the introduction of this code.

Deferred Items

The following items were previously scoped in the privilege separation implementation but have been deferred.

Session Authorization

To reduce the possibility that any single compromise of the Pegasus server code that is run as non-root does not compromise the decision processes concerning elevation of privilege for provider access, the communication channel between server and executor must not allow for privilege elevation of the server unless the server provides valid privileged credentials.  To meet this requirement, these changes are needed:
  1. Each operation connection will be allocated a session key by the executor as part of authentication. This session key will be carried as part of all operations that are received by the server during the client connection. This key can be used by the executor to validate that the user identification for each operation is the same identification as the original authentication. Specifically, it will be tested as part of the executor function of starting a provider agent.
  2. Dynamic policies will be used to constrain which providers may be started in which user contexts.
  3. The executor must implement these additional functions and operations to manage session keys:
Operation Description
startProviderAgent This existing operation has additional requirements:  If the provider UserContext is Requestor, then authentication credentials of the requestor must be passed so that they may be authenticated.  Otherwise, the provider UserContext setting is compared with the authentication credentials to validate that the provider may be started in the requested context.
authenticatePassword This existing operation must be extended to generate a new session key when authentication is successful.
challengeLocal This existing operation must be extended to generate a new session key marked as "unauthenticated".
authenticateLocal This existing operation must be extended to validate the session key when authentication is successful.
newSessionKey (new) Generates a new session key. This operation is needed by the SSL peer authentication scheme, which will continue to reside entirely in the server process for now.  It is also needed to handle the cases of indication provider start-up during initialization and CIMOMHandle calls by in-process providers, since these are not performed on the behalf of an authenticated client.
deleteSessionKey (new) Deletes a session key. When connections are closed, session keys are deleted.

Dynamic Policies

Static policies are compiled into the executor and cannot change.  Dynamic policies can be specified at runtime.  Dynamic policies are stored in a file (cimserver_policy.conf) owned by root and writable only by root.  The policies used by startProviderAgent to define provider access are the only policies that are dynamic.  These dynamic policies are intended to encapsulate the provider user context definitions in the provider registration, so that the CIM Server cannot request a provider to be started in a user context that is not intended.

It may seem that provider user context configuration could be retrieved directly from the provider registration information in the repository instead of maintaining a separate but parallel policy file.  However, the executor cannot trust the repository since it can be written by the server user.  Also, it is technically infeasible for the executor to access the repository because that program is prohibited from linking with Pegasus libraries (for code auditing purposes).

Therefore, a mechanism is needed to keep the dynamic policy definition consistent with the provider registration information.  Some options are:

To implement the third option, these changes are required to the MOF compiler (cimmof):
  1. Before cimmof creates a PG_ProviderModule instance, it checks to see if the MOF file resides in or below a "trusted directory". If not, the registration attempt is rejected. The list of trusted directories is defined by two sources:
    • A static compile-time array of trusted directories (see pegasus/src/Pegasus/Compiler/Policy.cpp). This array includes /usr, /opt/ and /etc. Vendors may wish to modify this list for their platforms.
    • The PEGASUS_TRUSTED_DIRS environment variable (defined by the user who runs cimmof). The format of the value is identical to the format used in the PATH environment variable (i.e., a colon-separated list of directories).
  2. After cimmof creates a PG_ProviderModule instance (via the client), it adds a corresponding line to the cimserver_policy.conf file.  cimmof must run as root in order to write to the cimserver_policy.conf file (which is already required by the CIM Server when registering a provider module).

References

[1]  INSIDE THE BUFFER OVERFLOW ATTACK: MECHANISM, METHOD, & PREVENTION, Mark E. Donaldson, April 3, 2002, GSEC Version 1.3
       http://www.sans.org/reading_room/whitepapers/securecode/386.php

[2]  Preventing Privilege Escalation, Niels Provos, Markus Friedl and Peter Honeyman, 12th USENIX Security Symposium, Washington, DC, August 2003.
       http://niels.xtdnet.nl/papers/privsep.pdf

 


Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
EMC Corporation; Symantec Corporation; The Open Group.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.