Copyright © 2005,2006,2007,2008 Red Hat, Inc.
The oddjob package provides a means for unprivileged applications to invoke and take limited control of privileged applications by issuing requests using the D-Bus system message bus.
Table of Contents
In many applications, particularly those which perform some sort of administrative task, it becomes useful to separate the presentation (which needn't have any special privileges, and frequently shouldn't) from the logic which actually performs the task (which frequently needs privileges).
In these situations, the unprivileged application typically accomplishes the privileged task either by running a setuid helper application or by connecting to a long-running process and issuing a request.
Of the two options, the setuid helper approach offers the advantage of requiring less configuration beforehand, but can be undesirable in the context of libraries or demand-loaded modules, when the direct caller may be unaware of the state of the calling application with regard to signal handling, which must be taken into account if the caller expects to wait for the privileged child process to complete its task. Additionally, certain attributes of the unprivileged process which are inherited by the privileged helper (current directory, other open file handles, environment variables) may be manipulated to become vectors for attack by malicious users.
The alternative, the use of a long-running process which services these requests, becomes attractive for these reasons. The primary objections to this approach are frequently:
that it adds yet another long-running resource-consuming process to the system
that the long-running process must be installed properly to ensure that it is started and stopped at system boot and shutdown
an entire protocol for encoding communications between the client and server must be invented
A third option has recently become available: the D-Bus system message bus. The system message bus provides an interprocess communication mechanism to processes on the system. The bus carries three types of messages:
events, which are broadcast and for which no response is expected
method calls, which are unicast and for which a response is usually expected
method responses, which are unicast and which are sent in response to a method call
Method call and response messages which are sent over the system message bus provide a loosely coupled object-oriented RPC mechanism.
The D-Bus libraries also provide a means of encoding, transmitting, listening for, receiving, and parsing messages which are sent over the bus. Using D-Bus as the mechanism for a long-running process reduces the time required to implement both a long-running server and its clients. Because the implementation of the bus protocols is designed to be reused, it also offers the opportunity to have one privileged process service a potentially large number of clients.
The oddjob package implements such a server.
The server provided by oddjob, oddjobd, provides services which appear to be indistinguishable from other services provided through D-Bus, but in an unconventional manner. Before continuing, it is instructive to look at how clients and servers interact over the bus.
The fundamental unit of communication over the system bus is the message. For practical purposes, a message is composed of the addressing information which is needed to route that message to the proper recipient, and one of:
information about the event being broadcast (the sender, additional information)
the method being called (the caller, the provider, method arguments)
the method being responded to (the caller, the provider, results)
More specifically, each message includes the address of its sender. Method call and response messages should also include the address of the intended recipient. A bus address uniquely identifies a process which is connected to the message bus. Generally, a single process will have exactly one open connection to the message bus.
A message also includes an object path, which allows multiple client sessions to be serviced by the same server. Each object can provide multiple interfaces, which are groups of related methods.
The function of the oddjobd daemon is to allow arbitrary services to be provided over the message bus with a minimum of "glue". Services provided by oddjobd can be implemented very simply. This simplicity of implementation is gained at the cost of some robustness, but for certain applications this is an acceptable tradeoff.
An analogous comparison can be made between applications which are implemented on web servers using scripting engines and applications which are implemented using the Common Gateway Interface (CGI).
Take for example, a service which is provided by the well-known
address "com.example.system_manager". This management service
controls multiple systems, each of which is represented as a different
object with a name of the form
object offers multiple interfaces, such as the
"com.example.power" interface, which
provides methods such as "
Conventionally, the implementation of the "com.example.system_manager" service would require a long-running server process which would need to include logic for connecting to the system message bus, receiving requests, and issuing replies, in addition to its core functionality.
Using oddjobd, the entire implementation can be synthesized by providing the proper configuration files and shell scripts.
The D-Bus system
bus enforces restrictions on which applications can reserve
well-known bus addresses. It bases this restriction on the UID
under which a client process is executing when it requests the
reservation. Additionally, the system message bus can impose
access controls on the ability to send or receive messages to
or from a particular service/object/interface/method set using
the bus. This example allows processes running as
"root" to reserve the name
"com.example.system_manager", and allows any user to
The message bus daemon itself has no specific use for object paths and interface and method names beyond allowing their use in access control statements. It is your application which gives the names their meanings.
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <!-- Only root can own the system manager service. --> <policy user="root"> <allow own="com.example.system_manager"> </policy> <!-- Allow anyone to call the reboot and poweroff methods. This is probably a bad idea. --> <policy context="default"> <allow send_destination="com.example.system_manager" send_path="/com/example/Systems/server1" send_interface="com.example.power" send_member="reboot"/> <allow send_destination="com.example.system_manager" send_path="/com/example/Systems/server1" send_interface="com.example.power" send_member="poweroff"/> </policy> </busconfig>
The oddjobd daemon provides services which
are implemented by external helper applications. The helpers
are executed with superuser privileges, and receive their
arguments as strings, as the configuration dictates, either as
command-line parameters or via stdin. The exit status of the
helpers, along with any text they print, is returned to the
caller. This example configures oddjobd to
provide a "com.example.system_manager" service which
provides a "/com/example/Systems/server1" object
which implements the
which provides methods named
poweroff", and allows the
superuser to invoke either method.
<?xml version="1.0"?> <oddjobconfig> <service name="com.example.system_manager"> <object name="/com/example/Systems/server1"> <interface name="com.example.power"> <method name="reboot"> <helper exec="/sbin/reboot" arguments="0" prepend_user_name="no"/> <!-- Only root and jimbo can use this --> <allow user="root"/> <allow user="jimbo"/> </method> <method name="poweroff"> <helper exec="/sbin/poweroff" arguments="0" prepend_user_name="no"/> <!-- Only root and jimbo can use this --> <allow user="root"/> <allow user="jimbo"/> </method> </interface> </object> </service> </oddjobconfig>
The oddjobd configuration is normally read from
/etc/oddjobd.conf, but in most deployments it
will direct the daemon to read all of the configuration files from
/etc/oddjobd.conf.d, taking the union of the
contents of all of the configuration files as its configuration.
When installing your application, dropping a new configuration file into this directory is recommended over editing the existing configuration files.
Each configuration file includes an <oddjobconfig> element. An <oddjobconfig> element contains <include> elements, <service> elements, <allow> elements, and <deny> elements.
An <include> element names a file which should also be scanned for configuration data, and optionally defines an attribute named "ignore_missing", which if set to "yes" will cause failure to read the named file to be treated as a non-fatal error.
A <service> element names a D-Bus service address at which services defined within the scope of this element will be provided by oddjobd. The service address is given as the value of its "name" attribute, and it may contain <object> elements, <allow> elements, and <deny> elements.
The service name should be unique to your application. The name "com.redhat.oddjob" is reserved.
An <object> element names a D-Bus object path which provides one or more interfaces to client processes. The object path is given as the value of its "name" attribute, and it may contain <interface> elements. Its name may include wildcards, in which case any request to an object whose name matches the wildcard will be considered to match this element.
If a single client request matches multiple different <object> elements, the behavior of oddjobd becomes undefined.
The <object> element may also contain <allow> and <deny> elements.
An <interface> element names a group of related methods which oddjobd will provide at the containing <object>'s path. The interface name is given as the value of its "name" attribute. It may contain <method>, <allow>, and <deny> elements.
An <method> element names a specific method which oddjobd will provide as part of the containing <interface> at the containing <object>'s path. The method name is given as the value of its "name" attribute, and it may contain <helper>, <allow>, and <deny> elements.
An <helper> element specifies that a method is implemented as an external helper (oddjobd itself implements a limited number of methods internally). It includes a number of attributes:
"exec", the path of the binary to run
"arguments", the number of arguments which a client is expected to supply to the method
"prepend_user_name", whether or not the calling user's name should be prepended to the list of arguments before they are passed to the helper
"argument_passing_method", either "stdin" (the default) or "cmdline", specifying that method arguments should be passed in on stdin, one parameter per line, or as command-line arguments
An <allow> or <deny> element specifies an entry in an access control list (ACL) which controls whether or not oddjobd will attempt to fulfill client requests.
Each entry defines a set of values for attributes of a client request. If a specific request provides these values, the access control list entry is considered to match it. Attributes which are not specified as part of an access control entry are ignored when checking if a request matches that entry. The defined attributes are:
"user", the client's user name
"min_uid", a UID which the user's UID must be greater than or equal to
"max_uid", a UID which the user's UID must be less than or equal to
"selinux_enforcing", "yes" or "no", corresponding to whether SELinux is enabled and in enforcing mode
"selinux_context", the SELinux context of the client process
"selinux_user", the SELinux user of the client process
"selinux_role", the SELinux role of the client process
"selinux_type", the SELinux type of the client process
The process of checking the ACL for any method is simple:
First, if the ACL contains a <deny> element which matches the request, access is denied.
Second, if the ACL contains an <allow> element which matches the request, access is allowed.
If no entry in the ACL matches the request, the check continues on to the containing <interface> element's ACL. If no match is found, the containing <object> element's ACL is consulted, then that of the containing <service> element. Lastly, the ACL of the top-level <oddjobconfig> element is scanned. If, after all of these checks, no match was found, access is denied.
The helpers themselves are executed with superuser privileges and with these variables set in the environment:
"ODDJOB_CALLING_USER", the name of the calling user
"ODDJOB_SERVICE_NAME", the name of the called service
"ODDJOB_OBJECT_PATH", the name of the called object
"ODDJOB_INTERFACE_NAME", the name of the called interface
"ODDJOB_METHOD_NAME", the name of the called method
Helpers must treat arguments as untrusted data to avoid escalating a compromise of an account which would be allowed to call the method to a compromise of the superuser account. The oddjobd server performs no other authentication.
If an error occurs when oddjobd attempts to start the helper, or if the helper exits abnormally due to a signal, the caller receives an error message.
If the helper runs to completion, oddjobd reports the helper's exit status, along with any data which the helper output to its standard output and error descriptors.
The oddjobd server implements a small set of methods internally as part of the com.redhat.oddjob interface of the /com/redhat/oddjob object provided by the com.redhat.oddjob service. These include:
list: List the methods which the calling user would be allowed to call.
listall: List all configured methods.
reload: Reload the configuration without restarting.
Additionally, the oddjobd server implements the D-Bus introspection interface for all services which it provides to clients. By default, attempts to call these interfaces are denied unless ACLs have been put in place to allow it.
The current implementation of oddjobd imposes some limitations on clients.
A client request can contain no more than 65535 arguments.
No more than 65535 services may be defined.
A service can provide no more than 65535 objects.
An object can provide no more than 65535 interfaces.
An interface can provide no more than 65535 methods.